Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
freebsd
GitHub Repository: freebsd/freebsd-src
Path: blob/main/sbin/bectl/bectl.c
39475 views
1
/*
2
* Copyright (c) 2017 Kyle J. Kneitinger <[email protected]>
3
*
4
* SPDX-License-Identifier: BSD-2-Clause
5
*/
6
7
#include <sys/param.h>
8
#include <sys/mount.h>
9
#include <errno.h>
10
#include <libutil.h>
11
#include <stdbool.h>
12
#include <stdio.h>
13
#include <stdint.h>
14
#include <stdlib.h>
15
#include <string.h>
16
#include <sysexits.h>
17
#include <time.h>
18
#include <unistd.h>
19
20
#include <be.h>
21
22
#include "bectl.h"
23
24
static int bectl_cmd_activate(int argc, char *argv[]);
25
static int bectl_cmd_check(int argc, char *argv[]);
26
static int bectl_cmd_create(int argc, char *argv[]);
27
static int bectl_cmd_destroy(int argc, char *argv[]);
28
static int bectl_cmd_export(int argc, char *argv[]);
29
static int bectl_cmd_import(int argc, char *argv[]);
30
#if SOON
31
static int bectl_cmd_add(int argc, char *argv[]);
32
#endif
33
static int bectl_cmd_mount(int argc, char *argv[]);
34
static int bectl_cmd_rename(int argc, char *argv[]);
35
static int bectl_cmd_unmount(int argc, char *argv[]);
36
37
libbe_handle_t *be;
38
39
int
40
usage(bool explicit)
41
{
42
FILE *fp;
43
44
fp = explicit ? stdout : stderr;
45
fprintf(fp, "%s",
46
"Usage:\tbectl {-h | subcommand [args...]}\n"
47
#if SOON
48
"\tbectl [-r beroot] add (path)*\n"
49
#endif
50
"\tbectl [-r beroot] activate [-t] beName\n"
51
"\tbectl [-r beroot] activate [-T]\n"
52
"\tbectl [-r beroot] check\n"
53
"\tbectl [-r beroot] create [-r] [-e {nonActiveBe | beName@snapshot}] beName\n"
54
"\tbectl [-r beroot] create [-r] beName@snapshot\n"
55
"\tbectl [-r beroot] destroy [-Fo] {beName | beName@snapshot}\n"
56
"\tbectl [-r beroot] export sourceBe\n"
57
"\tbectl [-r beroot] import targetBe\n"
58
"\tbectl [-r beroot] jail [-bU] [{-o key=value | -u key}]... beName\n"
59
"\t [utility [argument ...]]\n"
60
"\tbectl [-r beroot] list [-aDHs] [{-c property | -C property}]\n"
61
"\tbectl [-r beroot] mount beName [mountpoint]\n"
62
"\tbectl [-r beroot] rename origBeName newBeName\n"
63
"\tbectl [-r beroot] {ujail | unjail} {jailID | jailName | beName}\n"
64
"\tbectl [-r beroot] {umount | unmount} [-f] beName\n");
65
66
return (explicit ? 0 : EX_USAGE);
67
}
68
69
70
/*
71
* Represents a relationship between the command name and the parser action
72
* that handles it.
73
*/
74
struct command_map_entry {
75
const char *command;
76
int (*fn)(int argc, char *argv[]);
77
/* True if libbe_print_on_error should be disabled */
78
bool silent;
79
};
80
81
static struct command_map_entry command_map[] =
82
{
83
{ "activate", bectl_cmd_activate,false },
84
{ "create", bectl_cmd_create, false },
85
{ "destroy", bectl_cmd_destroy, false },
86
{ "export", bectl_cmd_export, false },
87
{ "import", bectl_cmd_import, false },
88
#if SOON
89
{ "add", bectl_cmd_add, false },
90
#endif
91
{ "jail", bectl_cmd_jail, false },
92
{ "list", bectl_cmd_list, false },
93
{ "mount", bectl_cmd_mount, false },
94
{ "rename", bectl_cmd_rename, false },
95
{ "unjail", bectl_cmd_unjail, false },
96
{ "ujail", bectl_cmd_unjail, false },
97
{ "unmount", bectl_cmd_unmount, false },
98
{ "umount", bectl_cmd_unmount, false },
99
{ "check", bectl_cmd_check, true },
100
};
101
102
static struct command_map_entry *
103
get_cmd_info(const char *cmd)
104
{
105
size_t i;
106
107
for (i = 0; i < nitems(command_map); ++i) {
108
if (strcmp(cmd, command_map[i].command) == 0)
109
return (&command_map[i]);
110
}
111
112
return (NULL);
113
}
114
115
static int
116
bectl_cmd_activate(int argc, char *argv[])
117
{
118
int err, opt;
119
bool temp, reset;
120
121
temp = false;
122
reset = false;
123
while ((opt = getopt(argc, argv, "tT")) != -1) {
124
switch (opt) {
125
case 't':
126
if (reset)
127
return (usage(false));
128
temp = true;
129
break;
130
case 'T':
131
if (temp)
132
return (usage(false));
133
reset = true;
134
break;
135
default:
136
fprintf(stderr, "bectl activate: unknown option '-%c'\n",
137
optopt);
138
return (usage(false));
139
}
140
}
141
142
argc -= optind;
143
argv += optind;
144
145
if (argc != 1 && (!reset || argc != 0)) {
146
fprintf(stderr, "bectl activate: wrong number of arguments\n");
147
return (usage(false));
148
}
149
150
if (reset) {
151
if ((err = be_deactivate(be, NULL, reset)) == 0)
152
printf("Temporary activation removed\n");
153
else
154
printf("Failed to remove temporary activation\n");
155
return (err);
156
}
157
158
/* activate logic goes here */
159
if ((err = be_activate(be, argv[0], temp)) != 0)
160
/* XXX TODO: more specific error msg based on err */
161
printf("Did not successfully activate boot environment %s",
162
argv[0]);
163
else
164
printf("Successfully activated boot environment %s", argv[0]);
165
166
if (temp)
167
printf(" for next boot");
168
169
printf("\n");
170
171
return (err);
172
}
173
174
175
/*
176
* TODO: when only one arg is given, and it contains an "@" the this should
177
* create that snapshot
178
*/
179
static int
180
bectl_cmd_create(int argc, char *argv[])
181
{
182
char snapshot[BE_MAXPATHLEN];
183
char *atpos, *bootenv, *snapname;
184
int err, opt;
185
bool recursive;
186
187
snapname = NULL;
188
recursive = false;
189
while ((opt = getopt(argc, argv, "e:r")) != -1) {
190
switch (opt) {
191
case 'e':
192
snapname = optarg;
193
break;
194
case 'r':
195
recursive = true;
196
break;
197
default:
198
fprintf(stderr, "bectl create: unknown option '-%c'\n",
199
optopt);
200
return (usage(false));
201
}
202
}
203
204
argc -= optind;
205
argv += optind;
206
207
if (argc != 1) {
208
fprintf(stderr, "bectl create: wrong number of arguments\n");
209
return (usage(false));
210
}
211
212
bootenv = *argv;
213
214
err = BE_ERR_SUCCESS;
215
if ((atpos = strchr(bootenv, '@')) != NULL) {
216
/*
217
* This is the "create a snapshot variant". No new boot
218
* environment is to be created here.
219
*/
220
*atpos++ = '\0';
221
err = be_snapshot(be, bootenv, atpos, recursive, NULL);
222
} else {
223
if (snapname == NULL)
224
/* Create from currently booted BE */
225
err = be_snapshot(be, be_active_path(be), NULL,
226
recursive, snapshot);
227
else if (strchr(snapname, '@') != NULL)
228
/* Create from given snapshot */
229
strlcpy(snapshot, snapname, sizeof(snapshot));
230
else
231
/* Create from given BE */
232
err = be_snapshot(be, snapname, NULL, recursive,
233
snapshot);
234
235
if (err == BE_ERR_SUCCESS)
236
err = be_create_depth(be, bootenv, snapshot,
237
recursive == true ? -1 : 0);
238
}
239
240
switch (err) {
241
case BE_ERR_SUCCESS:
242
break;
243
case BE_ERR_INVALIDNAME:
244
fprintf(stderr,
245
"bectl create: boot environment name must not contain spaces\n");
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 *target, targetds[BE_MAXPATHLEN];
346
const char *origin;
347
int err, flags, opt;
348
349
flags = 0;
350
while ((opt = getopt(argc, argv, "Fo")) != -1) {
351
switch (opt) {
352
case 'F':
353
flags |= BE_DESTROY_FORCE;
354
break;
355
case 'o':
356
flags |= BE_DESTROY_ORIGIN;
357
break;
358
default:
359
fprintf(stderr, "bectl destroy: unknown option '-%c'\n",
360
optopt);
361
return (usage(false));
362
}
363
}
364
365
argc -= optind;
366
argv += optind;
367
368
if (argc != 1) {
369
fprintf(stderr, "bectl destroy: wrong number of arguments\n");
370
return (usage(false));
371
}
372
373
target = argv[0];
374
375
/* We'll emit a notice if there's an origin to be cleaned up */
376
if ((flags & BE_DESTROY_ORIGIN) == 0 && strchr(target, '@') == NULL) {
377
flags |= BE_DESTROY_AUTOORIGIN;
378
if (be_root_concat(be, target, targetds) != 0)
379
goto destroy;
380
if (be_prop_list_alloc(&props) != 0)
381
goto destroy;
382
if (be_get_dataset_props(be, targetds, props) != 0) {
383
be_prop_list_free(props);
384
goto destroy;
385
}
386
if (nvlist_lookup_string(props, "origin", &origin) == 0 &&
387
!be_is_auto_snapshot_name(be, origin))
388
fprintf(stderr, "bectl destroy: leaving origin '%s' intact\n",
389
origin);
390
be_prop_list_free(props);
391
}
392
393
destroy:
394
err = be_destroy(be, target, flags);
395
396
return (err);
397
}
398
399
static int
400
bectl_cmd_mount(int argc, char *argv[])
401
{
402
char result_loc[BE_MAXPATHLEN];
403
char *bootenv, *mountpoint;
404
int err, mntflags;
405
406
/* XXX TODO: Allow shallow */
407
mntflags = BE_MNT_DEEP;
408
if (argc < 2) {
409
fprintf(stderr, "bectl mount: missing argument(s)\n");
410
return (usage(false));
411
}
412
413
if (argc > 3) {
414
fprintf(stderr, "bectl mount: too many arguments\n");
415
return (usage(false));
416
}
417
418
bootenv = argv[1];
419
mountpoint = ((argc == 3) ? argv[2] : NULL);
420
421
err = be_mount(be, bootenv, mountpoint, mntflags, result_loc);
422
423
switch (err) {
424
case BE_ERR_SUCCESS:
425
printf("%s\n", result_loc);
426
break;
427
default:
428
fprintf(stderr,
429
(argc == 3) ? "Failed to mount bootenv %s at %s\n" :
430
"Failed to mount bootenv %s at temporary path %s\n",
431
bootenv, mountpoint);
432
}
433
434
return (err);
435
}
436
437
438
static int
439
bectl_cmd_rename(int argc, char *argv[])
440
{
441
char *dest, *src;
442
int err;
443
444
if (argc < 3) {
445
fprintf(stderr, "bectl rename: missing argument\n");
446
return (usage(false));
447
}
448
449
if (argc > 3) {
450
fprintf(stderr, "bectl rename: too many arguments\n");
451
return (usage(false));
452
}
453
454
src = argv[1];
455
dest = argv[2];
456
457
err = be_rename(be, src, dest);
458
switch (err) {
459
case BE_ERR_SUCCESS:
460
break;
461
default:
462
fprintf(stderr, "Failed to rename bootenv %s to %s\n",
463
src, dest);
464
}
465
466
return (err);
467
}
468
469
static int
470
bectl_cmd_unmount(int argc, char *argv[])
471
{
472
char *bootenv, *cmd;
473
int err, flags, opt;
474
475
/* Store alias used */
476
cmd = argv[0];
477
478
flags = 0;
479
while ((opt = getopt(argc, argv, "f")) != -1) {
480
switch (opt) {
481
case 'f':
482
flags |= BE_MNT_FORCE;
483
break;
484
default:
485
fprintf(stderr, "bectl %s: unknown option '-%c'\n",
486
cmd, optopt);
487
return (usage(false));
488
}
489
}
490
491
argc -= optind;
492
argv += optind;
493
494
if (argc != 1) {
495
fprintf(stderr, "bectl %s: wrong number of arguments\n", cmd);
496
return (usage(false));
497
}
498
499
bootenv = argv[0];
500
501
err = be_unmount(be, bootenv, flags);
502
503
switch (err) {
504
case BE_ERR_SUCCESS:
505
break;
506
default:
507
fprintf(stderr, "Failed to unmount bootenv %s\n", bootenv);
508
}
509
510
return (err);
511
}
512
513
static int
514
bectl_cmd_check(int argc, char *argv[] __unused)
515
{
516
517
/* The command is left as argv[0] */
518
if (argc != 1) {
519
fprintf(stderr, "bectl check: wrong number of arguments\n");
520
return (usage(false));
521
}
522
523
return (0);
524
}
525
526
int
527
main(int argc, char *argv[])
528
{
529
struct command_map_entry *cmd;
530
const char *command;
531
char *root = NULL;
532
int opt, rc;
533
534
while ((opt = getopt(argc, argv, "hr:")) != -1) {
535
switch (opt) {
536
case 'h':
537
exit(usage(true));
538
case 'r':
539
root = strdup(optarg);
540
break;
541
default:
542
exit(usage(false));
543
}
544
}
545
546
argc -= optind;
547
argv += optind;
548
549
if (argc == 0)
550
exit(usage(false));
551
552
command = *argv;
553
optreset = 1;
554
optind = 1;
555
556
if ((cmd = get_cmd_info(command)) == NULL) {
557
fprintf(stderr, "Unknown command: %s\n", command);
558
return (usage(false));
559
}
560
561
if ((be = libbe_init(root)) == NULL) {
562
if (!cmd->silent)
563
fprintf(stderr, "libbe_init(\"%s\") failed.\n",
564
root != NULL ? root : "");
565
return (-1);
566
}
567
568
libbe_print_on_error(be, !cmd->silent);
569
570
rc = cmd->fn(argc, argv);
571
572
libbe_close(be);
573
return (rc);
574
}
575
576