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