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