]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - sbin/bectl/bectl.c
bectl(8): Remove sneaky comment
[FreeBSD/FreeBSD.git] / sbin / bectl / bectl.c
1 /*
2  * be.c
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/jail.h>
31 #include <sys/mount.h>
32 #include <errno.h>
33 #include <jail.h>
34 #include <libutil.h>
35 #include <stdbool.h>
36 #include <stdio.h>
37 #include <stdint.h>
38 #include <stdlib.h>
39 #include <string.h>
40 #include <sysexits.h>
41 #include <time.h>
42 #include <unistd.h>
43
44 #include <be.h>
45
46 #define HEADER_BE               "BE"
47 #define HEADER_ACTIVE   "Active"
48 #define HEADER_MOUNT    "Mountpoint"
49 #define HEADER_SPACE    "Space"
50 #define HEADER_CREATED  "Created"
51
52 static int bectl_cmd_activate(int argc, char *argv[]);
53 static int bectl_cmd_create(int argc, char *argv[]);
54 static int bectl_cmd_destroy(int argc, char *argv[]);
55 static int bectl_cmd_export(int argc, char *argv[]);
56 static int bectl_cmd_import(int argc, char *argv[]);
57 static int bectl_cmd_add(int argc, char *argv[]);
58 static int bectl_cmd_jail(int argc, char *argv[]);
59 static int bectl_cmd_list(int argc, char *argv[]);
60 static int bectl_cmd_mount(int argc, char *argv[]);
61 static int bectl_cmd_rename(int argc, char *argv[]);
62 static int bectl_search_jail_paths(const char *mnt);
63 static int bectl_locate_jail(const char *ident);
64 static int bectl_cmd_unjail(int argc, char *argv[]);
65 static int bectl_cmd_unmount(int argc, char *argv[]);
66
67 static libbe_handle_t *be;
68
69 static int
70 usage(bool explicit)
71 {
72         FILE *fp;
73
74         fp =  explicit ? stdout : stderr;
75         fprintf(fp,
76             "usage:\tbectl ( -h | -? | subcommand [args...] )\n"
77             "\tbectl activate [-t] beName\n"
78             "\tbectl create [-e nonActiveBe | -e beName@snapshot] beName\n"
79             "\tbectl create beName@snapshot\n"
80             "\tbectl destroy [-F] beName | beName@snapshot⟩\n"
81             "\tbectl export sourceBe\n"
82             "\tbectl import targetBe\n"
83             "\tbectl add (path)*\n"
84             "\tbectl jail bootenv\n"
85             "\tbectl list [-a] [-D] [-H] [-s]\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 };
103
104 static struct command_map_entry command_map[] =
105 {
106         { "activate", bectl_cmd_activate },
107         { "create",   bectl_cmd_create   },
108         { "destroy",  bectl_cmd_destroy  },
109         { "export",   bectl_cmd_export   },
110         { "import",   bectl_cmd_import   },
111         { "add",      bectl_cmd_add      },
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 *index)
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                         *index = 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 *bootenv, *snapname, *source;
188         int err, opt;
189
190         snapname = NULL;
191         while ((opt = getopt(argc, argv, "e:")) != -1) {
192                 switch (opt) {
193                 case 'e':
194                         snapname = optarg;
195                         break;
196                 default:
197                         fprintf(stderr, "bectl create: unknown option '-%c'\n",
198                             optopt);
199                         return (usage(false));
200                 }
201         }
202
203         argc -= optind;
204         argv += optind;
205
206         if (argc != 1) {
207                 fprintf(stderr, "bectl create: wrong number of arguments\n");
208                 return (usage(false));
209         }
210
211         bootenv = *argv;
212
213         if (snapname != NULL) {
214                 if (strchr(snapname, '@') != NULL)
215                         err = be_create_from_existing_snap(be, bootenv,
216                             snapname);
217                 else
218                         err = be_create_from_existing(be, bootenv, snapname);
219         } else {
220                 if ((snapname = strchr(bootenv, '@')) != NULL) {
221                         *(snapname++) = '\0';
222                         if ((err = be_snapshot(be, be_active_path(be),
223                             snapname, true, NULL)) != BE_ERR_SUCCESS)
224                                 fprintf(stderr, "failed to create snapshot\n");
225                         asprintf(&source, "%s@%s", be_active_path(be), snapname);
226                         err = be_create_from_existing_snap(be, bootenv,
227                             source);
228                         return (err);
229                 } else
230                         err = be_create(be, bootenv);
231         }
232
233         switch (err) {
234         case BE_ERR_SUCCESS:
235                 break;
236         default:
237                 if (snapname == NULL)
238                         fprintf(stderr,
239                             "failed to create bootenv %s\n", bootenv);
240                 else
241                         fprintf(stderr,
242                             "failed to create bootenv %s from snapshot %s\n",
243                             bootenv, snapname);
244         }
245
246         return (err);
247 }
248
249
250 static int
251 bectl_cmd_export(int argc, char *argv[])
252 {
253         char *bootenv;
254
255         if (argc == 1) {
256                 fprintf(stderr, "bectl export: missing boot environment name\n");
257                 return (usage(false));
258         }
259
260         if (argc > 2) {
261                 fprintf(stderr, "bectl export: extra arguments provided\n");
262                 return (usage(false));
263         }
264
265         bootenv = argv[1];
266
267         if (isatty(STDOUT_FILENO)) {
268                 fprintf(stderr, "bectl export: must redirect output\n");
269                 return (EX_USAGE);
270         }
271
272         be_export(be, bootenv, STDOUT_FILENO);
273
274         return (0);
275 }
276
277
278 static int
279 bectl_cmd_import(int argc, char *argv[])
280 {
281         char *bootenv;
282         int err;
283
284         if (argc == 1) {
285                 fprintf(stderr, "bectl import: missing boot environment name\n");
286                 return (usage(false));
287         }
288
289         if (argc > 2) {
290                 fprintf(stderr, "bectl import: extra arguments provided\n");
291                 return (usage(false));
292         }
293
294         bootenv = argv[1];
295
296         if (isatty(STDIN_FILENO)) {
297                 fprintf(stderr, "bectl import: input can not be from terminal\n");
298                 return (EX_USAGE);
299         }
300
301         err = be_import(be, bootenv, STDIN_FILENO);
302
303         return (err);
304 }
305
306
307 static int
308 bectl_cmd_add(int argc, char *argv[])
309 {
310
311         if (argc < 2) {
312                 fprintf(stderr, "bectl add: must provide at least one path\n");
313                 return (usage(false));
314         }
315
316         for (int i = 1; i < argc; ++i) {
317                 printf("arg %d: %s\n", i, argv[i]);
318                 /* XXX TODO catch err */
319                 be_add_child(be, argv[i], true);
320         }
321
322         return (0);
323 }
324
325
326 static int
327 bectl_cmd_destroy(int argc, char *argv[])
328 {
329         char *target;
330         int opt, err;
331         bool force;
332
333         force = false;
334         while ((opt = getopt(argc, argv, "F")) != -1) {
335                 switch (opt) {
336                 case 'F':
337                         force = true;
338                         break;
339                 default:
340                         fprintf(stderr, "bectl destroy: unknown option '-%c'\n",
341                             optopt);
342                         return (usage(false));
343                 }
344         }
345
346         argc -= optind;
347         argv += optind;
348
349         if (argc != 1) {
350                 fprintf(stderr, "bectl destroy: wrong number of arguments\n");
351                 return (usage(false));
352         }
353
354         target = argv[0];
355
356         err = be_destroy(be, target, force);
357
358         return (err);
359 }
360
361
362 static int
363 bectl_cmd_jail(int argc, char *argv[])
364 {
365         char *bootenv;
366         char mnt_loc[BE_MAXPATHLEN];
367         int err, jid;
368
369         /* struct jail be_jail = { 0 }; */
370
371         if (argc == 1) {
372                 fprintf(stderr, "bectl jail: missing boot environment name\n");
373                 return (usage(false));
374         }
375         if (argc > 2) {
376                 fprintf(stderr, "bectl jail: too many arguments\n");
377                 return (usage(false));
378         }
379
380         bootenv = argv[1];
381
382         /*
383          * XXX TODO: if its already mounted, perhaps there should be a flag to
384          * indicate its okay to proceed??
385          */
386         if ((err = be_mount(be, bootenv, NULL, 0, mnt_loc)) != BE_ERR_SUCCESS) {
387                 fprintf(stderr, "could not mount bootenv\n");
388                 return (1);
389         }
390
391         /* XXX TODO: Make the IP/hostname configurable? */
392         jid = jail_setv(JAIL_CREATE | JAIL_ATTACH,
393             "name", bootenv,
394             "path", mnt_loc,
395             "host.hostname", bootenv,
396             "persist", "true",
397             "ip4.addr", "10.20.30.40",
398             "allow.mount", "true",
399             "allow.mount.devfs", "true",
400             "enforce_statfs", "1",
401             NULL);
402         if (jid == -1) {
403                 fprintf(stderr, "unable to create jail.  error: %d\n", errno);
404                 return (1);
405         }
406
407         /* We're attached within the jail... good bye! */
408         chdir("/");
409         execl("/bin/sh", "/bin/sh", NULL);
410         return (0);
411 }
412
413
414 static int
415 bectl_cmd_list(int argc, char *argv[])
416 {
417 #define BUFSZ   64
418         nvpair_t *cur;
419         nvlist_t *props, *dsprops;
420         unsigned long long ctimenum, space;
421         size_t be_maxcol;
422         int active_colsz, active_colsz_def, be_colsz, mount_colsz, opt, space_colsz;
423         char buf[BUFSZ], *creation, *mnt, *spacestr;
424         boolean_t active_now, active_reboot;
425         bool show_all_datasets, show_space, hide_headers, show_snaps;
426
427         props = NULL;
428         show_all_datasets = show_space = hide_headers = show_snaps = false;
429         while ((opt = getopt(argc, argv, "aDHs")) != -1) {
430                 switch (opt) {
431                 case 'a':
432                         show_all_datasets = true;
433                         break;
434                 case 'D':
435                         show_space = true;
436                         break;
437                 case 'H':
438                         hide_headers = true;
439                         break;
440                 case 's':
441                         show_space = true;
442                         break;
443                 default:
444                         fprintf(stderr, "bectl list: unknown option '-%c'\n",
445                             optopt);
446                         return (usage(false));
447                 }
448         }
449
450         argc -= optind;
451
452         if (argc != 0) {
453                 fprintf(stderr, "bectl list: extra argument provided\n");
454                 return (usage(false));
455         }
456
457         if (be_prop_list_alloc(&props) != 0) {
458                 fprintf(stderr, "bectl list: failed to allocate prop nvlist\n");
459                 return (1);
460         }
461         if (be_get_bootenv_props(be, props) != 0) {
462                 /* XXX TODO: Real errors */
463                 fprintf(stderr, "bectl list: failed to fetch boot environments\n");
464                 return (1);
465         }
466
467         be_maxcol = strlen(HEADER_BE);
468         for (cur = nvlist_next_nvpair(props, NULL); cur != NULL;
469             cur = nvlist_next_nvpair(props, cur)) {
470                 be_maxcol = MAX(be_maxcol, strlen(nvpair_name(cur)));
471         }
472
473         be_colsz = -be_maxcol;
474         /* To be made negative after calculating final col sz */
475         active_colsz_def = strlen(HEADER_ACTIVE);
476         mount_colsz = -(int)strlen(HEADER_MOUNT);
477         space_colsz = -(int)strlen(HEADER_SPACE);
478         printf("%*s %s %s %s %s\n", be_colsz, HEADER_BE, HEADER_ACTIVE,
479             HEADER_MOUNT, HEADER_SPACE, HEADER_CREATED);
480         buf[5] = '\0';
481         cur = NULL;
482         for (cur = nvlist_next_nvpair(props, NULL); cur != NULL;
483             cur = nvlist_next_nvpair(props, cur)) {
484                 printf("%*s ", be_colsz, nvpair_name(cur));
485                 // NR
486                 active_colsz = active_colsz_def;
487                 nvpair_value_nvlist(cur, &dsprops);
488                 if (nvlist_lookup_boolean_value(dsprops, "active",
489                     &active_now) == 0 && active_now) {
490                         printf("N");
491                         active_colsz--;
492                 }
493                 if (nvlist_lookup_boolean_value(dsprops, "nextboot",
494                     &active_reboot) == 0 && active_reboot) {
495                         printf("R");
496                         active_colsz--;
497                 }
498                 if (active_colsz == active_colsz_def) {
499                         printf("-");
500                         active_colsz--;
501                 }
502                 printf("%*s ", -active_colsz, " ");
503                 if (nvlist_lookup_string(dsprops, "mountpoint", &mnt) == 0)
504                         printf("%*s ", mount_colsz, mnt);
505                 else
506                         printf("%*s ", mount_colsz, "-");
507                 // used
508                 if (nvlist_lookup_string(dsprops, "used", &spacestr) == 0) {
509                         space = strtoull(spacestr, NULL, 10);
510                         humanize_number(buf, 6, space, "", HN_AUTOSCALE,
511                                 HN_DECIMAL | HN_NOSPACE | HN_B);
512                         printf("%*s ", space_colsz, buf);
513                 } else
514                         printf("%*s ", space_colsz, "-");
515
516                 if (nvlist_lookup_string(dsprops, "creation", &creation) == 0) {
517                         ctimenum = strtoull(creation, NULL, 10);
518                         strftime(buf, BUFSZ, "%Y-%m-%d %H:%M",
519                             localtime((time_t *)&ctimenum));
520                         printf("%s", buf);
521                 }
522
523                 printf("\n");
524         }
525         be_prop_list_free(props);
526
527         return (0);
528 #undef BUFSZ
529 }
530
531
532 static int
533 bectl_cmd_mount(int argc, char *argv[])
534 {
535         char result_loc[BE_MAXPATHLEN];
536         char *bootenv, *mountpoint;
537         int err;
538
539         if (argc < 2) {
540                 fprintf(stderr, "bectl mount: missing argument(s)\n");
541                 return (usage(false));
542         }
543
544         if (argc > 3) {
545                 fprintf(stderr, "bectl mount: too many arguments\n");
546                 return (usage(false));
547         }
548
549         bootenv = argv[1];
550         mountpoint = ((argc == 3) ? argv[2] : NULL);
551
552         err = be_mount(be, bootenv, mountpoint, 0, result_loc);
553
554         switch (err) {
555         case BE_ERR_SUCCESS:
556                 printf("successfully mounted %s at %s\n", bootenv, result_loc);
557                 break;
558         default:
559                 fprintf(stderr,
560                     (argc == 3) ? "failed to mount bootenv %s at %s\n" :
561                     "failed to mount bootenv %s at temporary path %s\n",
562                     bootenv, mountpoint);
563         }
564
565         return (err);
566 }
567
568
569 static int
570 bectl_cmd_rename(int argc, char *argv[])
571 {
572         char *dest, *src;
573         int err;
574
575         if (argc < 3) {
576                 fprintf(stderr, "bectl rename: missing argument\n");
577                 return (usage(false));
578         }
579
580         if (argc > 3) {
581                 fprintf(stderr, "bectl rename: too many arguments\n");
582                 return (usage(false));
583         }
584
585         src = argv[1];
586         dest = argv[2];
587
588         err = be_rename(be, src, dest);
589
590         switch (err) {
591         case BE_ERR_SUCCESS:
592                 break;
593         default:
594                 fprintf(stderr, "failed to rename bootenv %s to %s\n",
595                     src, dest);
596         }
597
598         return (0);
599 }
600
601 static int
602 bectl_search_jail_paths(const char *mnt)
603 {
604         char jailpath[MAXPATHLEN + 1];
605         int jid;
606
607         jid = 0;
608         (void)mnt;
609         while ((jid = jail_getv(0, "lastjid", &jid, "path", &jailpath,
610             NULL)) != -1) {
611                 if (strcmp(jailpath, mnt) == 0)
612                         return (jid);
613         }
614
615         return (-1);
616 }
617
618 /*
619  * Locate a jail based on an arbitrary identifier.  This may be either a name,
620  * a jid, or a BE name.  Returns the jid or -1 on failure.
621  */
622 static int
623 bectl_locate_jail(const char *ident)
624 {
625         nvlist_t *belist, *props;
626         char *mnt;
627         int jid;
628
629         /* Try the easy-match first */
630         jid = jail_getid(ident);
631         if (jid != -1)
632                 return (jid);
633
634         /* Attempt to try it as a BE name, first */
635         if (be_prop_list_alloc(&belist) != 0)
636                 return (-1);
637
638         if (be_get_bootenv_props(be, belist) != 0)
639                 return (-1);
640
641         if (nvlist_lookup_nvlist(belist, ident, &props) == 0) {
642                 /* We'll attempt to resolve the jid by way of mountpoint */
643                 if (nvlist_lookup_string(props, "mountpoint", &mnt) == 0) {
644                         jid = bectl_search_jail_paths(mnt);
645                         be_prop_list_free(belist);
646                         return (jid);
647                 }
648
649                 be_prop_list_free(belist);
650         }
651
652         return (-1);
653 }
654
655 static int
656 bectl_cmd_unjail(int argc, char *argv[])
657 {
658         char path[MAXPATHLEN + 1];
659         char *cmd, *name, *target;
660         int jid;
661
662         /* Store alias used */
663         cmd = argv[0];
664
665         if (argc != 2) {
666                 fprintf(stderr, "bectl %s: wrong number of arguments\n", cmd);
667                 return (usage(false));
668         }
669
670         target = argv[1];
671
672         /* Locate the jail */
673         if ((jid = bectl_locate_jail(target)) == -1) {
674                 fprintf(stderr, "bectl %s: failed to locate BE by '%s'\n", cmd, target);
675                 return (1);
676         }
677
678         bzero(&path, MAXPATHLEN + 1);
679         name = jail_getname(jid);
680         if (jail_getv(0, "name", name, "path", path, NULL) != jid) {
681                 free(name);
682                 fprintf(stderr, "bectl %s: failed to get path for jail requested by '%s'\n", cmd, target);
683                 return (1);
684         }
685
686         free(name);
687
688         if (be_mounted_at(be, path, NULL) != 0) {
689                 fprintf(stderr, "bectl %s: jail requested by '%s' not a BE\n", cmd, target);
690                 return (1);
691         }
692
693         jail_remove(jid);
694         unmount(path, 0);
695
696         return (0);
697 }
698
699
700 static int
701 bectl_cmd_unmount(int argc, char *argv[])
702 {
703         char *bootenv, *cmd;
704         int err, flags, opt;
705
706         /* Store alias used */
707         cmd = argv[0];
708
709         flags = 0;
710         while ((opt = getopt(argc, argv, "f")) != -1) {
711                 switch (opt) {
712                 case 'f':
713                         flags |= BE_MNT_FORCE;
714                         break;
715                 default:
716                         fprintf(stderr, "bectl %s: unknown option '-%c'\n",
717                             cmd, optopt);
718                         return (usage(false));
719                 }
720         }
721
722         argc -= optind;
723         argv += optind;
724
725         if (argc != 1) {
726                 fprintf(stderr, "bectl %s: wrong number of arguments\n", cmd);
727                 return (usage(false));
728         }
729
730         bootenv = argv[0];
731
732         err = be_unmount(be, bootenv, flags);
733
734         switch (err) {
735         case BE_ERR_SUCCESS:
736                 break;
737         default:
738                 fprintf(stderr, "failed to unmount bootenv %s\n", bootenv);
739         }
740
741         return (err);
742 }
743
744
745 int
746 main(int argc, char *argv[])
747 {
748         const char *command;
749         int command_index, rc;
750
751         if (argc < 2) {
752                 fprintf(stderr, "missing command\n");
753                 return (usage(false));
754         }
755
756         command = argv[1];
757
758         /* Handle command aliases */
759         if (strcmp(command, "umount") == 0)
760                 command = "unmount";
761
762         if (strcmp(command, "ujail") == 0)
763                 command = "unjail";
764
765         if ((strcmp(command, "-?") == 0) || (strcmp(command, "-h") == 0))
766                 return (usage(true));
767
768         if (get_cmd_index(command, &command_index)) {
769                 fprintf(stderr, "unknown command: %s\n", command);
770                 return (usage(false));
771         }
772
773
774         if ((be = libbe_init()) == NULL)
775                 return (-1);
776
777         libbe_print_on_error(be, true);
778
779         /* XXX TODO: can be simplified if offset by 2 instead of one */
780         rc = command_map[command_index].fn(argc-1, argv+1);
781
782         libbe_close(be);
783         return (rc);
784 }