1 /*
2 * CDDL HEADER START
3 *
4 * The contents of this file are subject to the terms of the
5 * Common Development and Distribution License (the "License").
6 * You may not use this file except in compliance with the License.
7 *
8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9 * or http://www.opensolaris.org/os/licensing.
10 * See the License for the specific language governing permissions
11 * and limitations under the License.
12 *
13 * When distributing Covered Code, include this CDDL HEADER in each
14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15 * If applicable, add the following below this CDDL HEADER, with the
16 * fields enclosed by brackets "[]" replaced with your own identifying
17 * information: Portions Copyright [yyyy] [name of copyright owner]
18 *
19 * CDDL HEADER END
20 */
21
22 /*
23 * Copyright (c) 2008, 2010, Oracle and/or its affiliates. All rights reserved.
24 * Copyright 2013 Nexenta Systems, Inc. All rights reserved.
25 * Copyright 2016 Toomas Soome <tsoome@me.com>
26 * Copyright (c) 2015 by Delphix. All rights reserved.
27 * Copyright 2022 OmniOS Community Edition (OmniOSce) Association.
28 * Copyright (c) 2018, Joyent, Inc.
29 */
30
31
32 /*
33 * System includes
34 */
35 #include <assert.h>
36 #include <errno.h>
37 #include <libgen.h>
38 #include <libintl.h>
39 #include <libnvpair.h>
40 #include <libzfs.h>
41 #include <libgen.h>
42 #include <stdio.h>
43 #include <stdlib.h>
44 #include <string.h>
45 #include <sys/stat.h>
46 #include <sys/types.h>
47 #include <sys/vfstab.h>
48 #include <sys/param.h>
49 #include <sys/systeminfo.h>
50 #include <ctype.h>
51 #include <time.h>
52 #include <unistd.h>
53 #include <fcntl.h>
54 #include <deflt.h>
55 #include <wait.h>
56 #include <libdevinfo.h>
57 #include <libgen.h>
58
59 #include <libbe.h>
60 #include <libbe_priv.h>
61 #include <boot_utils.h>
62 #include <ficl.h>
63 #include <ficlplatform/emu.h>
64
65 /* Private function prototypes */
66 static int update_dataset(char *, int, char *, char *, char *);
67 static int _update_vfstab(char *, char *, char *, char *, be_fs_list_data_t *);
68 static int be_open_menu(char *, char *, FILE **, char *, boolean_t);
69 static int be_create_menu(char *, char *, FILE **, char *);
70 static char *be_get_auto_name(char *, char *, boolean_t);
71
72 /*
73 * Global error printing
74 */
75 boolean_t do_print = B_FALSE;
76
77 /*
78 * Private datatypes
79 */
80 typedef struct zone_be_name_cb_data {
81 char *base_be_name;
82 int num;
83 } zone_be_name_cb_data_t;
84
85 /* ******************************************************************** */
86 /* Public Functions */
87 /* ******************************************************************** */
88
89 /*
90 * Callback for ficl to suppress all output from ficl, as we do not
91 * want to confuse user with messages from ficl, and we are only
92 * checking results from function calls.
93 */
94 /*ARGSUSED*/
95 static void
ficlSuppressTextOutput(ficlCallback * cb,char * text)96 ficlSuppressTextOutput(ficlCallback *cb, char *text)
97 {
98 /* This function is intentionally doing nothing. */
99 }
100
101 /*
102 * Function: be_get_boot_args
103 * Description: Returns the fast boot argument string for enumerated BE.
104 * Parameters:
105 * fbarg - pointer to argument string.
106 * entry - index of BE.
107 * Returns:
108 * fast boot argument string.
109 * Scope:
110 * Public
111 */
112 int
be_get_boot_args(char ** fbarg,int entry)113 be_get_boot_args(char **fbarg, int entry)
114 {
115 be_node_list_t *node, *be_nodes = NULL;
116 be_transaction_data_t bt = {0};
117 char *mountpoint = NULL;
118 boolean_t be_mounted = B_FALSE;
119 int ret = BE_SUCCESS;
120 int index;
121 ficlVm *vm;
122
123 *fbarg = NULL;
124 if (!be_zfs_init())
125 return (BE_ERR_INIT);
126
127 /*
128 * need pool name, menu.lst has entries from our pool only
129 */
130 ret = be_find_current_be(&bt);
131 if (ret != BE_SUCCESS) {
132 be_zfs_fini();
133 return (ret);
134 }
135
136 /*
137 * be_get_boot_args() is for loader, fail with grub will trigger
138 * normal boot.
139 */
140 if (be_has_grub()) {
141 ret = BE_ERR_INIT;
142 goto done;
143 }
144
145 ret = _be_list(NULL, &be_nodes, BE_LIST_DEFAULT);
146 if (ret != BE_SUCCESS)
147 goto done;
148
149 /*
150 * iterate through be_nodes,
151 * if entry == -1, stop if be_active_on_boot,
152 * else stop if index == entry.
153 */
154 index = 0;
155 for (node = be_nodes; node != NULL; node = node->be_next_node) {
156 if (strcmp(node->be_rpool, bt.obe_zpool) != 0)
157 continue;
158 if (entry == BE_ENTRY_DEFAULT &&
159 node->be_active_on_boot == B_TRUE)
160 break;
161 if (index == entry)
162 break;
163 index++;
164 }
165 if (node == NULL) {
166 be_free_list(be_nodes);
167 ret = BE_ERR_NOENT;
168 goto done;
169 }
170
171 /* try to mount inactive be */
172 if (node->be_active == B_FALSE) {
173 ret = _be_mount(node->be_node_name, &mountpoint,
174 BE_MOUNT_FLAG_NO_ZONES);
175 if (ret != BE_SUCCESS && ret != BE_ERR_MOUNTED) {
176 be_free_list(be_nodes);
177 goto done;
178 } else
179 be_mounted = B_TRUE;
180 }
181
182 vm = bf_init("", ficlSuppressTextOutput);
183 if (vm != NULL) {
184 /*
185 * zfs MAXNAMELEN is 256, so we need to pick buf large enough
186 * to contain such names.
187 */
188 char buf[MAXNAMELEN * 2];
189 char *kernel_options = NULL;
190 char *kernel = NULL;
191 char *tmp;
192 zpool_handle_t *zph;
193
194 /*
195 * just try to interpret following words. on error
196 * we will be missing kernelname, and will get out.
197 */
198 (void) snprintf(buf, sizeof (buf), "set currdev=zfs:%s:",
199 node->be_root_ds);
200 ret = ficlVmEvaluate(vm, buf);
201 if (ret != FICL_VM_STATUS_OUT_OF_TEXT) {
202 be_print_err(gettext("be_get_boot_args: error "
203 "interpreting boot config: %d\n"), ret);
204 bf_fini();
205 ret = BE_ERR_NO_MENU;
206 goto cleanup;
207 }
208 (void) snprintf(buf, sizeof (buf),
209 "include /boot/forth/loader.4th");
210 ret = ficlVmEvaluate(vm, buf);
211 if (ret != FICL_VM_STATUS_OUT_OF_TEXT) {
212 be_print_err(gettext("be_get_boot_args: error "
213 "interpreting boot config: %d\n"), ret);
214 bf_fini();
215 ret = BE_ERR_NO_MENU;
216 goto cleanup;
217 }
218 (void) snprintf(buf, sizeof (buf), "start");
219 ret = ficlVmEvaluate(vm, buf);
220 if (ret != FICL_VM_STATUS_OUT_OF_TEXT) {
221 be_print_err(gettext("be_get_boot_args: error "
222 "interpreting boot config: %d\n"), ret);
223 bf_fini();
224 ret = BE_ERR_NO_MENU;
225 goto cleanup;
226 }
227 (void) snprintf(buf, sizeof (buf), "boot");
228 ret = ficlVmEvaluate(vm, buf);
229 bf_fini();
230 if (ret != FICL_VM_STATUS_OUT_OF_TEXT) {
231 be_print_err(gettext("be_get_boot_args: error "
232 "interpreting boot config: %d\n"), ret);
233 ret = BE_ERR_NO_MENU;
234 goto cleanup;
235 }
236
237 kernel_options = getenv("boot-args");
238 kernel = getenv("kernelname");
239
240 if (kernel == NULL) {
241 be_print_err(gettext("be_get_boot_args: no kernel\n"));
242 ret = BE_ERR_NOENT;
243 goto cleanup;
244 }
245
246 if ((zph = zpool_open(g_zfs, node->be_rpool)) == NULL) {
247 be_print_err(gettext("be_get_boot_args: failed to "
248 "open root pool (%s): %s\n"), node->be_rpool,
249 libzfs_error_description(g_zfs));
250 ret = zfs_err_to_be_err(g_zfs);
251 goto cleanup;
252 }
253 ret = zpool_get_physpath(zph, buf, sizeof (buf));
254 zpool_close(zph);
255 if (ret != 0) {
256 be_print_err(gettext("be_get_boot_args: failed to "
257 "get physpath\n"));
258 goto cleanup;
259 }
260
261 /* zpool_get_physpath() can return space separated list */
262 tmp = buf;
263 tmp = strsep(&tmp, " ");
264
265 if (kernel_options == NULL || *kernel_options == '\0')
266 (void) asprintf(fbarg, "/ %s "
267 "-B zfs-bootfs=%s,bootpath=\"%s\"\n", kernel,
268 node->be_root_ds, tmp);
269 else
270 (void) asprintf(fbarg, "/ %s %s "
271 "-B zfs-bootfs=%s,bootpath=\"%s\"\n", kernel,
272 kernel_options, node->be_root_ds, tmp);
273
274 if (*fbarg == NULL)
275 ret = BE_ERR_NOMEM;
276 else
277 ret = 0;
278 } else
279 ret = BE_ERR_NOMEM;
280 cleanup:
281 if (be_mounted == B_TRUE)
282 (void) _be_unmount(node->be_node_name, BE_UNMOUNT_FLAG_FORCE);
283 be_free_list(be_nodes);
284 done:
285 free(mountpoint);
286 free(bt.obe_name);
287 free(bt.obe_root_ds);
288 free(bt.obe_zpool);
289 free(bt.obe_snap_name);
290 free(bt.obe_altroot);
291 be_zfs_fini();
292 return (ret);
293 }
294
295 /*
296 * Function: be_max_avail
297 * Description: Returns the available size for the zfs dataset passed in.
298 * Parameters:
299 * dataset - The dataset we want to get the available space for.
300 * ret - The available size will be returned in this.
301 * Returns:
302 * The error returned by the zfs get property function.
303 * Scope:
304 * Public
305 */
306 int
be_max_avail(char * dataset,uint64_t * ret)307 be_max_avail(char *dataset, uint64_t *ret)
308 {
309 zfs_handle_t *zhp;
310 int err = 0;
311
312 /* Initialize libzfs handle */
313 if (!be_zfs_init())
314 return (BE_ERR_INIT);
315
316 zhp = zfs_open(g_zfs, dataset, ZFS_TYPE_DATASET);
317 if (zhp == NULL) {
318 /*
319 * The zfs_open failed return an error
320 */
321 err = zfs_err_to_be_err(g_zfs);
322 } else {
323 err = be_maxsize_avail(zhp, ret);
324 }
325 ZFS_CLOSE(zhp);
326 be_zfs_fini();
327 return (err);
328 }
329
330 /*
331 * Function: libbe_print_errors
332 * Description: Turns on/off error output for the library.
333 * Parameter:
334 * set_do_print - Boolean that turns library error
335 * printing on or off.
336 * Returns:
337 * None
338 * Scope:
339 * Public;
340 */
341 void
libbe_print_errors(boolean_t set_do_print)342 libbe_print_errors(boolean_t set_do_print)
343 {
344 do_print = set_do_print;
345 }
346
347 /* ******************************************************************** */
348 /* Semi-Private Functions */
349 /* ******************************************************************** */
350
351 /*
352 * Function: be_zfs_init
353 * Description: Initializes the libary global libzfs handle.
354 * Parameters:
355 * None
356 * Returns:
357 * B_TRUE - Success
358 * B_FALSE - Failure
359 * Scope:
360 * Semi-private (library wide use only)
361 */
362 boolean_t
be_zfs_init(void)363 be_zfs_init(void)
364 {
365 be_zfs_fini();
366
367 if ((g_zfs = libzfs_init()) == NULL) {
368 be_print_err(gettext("be_zfs_init: failed to initialize ZFS "
369 "library\n"));
370 return (B_FALSE);
371 }
372
373 return (B_TRUE);
374 }
375
376 /*
377 * Function: be_zfs_fini
378 * Description: Closes the library global libzfs handle if it currently open.
379 * Parameter:
380 * None
381 * Returns:
382 * None
383 * Scope:
384 * Semi-private (library wide use only)
385 */
386 void
be_zfs_fini(void)387 be_zfs_fini(void)
388 {
389 if (g_zfs)
390 libzfs_fini(g_zfs);
391
392 g_zfs = NULL;
393 }
394
395 /*
396 * Function: be_get_defaults
397 * Description: Open defaults and gets be default paramets
398 * Parameters:
399 * defaults - be defaults struct
400 * Returns:
401 * None
402 * Scope:
403 * Semi-private (library wide use only)
404 */
405 void
be_get_defaults(struct be_defaults * defaults)406 be_get_defaults(struct be_defaults *defaults)
407 {
408 void *defp;
409
410 defaults->be_deflt_grub = B_FALSE;
411 defaults->be_deflt_rpool_container = B_FALSE;
412 defaults->be_deflt_bename_starts_with[0] = '\0';
413
414 if ((defp = defopen_r(BE_DEFAULTS)) != NULL) {
415 const char *res = defread_r(BE_DFLT_BENAME_STARTS, defp);
416 if (res != NULL && res[0] != '\0') {
417 (void) strlcpy(defaults->be_deflt_bename_starts_with,
418 res, ZFS_MAX_DATASET_NAME_LEN);
419 defaults->be_deflt_rpool_container = B_TRUE;
420 }
421 if (be_is_isa("i386")) {
422 res = defread_r(BE_DFLT_BE_HAS_GRUB, defp);
423 if (res != NULL && res[0] != '\0') {
424 if (strcasecmp(res, "true") == 0)
425 defaults->be_deflt_grub = B_TRUE;
426 }
427 }
428 defclose_r(defp);
429 }
430 }
431
432 /*
433 * Function: be_make_root_ds
434 * Description: Generate string for BE's root dataset given the pool
435 * it lives in and the BE name.
436 * Parameters:
437 * zpool - pointer zpool name.
438 * be_name - pointer to BE name.
439 * be_root_ds - pointer to buffer to return BE root dataset in.
440 * be_root_ds_size - size of be_root_ds
441 * Returns:
442 * BE_SUCCESS - Success
443 * be_errno_t - Failure
444 * Scope:
445 * Semi-private (library wide use only)
446 */
447 int
be_make_root_ds(const char * zpool,const char * be_name,char * be_root_ds,int be_root_ds_size)448 be_make_root_ds(const char *zpool, const char *be_name, char *be_root_ds,
449 int be_root_ds_size)
450 {
451 struct be_defaults be_defaults;
452 be_get_defaults(&be_defaults);
453
454 assert(zpool != NULL);
455
456 if (getzoneid() == GLOBAL_ZONEID) {
457 if (be_defaults.be_deflt_rpool_container) {
458 (void) snprintf(be_root_ds, be_root_ds_size,
459 "%s/%s", zpool, be_name);
460 } else {
461 (void) snprintf(be_root_ds, be_root_ds_size,
462 "%s/%s/%s", zpool, BE_CONTAINER_DS_NAME, be_name);
463 }
464 } else {
465 /*
466 * In a non-global zone we can use the path from the mounted
467 * root dataset to generate the BE's root dataset string.
468 */
469 char *root_ds = be_get_ds_from_dir("/");
470
471 if (root_ds == NULL) {
472 be_print_err(gettext("be_make_root_ds: zone root "
473 "dataset is not mounted\n"));
474 return (BE_ERR_NOTMOUNTED);
475 }
476 if (strncmp(root_ds, zpool, strlen(zpool)) != 0 ||
477 root_ds[strlen(zpool)] != '/') {
478 /*
479 * This pool is not the one that contains the zone
480 * root.
481 */
482 return (BE_ERR_ACCESS);
483 }
484
485 (void) snprintf(be_root_ds, be_root_ds_size, "%s/%s",
486 dirname(root_ds), be_name);
487 }
488
489 return (BE_SUCCESS);
490 }
491
492 /*
493 * Function: be_make_container_ds
494 * Description: Generate string for the BE container dataset given a pool name.
495 * Parameters:
496 * zpool - pointer zpool name.
497 * container_ds - pointer to buffer to return BE container
498 * dataset in.
499 * container_ds_size - size of container_ds
500 * Returns:
501 * BE_SUCCESS - Success
502 * be_errno_t - Failure
503 * Scope:
504 * Semi-private (library wide use only)
505 */
506 int
be_make_container_ds(const char * zpool,char * container_ds,int container_ds_size)507 be_make_container_ds(const char *zpool, char *container_ds,
508 int container_ds_size)
509 {
510 struct be_defaults be_defaults;
511 be_get_defaults(&be_defaults);
512
513 if (getzoneid() == GLOBAL_ZONEID) {
514 if (be_defaults.be_deflt_rpool_container) {
515 (void) snprintf(container_ds, container_ds_size,
516 "%s", zpool);
517 } else {
518 (void) snprintf(container_ds, container_ds_size,
519 "%s/%s", zpool, BE_CONTAINER_DS_NAME);
520 }
521 } else {
522 char *root_ds = be_get_ds_from_dir("/");
523
524 if (root_ds == NULL) {
525 be_print_err(gettext("be_make_container_ds: zone root "
526 "dataset is not mounted\n"));
527 return (BE_ERR_NOTMOUNTED);
528 }
529 if (strncmp(root_ds, zpool, strlen(zpool)) != 0 ||
530 root_ds[strlen(zpool)] != '/') {
531 /*
532 * This pool is not the one that contains the zone
533 * root.
534 */
535 return (BE_ERR_ACCESS);
536 }
537 (void) strlcpy(container_ds, dirname(root_ds),
538 container_ds_size);
539 }
540
541 return (BE_SUCCESS);
542 }
543
544 /*
545 * Function: be_make_root_container_ds
546 * Description: Generate string for the BE root container dataset given a pool
547 * name.
548 * Parameters:
549 * zpool - pointer zpool name.
550 * container_ds - pointer to buffer in which to return result
551 * container_ds_size - size of container_ds
552 * Returns:
553 * BE_SUCCESS - Success
554 * be_errno_t - Failure
555 * Scope:
556 * Semi-private (library wide use only)
557 */
558 int
be_make_root_container_ds(const char * zpool,char * container_ds,int container_ds_size)559 be_make_root_container_ds(const char *zpool, char *container_ds,
560 int container_ds_size)
561 {
562 char *root;
563 int ret;
564
565 if ((ret = be_make_container_ds(zpool, container_ds,
566 container_ds_size)) != BE_SUCCESS) {
567 return (ret);
568 }
569
570 /* If the container DS ends with /ROOT, remove it. */
571
572 if ((root = strrchr(container_ds, '/')) != NULL &&
573 strcmp(root + 1, BE_CONTAINER_DS_NAME) == 0) {
574 *root = '\0';
575 }
576
577 return (BE_SUCCESS);
578 }
579
580 /*
581 * Function: be_make_name_from_ds
582 * Description: This function takes a dataset name and strips off the
583 * BE container dataset portion from the beginning. The
584 * returned name is allocated in heap storage, so the caller
585 * is responsible for freeing it.
586 * Parameters:
587 * dataset - dataset to get name from.
588 * rc_loc - dataset underwhich the root container dataset lives.
589 * Returns:
590 * name of dataset relative to BE container dataset.
591 * NULL if dataset is not under a BE root dataset.
592 * Scope:
593 * Semi-primate (library wide use only)
594 */
595 char *
be_make_name_from_ds(const char * dataset,char * rc_loc)596 be_make_name_from_ds(const char *dataset, char *rc_loc)
597 {
598 char ds[ZFS_MAX_DATASET_NAME_LEN];
599 char *tok = NULL;
600 char *name = NULL;
601 struct be_defaults be_defaults;
602 int rlen = strlen(rc_loc);
603
604 be_get_defaults(&be_defaults);
605
606 /*
607 * First token is the location of where the root container dataset
608 * lives; it must match rc_loc.
609 */
610 if (strncmp(dataset, rc_loc, rlen) == 0 && dataset[rlen] == '/')
611 (void) strlcpy(ds, dataset + rlen + 1, sizeof (ds));
612 else
613 return (NULL);
614
615 if (be_defaults.be_deflt_rpool_container) {
616 if ((name = strdup(ds)) == NULL) {
617 be_print_err(gettext("be_make_name_from_ds: "
618 "memory allocation failed\n"));
619 return (NULL);
620 }
621 } else {
622 /* Second token must be BE container dataset name */
623 if ((tok = strtok(ds, "/")) == NULL ||
624 strcmp(tok, BE_CONTAINER_DS_NAME) != 0)
625 return (NULL);
626
627 /* Return the remaining token if one exists */
628 if ((tok = strtok(NULL, "")) == NULL)
629 return (NULL);
630
631 if ((name = strdup(tok)) == NULL) {
632 be_print_err(gettext("be_make_name_from_ds: "
633 "memory allocation failed\n"));
634 return (NULL);
635 }
636 }
637
638 return (name);
639 }
640
641 /*
642 * Function: be_maxsize_avail
643 * Description: Returns the available size for the zfs handle passed in.
644 * Parameters:
645 * zhp - A pointer to the open zfs handle.
646 * ret - The available size will be returned in this.
647 * Returns:
648 * The error returned by the zfs get property function.
649 * Scope:
650 * Semi-private (library wide use only)
651 */
652 int
be_maxsize_avail(zfs_handle_t * zhp,uint64_t * ret)653 be_maxsize_avail(zfs_handle_t *zhp, uint64_t *ret)
654 {
655 return ((*ret = zfs_prop_get_int(zhp, ZFS_PROP_AVAILABLE)));
656 }
657
658 /*
659 * Function: be_append_menu
660 * Description: Appends an entry for a BE into the menu.lst.
661 * Parameters:
662 * be_name - pointer to name of BE to add boot menu entry for.
663 * be_root_pool - pointer to name of pool BE lives in.
664 * boot_pool - Used if the pool containing the grub menu is
665 * different than the one contaiing the BE. This
666 * will normally be NULL.
667 * be_orig_root_ds - The root dataset for the BE. This is
668 * used to check to see if an entry already exists
669 * for this BE.
670 * description - pointer to description of BE to be added in
671 * the title line for this BEs entry.
672 * Returns:
673 * BE_SUCCESS - Success
674 * be_errno_t - Failure
675 * Scope:
676 * Semi-private (library wide use only)
677 */
678 int
be_append_menu(char * be_name,char * be_root_pool,char * boot_pool,char * be_orig_root_ds,char * description)679 be_append_menu(char *be_name, char *be_root_pool, char *boot_pool,
680 char *be_orig_root_ds, char *description)
681 {
682 zfs_handle_t *zhp = NULL;
683 char menu_file[MAXPATHLEN];
684 char be_root_ds[MAXPATHLEN];
685 char line[BUFSIZ];
686 char temp_line[BUFSIZ];
687 char title[MAXPATHLEN];
688 char *entries[BUFSIZ];
689 char *tmp_entries[BUFSIZ];
690 char *pool_mntpnt = NULL;
691 char *ptmp_mntpnt = NULL;
692 char *orig_mntpnt = NULL;
693 boolean_t found_be = B_FALSE;
694 boolean_t found_orig_be = B_FALSE;
695 boolean_t found_title = B_FALSE;
696 boolean_t pool_mounted = B_FALSE;
697 boolean_t collect_lines = B_FALSE;
698 FILE *menu_fp = NULL;
699 int err = 0, ret = BE_SUCCESS;
700 int i, num_tmp_lines = 0, num_lines = 0;
701
702 if (be_name == NULL || be_root_pool == NULL)
703 return (BE_ERR_INVAL);
704
705 if (boot_pool == NULL)
706 boot_pool = be_root_pool;
707
708 if ((zhp = zfs_open(g_zfs, be_root_pool, ZFS_TYPE_DATASET)) == NULL) {
709 be_print_err(gettext("be_append_menu: failed to open "
710 "pool dataset for %s: %s\n"), be_root_pool,
711 libzfs_error_description(g_zfs));
712 return (zfs_err_to_be_err(g_zfs));
713 }
714
715 /*
716 * Check to see if the pool's dataset is mounted. If it isn't we'll
717 * attempt to mount it.
718 */
719 if ((ret = be_mount_pool(zhp, &ptmp_mntpnt, &orig_mntpnt,
720 &pool_mounted)) != BE_SUCCESS) {
721 be_print_err(gettext("be_append_menu: pool dataset "
722 "(%s) could not be mounted\n"), be_root_pool);
723 ZFS_CLOSE(zhp);
724 return (ret);
725 }
726
727 /*
728 * Get the mountpoint for the root pool dataset.
729 */
730 if (!zfs_is_mounted(zhp, &pool_mntpnt)) {
731 be_print_err(gettext("be_append_menu: pool "
732 "dataset (%s) is not mounted. Can't set "
733 "the default BE in the grub menu.\n"), be_root_pool);
734 ret = BE_ERR_NO_MENU;
735 goto cleanup;
736 }
737
738 /*
739 * Check to see if this system supports grub
740 */
741 if (be_has_grub()) {
742 (void) snprintf(menu_file, sizeof (menu_file),
743 "%s%s", pool_mntpnt, BE_GRUB_MENU);
744 } else {
745 (void) snprintf(menu_file, sizeof (menu_file),
746 "%s%s", pool_mntpnt, BE_SPARC_MENU);
747 }
748
749 if ((ret = be_make_root_ds(be_root_pool, be_name, be_root_ds,
750 sizeof (be_root_ds))) != BE_SUCCESS) {
751 be_print_err(gettext("%s: failed to get BE container dataset "
752 "for %s/%s\n"), __func__, be_root_pool, be_name);
753 goto cleanup;
754 }
755
756 /*
757 * Iterate through menu first to make sure the BE doesn't already
758 * have an entry in the menu.
759 *
760 * Additionally while iterating through the menu, if we have an
761 * original root dataset for a BE we're cloning from, we need to keep
762 * track of that BE's menu entry. We will then use the lines from
763 * that entry to create the entry for the new BE.
764 */
765 if ((ret = be_open_menu(be_root_pool, menu_file,
766 &menu_fp, "r", B_TRUE)) != BE_SUCCESS) {
767 goto cleanup;
768 } else if (menu_fp == NULL) {
769 ret = BE_ERR_NO_MENU;
770 goto cleanup;
771 }
772
773 free(pool_mntpnt);
774 pool_mntpnt = NULL;
775
776 while (fgets(line, BUFSIZ, menu_fp)) {
777 char *tok = NULL;
778
779 (void) strlcpy(temp_line, line, BUFSIZ);
780 tok = strtok(line, BE_WHITE_SPACE);
781
782 if (tok == NULL || tok[0] == '#') {
783 continue;
784 } else if (strcmp(tok, "title") == 0) {
785 collect_lines = B_FALSE;
786 if ((tok = strtok(NULL, "\n")) == NULL)
787 (void) strlcpy(title, "", sizeof (title));
788 else
789 (void) strlcpy(title, tok, sizeof (title));
790 found_title = B_TRUE;
791
792 if (num_tmp_lines != 0) {
793 for (i = 0; i < num_tmp_lines; i++) {
794 free(tmp_entries[i]);
795 tmp_entries[i] = NULL;
796 }
797 num_tmp_lines = 0;
798 }
799 } else if (strcmp(tok, "bootfs") == 0) {
800 char *bootfs = strtok(NULL, BE_WHITE_SPACE);
801 found_title = B_FALSE;
802 if (bootfs == NULL)
803 continue;
804
805 if (strcmp(bootfs, be_root_ds) == 0) {
806 found_be = B_TRUE;
807 break;
808 }
809
810 if (be_orig_root_ds != NULL &&
811 strcmp(bootfs, be_orig_root_ds) == 0 &&
812 !found_orig_be) {
813 char str[BUFSIZ];
814 found_orig_be = B_TRUE;
815 num_lines = 0;
816 /*
817 * Store the new title line
818 */
819 (void) snprintf(str, BUFSIZ, "title %s\n",
820 description ? description : be_name);
821 entries[num_lines] = strdup(str);
822 num_lines++;
823 /*
824 * If there are any lines between the title
825 * and the bootfs line store these. Also
826 * free the temporary lines.
827 */
828 for (i = 0; i < num_tmp_lines; i++) {
829 entries[num_lines] = tmp_entries[i];
830 tmp_entries[i] = NULL;
831 num_lines++;
832 }
833 num_tmp_lines = 0;
834 /*
835 * Store the new bootfs line.
836 */
837 (void) snprintf(str, BUFSIZ, "bootfs %s\n",
838 be_root_ds);
839 entries[num_lines] = strdup(str);
840 num_lines++;
841 collect_lines = B_TRUE;
842 }
843 } else if (found_orig_be && collect_lines) {
844 /*
845 * get the rest of the lines for the original BE and
846 * store them.
847 */
848 if (strstr(line, BE_GRUB_COMMENT) != NULL ||
849 strstr(line, "BOOTADM") != NULL)
850 continue;
851 if (strcmp(tok, "splashimage") == 0) {
852 entries[num_lines] =
853 strdup("splashimage "
854 "/boot/splashimage.xpm\n");
855 } else {
856 entries[num_lines] = strdup(temp_line);
857 }
858 num_lines++;
859 } else if (found_title && !found_orig_be) {
860 tmp_entries[num_tmp_lines] = strdup(temp_line);
861 num_tmp_lines++;
862 }
863 }
864
865 (void) fclose(menu_fp);
866
867 if (found_be) {
868 /*
869 * If an entry for this BE was already in the menu, then if
870 * that entry's title matches what we would have put in
871 * return success. Otherwise return failure.
872 */
873 char *new_title = description ? description : be_name;
874
875 if (strcmp(title, new_title) == 0) {
876 ret = BE_SUCCESS;
877 goto cleanup;
878 } else {
879 if (be_remove_menu(be_name, be_root_pool,
880 boot_pool) != BE_SUCCESS) {
881 be_print_err(gettext("be_append_menu: "
882 "Failed to remove existing unusable "
883 "entry '%s' in boot menu.\n"), be_name);
884 ret = BE_ERR_BE_EXISTS;
885 goto cleanup;
886 }
887 }
888 }
889
890 /* Append BE entry to the end of the file */
891 menu_fp = fopen(menu_file, "a+");
892 err = errno;
893 if (menu_fp == NULL) {
894 be_print_err(gettext("be_append_menu: failed "
895 "to open menu.lst file %s\n"), menu_file);
896 ret = errno_to_be_err(err);
897 goto cleanup;
898 }
899
900 if (found_orig_be) {
901 /*
902 * write out all the stored lines
903 */
904 for (i = 0; i < num_lines; i++) {
905 (void) fprintf(menu_fp, "%s", entries[i]);
906 free(entries[i]);
907 }
908 num_lines = 0;
909
910 /*
911 * Check to see if this system supports grub
912 */
913 if (be_has_grub())
914 (void) fprintf(menu_fp, "%s\n", BE_GRUB_COMMENT);
915 ret = BE_SUCCESS;
916 } else {
917 (void) fprintf(menu_fp, "title %s\n",
918 description ? description : be_name);
919 (void) fprintf(menu_fp, "bootfs %s\n", be_root_ds);
920
921 /*
922 * Check to see if this system supports grub
923 */
924 if (be_has_grub()) {
925 (void) fprintf(menu_fp, "kernel$ "
926 "/platform/i86pc/kernel/$ISADIR/unix -B "
927 "$ZFS-BOOTFS\n");
928 (void) fprintf(menu_fp, "module$ "
929 "/platform/i86pc/$ISADIR/boot_archive\n");
930 (void) fprintf(menu_fp, "%s\n", BE_GRUB_COMMENT);
931 }
932 ret = BE_SUCCESS;
933 }
934 (void) fclose(menu_fp);
935 cleanup:
936 if (pool_mounted) {
937 int err = BE_SUCCESS;
938 err = be_unmount_pool(zhp, ptmp_mntpnt, orig_mntpnt);
939 if (ret == BE_SUCCESS)
940 ret = err;
941 free(orig_mntpnt);
942 free(ptmp_mntpnt);
943 }
944 ZFS_CLOSE(zhp);
945 if (num_tmp_lines > 0) {
946 for (i = 0; i < num_tmp_lines; i++) {
947 free(tmp_entries[i]);
948 tmp_entries[i] = NULL;
949 }
950 }
951 if (num_lines > 0) {
952 for (i = 0; i < num_lines; i++) {
953 free(entries[i]);
954 entries[i] = NULL;
955 }
956 }
957 return (ret);
958 }
959
960 /*
961 * Function: be_remove_menu
962 * Description: Removes a BE's entry from a menu.lst file.
963 * Parameters:
964 * be_name - the name of BE whose entry is to be removed from
965 * the menu.lst file.
966 * be_root_pool - the pool that be_name lives in.
967 * boot_pool - the pool where the BE is, if different than
968 * the pool containing the boot menu. If this is
969 * NULL it will be set to be_root_pool.
970 * Returns:
971 * BE_SUCCESS - Success
972 * be_errno_t - Failure
973 * Scope:
974 * Semi-private (library wide use only)
975 */
976 int
be_remove_menu(char * be_name,char * be_root_pool,char * boot_pool)977 be_remove_menu(char *be_name, char *be_root_pool, char *boot_pool)
978 {
979 zfs_handle_t *zhp = NULL;
980 char be_root_ds[MAXPATHLEN];
981 char **buffer = NULL;
982 char menu_buf[BUFSIZ];
983 char menu[MAXPATHLEN];
984 char *pool_mntpnt = NULL;
985 char *ptmp_mntpnt = NULL;
986 char *orig_mntpnt = NULL;
987 char *tmp_menu = NULL;
988 FILE *menu_fp = NULL;
989 FILE *tmp_menu_fp = NULL;
990 struct stat sb;
991 int ret = BE_SUCCESS;
992 int i;
993 int fd;
994 int err = 0;
995 int nlines = 0;
996 int default_entry = 0;
997 int entry_cnt = 0;
998 int entry_del = 0;
999 int num_entry_del = 0;
1000 int tmp_menu_len = 0;
1001 boolean_t write = B_TRUE;
1002 boolean_t do_buffer = B_FALSE;
1003 boolean_t pool_mounted = B_FALSE;
1004
1005 if (boot_pool == NULL)
1006 boot_pool = be_root_pool;
1007
1008 /* Get name of BE's root dataset */
1009 if ((ret = be_make_root_ds(be_root_pool, be_name, be_root_ds,
1010 sizeof (be_root_ds))) != BE_SUCCESS) {
1011 be_print_err(gettext("%s: failed to get BE container dataset "
1012 "for %s/%s\n"), __func__, be_root_pool, be_name);
1013 return (ret);
1014 }
1015
1016 /* Get handle to pool dataset */
1017 if ((zhp = zfs_open(g_zfs, be_root_pool, ZFS_TYPE_DATASET)) == NULL) {
1018 be_print_err(gettext("be_remove_menu: "
1019 "failed to open pool dataset for %s: %s"),
1020 be_root_pool, libzfs_error_description(g_zfs));
1021 return (zfs_err_to_be_err(g_zfs));
1022 }
1023
1024 /*
1025 * Check to see if the pool's dataset is mounted. If it isn't we'll
1026 * attempt to mount it.
1027 */
1028 if ((ret = be_mount_pool(zhp, &ptmp_mntpnt, &orig_mntpnt,
1029 &pool_mounted)) != BE_SUCCESS) {
1030 be_print_err(gettext("be_remove_menu: pool dataset "
1031 "(%s) could not be mounted\n"), be_root_pool);
1032 ZFS_CLOSE(zhp);
1033 return (ret);
1034 }
1035
1036 /*
1037 * Get the mountpoint for the root pool dataset.
1038 */
1039 if (!zfs_is_mounted(zhp, &pool_mntpnt)) {
1040 be_print_err(gettext("be_remove_menu: pool "
1041 "dataset (%s) is not mounted. Can't set "
1042 "the default BE in the grub menu.\n"), be_root_pool);
1043 ret = BE_ERR_NO_MENU;
1044 goto cleanup;
1045 }
1046
1047 /* Get path to boot menu */
1048 (void) strlcpy(menu, pool_mntpnt, sizeof (menu));
1049
1050 /*
1051 * Check to see if this system supports grub
1052 */
1053 if (be_has_grub())
1054 (void) strlcat(menu, BE_GRUB_MENU, sizeof (menu));
1055 else
1056 (void) strlcat(menu, BE_SPARC_MENU, sizeof (menu));
1057
1058 /* Get handle to boot menu file */
1059 if ((ret = be_open_menu(be_root_pool, menu, &menu_fp, "r",
1060 B_TRUE)) != BE_SUCCESS) {
1061 goto cleanup;
1062 } else if (menu_fp == NULL) {
1063 ret = BE_ERR_NO_MENU;
1064 goto cleanup;
1065 }
1066
1067 free(pool_mntpnt);
1068 pool_mntpnt = NULL;
1069
1070 /* Grab the stats of the original menu file */
1071 if (stat(menu, &sb) != 0) {
1072 err = errno;
1073 be_print_err(gettext("be_remove_menu: "
1074 "failed to stat file %s: %s\n"), menu, strerror(err));
1075 ret = errno_to_be_err(err);
1076 goto cleanup;
1077 }
1078
1079 /* Create a tmp file for the modified menu.lst */
1080 tmp_menu_len = strlen(menu) + 7;
1081 if ((tmp_menu = (char *)malloc(tmp_menu_len)) == NULL) {
1082 be_print_err(gettext("be_remove_menu: malloc failed\n"));
1083 ret = BE_ERR_NOMEM;
1084 goto cleanup;
1085 }
1086 (void) memset(tmp_menu, 0, tmp_menu_len);
1087 (void) strlcpy(tmp_menu, menu, tmp_menu_len);
1088 (void) strlcat(tmp_menu, "XXXXXX", tmp_menu_len);
1089 if ((fd = mkstemp(tmp_menu)) == -1) {
1090 err = errno;
1091 be_print_err(gettext("be_remove_menu: mkstemp failed\n"));
1092 ret = errno_to_be_err(err);
1093 free(tmp_menu);
1094 tmp_menu = NULL;
1095 goto cleanup;
1096 }
1097 if ((tmp_menu_fp = fdopen(fd, "w")) == NULL) {
1098 err = errno;
1099 be_print_err(gettext("be_remove_menu: "
1100 "could not open tmp file for write: %s\n"), strerror(err));
1101 (void) close(fd);
1102 ret = errno_to_be_err(err);
1103 goto cleanup;
1104 }
1105
1106 while (fgets(menu_buf, BUFSIZ, menu_fp)) {
1107 char tline [BUFSIZ];
1108 char *tok = NULL;
1109
1110 (void) strlcpy(tline, menu_buf, sizeof (tline));
1111
1112 /* Tokenize line */
1113 tok = strtok(tline, BE_WHITE_SPACE);
1114
1115 if (tok == NULL || tok[0] == '#') {
1116 /* Found empty line or comment line */
1117 if (do_buffer) {
1118 /* Buffer this line */
1119 if ((buffer = (char **)realloc(buffer,
1120 sizeof (char *)*(nlines + 1))) == NULL) {
1121 ret = BE_ERR_NOMEM;
1122 goto cleanup;
1123 }
1124 if ((buffer[nlines++] = strdup(menu_buf))
1125 == NULL) {
1126 ret = BE_ERR_NOMEM;
1127 goto cleanup;
1128 }
1129
1130 } else if (write || strncmp(menu_buf, BE_GRUB_COMMENT,
1131 strlen(BE_GRUB_COMMENT)) != 0) {
1132 /* Write this line out */
1133 (void) fputs(menu_buf, tmp_menu_fp);
1134 }
1135 } else if (strcmp(tok, "default") == 0) {
1136 /*
1137 * Record what 'default' is set to because we might
1138 * need to adjust this upon deleting an entry.
1139 */
1140 tok = strtok(NULL, BE_WHITE_SPACE);
1141
1142 if (tok != NULL) {
1143 default_entry = atoi(tok);
1144 }
1145
1146 (void) fputs(menu_buf, tmp_menu_fp);
1147 } else if (strcmp(tok, "title") == 0) {
1148 /*
1149 * If we've reached a 'title' line and do_buffer is
1150 * is true, that means we've just buffered an entire
1151 * entry without finding a 'bootfs' directive. We
1152 * need to write that entry out and keep searching.
1153 */
1154 if (do_buffer) {
1155 for (i = 0; i < nlines; i++) {
1156 (void) fputs(buffer[i], tmp_menu_fp);
1157 free(buffer[i]);
1158 }
1159 free(buffer);
1160 buffer = NULL;
1161 nlines = 0;
1162 }
1163
1164 /*
1165 * Turn writing off and buffering on, and increment
1166 * our entry counter.
1167 */
1168 write = B_FALSE;
1169 do_buffer = B_TRUE;
1170 entry_cnt++;
1171
1172 /* Buffer this 'title' line */
1173 if ((buffer = (char **)realloc(buffer,
1174 sizeof (char *)*(nlines + 1))) == NULL) {
1175 ret = BE_ERR_NOMEM;
1176 goto cleanup;
1177 }
1178 if ((buffer[nlines++] = strdup(menu_buf)) == NULL) {
1179 ret = BE_ERR_NOMEM;
1180 goto cleanup;
1181 }
1182
1183 } else if (strcmp(tok, "bootfs") == 0) {
1184 char *bootfs = NULL;
1185
1186 /*
1187 * Found a 'bootfs' line. See if it matches the
1188 * BE we're looking for.
1189 */
1190 if ((bootfs = strtok(NULL, BE_WHITE_SPACE)) == NULL ||
1191 strcmp(bootfs, be_root_ds) != 0) {
1192 /*
1193 * Either there's nothing after the 'bootfs'
1194 * or this is not the BE we're looking for,
1195 * write out the line(s) we've buffered since
1196 * finding the title.
1197 */
1198 for (i = 0; i < nlines; i++) {
1199 (void) fputs(buffer[i], tmp_menu_fp);
1200 free(buffer[i]);
1201 }
1202 free(buffer);
1203 buffer = NULL;
1204 nlines = 0;
1205
1206 /*
1207 * Turn writing back on, and turn off buffering
1208 * since this isn't the entry we're looking
1209 * for.
1210 */
1211 write = B_TRUE;
1212 do_buffer = B_FALSE;
1213
1214 /* Write this 'bootfs' line out. */
1215 (void) fputs(menu_buf, tmp_menu_fp);
1216 } else {
1217 /*
1218 * Found the entry we're looking for.
1219 * Record its entry number, increment the
1220 * number of entries we've deleted, and turn
1221 * writing off. Also, throw away the lines
1222 * we've buffered for this entry so far, we
1223 * don't need them.
1224 */
1225 entry_del = entry_cnt - 1;
1226 num_entry_del++;
1227 write = B_FALSE;
1228 do_buffer = B_FALSE;
1229
1230 for (i = 0; i < nlines; i++) {
1231 free(buffer[i]);
1232 }
1233 free(buffer);
1234 buffer = NULL;
1235 nlines = 0;
1236 }
1237 } else {
1238 if (do_buffer) {
1239 /* Buffer this line */
1240 if ((buffer = (char **)realloc(buffer,
1241 sizeof (char *)*(nlines + 1))) == NULL) {
1242 ret = BE_ERR_NOMEM;
1243 goto cleanup;
1244 }
1245 if ((buffer[nlines++] = strdup(menu_buf))
1246 == NULL) {
1247 ret = BE_ERR_NOMEM;
1248 goto cleanup;
1249 }
1250 } else if (write) {
1251 /* Write this line out */
1252 (void) fputs(menu_buf, tmp_menu_fp);
1253 }
1254 }
1255 }
1256
1257 (void) fclose(menu_fp);
1258 menu_fp = NULL;
1259 (void) fclose(tmp_menu_fp);
1260 tmp_menu_fp = NULL;
1261
1262 /* Copy the modified menu.lst into place */
1263 if (rename(tmp_menu, menu) != 0) {
1264 err = errno;
1265 be_print_err(gettext("be_remove_menu: "
1266 "failed to rename file %s to %s: %s\n"),
1267 tmp_menu, menu, strerror(err));
1268 ret = errno_to_be_err(err);
1269 goto cleanup;
1270 }
1271 free(tmp_menu);
1272 tmp_menu = NULL;
1273
1274 /*
1275 * If we've removed an entry, see if we need to
1276 * adjust the default value in the menu.lst. If the
1277 * entry we've deleted comes before the default entry
1278 * we need to adjust the default value accordingly.
1279 *
1280 * be_has_grub is used here to check to see if this system
1281 * supports grub.
1282 */
1283 if (be_has_grub() && num_entry_del > 0) {
1284 if (entry_del <= default_entry) {
1285 default_entry = default_entry - num_entry_del;
1286 if (default_entry < 0)
1287 default_entry = 0;
1288
1289 /*
1290 * Adjust the default value by rewriting the
1291 * menu.lst file. This may be overkill, but to
1292 * preserve the location of the 'default' entry
1293 * in the file, we need to do this.
1294 */
1295
1296 /* Get handle to boot menu file */
1297 if ((menu_fp = fopen(menu, "r")) == NULL) {
1298 err = errno;
1299 be_print_err(gettext("be_remove_menu: "
1300 "failed to open menu.lst (%s): %s\n"),
1301 menu, strerror(err));
1302 ret = errno_to_be_err(err);
1303 goto cleanup;
1304 }
1305
1306 /* Create a tmp file for the modified menu.lst */
1307 tmp_menu_len = strlen(menu) + 7;
1308 if ((tmp_menu = (char *)malloc(tmp_menu_len))
1309 == NULL) {
1310 be_print_err(gettext("be_remove_menu: "
1311 "malloc failed\n"));
1312 ret = BE_ERR_NOMEM;
1313 goto cleanup;
1314 }
1315 (void) memset(tmp_menu, 0, tmp_menu_len);
1316 (void) strlcpy(tmp_menu, menu, tmp_menu_len);
1317 (void) strlcat(tmp_menu, "XXXXXX", tmp_menu_len);
1318 if ((fd = mkstemp(tmp_menu)) == -1) {
1319 err = errno;
1320 be_print_err(gettext("be_remove_menu: "
1321 "mkstemp failed: %s\n"), strerror(err));
1322 ret = errno_to_be_err(err);
1323 free(tmp_menu);
1324 tmp_menu = NULL;
1325 goto cleanup;
1326 }
1327 if ((tmp_menu_fp = fdopen(fd, "w")) == NULL) {
1328 err = errno;
1329 be_print_err(gettext("be_remove_menu: "
1330 "could not open tmp file for write: %s\n"),
1331 strerror(err));
1332 (void) close(fd);
1333 ret = errno_to_be_err(err);
1334 goto cleanup;
1335 }
1336
1337 while (fgets(menu_buf, BUFSIZ, menu_fp)) {
1338 char tline [BUFSIZ];
1339 char *tok = NULL;
1340
1341 (void) strlcpy(tline, menu_buf, sizeof (tline));
1342
1343 /* Tokenize line */
1344 tok = strtok(tline, BE_WHITE_SPACE);
1345
1346 if (tok == NULL) {
1347 /* Found empty line, write it out */
1348 (void) fputs(menu_buf, tmp_menu_fp);
1349 } else if (strcmp(tok, "default") == 0) {
1350 /* Found the default line, adjust it */
1351 (void) snprintf(tline, sizeof (tline),
1352 "default %d\n", default_entry);
1353
1354 (void) fputs(tline, tmp_menu_fp);
1355 } else {
1356 /* Pass through all other lines */
1357 (void) fputs(menu_buf, tmp_menu_fp);
1358 }
1359 }
1360
1361 (void) fclose(menu_fp);
1362 menu_fp = NULL;
1363 (void) fclose(tmp_menu_fp);
1364 tmp_menu_fp = NULL;
1365
1366 /* Copy the modified menu.lst into place */
1367 if (rename(tmp_menu, menu) != 0) {
1368 err = errno;
1369 be_print_err(gettext("be_remove_menu: "
1370 "failed to rename file %s to %s: %s\n"),
1371 tmp_menu, menu, strerror(err));
1372 ret = errno_to_be_err(err);
1373 goto cleanup;
1374 }
1375
1376 free(tmp_menu);
1377 tmp_menu = NULL;
1378 }
1379 }
1380
1381 /* Set the perms and ownership of the updated file */
1382 if (chmod(menu, sb.st_mode) != 0) {
1383 err = errno;
1384 be_print_err(gettext("be_remove_menu: "
1385 "failed to chmod %s: %s\n"), menu, strerror(err));
1386 ret = errno_to_be_err(err);
1387 goto cleanup;
1388 }
1389 if (chown(menu, sb.st_uid, sb.st_gid) != 0) {
1390 err = errno;
1391 be_print_err(gettext("be_remove_menu: "
1392 "failed to chown %s: %s\n"), menu, strerror(err));
1393 ret = errno_to_be_err(err);
1394 goto cleanup;
1395 }
1396
1397 cleanup:
1398 if (pool_mounted) {
1399 int err = BE_SUCCESS;
1400 err = be_unmount_pool(zhp, ptmp_mntpnt, orig_mntpnt);
1401 if (ret == BE_SUCCESS)
1402 ret = err;
1403 free(orig_mntpnt);
1404 free(ptmp_mntpnt);
1405 }
1406 ZFS_CLOSE(zhp);
1407
1408 free(buffer);
1409 if (menu_fp != NULL)
1410 (void) fclose(menu_fp);
1411 if (tmp_menu_fp != NULL)
1412 (void) fclose(tmp_menu_fp);
1413 if (tmp_menu != NULL) {
1414 (void) unlink(tmp_menu);
1415 free(tmp_menu);
1416 }
1417
1418 return (ret);
1419 }
1420
1421 /*
1422 * Function: be_default_grub_bootfs
1423 * Description: This function returns the dataset in the default entry of
1424 * the grub menu. If no default entry is found with a valid bootfs
1425 * entry NULL is returned.
1426 * Parameters:
1427 * be_root_pool - This is the name of the root pool where the
1428 * grub menu can be found.
1429 * def_bootfs - This is used to pass back the bootfs string. On
1430 * error NULL is returned here.
1431 * Returns:
1432 * Success - BE_SUCCESS is returned.
1433 * Failure - a be_errno_t is returned.
1434 * Scope:
1435 * Semi-private (library wide use only)
1436 */
1437 int
be_default_grub_bootfs(const char * be_root_pool,char ** def_bootfs)1438 be_default_grub_bootfs(const char *be_root_pool, char **def_bootfs)
1439 {
1440 zfs_handle_t *zhp = NULL;
1441 char grub_file[MAXPATHLEN];
1442 FILE *menu_fp;
1443 char line[BUFSIZ];
1444 char *pool_mntpnt = NULL;
1445 char *ptmp_mntpnt = NULL;
1446 char *orig_mntpnt = NULL;
1447 int default_entry = 0, entries = 0;
1448 int found_default = 0;
1449 int ret = BE_SUCCESS;
1450 boolean_t pool_mounted = B_FALSE;
1451
1452 errno = 0;
1453
1454 /*
1455 * Check to see if this system supports grub
1456 */
1457 if (!be_has_grub()) {
1458 be_print_err(gettext("be_default_grub_bootfs: operation "
1459 "not supported on this architecture\n"));
1460 return (BE_ERR_NOTSUP);
1461 }
1462
1463 *def_bootfs = NULL;
1464
1465 /* Get handle to pool dataset */
1466 if ((zhp = zfs_open(g_zfs, be_root_pool, ZFS_TYPE_DATASET)) == NULL) {
1467 be_print_err(gettext("be_default_grub_bootfs: "
1468 "failed to open pool dataset for %s: %s"),
1469 be_root_pool, libzfs_error_description(g_zfs));
1470 return (zfs_err_to_be_err(g_zfs));
1471 }
1472
1473 /*
1474 * Check to see if the pool's dataset is mounted. If it isn't we'll
1475 * attempt to mount it.
1476 */
1477 if ((ret = be_mount_pool(zhp, &ptmp_mntpnt, &orig_mntpnt,
1478 &pool_mounted)) != BE_SUCCESS) {
1479 be_print_err(gettext("be_default_grub_bootfs: pool dataset "
1480 "(%s) could not be mounted\n"), be_root_pool);
1481 ZFS_CLOSE(zhp);
1482 return (ret);
1483 }
1484
1485 /*
1486 * Get the mountpoint for the root pool dataset.
1487 */
1488 if (!zfs_is_mounted(zhp, &pool_mntpnt)) {
1489 be_print_err(gettext("be_default_grub_bootfs: failed "
1490 "to get mount point for the root pool. Can't set "
1491 "the default BE in the grub menu.\n"));
1492 ret = BE_ERR_NO_MENU;
1493 goto cleanup;
1494 }
1495
1496 (void) snprintf(grub_file, MAXPATHLEN, "%s%s",
1497 pool_mntpnt, BE_GRUB_MENU);
1498
1499 if ((ret = be_open_menu((char *)be_root_pool, grub_file,
1500 &menu_fp, "r", B_FALSE)) != BE_SUCCESS) {
1501 goto cleanup;
1502 } else if (menu_fp == NULL) {
1503 ret = BE_ERR_NO_MENU;
1504 goto cleanup;
1505 }
1506
1507 free(pool_mntpnt);
1508 pool_mntpnt = NULL;
1509
1510 while (fgets(line, BUFSIZ, menu_fp)) {
1511 char *tok = strtok(line, BE_WHITE_SPACE);
1512
1513 if (tok != NULL && tok[0] != '#') {
1514 if (!found_default) {
1515 if (strcmp(tok, "default") == 0) {
1516 tok = strtok(NULL, BE_WHITE_SPACE);
1517 if (tok != NULL) {
1518 default_entry = atoi(tok);
1519 rewind(menu_fp);
1520 found_default = 1;
1521 }
1522 }
1523 continue;
1524 }
1525 if (strcmp(tok, "title") == 0) {
1526 entries++;
1527 } else if (default_entry == entries - 1) {
1528 if (strcmp(tok, "bootfs") == 0) {
1529 tok = strtok(NULL, BE_WHITE_SPACE);
1530 (void) fclose(menu_fp);
1531
1532 if (tok == NULL) {
1533 ret = BE_SUCCESS;
1534 goto cleanup;
1535 }
1536
1537 if ((*def_bootfs = strdup(tok)) !=
1538 NULL) {
1539 ret = BE_SUCCESS;
1540 goto cleanup;
1541 }
1542 be_print_err(gettext(
1543 "be_default_grub_bootfs: "
1544 "memory allocation failed\n"));
1545 ret = BE_ERR_NOMEM;
1546 goto cleanup;
1547 }
1548 } else if (default_entry < entries - 1) {
1549 /*
1550 * no bootfs entry for the default entry.
1551 */
1552 break;
1553 }
1554 }
1555 }
1556 (void) fclose(menu_fp);
1557
1558 cleanup:
1559 if (pool_mounted) {
1560 int err = BE_SUCCESS;
1561 err = be_unmount_pool(zhp, ptmp_mntpnt, orig_mntpnt);
1562 if (ret == BE_SUCCESS)
1563 ret = err;
1564 free(orig_mntpnt);
1565 free(ptmp_mntpnt);
1566 }
1567 ZFS_CLOSE(zhp);
1568 return (ret);
1569 }
1570
1571 /*
1572 * Function: be_change_grub_default
1573 * Description: This function takes two parameters. These are the name of
1574 * the BE we want to have as the default booted in the grub
1575 * menu and the root pool where the path to the grub menu exists.
1576 * The code takes this and finds the BE's entry in the grub menu
1577 * and changes the default entry to point to that entry in the
1578 * list.
1579 * Parameters:
1580 * be_name - This is the name of the BE wanted as the default
1581 * for the next boot.
1582 * be_root_pool - This is the name of the root pool where the
1583 * grub menu can be found.
1584 * Returns:
1585 * BE_SUCCESS - Success
1586 * be_errno_t - Failure
1587 * Scope:
1588 * Semi-private (library wide use only)
1589 */
1590 int
be_change_grub_default(char * be_name,char * be_root_pool)1591 be_change_grub_default(char *be_name, char *be_root_pool)
1592 {
1593 zfs_handle_t *zhp = NULL;
1594 char grub_file[MAXPATHLEN];
1595 char *temp_grub = NULL;
1596 char *pool_mntpnt = NULL;
1597 char *ptmp_mntpnt = NULL;
1598 char *orig_mntpnt = NULL;
1599 char line[BUFSIZ];
1600 char temp_line[BUFSIZ];
1601 char be_root_ds[MAXPATHLEN];
1602 FILE *grub_fp = NULL;
1603 FILE *temp_fp = NULL;
1604 struct stat sb;
1605 int temp_grub_len = 0;
1606 int fd, entries = 0;
1607 int err = 0;
1608 int ret = BE_SUCCESS;
1609 boolean_t found_default = B_FALSE;
1610 boolean_t pool_mounted = B_FALSE;
1611
1612 errno = 0;
1613
1614 /*
1615 * Check to see if this system supports grub
1616 */
1617 if (!be_has_grub()) {
1618 be_print_err(gettext("be_change_grub_default: operation "
1619 "not supported on this architecture\n"));
1620 return (BE_ERR_NOTSUP);
1621 }
1622
1623 /* Generate string for BE's root dataset */
1624 if ((ret = be_make_root_ds(be_root_pool, be_name, be_root_ds,
1625 sizeof (be_root_ds))) != BE_SUCCESS) {
1626 be_print_err(gettext("%s: failed to get BE container dataset "
1627 "for %s/%s\n"), __func__, be_root_pool, be_name);
1628 return (ret);
1629 }
1630
1631 /* Get handle to pool dataset */
1632 if ((zhp = zfs_open(g_zfs, be_root_pool, ZFS_TYPE_DATASET)) == NULL) {
1633 be_print_err(gettext("be_change_grub_default: "
1634 "failed to open pool dataset for %s: %s"),
1635 be_root_pool, libzfs_error_description(g_zfs));
1636 return (zfs_err_to_be_err(g_zfs));
1637 }
1638
1639 /*
1640 * Check to see if the pool's dataset is mounted. If it isn't we'll
1641 * attempt to mount it.
1642 */
1643 if ((ret = be_mount_pool(zhp, &ptmp_mntpnt, &orig_mntpnt,
1644 &pool_mounted)) != BE_SUCCESS) {
1645 be_print_err(gettext("be_change_grub_default: pool dataset "
1646 "(%s) could not be mounted\n"), be_root_pool);
1647 ZFS_CLOSE(zhp);
1648 return (ret);
1649 }
1650
1651 /*
1652 * Get the mountpoint for the root pool dataset.
1653 */
1654 if (!zfs_is_mounted(zhp, &pool_mntpnt)) {
1655 be_print_err(gettext("be_change_grub_default: pool "
1656 "dataset (%s) is not mounted. Can't set "
1657 "the default BE in the grub menu.\n"), be_root_pool);
1658 ret = BE_ERR_NO_MENU;
1659 goto cleanup;
1660 }
1661
1662 (void) snprintf(grub_file, MAXPATHLEN, "%s%s",
1663 pool_mntpnt, BE_GRUB_MENU);
1664
1665 if ((ret = be_open_menu(be_root_pool, grub_file,
1666 &grub_fp, "r+", B_TRUE)) != BE_SUCCESS) {
1667 goto cleanup;
1668 } else if (grub_fp == NULL) {
1669 ret = BE_ERR_NO_MENU;
1670 goto cleanup;
1671 }
1672
1673 free(pool_mntpnt);
1674 pool_mntpnt = NULL;
1675
1676 /* Grab the stats of the original menu file */
1677 if (stat(grub_file, &sb) != 0) {
1678 err = errno;
1679 be_print_err(gettext("be_change_grub_default: "
1680 "failed to stat file %s: %s\n"), grub_file, strerror(err));
1681 ret = errno_to_be_err(err);
1682 goto cleanup;
1683 }
1684
1685 /* Create a tmp file for the modified menu.lst */
1686 temp_grub_len = strlen(grub_file) + 7;
1687 if ((temp_grub = (char *)malloc(temp_grub_len)) == NULL) {
1688 be_print_err(gettext("be_change_grub_default: "
1689 "malloc failed\n"));
1690 ret = BE_ERR_NOMEM;
1691 goto cleanup;
1692 }
1693 (void) memset(temp_grub, 0, temp_grub_len);
1694 (void) strlcpy(temp_grub, grub_file, temp_grub_len);
1695 (void) strlcat(temp_grub, "XXXXXX", temp_grub_len);
1696 if ((fd = mkstemp(temp_grub)) == -1) {
1697 err = errno;
1698 be_print_err(gettext("be_change_grub_default: "
1699 "mkstemp failed: %s\n"), strerror(err));
1700 ret = errno_to_be_err(err);
1701 free(temp_grub);
1702 temp_grub = NULL;
1703 goto cleanup;
1704 }
1705 if ((temp_fp = fdopen(fd, "w")) == NULL) {
1706 err = errno;
1707 be_print_err(gettext("be_change_grub_default: "
1708 "failed to open %s file: %s\n"),
1709 temp_grub, strerror(err));
1710 (void) close(fd);
1711 ret = errno_to_be_err(err);
1712 goto cleanup;
1713 }
1714
1715 while (fgets(line, BUFSIZ, grub_fp)) {
1716 char *tok = strtok(line, BE_WHITE_SPACE);
1717
1718 if (tok == NULL || tok[0] == '#') {
1719 continue;
1720 } else if (strcmp(tok, "title") == 0) {
1721 entries++;
1722 continue;
1723 } else if (strcmp(tok, "bootfs") == 0) {
1724 char *bootfs = strtok(NULL, BE_WHITE_SPACE);
1725 if (bootfs == NULL)
1726 continue;
1727
1728 if (strcmp(bootfs, be_root_ds) == 0) {
1729 found_default = B_TRUE;
1730 break;
1731 }
1732 }
1733 }
1734
1735 if (!found_default) {
1736 be_print_err(gettext("be_change_grub_default: failed "
1737 "to find entry for %s in the grub menu\n"),
1738 be_name);
1739 ret = BE_ERR_BE_NOENT;
1740 goto cleanup;
1741 }
1742
1743 rewind(grub_fp);
1744
1745 while (fgets(line, BUFSIZ, grub_fp)) {
1746 char *tok = NULL;
1747
1748 (void) strncpy(temp_line, line, BUFSIZ);
1749
1750 if ((tok = strtok(temp_line, BE_WHITE_SPACE)) != NULL &&
1751 strcmp(tok, "default") == 0) {
1752 (void) snprintf(temp_line, BUFSIZ, "default %d\n",
1753 entries - 1 >= 0 ? entries - 1 : 0);
1754 (void) fputs(temp_line, temp_fp);
1755 } else {
1756 (void) fputs(line, temp_fp);
1757 }
1758 }
1759
1760 (void) fclose(grub_fp);
1761 grub_fp = NULL;
1762 (void) fclose(temp_fp);
1763 temp_fp = NULL;
1764
1765 if (rename(temp_grub, grub_file) != 0) {
1766 err = errno;
1767 be_print_err(gettext("be_change_grub_default: "
1768 "failed to rename file %s to %s: %s\n"),
1769 temp_grub, grub_file, strerror(err));
1770 ret = errno_to_be_err(err);
1771 goto cleanup;
1772 }
1773 free(temp_grub);
1774 temp_grub = NULL;
1775
1776 /* Set the perms and ownership of the updated file */
1777 if (chmod(grub_file, sb.st_mode) != 0) {
1778 err = errno;
1779 be_print_err(gettext("be_change_grub_default: "
1780 "failed to chmod %s: %s\n"), grub_file, strerror(err));
1781 ret = errno_to_be_err(err);
1782 goto cleanup;
1783 }
1784 if (chown(grub_file, sb.st_uid, sb.st_gid) != 0) {
1785 err = errno;
1786 be_print_err(gettext("be_change_grub_default: "
1787 "failed to chown %s: %s\n"), grub_file, strerror(err));
1788 ret = errno_to_be_err(err);
1789 }
1790
1791 cleanup:
1792 if (pool_mounted) {
1793 int err = BE_SUCCESS;
1794 err = be_unmount_pool(zhp, ptmp_mntpnt, orig_mntpnt);
1795 if (ret == BE_SUCCESS)
1796 ret = err;
1797 free(orig_mntpnt);
1798 free(ptmp_mntpnt);
1799 }
1800 ZFS_CLOSE(zhp);
1801 if (grub_fp != NULL)
1802 (void) fclose(grub_fp);
1803 if (temp_fp != NULL)
1804 (void) fclose(temp_fp);
1805 if (temp_grub != NULL) {
1806 (void) unlink(temp_grub);
1807 free(temp_grub);
1808 }
1809
1810 return (ret);
1811 }
1812
1813 /*
1814 * Function: be_update_menu
1815 * Description: This function is used by be_rename to change the BE name in
1816 * an existing entry in the grub menu to the new name of the BE.
1817 * Parameters:
1818 * be_orig_name - the original name of the BE
1819 * be_new_name - the new name the BE is being renameed to.
1820 * be_root_pool - The pool which contains the grub menu
1821 * boot_pool - the pool where the BE is, if different than
1822 * the pool containing the boot menu. If this is
1823 * NULL it will be set to be_root_pool.
1824 * Returns:
1825 * BE_SUCCESS - Success
1826 * be_errno_t - Failure
1827 * Scope:
1828 * Semi-private (library wide use only)
1829 */
1830 int
be_update_menu(char * be_orig_name,char * be_new_name,char * be_root_pool,char * boot_pool)1831 be_update_menu(char *be_orig_name, char *be_new_name, char *be_root_pool,
1832 char *boot_pool)
1833 {
1834 zfs_handle_t *zhp = NULL;
1835 char menu_file[MAXPATHLEN];
1836 char be_root_ds[MAXPATHLEN];
1837 char be_new_root_ds[MAXPATHLEN];
1838 char line[BUFSIZ];
1839 char *pool_mntpnt = NULL;
1840 char *ptmp_mntpnt = NULL;
1841 char *orig_mntpnt = NULL;
1842 char *temp_menu = NULL;
1843 FILE *menu_fp = NULL;
1844 FILE *new_fp = NULL;
1845 struct stat sb;
1846 int temp_menu_len = 0;
1847 int tmp_fd;
1848 int ret = BE_SUCCESS;
1849 int err = 0;
1850 boolean_t pool_mounted = B_FALSE;
1851
1852 errno = 0;
1853
1854 if (boot_pool == NULL)
1855 boot_pool = be_root_pool;
1856
1857 if ((zhp = zfs_open(g_zfs, be_root_pool, ZFS_TYPE_DATASET)) == NULL) {
1858 be_print_err(gettext("be_update_menu: failed to open "
1859 "pool dataset for %s: %s\n"), be_root_pool,
1860 libzfs_error_description(g_zfs));
1861 return (zfs_err_to_be_err(g_zfs));
1862 }
1863
1864 /*
1865 * Check to see if the pool's dataset is mounted. If it isn't we'll
1866 * attempt to mount it.
1867 */
1868 if ((ret = be_mount_pool(zhp, &ptmp_mntpnt, &orig_mntpnt,
1869 &pool_mounted)) != BE_SUCCESS) {
1870 be_print_err(gettext("be_update_menu: pool dataset "
1871 "(%s) could not be mounted\n"), be_root_pool);
1872 ZFS_CLOSE(zhp);
1873 return (ret);
1874 }
1875
1876 /*
1877 * Get the mountpoint for the root pool dataset.
1878 */
1879 if (!zfs_is_mounted(zhp, &pool_mntpnt)) {
1880 be_print_err(gettext("be_update_menu: failed "
1881 "to get mount point for the root pool. Can't set "
1882 "the default BE in the grub menu.\n"));
1883 ret = BE_ERR_NO_MENU;
1884 goto cleanup;
1885 }
1886
1887 /*
1888 * Check to see if this system supports grub
1889 */
1890 if (be_has_grub()) {
1891 (void) snprintf(menu_file, sizeof (menu_file),
1892 "%s%s", pool_mntpnt, BE_GRUB_MENU);
1893 } else {
1894 (void) snprintf(menu_file, sizeof (menu_file),
1895 "%s%s", pool_mntpnt, BE_SPARC_MENU);
1896 }
1897
1898 if ((ret = be_make_root_ds(be_root_pool, be_orig_name, be_root_ds,
1899 sizeof (be_root_ds))) != BE_SUCCESS) {
1900 be_print_err(gettext("%s: failed to get BE container dataset "
1901 "for %s/%s\n"), __func__, be_root_pool, be_orig_name);
1902 goto cleanup;
1903 }
1904 if ((ret = be_make_root_ds(be_root_pool, be_new_name, be_new_root_ds,
1905 sizeof (be_new_root_ds))) != BE_SUCCESS) {
1906 be_print_err(gettext("%s: failed to get BE container dataset "
1907 "for %s/%s\n"), __func__, be_root_pool, be_new_name);
1908 goto cleanup;
1909 }
1910
1911 if ((ret = be_open_menu(be_root_pool, menu_file,
1912 &menu_fp, "r", B_TRUE)) != BE_SUCCESS) {
1913 goto cleanup;
1914 } else if (menu_fp == NULL) {
1915 ret = BE_ERR_NO_MENU;
1916 goto cleanup;
1917 }
1918
1919 free(pool_mntpnt);
1920 pool_mntpnt = NULL;
1921
1922 /* Grab the stat of the original menu file */
1923 if (stat(menu_file, &sb) != 0) {
1924 err = errno;
1925 be_print_err(gettext("be_update_menu: "
1926 "failed to stat file %s: %s\n"), menu_file, strerror(err));
1927 (void) fclose(menu_fp);
1928 ret = errno_to_be_err(err);
1929 goto cleanup;
1930 }
1931
1932 /* Create tmp file for modified menu.lst */
1933 temp_menu_len = strlen(menu_file) + 7;
1934 if ((temp_menu = (char *)malloc(temp_menu_len))
1935 == NULL) {
1936 be_print_err(gettext("be_update_menu: "
1937 "malloc failed\n"));
1938 (void) fclose(menu_fp);
1939 ret = BE_ERR_NOMEM;
1940 goto cleanup;
1941 }
1942 (void) memset(temp_menu, 0, temp_menu_len);
1943 (void) strlcpy(temp_menu, menu_file, temp_menu_len);
1944 (void) strlcat(temp_menu, "XXXXXX", temp_menu_len);
1945 if ((tmp_fd = mkstemp(temp_menu)) == -1) {
1946 err = errno;
1947 be_print_err(gettext("be_update_menu: "
1948 "mkstemp failed: %s\n"), strerror(err));
1949 (void) fclose(menu_fp);
1950 free(temp_menu);
1951 ret = errno_to_be_err(err);
1952 goto cleanup;
1953 }
1954 if ((new_fp = fdopen(tmp_fd, "w")) == NULL) {
1955 err = errno;
1956 be_print_err(gettext("be_update_menu: "
1957 "fdopen failed: %s\n"), strerror(err));
1958 (void) close(tmp_fd);
1959 (void) fclose(menu_fp);
1960 free(temp_menu);
1961 ret = errno_to_be_err(err);
1962 goto cleanup;
1963 }
1964
1965 while (fgets(line, BUFSIZ, menu_fp)) {
1966 char tline[BUFSIZ];
1967 char new_line[BUFSIZ];
1968 char *c = NULL;
1969
1970 (void) strlcpy(tline, line, sizeof (tline));
1971
1972 /* Tokenize line */
1973 c = strtok(tline, BE_WHITE_SPACE);
1974
1975 if (c == NULL) {
1976 /* Found empty line, write it out. */
1977 (void) fputs(line, new_fp);
1978 } else if (c[0] == '#') {
1979 /* Found a comment line, write it out. */
1980 (void) fputs(line, new_fp);
1981 } else if (strcmp(c, "title") == 0) {
1982 char *name = NULL;
1983 char *desc = NULL;
1984
1985 /*
1986 * Found a 'title' line, parse out BE name or
1987 * the description.
1988 */
1989 name = strtok(NULL, BE_WHITE_SPACE);
1990
1991 if (name == NULL) {
1992 /*
1993 * Nothing after 'title', just push
1994 * this line through
1995 */
1996 (void) fputs(line, new_fp);
1997 } else {
1998 /*
1999 * Grab the remainder of the title which
2000 * could be a multi worded description
2001 */
2002 desc = strtok(NULL, "\n");
2003
2004 if (strcmp(name, be_orig_name) == 0) {
2005 /*
2006 * The first token of the title is
2007 * the old BE name, replace it with
2008 * the new one, and write it out
2009 * along with the remainder of
2010 * description if there is one.
2011 */
2012 if (desc) {
2013 (void) snprintf(new_line,
2014 sizeof (new_line),
2015 "title %s %s\n",
2016 be_new_name, desc);
2017 } else {
2018 (void) snprintf(new_line,
2019 sizeof (new_line),
2020 "title %s\n", be_new_name);
2021 }
2022
2023 (void) fputs(new_line, new_fp);
2024 } else {
2025 (void) fputs(line, new_fp);
2026 }
2027 }
2028 } else if (strcmp(c, "bootfs") == 0) {
2029 /*
2030 * Found a 'bootfs' line, parse out the BE root
2031 * dataset value.
2032 */
2033 char *root_ds = strtok(NULL, BE_WHITE_SPACE);
2034
2035 if (root_ds == NULL) {
2036 /*
2037 * Nothing after 'bootfs', just push
2038 * this line through
2039 */
2040 (void) fputs(line, new_fp);
2041 } else {
2042 /*
2043 * If this bootfs is the one we're renaming,
2044 * write out the new root dataset value
2045 */
2046 if (strcmp(root_ds, be_root_ds) == 0) {
2047 (void) snprintf(new_line,
2048 sizeof (new_line), "bootfs %s\n",
2049 be_new_root_ds);
2050
2051 (void) fputs(new_line, new_fp);
2052 } else {
2053 (void) fputs(line, new_fp);
2054 }
2055 }
2056 } else {
2057 /*
2058 * Found some other line we don't care
2059 * about, write it out.
2060 */
2061 (void) fputs(line, new_fp);
2062 }
2063 }
2064
2065 (void) fclose(menu_fp);
2066 (void) fclose(new_fp);
2067 (void) close(tmp_fd);
2068
2069 if (rename(temp_menu, menu_file) != 0) {
2070 err = errno;
2071 be_print_err(gettext("be_update_menu: "
2072 "failed to rename file %s to %s: %s\n"),
2073 temp_menu, menu_file, strerror(err));
2074 ret = errno_to_be_err(err);
2075 }
2076 free(temp_menu);
2077
2078 /* Set the perms and ownership of the updated file */
2079 if (chmod(menu_file, sb.st_mode) != 0) {
2080 err = errno;
2081 be_print_err(gettext("be_update_menu: "
2082 "failed to chmod %s: %s\n"), menu_file, strerror(err));
2083 ret = errno_to_be_err(err);
2084 goto cleanup;
2085 }
2086 if (chown(menu_file, sb.st_uid, sb.st_gid) != 0) {
2087 err = errno;
2088 be_print_err(gettext("be_update_menu: "
2089 "failed to chown %s: %s\n"), menu_file, strerror(err));
2090 ret = errno_to_be_err(err);
2091 }
2092
2093 cleanup:
2094 if (pool_mounted) {
2095 int err = BE_SUCCESS;
2096 err = be_unmount_pool(zhp, ptmp_mntpnt, orig_mntpnt);
2097 if (ret == BE_SUCCESS)
2098 ret = err;
2099 free(orig_mntpnt);
2100 free(ptmp_mntpnt);
2101 }
2102 ZFS_CLOSE(zhp);
2103 return (ret);
2104 }
2105
2106 /*
2107 * Function: be_has_menu_entry
2108 * Description: Checks to see if the BEs root dataset has an entry in the grub
2109 * menu.
2110 * Parameters:
2111 * be_dataset - The root dataset of the BE
2112 * be_root_pool - The pool which contains the boot menu
2113 * entry - A pointer the the entry number of the BE if found.
2114 * Returns:
2115 * B_TRUE - Success
2116 * B_FALSE - Failure
2117 * Scope:
2118 * Semi-private (library wide use only)
2119 */
2120 boolean_t
be_has_menu_entry(char * be_dataset,char * be_root_pool,int * entry)2121 be_has_menu_entry(char *be_dataset, char *be_root_pool, int *entry)
2122 {
2123 zfs_handle_t *zhp = NULL;
2124 char menu_file[MAXPATHLEN];
2125 FILE *menu_fp = NULL;
2126 char line[BUFSIZ];
2127 char *last;
2128 char *rpool_mntpnt = NULL;
2129 char *ptmp_mntpnt = NULL;
2130 char *orig_mntpnt = NULL;
2131 int ent_num = 0;
2132 boolean_t ret = 0;
2133 boolean_t pool_mounted = B_FALSE;
2134
2135
2136 /*
2137 * Check to see if this system supports grub
2138 */
2139 if ((zhp = zfs_open(g_zfs, be_root_pool, ZFS_TYPE_DATASET)) == NULL) {
2140 be_print_err(gettext("be_has_menu_entry: failed to open "
2141 "pool dataset for %s: %s\n"), be_root_pool,
2142 libzfs_error_description(g_zfs));
2143 return (B_FALSE);
2144 }
2145
2146 /*
2147 * Check to see if the pool's dataset is mounted. If it isn't we'll
2148 * attempt to mount it.
2149 */
2150 if (be_mount_pool(zhp, &ptmp_mntpnt, &orig_mntpnt,
2151 &pool_mounted) != 0) {
2152 be_print_err(gettext("be_has_menu_entry: pool dataset "
2153 "(%s) could not be mounted\n"), be_root_pool);
2154 ZFS_CLOSE(zhp);
2155 return (B_FALSE);
2156 }
2157
2158 /*
2159 * Get the mountpoint for the root pool dataset.
2160 */
2161 if (!zfs_is_mounted(zhp, &rpool_mntpnt)) {
2162 be_print_err(gettext("be_has_menu_entry: pool "
2163 "dataset (%s) is not mounted. Can't set "
2164 "the default BE in the grub menu.\n"), be_root_pool);
2165 ret = B_FALSE;
2166 goto cleanup;
2167 }
2168
2169 if (be_has_grub()) {
2170 (void) snprintf(menu_file, MAXPATHLEN, "/%s%s",
2171 rpool_mntpnt, BE_GRUB_MENU);
2172 } else {
2173 (void) snprintf(menu_file, MAXPATHLEN, "/%s%s",
2174 rpool_mntpnt, BE_SPARC_MENU);
2175 }
2176
2177 if (be_open_menu(be_root_pool, menu_file, &menu_fp, "r",
2178 B_FALSE) != 0) {
2179 ret = B_FALSE;
2180 goto cleanup;
2181 } else if (menu_fp == NULL) {
2182 ret = B_FALSE;
2183 goto cleanup;
2184 }
2185
2186 free(rpool_mntpnt);
2187 rpool_mntpnt = NULL;
2188
2189 while (fgets(line, BUFSIZ, menu_fp)) {
2190 char *tok = strtok_r(line, BE_WHITE_SPACE, &last);
2191
2192 if (tok != NULL && tok[0] != '#') {
2193 if (strcmp(tok, "bootfs") == 0) {
2194 tok = strtok_r(last, BE_WHITE_SPACE, &last);
2195 if (tok != NULL && strcmp(tok,
2196 be_dataset) == 0) {
2197 /*
2198 * The entry number needs to be
2199 * decremented here because the title
2200 * will always be the first line for
2201 * an entry. Because of this we'll
2202 * always be off by one entry when we
2203 * check for bootfs.
2204 */
2205 *entry = ent_num - 1;
2206 ret = B_TRUE;
2207 goto cleanup;
2208 }
2209 } else if (strcmp(tok, "title") == 0)
2210 ent_num++;
2211 }
2212 }
2213
2214 cleanup:
2215 if (pool_mounted) {
2216 (void) be_unmount_pool(zhp, ptmp_mntpnt, orig_mntpnt);
2217 free(orig_mntpnt);
2218 free(ptmp_mntpnt);
2219 }
2220 ZFS_CLOSE(zhp);
2221 (void) fclose(menu_fp);
2222 return (ret);
2223 }
2224
2225 /*
2226 * Function: be_update_vfstab
2227 * Description: This function digs into a BE's vfstab and updates all
2228 * entries with file systems listed in be_fs_list_data_t.
2229 * The entry's root container dataset and be_name will be
2230 * updated with the parameters passed in.
2231 * Parameters:
2232 * be_name - name of BE to update
2233 * old_rc_loc - dataset under which the root container dataset
2234 * of the old BE resides in.
2235 * new_rc_loc - dataset under which the root container dataset
2236 * of the new BE resides in.
2237 * fld - be_fs_list_data_t pointer providing the list of
2238 * file systems to look for in vfstab.
2239 * mountpoint - directory of where BE is currently mounted.
2240 * If NULL, then BE is not currently mounted.
2241 * Returns:
2242 * BE_SUCCESS - Success
2243 * be_errno_t - Failure
2244 * Scope:
2245 * Semi-private (library wide use only)
2246 */
2247 int
be_update_vfstab(char * be_name,char * old_rc_loc,char * new_rc_loc,be_fs_list_data_t * fld,char * mountpoint)2248 be_update_vfstab(char *be_name, char *old_rc_loc, char *new_rc_loc,
2249 be_fs_list_data_t *fld, char *mountpoint)
2250 {
2251 char *tmp_mountpoint = NULL;
2252 char alt_vfstab[MAXPATHLEN];
2253 int ret = BE_SUCCESS, err = BE_SUCCESS;
2254
2255 if (fld == NULL || fld->fs_list == NULL || fld->fs_num == 0)
2256 return (BE_SUCCESS);
2257
2258 /* If BE not already mounted, mount the BE */
2259 if (mountpoint == NULL) {
2260 if ((ret = _be_mount(be_name, &tmp_mountpoint,
2261 BE_MOUNT_FLAG_NO_ZONES)) != BE_SUCCESS) {
2262 be_print_err(gettext("be_update_vfstab: "
2263 "failed to mount BE (%s)\n"), be_name);
2264 return (ret);
2265 }
2266 } else {
2267 tmp_mountpoint = mountpoint;
2268 }
2269
2270 /* Get string for vfstab in the mounted BE. */
2271 (void) snprintf(alt_vfstab, sizeof (alt_vfstab), "%s/etc/vfstab",
2272 tmp_mountpoint);
2273
2274 /* Update the vfstab */
2275 ret = _update_vfstab(alt_vfstab, be_name, old_rc_loc, new_rc_loc,
2276 fld);
2277
2278 /* Unmount BE if we mounted it */
2279 if (mountpoint == NULL) {
2280 if ((err = _be_unmount(be_name, 0)) == BE_SUCCESS) {
2281 /* Remove temporary mountpoint */
2282 (void) rmdir(tmp_mountpoint);
2283 } else {
2284 be_print_err(gettext("be_update_vfstab: "
2285 "failed to unmount BE %s mounted at %s\n"),
2286 be_name, tmp_mountpoint);
2287 if (ret == BE_SUCCESS)
2288 ret = err;
2289 }
2290
2291 free(tmp_mountpoint);
2292 }
2293
2294 return (ret);
2295 }
2296
2297 /*
2298 * Function: be_update_zone_vfstab
2299 * Description: This function digs into a zone BE's vfstab and updates all
2300 * entries with file systems listed in be_fs_list_data_t.
2301 * The entry's root container dataset and be_name will be
2302 * updated with the parameters passed in.
2303 * Parameters:
2304 * zhp - zfs_handle_t pointer to zone root dataset.
2305 * be_name - name of zone BE to update
2306 * old_rc_loc - dataset under which the root container dataset
2307 * of the old zone BE resides in.
2308 * new_rc_loc - dataset under which the root container dataset
2309 * of the new zone BE resides in.
2310 * fld - be_fs_list_data_t pointer providing the list of
2311 * file systems to look for in vfstab.
2312 * Returns:
2313 * BE_SUCCESS - Success
2314 * be_errno_t - Failure
2315 * Scope:
2316 * Semi-private (library wide use only)
2317 */
2318 int
be_update_zone_vfstab(zfs_handle_t * zhp,char * be_name,char * old_rc_loc,char * new_rc_loc,be_fs_list_data_t * fld)2319 be_update_zone_vfstab(zfs_handle_t *zhp, char *be_name, char *old_rc_loc,
2320 char *new_rc_loc, be_fs_list_data_t *fld)
2321 {
2322 be_mount_data_t md = { 0 };
2323 be_unmount_data_t ud = { 0 };
2324 char alt_vfstab[MAXPATHLEN];
2325 boolean_t mounted_here = B_FALSE;
2326 int ret = BE_SUCCESS;
2327
2328 /*
2329 * If zone root not already mounted, mount it at a
2330 * temporary location.
2331 */
2332 if (!zfs_is_mounted(zhp, &md.altroot)) {
2333 /* Generate temporary mountpoint to mount zone root */
2334 if ((ret = be_make_tmp_mountpoint(&md.altroot)) != BE_SUCCESS) {
2335 be_print_err(gettext("be_update_zone_vfstab: "
2336 "failed to make temporary mountpoint to "
2337 "mount zone root\n"));
2338 return (ret);
2339 }
2340
2341 if (be_mount_zone_root(zhp, &md) != BE_SUCCESS) {
2342 be_print_err(gettext("be_update_zone_vfstab: "
2343 "failed to mount zone root %s\n"),
2344 zfs_get_name(zhp));
2345 free(md.altroot);
2346 return (BE_ERR_MOUNT_ZONEROOT);
2347 }
2348 mounted_here = B_TRUE;
2349 }
2350
2351 /* Get string from vfstab in the mounted zone BE */
2352 (void) snprintf(alt_vfstab, sizeof (alt_vfstab), "%s/etc/vfstab",
2353 md.altroot);
2354
2355 /* Update the vfstab */
2356 ret = _update_vfstab(alt_vfstab, be_name, old_rc_loc, new_rc_loc,
2357 fld);
2358
2359 /* Unmount zone root if we mounted it */
2360 if (mounted_here) {
2361 ud.force = B_TRUE;
2362
2363 if (be_unmount_zone_root(zhp, &ud) == BE_SUCCESS) {
2364 /* Remove the temporary mountpoint */
2365 (void) rmdir(md.altroot);
2366 } else {
2367 be_print_err(gettext("be_update_zone_vfstab: "
2368 "failed to unmount zone root %s from %s\n"),
2369 zfs_get_name(zhp), md.altroot);
2370 if (ret == 0)
2371 ret = BE_ERR_UMOUNT_ZONEROOT;
2372 }
2373 }
2374
2375 free(md.altroot);
2376 return (ret);
2377 }
2378
2379 /*
2380 * Function: be_auto_snap_name
2381 * Description: Generate an auto snapshot name constructed based on the
2382 * current date and time. The auto snapshot name is of the form:
2383 *
2384 * <date>-<time>
2385 *
2386 * where <date> is in ISO standard format, so the resultant name
2387 * is of the form:
2388 *
2389 * %Y-%m-%d-%H:%M:%S
2390 *
2391 * Parameters:
2392 * None
2393 * Returns:
2394 * Success - pointer to auto generated snapshot name. The name
2395 * is allocated in heap storage so the caller is
2396 * responsible for free'ing the name.
2397 * Failure - NULL
2398 * Scope:
2399 * Semi-private (library wide use only)
2400 */
2401 char *
be_auto_snap_name(void)2402 be_auto_snap_name(void)
2403 {
2404 time_t utc_tm = 0;
2405 struct tm *gmt_tm = NULL;
2406 char gmt_time_str[64];
2407 char *auto_snap_name = NULL;
2408
2409 if (time(&utc_tm) == -1) {
2410 be_print_err(gettext("be_auto_snap_name: time() failed\n"));
2411 return (NULL);
2412 }
2413
2414 if ((gmt_tm = gmtime(&utc_tm)) == NULL) {
2415 be_print_err(gettext("be_auto_snap_name: gmtime() failed\n"));
2416 return (NULL);
2417 }
2418
2419 (void) strftime(gmt_time_str, sizeof (gmt_time_str), "%F-%T", gmt_tm);
2420
2421 if ((auto_snap_name = strdup(gmt_time_str)) == NULL) {
2422 be_print_err(gettext("be_auto_snap_name: "
2423 "memory allocation failed\n"));
2424 return (NULL);
2425 }
2426
2427 return (auto_snap_name);
2428 }
2429
2430 /*
2431 * Function: be_auto_be_name
2432 * Description: Generate an auto BE name constructed based on the BE name
2433 * of the original BE being cloned.
2434 * Parameters:
2435 * obe_name - name of the original BE being cloned.
2436 * Returns:
2437 * Success - pointer to auto generated BE name. The name
2438 * is allocated in heap storage so the caller is
2439 * responsible for free'ing the name.
2440 * Failure - NULL
2441 * Scope:
2442 * Semi-private (library wide use only)
2443 */
2444 char *
be_auto_be_name(char * obe_name)2445 be_auto_be_name(char *obe_name)
2446 {
2447 return (be_get_auto_name(obe_name, NULL, B_FALSE));
2448 }
2449
2450 /*
2451 * Function: be_auto_zone_be_name
2452 * Description: Generate an auto BE name for a zone constructed based on
2453 * the BE name of the original zone BE being cloned.
2454 * Parameters:
2455 * container_ds - container dataset for the zone.
2456 * zbe_name - name of the original zone BE being cloned.
2457 * Returns:
2458 * Success - pointer to auto generated BE name. The name
2459 * is allocated in heap storage so the caller is
2460 * responsible for free'ing the name.
2461 * Failure - NULL
2462 * Scope:
2463 * Semi-private (library wide use only)
2464 */
2465 char *
be_auto_zone_be_name(char * container_ds,char * zbe_name)2466 be_auto_zone_be_name(char *container_ds, char *zbe_name)
2467 {
2468 return (be_get_auto_name(zbe_name, container_ds, B_TRUE));
2469 }
2470
2471 /*
2472 * Function: be_valid_be_name
2473 * Description: Validates a BE name.
2474 * Parameters:
2475 * be_name - name of BE to validate
2476 * Returns:
2477 * B_TRUE - be_name is valid
2478 * B_FALSE - be_name is invalid
2479 * Scope:
2480 * Semi-private (library wide use only)
2481 */
2482
2483 boolean_t
be_valid_be_name(const char * be_name)2484 be_valid_be_name(const char *be_name)
2485 {
2486 const char *c = NULL;
2487 struct be_defaults be_defaults;
2488
2489 if (be_name == NULL)
2490 return (B_FALSE);
2491
2492 be_get_defaults(&be_defaults);
2493
2494 /*
2495 * A BE name must not be a multi-level dataset name. We also check
2496 * that it does not contain the ' ' and '%' characters. The ' ' is
2497 * a valid character for datasets, however we don't allow that in a
2498 * BE name. The '%' is invalid, but zfs_name_valid() allows it for
2499 * internal reasons, so we explicitly check for it here.
2500 */
2501 c = be_name;
2502 while (*c != '\0' && *c != '/' && *c != ' ' && *c != '%')
2503 c++;
2504
2505 if (*c != '\0')
2506 return (B_FALSE);
2507
2508 /*
2509 * The BE name must comply with a zfs dataset filesystem. We also
2510 * verify its length to be < BE_NAME_MAX_LEN.
2511 */
2512 if (!zfs_name_valid(be_name, ZFS_TYPE_FILESYSTEM) ||
2513 strlen(be_name) > BE_NAME_MAX_LEN)
2514 return (B_FALSE);
2515
2516 if (be_defaults.be_deflt_bename_starts_with[0] != '\0' &&
2517 strstr(be_name, be_defaults.be_deflt_bename_starts_with) == NULL) {
2518 return (B_FALSE);
2519 }
2520
2521 return (B_TRUE);
2522 }
2523
2524 /*
2525 * Function: be_valid_auto_snap_name
2526 * Description: This function checks that a snapshot name is a valid auto
2527 * generated snapshot name. A valid auto generated snapshot
2528 * name is of the form:
2529 *
2530 * %Y-%m-%d-%H:%M:%S
2531 *
2532 * An older form of the auto generated snapshot name also
2533 * included the snapshot's BE cleanup policy and a reserved
2534 * field. Those names will also be verified by this function.
2535 *
2536 * Examples of valid auto snapshot names are:
2537 *
2538 * 2008-03-31-18:41:30
2539 * 2008-03-31-22:17:24
2540 * <policy>:-:2008:04-05-09:12:55
2541 * <policy>:-:2008:04-06-15:34:12
2542 *
2543 * Parameters:
2544 * name - name of the snapshot to be validated.
2545 * Returns:
2546 * B_TRUE - the name is a valid auto snapshot name.
2547 * B_FALSE - the name is not a valid auto snapshot name.
2548 * Scope:
2549 * Semi-private (library wide use only)
2550 */
2551 boolean_t
be_valid_auto_snap_name(char * name)2552 be_valid_auto_snap_name(char *name)
2553 {
2554 struct tm gmt_tm;
2555
2556 char *policy = NULL;
2557 char *reserved = NULL;
2558 char *date = NULL;
2559 char *c = NULL;
2560
2561 /* Validate the snapshot name by converting it into utc time */
2562 if (strptime(name, "%Y-%m-%d-%T", &gmt_tm) != NULL &&
2563 (mktime(&gmt_tm) != -1)) {
2564 return (B_TRUE);
2565 }
2566
2567 /*
2568 * Validate the snapshot name against the older form of an
2569 * auto generated snapshot name.
2570 */
2571 policy = strdup(name);
2572
2573 /*
2574 * Get the first field from the snapshot name,
2575 * which is the BE policy
2576 */
2577 c = strchr(policy, ':');
2578 if (c == NULL) {
2579 free(policy);
2580 return (B_FALSE);
2581 }
2582 c[0] = '\0';
2583
2584 /* Validate the policy name */
2585 if (!valid_be_policy(policy)) {
2586 free(policy);
2587 return (B_FALSE);
2588 }
2589
2590 /* Get the next field, which is the reserved field. */
2591 if (c[1] == '\0') {
2592 free(policy);
2593 return (B_FALSE);
2594 }
2595 reserved = c+1;
2596 c = strchr(reserved, ':');
2597 if (c == NULL) {
2598 free(policy);
2599 return (B_FALSE);
2600 }
2601 c[0] = '\0';
2602
2603 /* Validate the reserved field */
2604 if (strcmp(reserved, "-") != 0) {
2605 free(policy);
2606 return (B_FALSE);
2607 }
2608
2609 /* The remaining string should be the date field */
2610 if (c[1] == '\0') {
2611 free(policy);
2612 return (B_FALSE);
2613 }
2614 date = c+1;
2615
2616 /* Validate the date string by converting it into utc time */
2617 if (strptime(date, "%Y-%m-%d-%T", &gmt_tm) == NULL ||
2618 (mktime(&gmt_tm) == -1)) {
2619 be_print_err(gettext("be_valid_auto_snap_name: "
2620 "invalid auto snapshot name\n"));
2621 free(policy);
2622 return (B_FALSE);
2623 }
2624
2625 free(policy);
2626 return (B_TRUE);
2627 }
2628
2629 /*
2630 * Function: be_default_policy
2631 * Description: Temporary hardcoded policy support. This function returns
2632 * the default policy type to be used to create a BE or a BE
2633 * snapshot.
2634 * Parameters:
2635 * None
2636 * Returns:
2637 * Name of default BE policy.
2638 * Scope:
2639 * Semi-private (library wide use only)
2640 */
2641 char *
be_default_policy(void)2642 be_default_policy(void)
2643 {
2644 return (BE_PLCY_STATIC);
2645 }
2646
2647 /*
2648 * Function: valid_be_policy
2649 * Description: Temporary hardcoded policy support. This function valids
2650 * whether a policy is a valid known policy or not.
2651 * Paramters:
2652 * policy - name of policy to validate.
2653 * Returns:
2654 * B_TRUE - policy is a valid.
2655 * B_FALSE - policy is invalid.
2656 * Scope:
2657 * Semi-private (library wide use only)
2658 */
2659 boolean_t
valid_be_policy(char * policy)2660 valid_be_policy(char *policy)
2661 {
2662 if (policy == NULL)
2663 return (B_FALSE);
2664
2665 if (strcmp(policy, BE_PLCY_STATIC) == 0 ||
2666 strcmp(policy, BE_PLCY_VOLATILE) == 0) {
2667 return (B_TRUE);
2668 }
2669
2670 return (B_FALSE);
2671 }
2672
2673 /*
2674 * Function: be_print_err
2675 * Description: This function prints out error messages if do_print is
2676 * set to B_TRUE or if the BE_PRINT_ERR environment variable
2677 * is set to true.
2678 * Paramters:
2679 * prnt_str - the string we wish to print and any arguments
2680 * for the format of that string.
2681 * Returns:
2682 * void
2683 * Scope:
2684 * Semi-private (library wide use only)
2685 */
2686 void
be_print_err(char * prnt_str,...)2687 be_print_err(char *prnt_str, ...)
2688 {
2689 va_list ap;
2690 char buf[BUFSIZ];
2691 char *env_buf;
2692 static boolean_t env_checked = B_FALSE;
2693
2694 if (!env_checked) {
2695 if ((env_buf = getenv("BE_PRINT_ERR")) != NULL) {
2696 if (strcasecmp(env_buf, "true") == 0) {
2697 do_print = B_TRUE;
2698 }
2699 }
2700 env_checked = B_TRUE;
2701 }
2702
2703 if (do_print) {
2704 va_start(ap, prnt_str);
2705 /* LINTED variable format specifier */
2706 (void) vsnprintf(buf, BUFSIZ, prnt_str, ap);
2707 (void) fputs(buf, stderr);
2708 va_end(ap);
2709 }
2710 }
2711
2712 /*
2713 * Function: be_find_current_be
2714 * Description: Find the currently "active" BE. Fill in the
2715 * passed in be_transaction_data_t reference with the
2716 * active BE's data.
2717 * Paramters:
2718 * none
2719 * Returns:
2720 * BE_SUCCESS - Success
2721 * be_errnot_t - Failure
2722 * Scope:
2723 * Semi-private (library wide use only)
2724 * Notes:
2725 * The caller is responsible for initializing the libzfs handle
2726 * and freeing the memory used by the active be_name.
2727 */
2728 int
be_find_current_be(be_transaction_data_t * bt)2729 be_find_current_be(be_transaction_data_t *bt)
2730 {
2731 int zret;
2732
2733 if ((zret = zpool_iter(g_zfs, be_zpool_find_current_be_callback,
2734 bt)) == 0) {
2735 be_print_err(gettext("be_find_current_be: failed to "
2736 "find current BE name\n"));
2737 return (BE_ERR_BE_NOENT);
2738 } else if (zret < 0) {
2739 be_print_err(gettext("be_find_current_be: "
2740 "zpool_iter failed: %s\n"),
2741 libzfs_error_description(g_zfs));
2742 return (zfs_err_to_be_err(g_zfs));
2743 }
2744
2745 return (BE_SUCCESS);
2746 }
2747
2748 /*
2749 * Function: be_zpool_find_current_be_callback
2750 * Description: Callback function used to iterate through all existing pools
2751 * to find the BE that is the currently booted BE.
2752 * Parameters:
2753 * zlp - zpool_handle_t pointer to the current pool being
2754 * looked at.
2755 * data - be_transaction_data_t pointer.
2756 * Upon successfully finding the current BE, the
2757 * obe_zpool member of this parameter is set to the
2758 * pool it is found in.
2759 * Return:
2760 * 1 - Found current BE in this pool.
2761 * 0 - Did not find current BE in this pool.
2762 * Scope:
2763 * Semi-private (library wide use only)
2764 */
2765 int
be_zpool_find_current_be_callback(zpool_handle_t * zlp,void * data)2766 be_zpool_find_current_be_callback(zpool_handle_t *zlp, void *data)
2767 {
2768 be_transaction_data_t *bt = data;
2769 zfs_handle_t *zhp = NULL;
2770 const char *zpool = zpool_get_name(zlp);
2771 char be_container_ds[MAXPATHLEN];
2772
2773 /*
2774 * Generate string for BE container dataset
2775 */
2776 if (be_make_container_ds(zpool, be_container_ds,
2777 sizeof (be_container_ds)) != BE_SUCCESS) {
2778 zpool_close(zlp);
2779 return (0);
2780 }
2781
2782 /*
2783 * Check if a BE container dataset exists in this pool.
2784 */
2785 if (!zfs_dataset_exists(g_zfs, be_container_ds, ZFS_TYPE_FILESYSTEM)) {
2786 zpool_close(zlp);
2787 return (0);
2788 }
2789
2790 /*
2791 * Get handle to this zpool's BE container dataset.
2792 */
2793 if ((zhp = zfs_open(g_zfs, be_container_ds, ZFS_TYPE_FILESYSTEM)) ==
2794 NULL) {
2795 be_print_err(gettext("be_zpool_find_current_be_callback: "
2796 "failed to open BE container dataset (%s)\n"),
2797 be_container_ds);
2798 zpool_close(zlp);
2799 return (0);
2800 }
2801
2802 /*
2803 * Iterate through all potential BEs in this zpool
2804 */
2805 if (zfs_iter_filesystems(zhp, be_zfs_find_current_be_callback, bt)) {
2806 /*
2807 * Found current BE dataset; set obe_zpool
2808 */
2809 if ((bt->obe_zpool = strdup(zpool)) == NULL) {
2810 be_print_err(gettext(
2811 "be_zpool_find_current_be_callback: "
2812 "memory allocation failed\n"));
2813 ZFS_CLOSE(zhp);
2814 zpool_close(zlp);
2815 return (0);
2816 }
2817
2818 ZFS_CLOSE(zhp);
2819 zpool_close(zlp);
2820 return (1);
2821 }
2822
2823 ZFS_CLOSE(zhp);
2824 zpool_close(zlp);
2825
2826 return (0);
2827 }
2828
2829 /*
2830 * Function: be_zfs_find_current_be_callback
2831 * Description: Callback function used to iterate through all BEs in a
2832 * pool to find the BE that is the currently booted BE.
2833 * Parameters:
2834 * zhp - zfs_handle_t pointer to current filesystem being checked.
2835 * data - be_transaction-data_t pointer
2836 * Upon successfully finding the current BE, the
2837 * obe_name and obe_root_ds members of this parameter
2838 * are set to the BE name and BE's root dataset
2839 * respectively.
2840 * Return:
2841 * 1 - Found current BE.
2842 * 0 - Did not find current BE.
2843 * Scope:
2844 * Semi-private (library wide use only)
2845 */
2846 int
be_zfs_find_current_be_callback(zfs_handle_t * zhp,void * data)2847 be_zfs_find_current_be_callback(zfs_handle_t *zhp, void *data)
2848 {
2849 be_transaction_data_t *bt = data;
2850 char *mp = NULL;
2851
2852 /*
2853 * Check if dataset is mounted, and if so where.
2854 */
2855 if (zfs_is_mounted(zhp, &mp)) {
2856 /*
2857 * If mounted at root, set obe_root_ds and obe_name
2858 */
2859 if (mp != NULL && strcmp(mp, "/") == 0) {
2860 free(mp);
2861
2862 if ((bt->obe_root_ds = strdup(zfs_get_name(zhp)))
2863 == NULL) {
2864 be_print_err(gettext(
2865 "be_zfs_find_current_be_callback: "
2866 "memory allocation failed\n"));
2867 ZFS_CLOSE(zhp);
2868 return (0);
2869 }
2870
2871 if ((bt->obe_name = strdup(basename(bt->obe_root_ds)))
2872 == NULL) {
2873 be_print_err(gettext(
2874 "be_zfs_find_current_be_callback: "
2875 "memory allocation failed\n"));
2876 ZFS_CLOSE(zhp);
2877 return (0);
2878 }
2879
2880 ZFS_CLOSE(zhp);
2881 return (1);
2882 }
2883
2884 free(mp);
2885 }
2886 ZFS_CLOSE(zhp);
2887
2888 return (0);
2889 }
2890
2891 /*
2892 * Function: be_check_be_roots_callback
2893 * Description: This function checks whether or not the dataset name passed
2894 * is hierachically located under the BE root container dataset
2895 * for this pool.
2896 * Parameters:
2897 * zlp - zpool_handle_t pointer to current pool being processed.
2898 * data - name of dataset to check
2899 * Returns:
2900 * 0 - dataset is not in this pool's BE root container dataset
2901 * 1 - dataset is in this pool's BE root container dataset
2902 * Scope:
2903 * Semi-private (library wide use only)
2904 */
2905 int
be_check_be_roots_callback(zpool_handle_t * zlp,void * data)2906 be_check_be_roots_callback(zpool_handle_t *zlp, void *data)
2907 {
2908 const char *zpool = zpool_get_name(zlp);
2909 char *ds = data;
2910 char be_container_ds[MAXPATHLEN];
2911
2912 /* Generate string for this pool's BE root container dataset */
2913 if (be_make_container_ds(zpool, be_container_ds,
2914 sizeof (be_container_ds)) != BE_SUCCESS) {
2915 return (0);
2916 }
2917
2918 /*
2919 * If dataset lives under the BE root container dataset
2920 * of this pool, return failure.
2921 */
2922 if (strncmp(be_container_ds, ds, strlen(be_container_ds)) == 0 &&
2923 ds[strlen(be_container_ds)] == '/') {
2924 zpool_close(zlp);
2925 return (1);
2926 }
2927
2928 zpool_close(zlp);
2929 return (0);
2930 }
2931
2932 /*
2933 * Function: zfs_err_to_be_err
2934 * Description: This function takes the error stored in the libzfs handle
2935 * and maps it to an be_errno_t. If there are no matching
2936 * be_errno_t's then BE_ERR_ZFS is returned.
2937 * Paramters:
2938 * zfsh - The libzfs handle containing the error we're looking up.
2939 * Returns:
2940 * be_errno_t
2941 * Scope:
2942 * Semi-private (library wide use only)
2943 */
2944 int
zfs_err_to_be_err(libzfs_handle_t * zfsh)2945 zfs_err_to_be_err(libzfs_handle_t *zfsh)
2946 {
2947 int err = libzfs_errno(zfsh);
2948
2949 switch (err) {
2950 case 0:
2951 return (BE_SUCCESS);
2952 case EZFS_PERM:
2953 return (BE_ERR_PERM);
2954 case EZFS_INTR:
2955 return (BE_ERR_INTR);
2956 case EZFS_NOENT:
2957 return (BE_ERR_NOENT);
2958 case EZFS_NOSPC:
2959 return (BE_ERR_NOSPC);
2960 case EZFS_MOUNTFAILED:
2961 return (BE_ERR_MOUNT);
2962 case EZFS_UMOUNTFAILED:
2963 return (BE_ERR_UMOUNT);
2964 case EZFS_EXISTS:
2965 return (BE_ERR_BE_EXISTS);
2966 case EZFS_BUSY:
2967 return (BE_ERR_DEV_BUSY);
2968 case EZFS_POOLREADONLY:
2969 return (BE_ERR_ROFS);
2970 case EZFS_NAMETOOLONG:
2971 return (BE_ERR_NAMETOOLONG);
2972 case EZFS_NODEVICE:
2973 return (BE_ERR_NODEV);
2974 case EZFS_POOL_INVALARG:
2975 return (BE_ERR_INVAL);
2976 case EZFS_PROPTYPE:
2977 return (BE_ERR_INVALPROP);
2978 case EZFS_BADTYPE:
2979 return (BE_ERR_DSTYPE);
2980 case EZFS_PROPNONINHERIT:
2981 return (BE_ERR_NONINHERIT);
2982 case EZFS_PROPREADONLY:
2983 return (BE_ERR_READONLYPROP);
2984 case EZFS_RESILVERING:
2985 case EZFS_POOLUNAVAIL:
2986 return (BE_ERR_UNAVAIL);
2987 case EZFS_DSREADONLY:
2988 return (BE_ERR_READONLYDS);
2989 default:
2990 return (BE_ERR_ZFS);
2991 }
2992 }
2993
2994 /*
2995 * Function: errno_to_be_err
2996 * Description: This function takes an errno and maps it to an be_errno_t.
2997 * If there are no matching be_errno_t's then BE_ERR_UNKNOWN is
2998 * returned.
2999 * Paramters:
3000 * err - The errno we're compairing against.
3001 * Returns:
3002 * be_errno_t
3003 * Scope:
3004 * Semi-private (library wide use only)
3005 */
3006 int
errno_to_be_err(int err)3007 errno_to_be_err(int err)
3008 {
3009 switch (err) {
3010 case EPERM:
3011 return (BE_ERR_PERM);
3012 case EACCES:
3013 return (BE_ERR_ACCESS);
3014 case ECANCELED:
3015 return (BE_ERR_CANCELED);
3016 case EINTR:
3017 return (BE_ERR_INTR);
3018 case ENOENT:
3019 return (BE_ERR_NOENT);
3020 case ENOSPC:
3021 case EDQUOT:
3022 return (BE_ERR_NOSPC);
3023 case EEXIST:
3024 return (BE_ERR_BE_EXISTS);
3025 case EBUSY:
3026 return (BE_ERR_BUSY);
3027 case EROFS:
3028 return (BE_ERR_ROFS);
3029 case ENAMETOOLONG:
3030 return (BE_ERR_NAMETOOLONG);
3031 case ENXIO:
3032 return (BE_ERR_NXIO);
3033 case EINVAL:
3034 return (BE_ERR_INVAL);
3035 case EFAULT:
3036 return (BE_ERR_FAULT);
3037 default:
3038 return (BE_ERR_UNKNOWN);
3039 }
3040 }
3041
3042 /*
3043 * Function: be_err_to_str
3044 * Description: This function takes a be_errno_t and maps it to a message.
3045 * If there are no matching be_errno_t's then NULL is returned.
3046 * Paramters:
3047 * be_errno_t - The be_errno_t we're mapping.
3048 * Returns:
3049 * string or NULL if the error code is not known.
3050 * Scope:
3051 * Semi-private (library wide use only)
3052 */
3053 char *
be_err_to_str(int err)3054 be_err_to_str(int err)
3055 {
3056 switch (err) {
3057 case BE_ERR_ACCESS:
3058 return (gettext("Permission denied."));
3059 case BE_ERR_ACTIVATE_CURR:
3060 return (gettext("Activation of current BE failed."));
3061 case BE_ERR_AUTONAME:
3062 return (gettext("Auto naming failed."));
3063 case BE_ERR_BE_NOENT:
3064 return (gettext("No such BE."));
3065 case BE_ERR_BUSY:
3066 return (gettext("Mount busy."));
3067 case BE_ERR_DEV_BUSY:
3068 return (gettext("Device busy."));
3069 case BE_ERR_CANCELED:
3070 return (gettext("Operation canceled."));
3071 case BE_ERR_CLONE:
3072 return (gettext("BE clone failed."));
3073 case BE_ERR_COPY:
3074 return (gettext("BE copy failed."));
3075 case BE_ERR_CREATDS:
3076 return (gettext("Dataset creation failed."));
3077 case BE_ERR_CURR_BE_NOT_FOUND:
3078 return (gettext("Can't find current BE."));
3079 case BE_ERR_DESTROY:
3080 return (gettext("Failed to destroy BE or snapshot."));
3081 case BE_ERR_DESTROY_CURR_BE:
3082 return (gettext("Cannot destroy current BE."));
3083 case BE_ERR_DEMOTE:
3084 return (gettext("BE demotion failed."));
3085 case BE_ERR_DSTYPE:
3086 return (gettext("Invalid dataset type."));
3087 case BE_ERR_BE_EXISTS:
3088 return (gettext("BE exists."));
3089 case BE_ERR_INIT:
3090 return (gettext("be_zfs_init failed."));
3091 case BE_ERR_INTR:
3092 return (gettext("Interupted system call."));
3093 case BE_ERR_INVAL:
3094 return (gettext("Invalid argument."));
3095 case BE_ERR_INVALPROP:
3096 return (gettext("Invalid property for dataset."));
3097 case BE_ERR_INVALMOUNTPOINT:
3098 return (gettext("Unexpected mountpoint."));
3099 case BE_ERR_MOUNT:
3100 return (gettext("Mount failed."));
3101 case BE_ERR_MOUNTED:
3102 return (gettext("Already mounted."));
3103 case BE_ERR_NAMETOOLONG:
3104 return (gettext("name > BUFSIZ."));
3105 case BE_ERR_NOENT:
3106 return (gettext("Doesn't exist."));
3107 case BE_ERR_POOL_NOENT:
3108 return (gettext("No such pool."));
3109 case BE_ERR_NODEV:
3110 return (gettext("No such device."));
3111 case BE_ERR_NOTMOUNTED:
3112 return (gettext("File system not mounted."));
3113 case BE_ERR_NOMEM:
3114 return (gettext("Not enough memory."));
3115 case BE_ERR_NONINHERIT:
3116 return (gettext(
3117 "Property is not inheritable for the BE dataset."));
3118 case BE_ERR_NXIO:
3119 return (gettext("No such device or address."));
3120 case BE_ERR_NOSPC:
3121 return (gettext("No space on device."));
3122 case BE_ERR_NOTSUP:
3123 return (gettext("Operation not supported."));
3124 case BE_ERR_OPEN:
3125 return (gettext("Open failed."));
3126 case BE_ERR_PERM:
3127 return (gettext("Not owner."));
3128 case BE_ERR_UNAVAIL:
3129 return (gettext("The BE is currently unavailable."));
3130 case BE_ERR_PROMOTE:
3131 return (gettext("BE promotion failed."));
3132 case BE_ERR_ROFS:
3133 return (gettext("Read only file system."));
3134 case BE_ERR_READONLYDS:
3135 return (gettext("Read only dataset."));
3136 case BE_ERR_READONLYPROP:
3137 return (gettext("Read only property."));
3138 case BE_ERR_RENAME_ACTIVE:
3139 return (gettext("Renaming the active BE is not supported."));
3140 case BE_ERR_SS_EXISTS:
3141 return (gettext("Snapshot exists."));
3142 case BE_ERR_SS_NOENT:
3143 return (gettext("No such snapshot."));
3144 case BE_ERR_UMOUNT:
3145 return (gettext("Unmount failed."));
3146 case BE_ERR_UMOUNT_CURR_BE:
3147 return (gettext("Can't unmount the current BE."));
3148 case BE_ERR_UMOUNT_SHARED:
3149 return (gettext("Unmount of a shared File System failed."));
3150 case BE_ERR_FAULT:
3151 return (gettext("Bad address."));
3152 case BE_ERR_UNKNOWN:
3153 return (gettext("Unknown error."));
3154 case BE_ERR_ZFS:
3155 return (gettext("ZFS returned an error."));
3156 case BE_ERR_GEN_UUID:
3157 return (gettext("Failed to generate uuid."));
3158 case BE_ERR_PARSE_UUID:
3159 return (gettext("Failed to parse uuid."));
3160 case BE_ERR_NO_UUID:
3161 return (gettext("No uuid"));
3162 case BE_ERR_ZONE_NO_PARENTBE:
3163 return (gettext("No parent uuid"));
3164 case BE_ERR_ZONE_MULTIPLE_ACTIVE:
3165 return (gettext("Multiple active zone roots"));
3166 case BE_ERR_ZONE_NO_ACTIVE_ROOT:
3167 return (gettext("No active zone root"));
3168 case BE_ERR_ZONE_ROOT_NOT_LEGACY:
3169 return (gettext("Zone root not legacy"));
3170 case BE_ERR_MOUNT_ZONEROOT:
3171 return (gettext("Failed to mount a zone root."));
3172 case BE_ERR_UMOUNT_ZONEROOT:
3173 return (gettext("Failed to unmount a zone root."));
3174 case BE_ERR_NO_MOUNTED_ZONE:
3175 return (gettext("Zone is not mounted"));
3176 case BE_ERR_ZONES_UNMOUNT:
3177 return (gettext("Unable to unmount a zone BE."));
3178 case BE_ERR_NO_MENU:
3179 return (gettext("Missing boot menu file."));
3180 case BE_ERR_BAD_MENU_PATH:
3181 return (gettext("Invalid path for menu.lst file"));
3182 case BE_ERR_ZONE_SS_EXISTS:
3183 return (gettext("Zone snapshot exists."));
3184 case BE_ERR_BOOTFILE_INST:
3185 return (gettext("Error installing boot files."));
3186 case BE_ERR_EXTCMD:
3187 return (gettext("Error running an external command."));
3188 default:
3189 return (NULL);
3190 }
3191 }
3192
3193 /*
3194 * Function: be_has_grub
3195 * Description: Boolean function indicating whether the current system
3196 * uses grub.
3197 * Return: B_FALSE - the system does not have grub
3198 * B_TRUE - the system does have grub.
3199 * Scope:
3200 * Semi-private (library wide use only)
3201 */
3202 boolean_t
be_has_grub(void)3203 be_has_grub(void)
3204 {
3205 static struct be_defaults be_defaults;
3206 static boolean_t be_deflts_set = B_FALSE;
3207
3208 /* Cache the defaults, because be_has_grub is used often. */
3209 if (be_deflts_set == B_FALSE) {
3210 be_get_defaults(&be_defaults);
3211 be_deflts_set = B_TRUE;
3212 }
3213
3214 return (be_defaults.be_deflt_grub);
3215 }
3216
3217 /*
3218 * Function: be_is_isa
3219 * Description: Boolean function indicating whether the instruction set
3220 * architecture of the executing system matches the name provided.
3221 * The string must match a system defined architecture (e.g.
3222 * "i386", "sparc") and is case sensitive.
3223 * Parameters: name - string representing the name of instruction set
3224 * architecture being tested
3225 * Returns: B_FALSE - the system instruction set architecture is different
3226 * from the one specified
3227 * B_TRUE - the system instruction set architecture is the same
3228 * as the one specified
3229 * Scope:
3230 * Semi-private (library wide use only)
3231 */
3232 boolean_t
be_is_isa(char * name)3233 be_is_isa(char *name)
3234 {
3235 return ((strcmp((char *)be_get_default_isa(), name) == 0));
3236 }
3237
3238 /*
3239 * Function: be_get_default_isa
3240 * Description:
3241 * Returns the default instruction set architecture of the
3242 * machine it is executed on. (eg. sparc, i386, ...)
3243 * NOTE: SYS_INST environment variable may override default
3244 * return value
3245 * Parameters:
3246 * none
3247 * Returns:
3248 * NULL - the architecture returned by sysinfo() was too
3249 * long for local variables
3250 * char * - pointer to a string containing the default
3251 * implementation
3252 * Scope:
3253 * Semi-private (library wide use only)
3254 */
3255 char *
be_get_default_isa(void)3256 be_get_default_isa(void)
3257 {
3258 int i;
3259 char *envp;
3260 static char default_inst[ARCH_LENGTH] = "";
3261
3262 if (default_inst[0] == '\0') {
3263 if ((envp = getenv("SYS_INST")) != NULL) {
3264 if ((int)strlen(envp) >= ARCH_LENGTH)
3265 return (NULL);
3266 else
3267 (void) strcpy(default_inst, envp);
3268 } else {
3269 i = sysinfo(SI_ARCHITECTURE, default_inst, ARCH_LENGTH);
3270 if (i < 0 || i > ARCH_LENGTH)
3271 return (NULL);
3272 }
3273 }
3274 return (default_inst);
3275 }
3276
3277 /*
3278 * Function: be_get_platform
3279 * Description:
3280 * Returns the platfom name
3281 * Parameters:
3282 * none
3283 * Returns:
3284 * NULL - the platform name returned by sysinfo() was too
3285 * long for local variables
3286 * char * - pointer to a string containing the platform name
3287 * Scope:
3288 * Semi-private (library wide use only)
3289 */
3290 char *
be_get_platform(void)3291 be_get_platform(void)
3292 {
3293 int i;
3294 static char default_inst[ARCH_LENGTH] = "";
3295
3296 if (default_inst[0] == '\0') {
3297 i = sysinfo(SI_PLATFORM, default_inst, ARCH_LENGTH);
3298 if (i < 0 || i > ARCH_LENGTH)
3299 return (NULL);
3300 }
3301 return (default_inst);
3302 }
3303
3304 /*
3305 * Function: be_run_cmd
3306 * Description:
3307 * Runs a command in a separate subprocess. Splits out stdout from stderr
3308 * and sends each to its own buffer. Buffers must be pre-allocated and
3309 * passed in as arguments. Buffer sizes are also passed in as arguments.
3310 *
3311 * Notes / caveats:
3312 * - Command being run is assumed to not have any stdout or stderr
3313 * redirection.
3314 * - Commands which emit total stderr output of greater than PIPE_BUF
3315 * bytes can hang. For such commands, a different implementation
3316 * which uses poll(2) must be used.
3317 * - stdout_buf can be NULL. In this case, stdout_bufsize is ignored, and
3318 * the stream which would have gone to it is sent to the bit
3319 * bucket.
3320 * - stderr_buf cannot be NULL.
3321 * - Only subprocess errors are appended to the stderr_buf. Errors
3322 * running the command are reported through be_print_err().
3323 * - Data which would overflow its respective buffer is sent to the bit
3324 * bucket.
3325 *
3326 * Parameters:
3327 * command: command to run. Assumed not to have embedded stdout
3328 * or stderr redirection. May have stdin redirection,
3329 * however.
3330 * stderr_buf: buffer returning subprocess stderr data. Errors
3331 * reported by this function are reported through
3332 * be_print_err().
3333 * stderr_bufsize: size of stderr_buf
3334 * stdout_buf: buffer returning subprocess stdout data.
3335 * stdout_bufsize: size of stdout_buf
3336 * Returns:
3337 * BE_SUCCESS - The command ran successfully without returning
3338 * errors.
3339 * BE_ERR_EXTCMD
3340 * - The command could not be run.
3341 * - The command terminated with error status.
3342 * - There were errors extracting or returning subprocess
3343 * data.
3344 * BE_ERR_NOMEM - The command exceeds the command buffer size.
3345 * BE_ERR_INVAL - An invalid argument was specified.
3346 * Scope:
3347 * Semi-private (library wide use only)
3348 */
3349 int
be_run_cmd(char * command,char * stderr_buf,int stderr_bufsize,char * stdout_buf,int stdout_bufsize)3350 be_run_cmd(char *command, char *stderr_buf, int stderr_bufsize,
3351 char *stdout_buf, int stdout_bufsize)
3352 {
3353 char *temp_filename = strdup(tmpnam(NULL));
3354 FILE *stdout_str = NULL;
3355 FILE *stderr_str = NULL;
3356 char cmdline[BUFSIZ];
3357 char oneline[BUFSIZ];
3358 int exit_status;
3359 int rval = BE_SUCCESS;
3360
3361 if ((command == NULL) || (stderr_buf == NULL) ||
3362 (stderr_bufsize <= 0) || (stdout_bufsize < 0) ||
3363 ((stdout_buf != NULL) ^ (stdout_bufsize != 0))) {
3364 return (BE_ERR_INVAL);
3365 }
3366
3367 /* Set up command so popen returns stderr, not stdout */
3368 if (snprintf(cmdline, BUFSIZ, "%s 2> %s", command,
3369 temp_filename) >= BUFSIZ) {
3370 rval = BE_ERR_NOMEM;
3371 goto cleanup;
3372 }
3373
3374 /* Set up the fifo that will make stderr available. */
3375 if (mkfifo(temp_filename, 0600) != 0) {
3376 (void) be_print_err(gettext("be_run_cmd: mkfifo: %s\n"),
3377 strerror(errno));
3378 rval = BE_ERR_EXTCMD;
3379 goto cleanup;
3380 }
3381
3382 if ((stdout_str = popen(cmdline, "r")) == NULL) {
3383 (void) be_print_err(gettext("be_run_cmd: popen: %s\n"),
3384 strerror(errno));
3385 rval = BE_ERR_EXTCMD;
3386 goto cleanup;
3387 }
3388
3389 if ((stderr_str = fopen(temp_filename, "r")) == NULL) {
3390 (void) be_print_err(gettext("be_run_cmd: fopen: %s\n"),
3391 strerror(errno));
3392 (void) pclose(stdout_str);
3393 rval = BE_ERR_EXTCMD;
3394 goto cleanup;
3395 }
3396
3397 /* Read stdout first, as it usually outputs more than stderr. */
3398 oneline[BUFSIZ-1] = '\0';
3399 while (fgets(oneline, BUFSIZ-1, stdout_str) != NULL) {
3400 if (stdout_str != NULL) {
3401 (void) strlcat(stdout_buf, oneline, stdout_bufsize);
3402 }
3403 }
3404
3405 while (fgets(oneline, BUFSIZ-1, stderr_str) != NULL) {
3406 (void) strlcat(stderr_buf, oneline, stderr_bufsize);
3407 }
3408
3409 /* Close pipe, get exit status. */
3410 if ((exit_status = pclose(stdout_str)) == -1) {
3411 (void) be_print_err(gettext("be_run_cmd: pclose: %s\n"),
3412 strerror(errno));
3413 rval = BE_ERR_EXTCMD;
3414 } else if (WIFEXITED(exit_status)) {
3415 exit_status = (int)((char)WEXITSTATUS(exit_status));
3416 /*
3417 * error code BC_NOUPDT means more recent version
3418 * is installed
3419 */
3420 if (exit_status != BC_SUCCESS && exit_status != BC_NOUPDT) {
3421 (void) snprintf(oneline, BUFSIZ, gettext("be_run_cmd: "
3422 "command terminated with error status: %d\n"),
3423 exit_status);
3424 (void) strlcat(stderr_buf, oneline, stderr_bufsize);
3425 rval = BE_ERR_EXTCMD;
3426 }
3427 } else {
3428 (void) snprintf(oneline, BUFSIZ, gettext("be_run_cmd: command "
3429 "terminated on signal: %s\n"),
3430 strsignal(WTERMSIG(exit_status)));
3431 (void) strlcat(stderr_buf, oneline, stderr_bufsize);
3432 rval = BE_ERR_EXTCMD;
3433 }
3434
3435 cleanup:
3436 (void) unlink(temp_filename);
3437 (void) free(temp_filename);
3438
3439 return (rval);
3440 }
3441
3442 /* ******************************************************************** */
3443 /* Private Functions */
3444 /* ******************************************************************** */
3445
3446 /*
3447 * Function: update_dataset
3448 * Description: This function takes a dataset name and replaces the zpool
3449 * and be_name components of the dataset with the new be_name
3450 * zpool passed in.
3451 * Parameters:
3452 * dataset - name of dataset
3453 * dataset_len - lenth of buffer in which dataset is passed in.
3454 * be_name - name of new BE name to update to.
3455 * old_rc_loc - dataset under which the root container dataset
3456 * for the old BE lives.
3457 * new_rc_loc - dataset under which the root container dataset
3458 * for the new BE lives.
3459 * Returns:
3460 * BE_SUCCESS - Success
3461 * be_errno_t - Failure
3462 * Scope:
3463 * Private
3464 */
3465 static int
update_dataset(char * dataset,int dataset_len,char * be_name,char * old_rc_loc,char * new_rc_loc)3466 update_dataset(char *dataset, int dataset_len, char *be_name,
3467 char *old_rc_loc, char *new_rc_loc)
3468 {
3469 char *ds = NULL;
3470 char *sub_ds = NULL;
3471 int ret;
3472
3473 /* Tear off the BE container dataset */
3474 if ((ds = be_make_name_from_ds(dataset, old_rc_loc)) == NULL) {
3475 return (BE_ERR_INVAL);
3476 }
3477
3478 /* Get dataset name relative to BE root, if there is one */
3479 sub_ds = strchr(ds, '/');
3480
3481 /* Generate the BE root dataset name */
3482 if ((ret = be_make_root_ds(new_rc_loc, be_name, dataset,
3483 dataset_len)) != BE_SUCCESS) {
3484 return (ret);
3485 }
3486
3487 /* If a subordinate dataset name was found, append it */
3488 if (sub_ds != NULL)
3489 (void) strlcat(dataset, sub_ds, dataset_len);
3490
3491 free(ds);
3492 return (BE_SUCCESS);
3493 }
3494
3495 /*
3496 * Function: _update_vfstab
3497 * Description: This function updates a vfstab file to reflect the new
3498 * root container dataset location and be_name for all
3499 * entries listed in the be_fs_list_data_t structure passed in.
3500 * Parameters:
3501 * vfstab - vfstab file to modify
3502 * be_name - name of BE to update.
3503 * old_rc_loc - dataset under which the root container dataset
3504 * of the old BE resides in.
3505 * new_rc_loc - dataset under which the root container dataset
3506 * of the new BE resides in.
3507 * fld - be_fs_list_data_t pointer providing the list of
3508 * file systems to look for in vfstab.
3509 * Returns:
3510 * BE_SUCCESS - Success
3511 * be_errno_t - Failure
3512 * Scope:
3513 * Private
3514 */
3515 static int
_update_vfstab(char * vfstab,char * be_name,char * old_rc_loc,char * new_rc_loc,be_fs_list_data_t * fld)3516 _update_vfstab(char *vfstab, char *be_name, char *old_rc_loc,
3517 char *new_rc_loc, be_fs_list_data_t *fld)
3518 {
3519 struct vfstab vp;
3520 char *tmp_vfstab = NULL;
3521 char comments_buf[BUFSIZ];
3522 FILE *comments = NULL;
3523 FILE *vfs_ents = NULL;
3524 FILE *tfile = NULL;
3525 struct stat sb;
3526 char dev[MAXPATHLEN];
3527 char *c;
3528 int fd;
3529 int ret = BE_SUCCESS, err = 0;
3530 int i;
3531 int tmp_vfstab_len = 0;
3532
3533 errno = 0;
3534
3535 /*
3536 * Open vfstab for reading twice. First is for comments,
3537 * second is for actual entries.
3538 */
3539 if ((comments = fopen(vfstab, "r")) == NULL ||
3540 (vfs_ents = fopen(vfstab, "r")) == NULL) {
3541 err = errno;
3542 be_print_err(gettext("_update_vfstab: "
3543 "failed to open vfstab (%s): %s\n"), vfstab,
3544 strerror(err));
3545 ret = errno_to_be_err(err);
3546 goto cleanup;
3547 }
3548
3549 /* Grab the stats of the original vfstab file */
3550 if (stat(vfstab, &sb) != 0) {
3551 err = errno;
3552 be_print_err(gettext("_update_vfstab: "
3553 "failed to stat file %s: %s\n"), vfstab,
3554 strerror(err));
3555 ret = errno_to_be_err(err);
3556 goto cleanup;
3557 }
3558
3559 /* Create tmp file for modified vfstab */
3560 if ((tmp_vfstab = (char *)malloc(strlen(vfstab) + 7))
3561 == NULL) {
3562 be_print_err(gettext("_update_vfstab: "
3563 "malloc failed\n"));
3564 ret = BE_ERR_NOMEM;
3565 goto cleanup;
3566 }
3567 tmp_vfstab_len = strlen(vfstab) + 7;
3568 (void) memset(tmp_vfstab, 0, tmp_vfstab_len);
3569 (void) strlcpy(tmp_vfstab, vfstab, tmp_vfstab_len);
3570 (void) strlcat(tmp_vfstab, "XXXXXX", tmp_vfstab_len);
3571 if ((fd = mkstemp(tmp_vfstab)) == -1) {
3572 err = errno;
3573 be_print_err(gettext("_update_vfstab: "
3574 "mkstemp failed: %s\n"), strerror(err));
3575 ret = errno_to_be_err(err);
3576 goto cleanup;
3577 }
3578 if ((tfile = fdopen(fd, "w")) == NULL) {
3579 err = errno;
3580 be_print_err(gettext("_update_vfstab: "
3581 "could not open file for write\n"));
3582 (void) close(fd);
3583 ret = errno_to_be_err(err);
3584 goto cleanup;
3585 }
3586
3587 while (fgets(comments_buf, BUFSIZ, comments)) {
3588 for (c = comments_buf; *c != '\0' && isspace(*c); c++)
3589 ;
3590 if (*c == '\0') {
3591 continue;
3592 } else if (*c == '#') {
3593 /*
3594 * If line is a comment line, just put
3595 * it through to the tmp vfstab.
3596 */
3597 (void) fputs(comments_buf, tfile);
3598 } else {
3599 /*
3600 * Else line is a vfstab entry, grab it
3601 * into a vfstab struct.
3602 */
3603 if (getvfsent(vfs_ents, &vp) != 0) {
3604 err = errno;
3605 be_print_err(gettext("_update_vfstab: "
3606 "getvfsent failed: %s\n"), strerror(err));
3607 ret = errno_to_be_err(err);
3608 goto cleanup;
3609 }
3610
3611 if (vp.vfs_special == NULL || vp.vfs_mountp == NULL) {
3612 (void) putvfsent(tfile, &vp);
3613 continue;
3614 }
3615
3616 /*
3617 * If the entry is one of the entries in the list
3618 * of file systems to update, modify it's device
3619 * field to be correct for this BE.
3620 */
3621 for (i = 0; i < fld->fs_num; i++) {
3622 if (strcmp(vp.vfs_special, fld->fs_list[i])
3623 == 0) {
3624 /*
3625 * Found entry that needs an update.
3626 * Replace the root container dataset
3627 * location and be_name in the
3628 * entry's device.
3629 */
3630 (void) strlcpy(dev, vp.vfs_special,
3631 sizeof (dev));
3632
3633 if ((ret = update_dataset(dev,
3634 sizeof (dev), be_name, old_rc_loc,
3635 new_rc_loc)) != 0) {
3636 be_print_err(
3637 gettext("_update_vfstab: "
3638 "Failed to update device "
3639 "field for vfstab entry "
3640 "%s\n"), fld->fs_list[i]);
3641 goto cleanup;
3642 }
3643
3644 vp.vfs_special = dev;
3645 break;
3646 }
3647 }
3648
3649 /* Put entry through to tmp vfstab */
3650 (void) putvfsent(tfile, &vp);
3651 }
3652 }
3653
3654 (void) fclose(comments);
3655 comments = NULL;
3656 (void) fclose(vfs_ents);
3657 vfs_ents = NULL;
3658 (void) fclose(tfile);
3659 tfile = NULL;
3660
3661 /* Copy tmp vfstab into place */
3662 if (rename(tmp_vfstab, vfstab) != 0) {
3663 err = errno;
3664 be_print_err(gettext("_update_vfstab: "
3665 "failed to rename file %s to %s: %s\n"), tmp_vfstab,
3666 vfstab, strerror(err));
3667 ret = errno_to_be_err(err);
3668 goto cleanup;
3669 }
3670
3671 /* Set the perms and ownership of the updated file */
3672 if (chmod(vfstab, sb.st_mode) != 0) {
3673 err = errno;
3674 be_print_err(gettext("_update_vfstab: "
3675 "failed to chmod %s: %s\n"), vfstab, strerror(err));
3676 ret = errno_to_be_err(err);
3677 goto cleanup;
3678 }
3679 if (chown(vfstab, sb.st_uid, sb.st_gid) != 0) {
3680 err = errno;
3681 be_print_err(gettext("_update_vfstab: "
3682 "failed to chown %s: %s\n"), vfstab, strerror(err));
3683 ret = errno_to_be_err(err);
3684 goto cleanup;
3685 }
3686
3687 cleanup:
3688 if (comments != NULL)
3689 (void) fclose(comments);
3690 if (vfs_ents != NULL)
3691 (void) fclose(vfs_ents);
3692 (void) unlink(tmp_vfstab);
3693 (void) free(tmp_vfstab);
3694 if (tfile != NULL)
3695 (void) fclose(tfile);
3696
3697 return (ret);
3698 }
3699
3700
3701 /*
3702 * Function: be_get_auto_name
3703 * Description: Generate an auto name constructed based on the BE name
3704 * of the original BE or zone BE being cloned.
3705 * Parameters:
3706 * obe_name - name of the original BE or zone BE being cloned.
3707 * container_ds - container dataset for the zone.
3708 * Note: if zone_be is false this should be
3709 * NULL.
3710 * zone_be - flag that indicates if we are operating on a zone BE.
3711 * Returns:
3712 * Success - pointer to auto generated BE name. The name
3713 * is allocated in heap storage so the caller is
3714 * responsible for free'ing the name.
3715 * Failure - NULL
3716 * Scope:
3717 * Private
3718 */
3719 static char *
be_get_auto_name(char * obe_name,char * be_container_ds,boolean_t zone_be)3720 be_get_auto_name(char *obe_name, char *be_container_ds, boolean_t zone_be)
3721 {
3722 be_node_list_t *be_nodes = NULL;
3723 be_node_list_t *cur_be = NULL;
3724 char auto_be_name[MAXPATHLEN];
3725 char base_be_name[MAXPATHLEN];
3726 char cur_be_name[MAXPATHLEN];
3727 char *num_str = NULL;
3728 char *c = NULL;
3729 int num = 0;
3730 int cur_num = 0;
3731
3732 errno = 0;
3733
3734 /*
3735 * Check if obe_name is already in an auto BE name format.
3736 * If it is, then strip off the increment number to get the
3737 * base name.
3738 */
3739 (void) strlcpy(base_be_name, obe_name, sizeof (base_be_name));
3740
3741 if ((num_str = strrchr(base_be_name, BE_AUTO_NAME_DELIM))
3742 != NULL) {
3743 /* Make sure remaining string is all digits */
3744 c = num_str + 1;
3745 while (c[0] != '\0' && isdigit(c[0]))
3746 c++;
3747 /*
3748 * If we're now at the end of the string strip off the
3749 * increment number.
3750 */
3751 if (c[0] == '\0')
3752 num_str[0] = '\0';
3753 }
3754
3755 if (zone_be) {
3756 if (be_container_ds == NULL)
3757 return (NULL);
3758 if (be_get_zone_be_list(obe_name, be_container_ds,
3759 &be_nodes) != BE_SUCCESS) {
3760 be_print_err(gettext("be_get_auto_name: "
3761 "be_get_zone_be_list failed\n"));
3762 return (NULL);
3763 }
3764 } else if (_be_list(NULL, &be_nodes, BE_LIST_DEFAULT) != BE_SUCCESS) {
3765 be_print_err(gettext("be_get_auto_name: be_list failed\n"));
3766 return (NULL);
3767 }
3768
3769 for (cur_be = be_nodes; cur_be != NULL; cur_be = cur_be->be_next_node) {
3770 (void) strlcpy(cur_be_name, cur_be->be_node_name,
3771 sizeof (cur_be_name));
3772
3773 /* If cur_be_name doesn't match at least base be name, skip. */
3774 if (strncmp(cur_be_name, base_be_name, strlen(base_be_name))
3775 != 0)
3776 continue;
3777
3778 /* Get the string following the base be name */
3779 num_str = cur_be_name + strlen(base_be_name);
3780
3781 /*
3782 * If nothing follows the base be name, this cur_be_name
3783 * is the BE named with the base be name, skip.
3784 */
3785 if (num_str == NULL || num_str[0] == '\0')
3786 continue;
3787
3788 /*
3789 * Remove the name delimiter. If its not there,
3790 * cur_be_name isn't part of this BE name stream, skip.
3791 */
3792 if (num_str[0] == BE_AUTO_NAME_DELIM)
3793 num_str++;
3794 else
3795 continue;
3796
3797 /* Make sure remaining string is all digits */
3798 c = num_str;
3799 while (c[0] != '\0' && isdigit(c[0]))
3800 c++;
3801 if (c[0] != '\0')
3802 continue;
3803
3804 /* Convert the number string to an int */
3805 cur_num = atoi(num_str);
3806
3807 /*
3808 * If failed to convert the string, skip it. If its too
3809 * long to be converted to an int, we wouldn't auto generate
3810 * this number anyway so there couldn't be a conflict.
3811 * We treat it as a manually created BE name.
3812 */
3813 if (cur_num == 0 && errno == EINVAL)
3814 continue;
3815
3816 /*
3817 * Compare current number to current max number,
3818 * take higher of the two.
3819 */
3820 if (cur_num > num)
3821 num = cur_num;
3822 }
3823
3824 /*
3825 * Store off a copy of 'num' incase we need it later. If incrementing
3826 * 'num' causes it to roll over, this means 'num' is the largest
3827 * positive int possible; we'll need it later in the loop to determine
3828 * if we've exhausted all possible increment numbers. We store it in
3829 * 'cur_num'.
3830 */
3831 cur_num = num;
3832
3833 /* Increment 'num' to get new auto BE name number */
3834 if (++num <= 0) {
3835 int ret = 0;
3836
3837 /*
3838 * Since incrementing 'num' caused it to rollover, start
3839 * over at 0 and find the first available number.
3840 */
3841 for (num = 0; num < cur_num; num++) {
3842
3843 (void) snprintf(cur_be_name, sizeof (cur_be_name),
3844 "%s%c%d", base_be_name, BE_AUTO_NAME_DELIM, num);
3845
3846 ret = zpool_iter(g_zfs, be_exists_callback,
3847 cur_be_name);
3848
3849 if (ret == 0) {
3850 /*
3851 * BE name doesn't exist, break out
3852 * to use 'num'.
3853 */
3854 break;
3855 } else if (ret == 1) {
3856 /* BE name exists, continue looking */
3857 continue;
3858 } else {
3859 be_print_err(gettext("be_get_auto_name: "
3860 "zpool_iter failed: %s\n"),
3861 libzfs_error_description(g_zfs));
3862 be_free_list(be_nodes);
3863 return (NULL);
3864 }
3865 }
3866
3867 /*
3868 * If 'num' equals 'cur_num', we've exhausted all possible
3869 * auto BE names for this base BE name.
3870 */
3871 if (num == cur_num) {
3872 be_print_err(gettext("be_get_auto_name: "
3873 "No more available auto BE names for base "
3874 "BE name %s\n"), base_be_name);
3875 be_free_list(be_nodes);
3876 return (NULL);
3877 }
3878 }
3879
3880 be_free_list(be_nodes);
3881
3882 /*
3883 * Generate string for auto BE name.
3884 */
3885 (void) snprintf(auto_be_name, sizeof (auto_be_name), "%s%c%d",
3886 base_be_name, BE_AUTO_NAME_DELIM, num);
3887
3888 if ((c = strdup(auto_be_name)) == NULL) {
3889 be_print_err(gettext("be_get_auto_name: "
3890 "memory allocation failed\n"));
3891 return (NULL);
3892 }
3893
3894 return (c);
3895 }
3896
3897 /*
3898 * Function: be_get_console_prop
3899 * Description: Determine console device.
3900 * Returns:
3901 * Success - pointer to console setting.
3902 * Failure - NULL
3903 * Scope:
3904 * Private
3905 */
3906 static char *
be_get_console_prop(void)3907 be_get_console_prop(void)
3908 {
3909 di_node_t dn;
3910 char *console = NULL;
3911
3912 if ((dn = di_init("/", DINFOPROP)) == DI_NODE_NIL) {
3913 be_print_err(gettext("be_get_console_prop: "
3914 "di_init() failed\n"));
3915 return (NULL);
3916 }
3917
3918 if (di_prop_lookup_strings(DDI_DEV_T_ANY, dn,
3919 "console", &console) != -1) {
3920 di_fini(dn);
3921 return (console);
3922 }
3923
3924 if (console == NULL) {
3925 if (di_prop_lookup_strings(DDI_DEV_T_ANY, dn,
3926 "output-device", &console) != -1) {
3927 di_fini(dn);
3928 if (strncmp(console, "screen", strlen("screen")) == 0)
3929 console = BE_DEFAULT_CONSOLE;
3930 }
3931 }
3932
3933 /*
3934 * Default console to text
3935 */
3936 if (console == NULL) {
3937 console = BE_DEFAULT_CONSOLE;
3938 }
3939
3940 return (console);
3941 }
3942
3943 /*
3944 * Function: be_create_menu
3945 * Description:
3946 * This function is used if no menu.lst file exists. In
3947 * this case a new file is created and if needed default
3948 * lines are added to the file.
3949 * Parameters:
3950 * pool - The name of the pool the menu.lst file is on
3951 * menu_file - The name of the file we're creating.
3952 * menu_fp - A pointer to the file pointer of the file we
3953 * created. This is also used to pass back the file
3954 * pointer to the newly created file.
3955 * mode - the original mode used for the failed attempt to
3956 * non-existent file.
3957 * Returns:
3958 * BE_SUCCESS - Success
3959 * be_errno_t - Failure
3960 * Scope:
3961 * Private
3962 */
3963 static int
be_create_menu(char * pool,char * menu_file,FILE ** menu_fp,char * mode)3964 be_create_menu(
3965 char *pool,
3966 char *menu_file,
3967 FILE **menu_fp,
3968 char *mode)
3969 {
3970 be_node_list_t *be_nodes = NULL;
3971 char *menu_path = NULL;
3972 char *be_rpool = NULL;
3973 char *be_name = NULL;
3974 char *console = NULL;
3975 errno = 0;
3976
3977 if (menu_file == NULL || menu_fp == NULL || mode == NULL)
3978 return (BE_ERR_INVAL);
3979
3980 menu_path = strdup(menu_file);
3981 if (menu_path == NULL)
3982 return (BE_ERR_NOMEM);
3983
3984 (void) dirname(menu_path);
3985 if (*menu_path == '.') {
3986 free(menu_path);
3987 return (BE_ERR_BAD_MENU_PATH);
3988 }
3989 if (mkdirp(menu_path,
3990 S_IRWXU | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH) == -1 &&
3991 errno != EEXIST) {
3992 be_print_err(gettext("be_create_menu: Failed to create the %s "
3993 "directory: %s\n"), menu_path, strerror(errno));
3994 free(menu_path);
3995 return (errno_to_be_err(errno));
3996 }
3997 free(menu_path);
3998
3999 /*
4000 * Check to see if this system supports grub
4001 */
4002 if (be_has_grub()) {
4003 /*
4004 * The grub menu is missing so we need to create it
4005 * and fill in the first few lines.
4006 */
4007 FILE *temp_fp = fopen(menu_file, "a+");
4008 if (temp_fp == NULL) {
4009 *menu_fp = NULL;
4010 return (errno_to_be_err(errno));
4011 }
4012
4013 if ((console = be_get_console_prop()) != NULL) {
4014
4015 /*
4016 * If console is redirected to serial line,
4017 * GRUB splash screen will not be enabled.
4018 */
4019 if (strncmp(console, "text", strlen("text")) == 0 ||
4020 strncmp(console, "graphics",
4021 strlen("graphics")) == 0) {
4022
4023 (void) fprintf(temp_fp, "%s\n", BE_GRUB_SPLASH);
4024 (void) fprintf(temp_fp, "%s\n",
4025 BE_GRUB_FOREGROUND);
4026 (void) fprintf(temp_fp, "%s\n",
4027 BE_GRUB_BACKGROUND);
4028 (void) fprintf(temp_fp, "%s\n",
4029 BE_GRUB_DEFAULT);
4030 } else {
4031 be_print_err(gettext("be_create_menu: "
4032 "console on serial line, "
4033 "GRUB splash image will be disabled\n"));
4034 }
4035 }
4036
4037 (void) fprintf(temp_fp, "timeout 30\n");
4038 (void) fclose(temp_fp);
4039
4040 } else {
4041 /*
4042 * The menu file doesn't exist so we need to create a
4043 * blank file.
4044 */
4045 FILE *temp_fp = fopen(menu_file, "w+");
4046 if (temp_fp == NULL) {
4047 *menu_fp = NULL;
4048 return (errno_to_be_err(errno));
4049 }
4050 (void) fclose(temp_fp);
4051 }
4052
4053 /*
4054 * Now we need to add all the BE's back into the the file.
4055 */
4056 if (_be_list(NULL, &be_nodes, BE_LIST_DEFAULT) == BE_SUCCESS) {
4057 while (be_nodes != NULL) {
4058 if (strcmp(pool, be_nodes->be_rpool) == 0) {
4059 (void) be_append_menu(be_nodes->be_node_name,
4060 be_nodes->be_rpool, NULL, NULL, NULL);
4061 }
4062 if (be_nodes->be_active_on_boot) {
4063 be_rpool = strdup(be_nodes->be_rpool);
4064 be_name = strdup(be_nodes->be_node_name);
4065 }
4066
4067 be_nodes = be_nodes->be_next_node;
4068 }
4069 }
4070 be_free_list(be_nodes);
4071
4072 /*
4073 * Check to see if this system supports grub
4074 */
4075 if (be_has_grub()) {
4076 int err = be_change_grub_default(be_name, be_rpool);
4077 if (err != BE_SUCCESS)
4078 return (err);
4079 }
4080 *menu_fp = fopen(menu_file, mode);
4081 if (*menu_fp == NULL)
4082 return (errno_to_be_err(errno));
4083
4084 return (BE_SUCCESS);
4085 }
4086
4087 /*
4088 * Function: be_open_menu
4089 * Description:
4090 * This function is used it open the menu.lst file. If this
4091 * file does not exist be_create_menu is called to create it
4092 * and the open file pointer is returned. If the file does
4093 * exist it is simply opened using the mode passed in.
4094 * Parameters:
4095 * pool - The name of the pool the menu.lst file is on
4096 * menu_file - The name of the file we're opening.
4097 * menu_fp - A pointer to the file pointer of the file we're
4098 * opening. This is also used to pass back the file
4099 * pointer.
4100 * mode - the original mode to be used for opening the menu.lst
4101 * file.
4102 * create_menu - If this is true and the menu.lst file does not
4103 * exist we will attempt to re-create it. However
4104 * if it's false the error returned from the fopen
4105 * will be returned.
4106 * Returns:
4107 * BE_SUCCESS - Success
4108 * be_errno_t - Failure
4109 * Scope:
4110 * Private
4111 */
4112 static int
be_open_menu(char * pool,char * menu_file,FILE ** menu_fp,char * mode,boolean_t create_menu)4113 be_open_menu(
4114 char *pool,
4115 char *menu_file,
4116 FILE **menu_fp,
4117 char *mode,
4118 boolean_t create_menu)
4119 {
4120 int err = 0;
4121 boolean_t set_print = B_FALSE;
4122
4123 *menu_fp = fopen(menu_file, mode);
4124 err = errno;
4125 if (*menu_fp == NULL) {
4126 if (err == ENOENT && create_menu) {
4127 be_print_err(gettext("be_open_menu: menu.lst "
4128 "file %s does not exist,\n"), menu_file);
4129 if (!do_print) {
4130 set_print = B_TRUE;
4131 do_print = B_TRUE;
4132 }
4133 be_print_err(gettext("WARNING: menu.lst "
4134 "file %s does not exist,\n generating "
4135 "a new menu.lst file\n"), menu_file);
4136 if (set_print)
4137 do_print = B_FALSE;
4138 err = 0;
4139 if ((err = be_create_menu(pool, menu_file,
4140 menu_fp, mode)) == ENOENT)
4141 return (BE_ERR_NO_MENU);
4142 else if (err != BE_SUCCESS)
4143 return (err);
4144 else if (*menu_fp == NULL)
4145 return (BE_ERR_NO_MENU);
4146 } else {
4147 be_print_err(gettext("be_open_menu: failed "
4148 "to open menu.lst file %s\n"), menu_file);
4149 if (err == ENOENT)
4150 return (BE_ERR_NO_MENU);
4151 else
4152 return (errno_to_be_err(err));
4153 }
4154 }
4155 return (BE_SUCCESS);
4156 }
4157