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(&regex,
83                   "(^| )dom0_mem=((|min:|max:)[0-9]+[bBkKmMgG]?,?)+($| )",
84                   REG_NOSUB | REG_EXTENDED);
85     if (ret)
86         return 1;
87 
88     ret = regexec(&regex, info->commandline, 0, NULL, 0);
89     regfree(&regex);
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