]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - sbin/bectl/bectl.c
libbe(3)/bectl(8): Standardize copyright headers
[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/param.h>
30 #include <sys/mount.h>
31 #include <errno.h>
32 #include <libutil.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 <time.h>
40 #include <unistd.h>
41
42 #include <be.h>
43
44 #include "bectl.h"
45
46 static int bectl_cmd_activate(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 static int bectl_cmd_add(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_unmount(int argc, char *argv[]);
55
56 libbe_handle_t *be;
57
58 int
59 usage(bool explicit)
60 {
61         FILE *fp;
62
63         fp =  explicit ? stdout : stderr;
64         fprintf(fp,
65             "usage:\tbectl ( -h | -? | subcommand [args...] )\n"
66             "\tbectl activate [-t] beName\n"
67             "\tbectl create [-e nonActiveBe | -e beName@snapshot] beName\n"
68             "\tbectl create beName@snapshot\n"
69             "\tbectl destroy [-F] beName | beName@snapshot⟩\n"
70             "\tbectl export sourceBe\n"
71             "\tbectl import targetBe\n"
72             "\tbectl add (path)*\n"
73             "\tbectl jail [ -o key=value | -u key ]... bootenv\n"
74             "\tbectl list [-a] [-D] [-H] [-s]\n"
75             "\tbectl mount beName [mountpoint]\n"
76             "\tbectl rename origBeName newBeName\n"
77             "\tbectl { ujail | unjail } ⟨jailID | jailName | bootenv)\n"
78             "\tbectl { 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", bectl_cmd_activate },
96         { "create",   bectl_cmd_create   },
97         { "destroy",  bectl_cmd_destroy  },
98         { "export",   bectl_cmd_export   },
99         { "import",   bectl_cmd_import   },
100         { "add",      bectl_cmd_add      },
101         { "jail",     bectl_cmd_jail     },
102         { "list",     bectl_cmd_list     },
103         { "mount",    bectl_cmd_mount    },
104         { "rename",   bectl_cmd_rename   },
105         { "unjail",   bectl_cmd_unjail   },
106         { "unmount",  bectl_cmd_unmount  },
107 };
108
109 static int
110 get_cmd_index(const char *cmd, int *index)
111 {
112         int map_size;
113
114         map_size = nitems(command_map);
115         for (int i = 0; i < map_size; ++i) {
116                 if (strcmp(cmd, command_map[i].command) == 0) {
117                         *index = i;
118                         return (0);
119                 }
120         }
121
122         return (1);
123 }
124
125
126 static int
127 bectl_cmd_activate(int argc, char *argv[])
128 {
129         int err, opt;
130         bool temp;
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, "bectl 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, "bectl 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                 /* XXX 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         if (temp)
163                 printf("for next boot\n");
164
165         return (err);
166 }
167
168
169 /*
170  * TODO: when only one arg is given, and it contains an "@" the this should
171  * create that snapshot
172  */
173 static int
174 bectl_cmd_create(int argc, char *argv[])
175 {
176         char *bootenv, *snapname, *source;
177         int err, opt;
178
179         snapname = NULL;
180         while ((opt = getopt(argc, argv, "e:")) != -1) {
181                 switch (opt) {
182                 case 'e':
183                         snapname = optarg;
184                         break;
185                 default:
186                         fprintf(stderr, "bectl create: unknown option '-%c'\n",
187                             optopt);
188                         return (usage(false));
189                 }
190         }
191
192         argc -= optind;
193         argv += optind;
194
195         if (argc != 1) {
196                 fprintf(stderr, "bectl create: wrong number of arguments\n");
197                 return (usage(false));
198         }
199
200         bootenv = *argv;
201
202         if (snapname != NULL) {
203                 if (strchr(snapname, '@') != NULL)
204                         err = be_create_from_existing_snap(be, bootenv,
205                             snapname);
206                 else
207                         err = be_create_from_existing(be, bootenv, snapname);
208         } else {
209                 if ((snapname = strchr(bootenv, '@')) != NULL) {
210                         *(snapname++) = '\0';
211                         if ((err = be_snapshot(be, be_active_path(be),
212                             snapname, true, NULL)) != BE_ERR_SUCCESS)
213                                 fprintf(stderr, "failed to create snapshot\n");
214                         asprintf(&source, "%s@%s", be_active_path(be), snapname);
215                         err = be_create_from_existing_snap(be, bootenv,
216                             source);
217                         return (err);
218                 } else
219                         err = be_create(be, bootenv);
220         }
221
222         switch (err) {
223         case BE_ERR_SUCCESS:
224                 break;
225         default:
226                 if (snapname == NULL)
227                         fprintf(stderr,
228                             "failed to create bootenv %s\n", bootenv);
229                 else
230                         fprintf(stderr,
231                             "failed to create bootenv %s from snapshot %s\n",
232                             bootenv, snapname);
233         }
234
235         return (err);
236 }
237
238
239 static int
240 bectl_cmd_export(int argc, char *argv[])
241 {
242         char *bootenv;
243
244         if (argc == 1) {
245                 fprintf(stderr, "bectl export: missing boot environment name\n");
246                 return (usage(false));
247         }
248
249         if (argc > 2) {
250                 fprintf(stderr, "bectl export: extra arguments provided\n");
251                 return (usage(false));
252         }
253
254         bootenv = argv[1];
255
256         if (isatty(STDOUT_FILENO)) {
257                 fprintf(stderr, "bectl export: must redirect output\n");
258                 return (EX_USAGE);
259         }
260
261         be_export(be, bootenv, STDOUT_FILENO);
262
263         return (0);
264 }
265
266
267 static int
268 bectl_cmd_import(int argc, char *argv[])
269 {
270         char *bootenv;
271         int err;
272
273         if (argc == 1) {
274                 fprintf(stderr, "bectl import: missing boot environment name\n");
275                 return (usage(false));
276         }
277
278         if (argc > 2) {
279                 fprintf(stderr, "bectl import: extra arguments provided\n");
280                 return (usage(false));
281         }
282
283         bootenv = argv[1];
284
285         if (isatty(STDIN_FILENO)) {
286                 fprintf(stderr, "bectl import: input can not be from terminal\n");
287                 return (EX_USAGE);
288         }
289
290         err = be_import(be, bootenv, STDIN_FILENO);
291
292         return (err);
293 }
294
295
296 static int
297 bectl_cmd_add(int argc, char *argv[])
298 {
299
300         if (argc < 2) {
301                 fprintf(stderr, "bectl add: must provide at least one path\n");
302                 return (usage(false));
303         }
304
305         for (int i = 1; i < argc; ++i) {
306                 printf("arg %d: %s\n", i, argv[i]);
307                 /* XXX TODO catch err */
308                 be_add_child(be, argv[i], true);
309         }
310
311         return (0);
312 }
313
314
315 static int
316 bectl_cmd_destroy(int argc, char *argv[])
317 {
318         char *target;
319         int opt, err;
320         bool force;
321
322         force = false;
323         while ((opt = getopt(argc, argv, "F")) != -1) {
324                 switch (opt) {
325                 case 'F':
326                         force = true;
327                         break;
328                 default:
329                         fprintf(stderr, "bectl destroy: unknown option '-%c'\n",
330                             optopt);
331                         return (usage(false));
332                 }
333         }
334
335         argc -= optind;
336         argv += optind;
337
338         if (argc != 1) {
339                 fprintf(stderr, "bectl destroy: wrong number of arguments\n");
340                 return (usage(false));
341         }
342
343         target = argv[0];
344
345         err = be_destroy(be, target, force);
346
347         return (err);
348 }
349
350 static int
351 bectl_cmd_mount(int argc, char *argv[])
352 {
353         char result_loc[BE_MAXPATHLEN];
354         char *bootenv, *mountpoint;
355         int err;
356
357         if (argc < 2) {
358                 fprintf(stderr, "bectl mount: missing argument(s)\n");
359                 return (usage(false));
360         }
361
362         if (argc > 3) {
363                 fprintf(stderr, "bectl mount: too many arguments\n");
364                 return (usage(false));
365         }
366
367         bootenv = argv[1];
368         mountpoint = ((argc == 3) ? argv[2] : NULL);
369
370         err = be_mount(be, bootenv, mountpoint, 0, result_loc);
371
372         switch (err) {
373         case BE_ERR_SUCCESS:
374                 printf("successfully mounted %s at %s\n", bootenv, result_loc);
375                 break;
376         default:
377                 fprintf(stderr,
378                     (argc == 3) ? "failed to mount bootenv %s at %s\n" :
379                     "failed to mount bootenv %s at temporary path %s\n",
380                     bootenv, mountpoint);
381         }
382
383         return (err);
384 }
385
386
387 static int
388 bectl_cmd_rename(int argc, char *argv[])
389 {
390         char *dest, *src;
391         int err;
392
393         if (argc < 3) {
394                 fprintf(stderr, "bectl rename: missing argument\n");
395                 return (usage(false));
396         }
397
398         if (argc > 3) {
399                 fprintf(stderr, "bectl rename: too many arguments\n");
400                 return (usage(false));
401         }
402
403         src = argv[1];
404         dest = argv[2];
405
406         err = be_rename(be, src, dest);
407
408         switch (err) {
409         case BE_ERR_SUCCESS:
410                 break;
411         default:
412                 fprintf(stderr, "failed to rename bootenv %s to %s\n",
413                     src, dest);
414         }
415
416         return (0);
417 }
418
419 static int
420 bectl_cmd_unmount(int argc, char *argv[])
421 {
422         char *bootenv, *cmd;
423         int err, flags, opt;
424
425         /* Store alias used */
426         cmd = argv[0];
427
428         flags = 0;
429         while ((opt = getopt(argc, argv, "f")) != -1) {
430                 switch (opt) {
431                 case 'f':
432                         flags |= BE_MNT_FORCE;
433                         break;
434                 default:
435                         fprintf(stderr, "bectl %s: unknown option '-%c'\n",
436                             cmd, optopt);
437                         return (usage(false));
438                 }
439         }
440
441         argc -= optind;
442         argv += optind;
443
444         if (argc != 1) {
445                 fprintf(stderr, "bectl %s: wrong number of arguments\n", cmd);
446                 return (usage(false));
447         }
448
449         bootenv = argv[0];
450
451         err = be_unmount(be, bootenv, flags);
452
453         switch (err) {
454         case BE_ERR_SUCCESS:
455                 break;
456         default:
457                 fprintf(stderr, "failed to unmount bootenv %s\n", bootenv);
458         }
459
460         return (err);
461 }
462
463
464 int
465 main(int argc, char *argv[])
466 {
467         const char *command;
468         int command_index, rc;
469
470         if (argc < 2) {
471                 fprintf(stderr, "missing command\n");
472                 return (usage(false));
473         }
474
475         command = argv[1];
476
477         /* Handle command aliases */
478         if (strcmp(command, "umount") == 0)
479                 command = "unmount";
480
481         if (strcmp(command, "ujail") == 0)
482                 command = "unjail";
483
484         if ((strcmp(command, "-?") == 0) || (strcmp(command, "-h") == 0))
485                 return (usage(true));
486
487         if (get_cmd_index(command, &command_index)) {
488                 fprintf(stderr, "unknown command: %s\n", command);
489                 return (usage(false));
490         }
491
492
493         if ((be = libbe_init()) == NULL)
494                 return (-1);
495
496         libbe_print_on_error(be, true);
497
498         /* XXX TODO: can be simplified if offset by 2 instead of one */
499         rc = command_map[command_index].fn(argc-1, argv+1);
500
501         libbe_close(be);
502         return (rc);
503 }