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(®ex,
75 "(^| )dom0_mem=((|min:|max:)[0-9]+[bBkKmMgG]?,?)+($| )",
76 REG_NOSUB | REG_EXTENDED);
77 if (ret)
78 return 1;
79
80 ret = regexec(®ex, info->commandline, 0, NULL, 0);
81 regfree(®ex);
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