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