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(&regex,
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(&regex, info->commandline, 0, NULL, 0);
97     regfree(&regex);
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