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