]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - sbin/bectl/bectl.c
bectl(8): Consistent style
[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 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 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(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         char *bootenv;
131         int err, opt;
132         bool temp;
133
134         temp = false;
135         while ((opt = getopt(argc, argv, "t")) != -1) {
136                 switch (opt) {
137                 case 't':
138                         temp = true;
139                         break;
140                 default:
141                         fprintf(stderr, "bectl activate: unknown option '-%c'\n",
142                             optopt);
143                         return (usage(false));
144                 }
145         }
146
147         argc -= optind;
148         argv += optind;
149
150         if (argc != 1) {
151                 fprintf(stderr, "bectl activate: wrong number of arguments\n");
152                 return (usage(false));
153         }
154
155
156         /* activate logic goes here */
157         if ((err = be_activate(be, argv[0], temp)) != 0)
158                 /* XXX TODO: more specific error msg based on err */
159                 printf("did not successfully activate boot environment %s\n",
160                     argv[0]);
161         else
162                 printf("successfully activated boot environment %s\n", argv[0]);
163
164         if (temp)
165                 printf("for next boot\n");
166
167         return (err);
168 }
169
170
171 /*
172  * TODO: when only one arg is given, and it contains an "@" the this should
173  * create that snapshot
174  */
175 static int
176 bectl_cmd_create(int argc, char *argv[])
177 {
178         char *bootenv, *snapname, *source;
179         int err, opt;
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, "bectl 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, "bectl create: wrong number of arguments\n");
199                 return (usage(false));
200         }
201
202         bootenv = *argv;
203
204         if (snapname != NULL) {
205                 if (strchr(snapname, '@') != NULL)
206                         err = be_create_from_existing_snap(be, bootenv,
207                             snapname);
208                 else
209                         err = be_create_from_existing(be, bootenv, snapname);
210         } else {
211                 if ((snapname = strchr(bootenv, '@')) != NULL) {
212                         *(snapname++) = '\0';
213                         if ((err = be_snapshot(be, (char *)be_active_path(be),
214                             snapname, true, NULL)) != BE_ERR_SUCCESS)
215                                 fprintf(stderr, "failed to create snapshot\n");
216                         asprintf(&source, "%s@%s", be_active_path(be), snapname);
217                         err = be_create_from_existing_snap(be, bootenv,
218                             source);
219                         return (err);
220                 } else
221                         err = be_create(be, bootenv);
222         }
223
224         switch (err) {
225         case BE_ERR_SUCCESS:
226                 break;
227         default:
228                 if (snapname == NULL)
229                         fprintf(stderr,
230                             "failed to create bootenv %s\n", bootenv);
231                 else
232                         fprintf(stderr,
233                             "failed to create bootenv %s from snapshot %s\n",
234                             bootenv, snapname);
235         }
236
237         return (err);
238 }
239
240
241 static int
242 bectl_cmd_export(int argc, char *argv[])
243 {
244         char *bootenv;
245         int opt;
246
247         if (argc == 1) {
248                 fprintf(stderr, "bectl export: missing boot environment name\n");
249                 return (usage(false));
250         }
251
252         if (argc > 2) {
253                 fprintf(stderr, "bectl export: extra arguments provided\n");
254                 return (usage(false));
255         }
256
257         bootenv = argv[1];
258
259         if (isatty(STDOUT_FILENO)) {
260                 fprintf(stderr, "bectl export: must redirect output\n");
261                 return (EX_USAGE);
262         }
263
264         be_export(be, bootenv, STDOUT_FILENO);
265
266         return (0);
267 }
268
269
270 static int
271 bectl_cmd_import(int argc, char *argv[])
272 {
273         char *bootenv;
274         int err;
275
276         if (argc == 1) {
277                 fprintf(stderr, "bectl import: missing boot environment name\n");
278                 return (usage(false));
279         }
280
281         if (argc > 2) {
282                 fprintf(stderr, "bectl import: extra arguments provided\n");
283                 return (usage(false));
284         }
285
286         bootenv = argv[1];
287
288         if (isatty(STDIN_FILENO)) {
289                 fprintf(stderr, "bectl import: input can not be from terminal\n");
290                 return (EX_USAGE);
291         }
292
293         err = be_import(be, bootenv, STDIN_FILENO);
294
295         return (err);
296 }
297
298
299 static int
300 bectl_cmd_add(int argc, char *argv[])
301 {
302         char *bootenv;
303
304         if (argc < 2) {
305                 fprintf(stderr, "bectl add: must provide at least one path\n");
306                 return (usage(false));
307         }
308
309         for (int i = 1; i < argc; ++i) {
310                 printf("arg %d: %s\n", i, argv[i]);
311                 /* XXX TODO catch err */
312                 be_add_child(be, argv[i], true);
313         }
314
315         return (0);
316 }
317
318
319 static int
320 bectl_cmd_destroy(int argc, char *argv[])
321 {
322         char *target;
323         int opt, err;
324         bool force;
325
326         force = false;
327         while ((opt = getopt(argc, argv, "F")) != -1) {
328                 switch (opt) {
329                 case 'F':
330                         force = true;
331                         break;
332                 default:
333                         fprintf(stderr, "bectl destroy: unknown option '-%c'\n",
334                             optopt);
335                         return (usage(false));
336                 }
337         }
338
339         argc -= optind;
340         argv += optind;
341
342         if (argc != 1) {
343                 fprintf(stderr, "bectl destroy: wrong number of arguments\n");
344                 return (usage(false));
345         }
346
347         target = argv[0];
348
349         err = be_destroy(be, target, force);
350
351         return (err);
352 }
353
354
355 static int
356 bectl_cmd_jail(int argc, char *argv[])
357 {
358         char *bootenv;
359         char mnt_loc[BE_MAXPATHLEN];
360         char buf[BE_MAXPATHLEN*2];
361         int err, jid;
362
363         /* struct jail be_jail = { 0 }; */
364
365         if (argc == 1) {
366                 fprintf(stderr, "bectl jail: missing boot environment name\n");
367                 return (usage(false));
368         }
369         if (argc > 2) {
370                 fprintf(stderr, "bectl jail: too many arguments\n");
371                 return (usage(false));
372         }
373
374         bootenv = argv[1];
375
376         /*
377          * XXX TODO: if its already mounted, perhaps there should be a flag to
378          * indicate its okay to proceed??
379          */
380         if ((err = be_mount(be, bootenv, NULL, 0, mnt_loc)) != BE_ERR_SUCCESS)
381                 fprintf(stderr, "could not mount bootenv\n");
382
383         /*
384          * NOTE: this is not quite functional:
385          * see https://github.com/vermaden/beadm/blob/master/HOWTO.htm on
386          * neccesary modifications to correctly boot the jail
387          */
388
389         /*
390          * snprintf(buf, BE_MAXPATHLEN*2, "jail %s %s %s /bin/sh /etc/rc",
391          *    mnt_loc, bootenv, "192.168.1.123");
392          */
393         snprintf(buf, BE_MAXPATHLEN*2, "jail %s %s %s /bin/sh", mnt_loc,
394             bootenv, "192.168.1.123");
395         system(buf);
396
397         unmount(mnt_loc, 0);
398
399         /*
400          * be_jail.version = JAIL_API_VERSION;
401          * be_jail.path = "/tmp/be_mount.hCCk";
402          * be_jail.jailname = "sdfs";
403          *
404          * if ((jid = jail(&be_jail)) != -1) {
405          *      printf("jail %d created at %s\n", jid, mnt_loc);
406          *      err = 0;
407          * } else {
408          *      fprintf(stderr, "unable to create jail.  error: %d\n", errno);
409          *      err = errno;
410          * }
411          */
412
413         return (0);
414 }
415
416
417 static int
418 bectl_cmd_list(int argc, char *argv[])
419 {
420         char *bootenv;
421         nvlist_t *props;
422         int opt;
423         bool show_all_datasets, show_space, hide_headers, show_snaps;
424
425         show_all_datasets = show_space = hide_headers = show_snaps = false;
426         while ((opt = getopt(argc, argv, "aDHs")) != -1) {
427                 switch (opt) {
428                 case 'a':
429                         show_all_datasets = true;
430                         break;
431                 case 'D':
432                         show_space = true;
433                         break;
434                 case 'H':
435                         hide_headers = true;
436                         break;
437                 case 's':
438                         show_space = true;
439                         break;
440                 default:
441                         fprintf(stderr, "bectl list: unknown option '-%c'\n",
442                             optopt);
443                         return (usage(false));
444                 }
445         }
446
447         argc -= optind;
448
449         if (argc != 0) {
450                 fprintf(stderr, "bectl list: extra argument provided\n");
451                 return (usage(false));
452         }
453
454         /* props = be_get_bootenv_props(be); */
455         return (0);
456 }
457
458
459 static int
460 bectl_cmd_mount(int argc, char *argv[])
461 {
462         char result_loc[BE_MAXPATHLEN];
463         char *bootenv, *mountpoint;
464         int err;
465
466         if (argc < 2) {
467                 fprintf(stderr, "bectl mount: missing argument(s)\n");
468                 return (usage(false));
469         }
470
471         if (argc > 3) {
472                 fprintf(stderr, "bectl mount: too many arguments\n");
473                 return (usage(false));
474         }
475
476         bootenv = argv[1];
477         mountpoint = ((argc == 3) ? argv[2] : NULL);
478
479         err = be_mount(be, bootenv, mountpoint, 0, result_loc);
480
481         switch (err) {
482         case BE_ERR_SUCCESS:
483                 printf("successfully mounted %s at %s\n", bootenv, result_loc);
484                 break;
485         default:
486                 fprintf(stderr,
487                     (argc == 3) ? "failed to mount bootenv %s at %s\n" :
488                     "failed to mount bootenv %s at temporary path %s\n",
489                     bootenv, mountpoint);
490         }
491
492         return (err);
493 }
494
495
496 static int
497 bectl_cmd_rename(int argc, char *argv[])
498 {
499         char *dest, *src;
500         int err;
501
502         if (argc < 3) {
503                 fprintf(stderr, "bectl rename: missing argument\n");
504                 return (usage(false));
505         }
506
507         if (argc > 3) {
508                 fprintf(stderr, "bectl rename: too many arguments\n");
509                 return (usage(false));
510         }
511
512         src = argv[1];
513         dest = argv[2];
514
515         err = be_rename(be, src, dest);
516
517         switch (err) {
518         case BE_ERR_SUCCESS:
519                 break;
520         default:
521                 fprintf(stderr, "failed to rename bootenv %s to %s\n",
522                     src, dest);
523         }
524
525         return (0);
526 }
527
528
529 static int
530 bectl_cmd_unjail(int argc, char *argv[])
531 {
532         char *cmd, *target;
533         int opt;
534         bool force;
535
536         /* Store alias used */
537         cmd = argv[0];
538
539         force = false;
540         while ((opt = getopt(argc, argv, "f")) != -1) {
541                 switch (opt) {
542                 case 'f':
543                         force = true;
544                         break;
545                 default:
546                         fprintf(stderr, "bectl %s: unknown option '-%c'\n",
547                             cmd, optopt);
548                         return (usage(false));
549                 }
550         }
551
552         argc -= optind;
553         argv += optind;
554
555         if (argc != 1) {
556                 fprintf(stderr, "bectl %s: wrong number of arguments\n", cmd);
557                 return (usage(false));
558         }
559
560         target = argv[0];
561
562         /* unjail logic goes here */
563         return (0);
564 }
565
566
567 static int
568 bectl_cmd_unmount(int argc, char *argv[])
569 {
570         char *bootenv, *cmd;
571         int err, flags, opt;
572
573         /* Store alias used */
574         cmd = argv[0];
575
576         flags = 0;
577         while ((opt = getopt(argc, argv, "f")) != -1) {
578                 switch (opt) {
579                 case 'f':
580                         flags |= BE_MNT_FORCE;
581                         break;
582                 default:
583                         fprintf(stderr, "bectl %s: unknown option '-%c'\n",
584                             cmd, optopt);
585                         return (usage(false));
586                 }
587         }
588
589         argc -= optind;
590         argv += optind;
591
592         if (argc != 1) {
593                 fprintf(stderr, "bectl %s: wrong number of arguments\n", cmd);
594                 return (usage(false));
595         }
596
597         bootenv = argv[0];
598
599         err = be_unmount(be, bootenv, flags);
600
601         switch (err) {
602         case BE_ERR_SUCCESS:
603                 break;
604         default:
605                 fprintf(stderr, "failed to unmount bootenv %s\n", bootenv);
606         }
607
608         return (err);
609 }
610
611
612 int
613 main(int argc, char *argv[])
614 {
615         char *command;
616         int command_index, rc;
617
618         if (argc < 2) {
619                 fprintf(stderr, "missing command\n");
620                 return (usage(false));
621         }
622
623         command = argv[1];
624
625         /* Handle command aliases */
626         if (strcmp(command, "umount") == 0)
627                 command = "unmount";
628
629         if (strcmp(command, "ujail") == 0)
630                 command = "unjail";
631
632         if ((strcmp(command, "-?") == 0) || (strcmp(command, "-h") == 0))
633                 return (usage(true));
634
635         if (get_cmd_index(command, &command_index)) {
636                 fprintf(stderr, "unknown command: %s\n", command);
637                 return (usage(false));
638         }
639
640
641         if ((be = libbe_init()) == NULL)
642                 return (-1);
643
644         libbe_print_on_error(be, true);
645
646         /* XXX TODO: can be simplified if offset by 2 instead of one */
647         rc = command_map[command_index].fn(argc-1, argv+1);
648
649         libbe_close(be);
650         return (rc);
651 }