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