]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - sbin/bectl/bectl.c
libbe(3)/be(8): Drop WARNS overrides, fix all fallout
[FreeBSD/FreeBSD.git] / sbin / bectl / bectl.c
1 /*
2  * be.c
3  *
4  * Copyright (c) 2017 Kyle J. Kneitinger <kyle@kneit.in>
5  * All rights reserved.
6  *
7  * Redistribution and use in source and binary forms, with or without
8  * modification, are permitted provided that the following conditions
9  * are met:
10  * 1. Redistributions of source code must retain the above copyright
11  *    notice, this list of conditions and the following disclaimer.
12  * 2. Redistributions in binary form must reproduce the above copyright
13  *    notice, this list of conditions and the following disclaimer in the
14  *    documentation and/or other materials provided with the distribution.
15  *
16  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
17  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
18  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
19  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
20  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
21  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
22  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
23  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
24  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
25  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
26  * SUCH DAMAGE.
27  */
28
29 #include <sys/param.h>
30 #include <sys/jail.h>
31 #include <sys/malloc.h>
32 #include <sys/mount.h>
33 #include <errno.h>
34 #include <stdbool.h>
35 #include <stdio.h>
36 #include <stdint.h>
37 #include <stdlib.h>
38 #include <string.h>
39 #include <sysexits.h>
40 #include <unistd.h>
41
42 #include <be.h>
43
44 static int bectl_cmd_activate(int argc, char *argv[]);
45 static int bectl_cmd_create(int argc, char *argv[]);
46 static int bectl_cmd_destroy(int argc, char *argv[]);
47 static int bectl_cmd_export(int argc, char *argv[]);
48 static int bectl_cmd_import(int argc, char *argv[]);
49 static int bectl_cmd_add(int argc, char *argv[]);
50 static int bectl_cmd_jail(int argc, char *argv[]);
51 static int bectl_cmd_list(int argc, char *argv[]);
52 static int bectl_cmd_mount(int argc, char *argv[]);
53 static int bectl_cmd_rename(int argc, char *argv[]);
54 static int bectl_cmd_unjail(int argc, char *argv[]);
55 static int bectl_cmd_unmount(int argc, char *argv[]);
56
57 static libbe_handle_t *be;
58
59 static int
60 usage(bool explicit)
61 {
62         FILE *fp;
63
64         fp =  explicit ? stdout : stderr;
65         fprintf(fp,
66             "usage:\tbectl ( -h | -? | subcommand [args...] )\n"
67             "\tbectl activate [-t] beName\n"
68             "\tbectl create [-e nonActiveBe | -e beName@snapshot] beName\n"
69             "\tbectl create beName@snapshot\n"
70             "\tbectl destroy [-F] beName | beName@snapshot⟩\n"
71             "\tbectl export sourceBe\n"
72             "\tbectl import targetBe\n"
73             "\tbectl add (path)*\n"
74             "\tbectl jail bootenv\n"
75             "\tbectl list [-a] [-D] [-H] [-s]\n"
76             "\tbectl mount beName [mountpoint]\n"
77             "\tbectl rename origBeName newBeName\n"
78             "\tbectl { ujail | unjail } ⟨jailID | jailName⟩ bootenv\n"
79             "\tbectl { umount | unmount } [-f] beName\n");
80
81         return (explicit ? 0 : EX_USAGE);
82 }
83
84
85 /*
86  * Represents a relationship between the command name and the parser action
87  * that handles it.
88  */
89 struct command_map_entry {
90         const char *command;
91         int (*fn)(int argc, char *argv[]);
92 };
93
94 static struct command_map_entry command_map[] =
95 {
96         { "activate", bectl_cmd_activate },
97         { "create",   bectl_cmd_create   },
98         { "destroy",  bectl_cmd_destroy  },
99         { "export",   bectl_cmd_export   },
100         { "import",   bectl_cmd_import   },
101         { "add",      bectl_cmd_add      },
102         { "jail",     bectl_cmd_jail     },
103         { "list",     bectl_cmd_list     },
104         { "mount",    bectl_cmd_mount    },
105         { "rename",   bectl_cmd_rename   },
106         { "unjail",   bectl_cmd_unjail   },
107         { "unmount",  bectl_cmd_unmount  },
108 };
109
110 static int
111 get_cmd_index(const char *cmd, int *index)
112 {
113         int map_size;
114
115         map_size = nitems(command_map);
116         for (int i = 0; i < map_size; ++i) {
117                 if (strcmp(cmd, command_map[i].command) == 0) {
118                         *index = i;
119                         return (0);
120                 }
121         }
122
123         return (1);
124 }
125
126
127 static int
128 bectl_cmd_activate(int argc, char *argv[])
129 {
130         int err, opt;
131         bool temp;
132
133         temp = false;
134         while ((opt = getopt(argc, argv, "t")) != -1) {
135                 switch (opt) {
136                 case 't':
137                         temp = true;
138                         break;
139                 default:
140                         fprintf(stderr, "bectl activate: unknown option '-%c'\n",
141                             optopt);
142                         return (usage(false));
143                 }
144         }
145
146         argc -= optind;
147         argv += optind;
148
149         if (argc != 1) {
150                 fprintf(stderr, "bectl activate: wrong number of arguments\n");
151                 return (usage(false));
152         }
153
154
155         /* activate logic goes here */
156         if ((err = be_activate(be, argv[0], temp)) != 0)
157                 /* XXX TODO: more specific error msg based on err */
158                 printf("did not successfully activate boot environment %s\n",
159                     argv[0]);
160         else
161                 printf("successfully activated boot environment %s\n", argv[0]);
162
163         if (temp)
164                 printf("for next boot\n");
165
166         return (err);
167 }
168
169
170 /*
171  * TODO: when only one arg is given, and it contains an "@" the this should
172  * create that snapshot
173  */
174 static int
175 bectl_cmd_create(int argc, char *argv[])
176 {
177         char *bootenv, *snapname, *source;
178         int err, opt;
179
180         snapname = NULL;
181         while ((opt = getopt(argc, argv, "e:")) != -1) {
182                 switch (opt) {
183                 case 'e':
184                         snapname = optarg;
185                         break;
186                 default:
187                         fprintf(stderr, "bectl create: unknown option '-%c'\n",
188                             optopt);
189                         return (usage(false));
190                 }
191         }
192
193         argc -= optind;
194         argv += optind;
195
196         if (argc != 1) {
197                 fprintf(stderr, "bectl create: wrong number of arguments\n");
198                 return (usage(false));
199         }
200
201         bootenv = *argv;
202
203         if (snapname != NULL) {
204                 if (strchr(snapname, '@') != NULL)
205                         err = be_create_from_existing_snap(be, bootenv,
206                             snapname);
207                 else
208                         err = be_create_from_existing(be, bootenv, snapname);
209         } else {
210                 if ((snapname = strchr(bootenv, '@')) != NULL) {
211                         *(snapname++) = '\0';
212                         if ((err = be_snapshot(be, be_active_path(be),
213                             snapname, true, NULL)) != BE_ERR_SUCCESS)
214                                 fprintf(stderr, "failed to create snapshot\n");
215                         asprintf(&source, "%s@%s", be_active_path(be), snapname);
216                         err = be_create_from_existing_snap(be, bootenv,
217                             source);
218                         return (err);
219                 } else
220                         err = be_create(be, bootenv);
221         }
222
223         switch (err) {
224         case BE_ERR_SUCCESS:
225                 break;
226         default:
227                 if (snapname == NULL)
228                         fprintf(stderr,
229                             "failed to create bootenv %s\n", bootenv);
230                 else
231                         fprintf(stderr,
232                             "failed to create bootenv %s from snapshot %s\n",
233                             bootenv, snapname);
234         }
235
236         return (err);
237 }
238
239
240 static int
241 bectl_cmd_export(int argc, char *argv[])
242 {
243         char *bootenv;
244
245         if (argc == 1) {
246                 fprintf(stderr, "bectl export: missing boot environment name\n");
247                 return (usage(false));
248         }
249
250         if (argc > 2) {
251                 fprintf(stderr, "bectl export: extra arguments provided\n");
252                 return (usage(false));
253         }
254
255         bootenv = argv[1];
256
257         if (isatty(STDOUT_FILENO)) {
258                 fprintf(stderr, "bectl export: must redirect output\n");
259                 return (EX_USAGE);
260         }
261
262         be_export(be, bootenv, STDOUT_FILENO);
263
264         return (0);
265 }
266
267
268 static int
269 bectl_cmd_import(int argc, char *argv[])
270 {
271         char *bootenv;
272         int err;
273
274         if (argc == 1) {
275                 fprintf(stderr, "bectl import: missing boot environment name\n");
276                 return (usage(false));
277         }
278
279         if (argc > 2) {
280                 fprintf(stderr, "bectl import: extra arguments provided\n");
281                 return (usage(false));
282         }
283
284         bootenv = argv[1];
285
286         if (isatty(STDIN_FILENO)) {
287                 fprintf(stderr, "bectl import: input can not be from terminal\n");
288                 return (EX_USAGE);
289         }
290
291         err = be_import(be, bootenv, STDIN_FILENO);
292
293         return (err);
294 }
295
296
297 static int
298 bectl_cmd_add(int argc, char *argv[])
299 {
300
301         if (argc < 2) {
302                 fprintf(stderr, "bectl add: must provide at least one path\n");
303                 return (usage(false));
304         }
305
306         for (int i = 1; i < argc; ++i) {
307                 printf("arg %d: %s\n", i, argv[i]);
308                 /* XXX TODO catch err */
309                 be_add_child(be, argv[i], true);
310         }
311
312         return (0);
313 }
314
315
316 static int
317 bectl_cmd_destroy(int argc, char *argv[])
318 {
319         char *target;
320         int opt, err;
321         bool force;
322
323         force = false;
324         while ((opt = getopt(argc, argv, "F")) != -1) {
325                 switch (opt) {
326                 case 'F':
327                         force = true;
328                         break;
329                 default:
330                         fprintf(stderr, "bectl destroy: unknown option '-%c'\n",
331                             optopt);
332                         return (usage(false));
333                 }
334         }
335
336         argc -= optind;
337         argv += optind;
338
339         if (argc != 1) {
340                 fprintf(stderr, "bectl destroy: wrong number of arguments\n");
341                 return (usage(false));
342         }
343
344         target = argv[0];
345
346         err = be_destroy(be, target, force);
347
348         return (err);
349 }
350
351
352 static int
353 bectl_cmd_jail(int argc, char *argv[])
354 {
355         char *bootenv;
356         char mnt_loc[BE_MAXPATHLEN];
357         char buf[BE_MAXPATHLEN*2];
358         int err;
359
360         /* struct jail be_jail = { 0 }; */
361
362         if (argc == 1) {
363                 fprintf(stderr, "bectl jail: missing boot environment name\n");
364                 return (usage(false));
365         }
366         if (argc > 2) {
367                 fprintf(stderr, "bectl jail: too many arguments\n");
368                 return (usage(false));
369         }
370
371         bootenv = argv[1];
372
373         /*
374          * XXX TODO: if its already mounted, perhaps there should be a flag to
375          * indicate its okay to proceed??
376          */
377         if ((err = be_mount(be, bootenv, NULL, 0, mnt_loc)) != BE_ERR_SUCCESS)
378                 fprintf(stderr, "could not mount bootenv\n");
379
380         /*
381          * NOTE: this is not quite functional:
382          * see https://github.com/vermaden/beadm/blob/master/HOWTO.htm on
383          * neccesary modifications to correctly boot the jail
384          */
385
386         /*
387          * snprintf(buf, BE_MAXPATHLEN*2, "jail %s %s %s /bin/sh /etc/rc",
388          *    mnt_loc, bootenv, "192.168.1.123");
389          */
390         snprintf(buf, BE_MAXPATHLEN*2, "jail %s %s %s /bin/sh", mnt_loc,
391             bootenv, "192.168.1.123");
392         system(buf);
393
394         unmount(mnt_loc, 0);
395
396         /*
397          * be_jail.version = JAIL_API_VERSION;
398          * be_jail.path = "/tmp/be_mount.hCCk";
399          * be_jail.jailname = "sdfs";
400          *
401          * if ((jid = jail(&be_jail)) != -1) {
402          *      printf("jail %d created at %s\n", jid, mnt_loc);
403          *      err = 0;
404          * } else {
405          *      fprintf(stderr, "unable to create jail.  error: %d\n", errno);
406          *      err = errno;
407          * }
408          */
409
410         return (0);
411 }
412
413
414 static int
415 bectl_cmd_list(int argc, char *argv[])
416 {
417         nvlist_t *props;
418         int opt;
419         bool show_all_datasets, show_space, hide_headers, show_snaps;
420
421         show_all_datasets = show_space = hide_headers = show_snaps = false;
422         while ((opt = getopt(argc, argv, "aDHs")) != -1) {
423                 switch (opt) {
424                 case 'a':
425                         show_all_datasets = true;
426                         break;
427                 case 'D':
428                         show_space = true;
429                         break;
430                 case 'H':
431                         hide_headers = true;
432                         break;
433                 case 's':
434                         show_space = true;
435                         break;
436                 default:
437                         fprintf(stderr, "bectl list: unknown option '-%c'\n",
438                             optopt);
439                         return (usage(false));
440                 }
441         }
442
443         argc -= optind;
444
445         if (argc != 0) {
446                 fprintf(stderr, "bectl list: extra argument provided\n");
447                 return (usage(false));
448         }
449
450
451         if (nvlist_alloc(&props, NV_UNIQUE_NAME, M_WAITOK) != 0) {
452                 fprintf(stderr, "bectl list: failed to allocate prop nvlist\n");
453                 return (1);
454         }
455         if (be_get_bootenv_props(be, props) != 0) {
456                 /* XXX TODO: Real errors */
457                 fprintf(stderr, "bectl list: failed to fetch boot environments\n");
458                 return (1);
459         }
460
461         dump_nvlist(props, 0);
462         nvlist_free(props);
463
464         return (0);
465 }
466
467
468 static int
469 bectl_cmd_mount(int argc, char *argv[])
470 {
471         char result_loc[BE_MAXPATHLEN];
472         char *bootenv, *mountpoint;
473         int err;
474
475         if (argc < 2) {
476                 fprintf(stderr, "bectl mount: missing argument(s)\n");
477                 return (usage(false));
478         }
479
480         if (argc > 3) {
481                 fprintf(stderr, "bectl mount: too many arguments\n");
482                 return (usage(false));
483         }
484
485         bootenv = argv[1];
486         mountpoint = ((argc == 3) ? argv[2] : NULL);
487
488         err = be_mount(be, bootenv, mountpoint, 0, result_loc);
489
490         switch (err) {
491         case BE_ERR_SUCCESS:
492                 printf("successfully mounted %s at %s\n", bootenv, result_loc);
493                 break;
494         default:
495                 fprintf(stderr,
496                     (argc == 3) ? "failed to mount bootenv %s at %s\n" :
497                     "failed to mount bootenv %s at temporary path %s\n",
498                     bootenv, mountpoint);
499         }
500
501         return (err);
502 }
503
504
505 static int
506 bectl_cmd_rename(int argc, char *argv[])
507 {
508         char *dest, *src;
509         int err;
510
511         if (argc < 3) {
512                 fprintf(stderr, "bectl rename: missing argument\n");
513                 return (usage(false));
514         }
515
516         if (argc > 3) {
517                 fprintf(stderr, "bectl rename: too many arguments\n");
518                 return (usage(false));
519         }
520
521         src = argv[1];
522         dest = argv[2];
523
524         err = be_rename(be, src, dest);
525
526         switch (err) {
527         case BE_ERR_SUCCESS:
528                 break;
529         default:
530                 fprintf(stderr, "failed to rename bootenv %s to %s\n",
531                     src, dest);
532         }
533
534         return (0);
535 }
536
537
538 static int
539 bectl_cmd_unjail(int argc, char *argv[])
540 {
541         char *cmd, *target;
542         int opt;
543         bool force;
544
545         /* Store alias used */
546         cmd = argv[0];
547
548         force = false;
549         while ((opt = getopt(argc, argv, "f")) != -1) {
550                 switch (opt) {
551                 case 'f':
552                         force = true;
553                         break;
554                 default:
555                         fprintf(stderr, "bectl %s: unknown option '-%c'\n",
556                             cmd, optopt);
557                         return (usage(false));
558                 }
559         }
560
561         argc -= optind;
562         argv += optind;
563
564         if (argc != 1) {
565                 fprintf(stderr, "bectl %s: wrong number of arguments\n", cmd);
566                 return (usage(false));
567         }
568
569         target = argv[0];
570
571         /* unjail logic goes here */
572         return (0);
573 }
574
575
576 static int
577 bectl_cmd_unmount(int argc, char *argv[])
578 {
579         char *bootenv, *cmd;
580         int err, flags, opt;
581
582         /* Store alias used */
583         cmd = argv[0];
584
585         flags = 0;
586         while ((opt = getopt(argc, argv, "f")) != -1) {
587                 switch (opt) {
588                 case 'f':
589                         flags |= BE_MNT_FORCE;
590                         break;
591                 default:
592                         fprintf(stderr, "bectl %s: unknown option '-%c'\n",
593                             cmd, optopt);
594                         return (usage(false));
595                 }
596         }
597
598         argc -= optind;
599         argv += optind;
600
601         if (argc != 1) {
602                 fprintf(stderr, "bectl %s: wrong number of arguments\n", cmd);
603                 return (usage(false));
604         }
605
606         bootenv = argv[0];
607
608         err = be_unmount(be, bootenv, flags);
609
610         switch (err) {
611         case BE_ERR_SUCCESS:
612                 break;
613         default:
614                 fprintf(stderr, "failed to unmount bootenv %s\n", bootenv);
615         }
616
617         return (err);
618 }
619
620
621 int
622 main(int argc, char *argv[])
623 {
624         const char *command;
625         int command_index, rc;
626
627         if (argc < 2) {
628                 fprintf(stderr, "missing command\n");
629                 return (usage(false));
630         }
631
632         command = argv[1];
633
634         /* Handle command aliases */
635         if (strcmp(command, "umount") == 0)
636                 command = "unmount";
637
638         if (strcmp(command, "ujail") == 0)
639                 command = "unjail";
640
641         if ((strcmp(command, "-?") == 0) || (strcmp(command, "-h") == 0))
642                 return (usage(true));
643
644         if (get_cmd_index(command, &command_index)) {
645                 fprintf(stderr, "unknown command: %s\n", command);
646                 return (usage(false));
647         }
648
649
650         if ((be = libbe_init()) == NULL)
651                 return (-1);
652
653         libbe_print_on_error(be, true);
654
655         /* XXX TODO: can be simplified if offset by 2 instead of one */
656         rc = command_map[command_index].fn(argc-1, argv+1);
657
658         libbe_close(be);
659         return (rc);
660 }