1 /*
2 * Copyright 2009-2017 Citrix Ltd and other contributors
3 *
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU Lesser General Public License as published
6 * by the Free Software Foundation; version 2.1 only. with the special
7 * exception on linking described in file LICENSE.
8 *
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU Lesser General Public License for more details.
13 */
14
15 #include <stdio.h>
16 #include <stdlib.h>
17 #include <string.h>
18 #include <unistd.h>
19 #include <time.h>
20 #include <sys/types.h>
21 #include <sys/stat.h>
22 #include <fcntl.h>
23 #include <ctype.h>
24 #include <inttypes.h>
25 #include <regex.h>
26 #include <limits.h>
27
28 #include <libxl.h>
29 #include <libxl_utils.h>
30 #include <libxlutil.h>
31 #include "xl.h"
32 #include "xl_parse.h"
33
34 #include "xl_utils.h"
35
36 xentoollog_logger_stdiostream *logger;
37 int dryrun_only;
38 int force_execution;
39 int autoballoon = -1;
40 char *blkdev_start;
41 int run_hotplug_scripts = 1;
42 char *lockfile;
43 char *default_vifscript = NULL;
44 char *default_bridge = NULL;
45 char *default_gatewaydev = NULL;
46 char *default_vifbackend = NULL;
47 char *default_remus_netbufscript = NULL;
48 char *default_colo_proxy_script = NULL;
49 libxl_bitmap global_vm_affinity_mask;
50 libxl_bitmap global_hvm_affinity_mask;
51 libxl_bitmap global_pv_affinity_mask;
52 enum output_format default_output_format = OUTPUT_FORMAT_JSON;
53 int claim_mode = 1;
54 bool progress_use_cr = 0;
55 bool timestamps = 0;
56 int max_grant_frames = -1;
57 int max_maptrack_frames = -1;
58 int max_grant_version = LIBXL_MAX_GRANT_DEFAULT;
59 libxl_domid domid_policy = INVALID_DOMID;
60 libxl_defbool bootloader_restrict;
61
62 xentoollog_level minmsglevel = minmsglevel_default;
63
64 int logfile = 2;
65
66 /* every libxl action in xl uses this same libxl context */
67 libxl_ctx *ctx;
68
69 xlchild children[child_max];
70
71 const char *common_domname;
72
73 /* Get autoballoon option based on presence of dom0_mem Xen command
74 line option. */
auto_autoballoon(void)75 static int auto_autoballoon(void)
76 {
77 const libxl_version_info *info;
78 regex_t regex;
79 int ret;
80
81 info = libxl_get_version_info(ctx);
82 if (!info || !info->commandline)
83 return 1; /* default to on */
84
85 #define SIZE_PATTERN "-?[0-9]+[bBkKmMgGtT]?"
86
87 ret = regcomp(®ex,
88 "(^| )dom0_mem=((|min:|max:)(" SIZE_PATTERN "|(" SIZE_PATTERN "\\+)?[0-9]{1,2}%),?)+($| )",
89 REG_NOSUB | REG_EXTENDED);
90
91 #undef SIZE_PATTERN
92
93 if (ret)
94 return 1;
95
96 ret = regexec(®ex, info->commandline, 0, NULL, 0);
97 regfree(®ex);
98
99 return ret == REG_NOMATCH;
100 }
101
parse_global_config(const char * configfile,const char * configfile_data,int configfile_len)102 static void parse_global_config(const char *configfile,
103 const char *configfile_data,
104 int configfile_len)
105 {
106 long l;
107 XLU_Config *config;
108 int e;
109 const char *buf;
110
111 config = xlu_cfg_init(stderr, configfile);
112 if (!config) {
113 fprintf(stderr, "Failed to allocate for configuration\n");
114 exit(1);
115 }
116
117 e = xlu_cfg_readdata(config, configfile_data, configfile_len);
118 if (e) {
119 fprintf(stderr, "Failed to parse config file: %s\n", strerror(e));
120 exit(1);
121 }
122
123 if (!xlu_cfg_get_string(config, "autoballoon", &buf, 0)) {
124 if (!strcmp(buf, "on") || !strcmp(buf, "1"))
125 autoballoon = 1;
126 else if (!strcmp(buf, "off") || !strcmp(buf, "0"))
127 autoballoon = 0;
128 else if (!strcmp(buf, "auto"))
129 autoballoon = -1;
130 else
131 fprintf(stderr, "invalid autoballoon option");
132 }
133 if (autoballoon == -1)
134 autoballoon = auto_autoballoon();
135
136 if (!xlu_cfg_get_long (config, "run_hotplug_scripts", &l, 0))
137 run_hotplug_scripts = l;
138
139 if (!xlu_cfg_get_string (config, "lockfile", &buf, 0))
140 lockfile = strdup(buf);
141 else {
142 lockfile = strdup(XL_LOCK_FILE);
143 }
144
145 if (!lockfile) {
146 fprintf(stderr, "failed to allocate lockfile\n");
147 exit(1);
148 }
149
150 /*
151 * For global options that are related to a specific type of device
152 * we use the following nomenclature:
153 *
154 * <device type>.default.<option name>
155 *
156 * This allows us to keep the default options classified for the
157 * different device kinds.
158 */
159
160 if (!xlu_cfg_get_string (config, "vifscript", &buf, 0)) {
161 fprintf(stderr, "the global config option vifscript is deprecated, "
162 "please switch to vif.default.script\n");
163 free(default_vifscript);
164 default_vifscript = strdup(buf);
165 }
166
167 if (!xlu_cfg_get_string (config, "vif.default.script", &buf, 0)) {
168 free(default_vifscript);
169 default_vifscript = strdup(buf);
170 }
171
172 if (!xlu_cfg_get_string (config, "defaultbridge", &buf, 0)) {
173 fprintf(stderr, "the global config option defaultbridge is deprecated, "
174 "please switch to vif.default.bridge\n");
175 free(default_bridge);
176 default_bridge = strdup(buf);
177 }
178
179 if (!xlu_cfg_get_string (config, "vif.default.bridge", &buf, 0)) {
180 free(default_bridge);
181 default_bridge = strdup(buf);
182 }
183
184 if (!xlu_cfg_get_string (config, "vif.default.gatewaydev", &buf, 0))
185 default_gatewaydev = strdup(buf);
186
187 if (!xlu_cfg_get_string (config, "vif.default.backend", &buf, 0))
188 default_vifbackend = strdup(buf);
189
190 if (!xlu_cfg_get_string (config, "output_format", &buf, 0)) {
191 if (!strcmp(buf, "json"))
192 default_output_format = OUTPUT_FORMAT_JSON;
193 else if (!strcmp(buf, "sxp"))
194 default_output_format = OUTPUT_FORMAT_SXP;
195 else {
196 fprintf(stderr, "invalid default output format \"%s\"\n", buf);
197 }
198 }
199 if (!xlu_cfg_get_string (config, "blkdev_start", &buf, 0))
200 blkdev_start = strdup(buf);
201
202 if (!xlu_cfg_get_long (config, "claim_mode", &l, 0))
203 claim_mode = l;
204
205 xlu_cfg_replace_string (config, "remus.default.netbufscript",
206 &default_remus_netbufscript, 0);
207 xlu_cfg_replace_string (config, "colo.default.proxyscript",
208 &default_colo_proxy_script, 0);
209
210 e = xlu_cfg_get_bounded_long (config, "max_grant_frames", 0, INT_MAX,
211 &l, 1);
212 if (!e)
213 max_grant_frames = l;
214 else if (e != ESRCH)
215 exit(1);
216
217 e = xlu_cfg_get_bounded_long (config, "max_maptrack_frames", 0,
218 INT_MAX, &l, 1);
219 if (!e)
220 max_maptrack_frames = l;
221 else if (e != ESRCH)
222 exit(1);
223
224 e = xlu_cfg_get_bounded_long (config, "max_grant_version", 0,
225 INT_MAX, &l, 1);
226 if (!e)
227 max_grant_version = l;
228 else if (e != ESRCH)
229 exit(1);
230
231 libxl_cpu_bitmap_alloc(ctx, &global_vm_affinity_mask, 0);
232 libxl_cpu_bitmap_alloc(ctx, &global_hvm_affinity_mask, 0);
233 libxl_cpu_bitmap_alloc(ctx, &global_pv_affinity_mask, 0);
234
235 if (!xlu_cfg_get_string (config, "vm.cpumask", &buf, 0))
236 parse_cpurange(buf, &global_vm_affinity_mask);
237 else
238 libxl_bitmap_set_any(&global_vm_affinity_mask);
239 if (!xlu_cfg_get_string (config, "vm.hvm.cpumask", &buf, 0))
240 parse_cpurange(buf, &global_hvm_affinity_mask);
241 else
242 libxl_bitmap_set_any(&global_hvm_affinity_mask);
243 if (!xlu_cfg_get_string (config, "vm.pv.cpumask", &buf, 0))
244 parse_cpurange(buf, &global_pv_affinity_mask);
245 else
246 libxl_bitmap_set_any(&global_pv_affinity_mask);
247
248 if (!xlu_cfg_get_string (config, "domid_policy", &buf, 0)) {
249 if (!strcmp(buf, "xen"))
250 domid_policy = INVALID_DOMID;
251 else if (!strcmp(buf, "random"))
252 domid_policy = RANDOM_DOMID;
253 else
254 fprintf(stderr, "invalid domid_policy option");
255 }
256
257 xlu_cfg_get_defbool(config, "bootloader_restrict",
258 &bootloader_restrict, 0);
259
260 xlu_cfg_destroy(config);
261 }
262
postfork(void)263 void postfork(void)
264 {
265 libxl_postfork_child_noexec(ctx); /* in case we don't exit/exec */
266 ctx = 0;
267
268 xl_ctx_alloc();
269 }
270
xl_fork(xlchildnum child,const char * description)271 pid_t xl_fork(xlchildnum child, const char *description) {
272 xlchild *ch = &children[child];
273 int i;
274
275 assert(!ch->pid);
276 ch->reaped = 0;
277 ch->description = description;
278
279 ch->pid = fork();
280 if (ch->pid == -1) {
281 perror("fork failed");
282 exit(-1);
283 }
284
285 if (!ch->pid) {
286 /* We are in the child now. So all these children are not ours. */
287 for (i=0; i<child_max; i++)
288 children[i].pid = 0;
289 }
290
291 return ch->pid;
292 }
293
xl_waitpid(xlchildnum child,int * status,int flags)294 pid_t xl_waitpid(xlchildnum child, int *status, int flags)
295 {
296 xlchild *ch = &children[child];
297 pid_t got = ch->pid;
298 assert(got);
299 if (ch->reaped) {
300 *status = ch->status;
301 ch->pid = 0;
302 return got;
303 }
304 for (;;) {
305 got = waitpid(ch->pid, status, flags);
306 if (got < 0 && errno == EINTR) continue;
307 if (got > 0) {
308 assert(got == ch->pid);
309 ch->pid = 0;
310 }
311 return got;
312 }
313 }
314
xl_child_pid(xlchildnum child)315 int xl_child_pid(xlchildnum child)
316 {
317 xlchild *ch = &children[child];
318 return ch->pid;
319 }
320
xl_report_child_exitstatus(xentoollog_level level,xlchildnum child,pid_t pid,int status)321 void xl_report_child_exitstatus(xentoollog_level level,
322 xlchildnum child, pid_t pid, int status)
323 {
324 libxl_report_child_exitstatus(ctx, level, children[child].description,
325 pid, status);
326 }
327
xl_reaped_callback(pid_t got,int status,void * user)328 static int xl_reaped_callback(pid_t got, int status, void *user)
329 {
330 int i;
331 assert(got);
332 for (i=0; i<child_max; i++) {
333 xlchild *ch = &children[i];
334 if (ch->pid == got) {
335 ch->reaped = 1;
336 ch->status = status;
337 return 0;
338 }
339 }
340 return ERROR_UNKNOWN_CHILD;
341 }
342
343 static const libxl_childproc_hooks childproc_hooks = {
344 .chldowner = libxl_sigchld_owner_libxl,
345 .reaped_callback = xl_reaped_callback,
346 };
347
xl_ctx_alloc(void)348 void xl_ctx_alloc(void) {
349 if (libxl_ctx_alloc(&ctx, LIBXL_VERSION, 0, (xentoollog_logger*)logger)) {
350 fprintf(stderr, "cannot init xl context\n");
351 exit(1);
352 }
353
354 libxl_bitmap_init(&global_vm_affinity_mask);
355 libxl_bitmap_init(&global_hvm_affinity_mask);
356 libxl_bitmap_init(&global_pv_affinity_mask);
357 libxl_childproc_setmode(ctx, &childproc_hooks, 0);
358 }
359
xl_ctx_free(void)360 static void xl_ctx_free(void)
361 {
362 libxl_bitmap_dispose(&global_pv_affinity_mask);
363 libxl_bitmap_dispose(&global_hvm_affinity_mask);
364 libxl_bitmap_dispose(&global_vm_affinity_mask);
365 if (ctx) {
366 libxl_ctx_free(ctx);
367 ctx = NULL;
368 }
369 if (logger) {
370 xtl_logger_destroy((xentoollog_logger*)logger);
371 logger = NULL;
372 }
373 if (lockfile) {
374 free(lockfile);
375 lockfile = NULL;
376 }
377 }
378
main(int argc,char ** argv)379 int main(int argc, char **argv)
380 {
381 int opt = 0;
382 char *cmd = 0;
383 const struct cmd_spec *cspec;
384 int ret;
385 void *config_data = 0;
386 int config_len = 0;
387 unsigned int xtl_flags = 0;
388
389 while ((opt = getopt(argc, argv, "+vftTN")) >= 0) {
390 switch (opt) {
391 case 'v':
392 if (minmsglevel > 0) minmsglevel--;
393 break;
394 case 'N':
395 dryrun_only = 1;
396 break;
397 case 'f':
398 force_execution = 1;
399 break;
400 case 't':
401 progress_use_cr = 1;
402 break;
403 case 'T':
404 timestamps = 1;
405 break;
406 default:
407 fprintf(stderr, "unknown global option\n");
408 exit(EXIT_FAILURE);
409 }
410 }
411
412 cmd = argv[optind];
413
414 if (!cmd) {
415 help(NULL);
416 exit(EXIT_FAILURE);
417 }
418 opterr = 0;
419
420 if (progress_use_cr)
421 xtl_flags |= XTL_STDIOSTREAM_PROGRESS_USE_CR;
422 if (timestamps)
423 xtl_flags |= XTL_STDIOSTREAM_SHOW_DATE | XTL_STDIOSTREAM_SHOW_PID;
424 logger = xtl_createlogger_stdiostream(stderr, minmsglevel, xtl_flags);
425 if (!logger) exit(EXIT_FAILURE);
426
427 xl_ctx_alloc();
428
429 atexit(xl_ctx_free);
430
431 ret = libxl_read_file_contents(ctx, XL_GLOBAL_CONFIG,
432 &config_data, &config_len);
433 if (ret)
434 fprintf(stderr, "Failed to read config file: %s: %s\n",
435 XL_GLOBAL_CONFIG, strerror(errno));
436 parse_global_config(XL_GLOBAL_CONFIG, config_data, config_len);
437 free(config_data);
438
439 /* Reset options for per-command use of getopt. */
440 argv += optind;
441 argc -= optind;
442 optind = 1;
443
444 cspec = cmdtable_lookup(cmd);
445 if (cspec) {
446 if (dryrun_only && !cspec->can_dryrun) {
447 fprintf(stderr, "command does not implement -N (dryrun) option\n");
448 ret = EXIT_FAILURE;
449 goto xit;
450 }
451 ret = cspec->cmd_impl(argc, argv);
452 } else if (!strcmp(cmd, "help")) {
453 help(argv[1]);
454 ret = EXIT_SUCCESS;
455 } else {
456 fprintf(stderr, "command not implemented\n");
457 ret = EXIT_FAILURE;
458 }
459
460 xit:
461 return ret;
462 }
463
child_report(xlchildnum child)464 int child_report(xlchildnum child)
465 {
466 int status;
467 pid_t got = xl_waitpid(child, &status, 0);
468 if (got < 0) {
469 fprintf(stderr, "xl: warning, failed to waitpid for %s: %s\n",
470 children[child].description, strerror(errno));
471 return ERROR_FAIL;
472 } else if (status) {
473 xl_report_child_exitstatus(XTL_ERROR, child, got, status);
474 return ERROR_FAIL;
475 } else {
476 return 0;
477 }
478 }
479
help(const char * command)480 void help(const char *command)
481 {
482 int i;
483 const struct cmd_spec *cmd;
484
485 if (!command || !strcmp(command, "help")) {
486 printf("Usage xl [-vfNtT] <subcommand> [args]\n\n");
487 printf("xl full list of subcommands:\n\n");
488 for (i = 0; i < cmdtable_len; i++) {
489 printf(" %-19s ", cmd_table[i].cmd_name);
490 if (strlen(cmd_table[i].cmd_name) > 19)
491 printf("\n %-19s ", "");
492 printf("%s\n", cmd_table[i].cmd_desc);
493 }
494 } else {
495 cmd = cmdtable_lookup(command);
496 if (cmd) {
497 printf("Usage: xl [-vtT%s%s] %s %s\n\n%s.\n\n",
498 cmd->modifies ? "f" : "",
499 cmd->can_dryrun ? "N" : "",
500 cmd->cmd_name,
501 cmd->cmd_usage,
502 cmd->cmd_desc);
503 if (cmd->cmd_option)
504 printf("Options:\n\n%s\n", cmd->cmd_option);
505 }
506 else {
507 printf("command \"%s\" not implemented\n", command);
508 }
509 }
510 }
511
512
513 /*
514 * Local variables:
515 * mode: C
516 * c-basic-offset: 4
517 * indent-tabs-mode: nil
518 * End:
519 */
520