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