8322 nl: misleading-indentation
[unleashed/tickless.git] / usr / src / lib / libbe / common / be_utils.c
blobfb2f662fc7fc4ddbb707c12f9ef2dd83657a3d8f
1 /*
2 * CDDL HEADER START
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.
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.
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]
19 * CDDL HEADER END
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.
31 * System includes
33 #include <assert.h>
34 #include <errno.h>
35 #include <libgen.h>
36 #include <libintl.h>
37 #include <libnvpair.h>
38 #include <libzfs.h>
39 #include <libgen.h>
40 #include <stdio.h>
41 #include <stdlib.h>
42 #include <string.h>
43 #include <sys/stat.h>
44 #include <sys/types.h>
45 #include <sys/vfstab.h>
46 #include <sys/param.h>
47 #include <sys/systeminfo.h>
48 #include <ctype.h>
49 #include <time.h>
50 #include <unistd.h>
51 #include <fcntl.h>
52 #include <deflt.h>
53 #include <wait.h>
54 #include <libdevinfo.h>
55 #include <libgen.h>
57 #include <libbe.h>
58 #include <libbe_priv.h>
59 #include <boot_utils.h>
60 #include <ficl.h>
61 #include <ficlplatform/emu.h>
63 /* Private function prototypes */
64 static int update_dataset(char *, int, char *, char *, char *);
65 static int _update_vfstab(char *, char *, char *, char *, be_fs_list_data_t *);
66 static int be_open_menu(char *, char *, FILE **, char *, boolean_t);
67 static int be_create_menu(char *, char *, FILE **, char *);
68 static char *be_get_auto_name(char *, char *, boolean_t);
71 * Global error printing
73 boolean_t do_print = B_FALSE;
76 * Private datatypes
78 typedef struct zone_be_name_cb_data {
79 char *base_be_name;
80 int num;
81 } zone_be_name_cb_data_t;
83 /* ******************************************************************** */
84 /* Public Functions */
85 /* ******************************************************************** */
88 * Callback for ficl to suppress all output from ficl, as we do not
89 * want to confuse user with messages from ficl, and we are only
90 * checking results from function calls.
92 /*ARGSUSED*/
93 static void
94 ficlSuppressTextOutput(ficlCallback *cb, char *text)
96 /* This function is intentionally doing nothing. */
100 * Function: be_get_boot_args
101 * Description: Returns the fast boot argument string for enumerated BE.
102 * Parameters:
103 * fbarg - pointer to argument string.
104 * entry - index of BE.
105 * Returns:
106 * fast boot argument string.
107 * Scope:
108 * Public
111 be_get_boot_args(char **fbarg, int entry)
113 be_node_list_t *node, *be_nodes = NULL;
114 be_transaction_data_t bt = {0};
115 char *mountpoint = NULL;
116 boolean_t be_mounted = B_FALSE;
117 int ret = BE_SUCCESS;
118 int index;
119 ficlVm *vm;
121 *fbarg = NULL;
122 if (!be_zfs_init())
123 return (BE_ERR_INIT);
126 * need pool name, menu.lst has entries from our pool only
128 ret = be_find_current_be(&bt);
129 if (ret != BE_SUCCESS) {
130 be_zfs_fini();
131 return (ret);
135 * be_get_boot_args() is for loader, fail with grub will trigger
136 * normal boot.
138 if (be_has_grub()) {
139 ret = BE_ERR_INIT;
140 goto done;
143 ret = _be_list(NULL, &be_nodes);
144 if (ret != BE_SUCCESS)
145 goto done;
148 * iterate through be_nodes,
149 * if entry == -1, stop if be_active_on_boot,
150 * else stop if index == entry.
152 index = 0;
153 for (node = be_nodes; node != NULL; node = node->be_next_node) {
154 if (strcmp(node->be_rpool, bt.obe_zpool) != 0)
155 continue;
156 if (entry == BE_ENTRY_DEFAULT &&
157 node->be_active_on_boot == B_TRUE)
158 break;
159 if (index == entry)
160 break;
161 index++;
163 if (node == NULL) {
164 be_free_list(be_nodes);
165 ret = BE_ERR_NOENT;
166 goto done;
169 /* try to mount inactive be */
170 if (node->be_active == B_FALSE) {
171 ret = _be_mount(node->be_node_name, &mountpoint,
172 BE_MOUNT_FLAG_NO_ZONES);
173 if (ret != BE_SUCCESS && ret != BE_ERR_MOUNTED) {
174 be_free_list(be_nodes);
175 goto done;
176 } else
177 be_mounted = B_TRUE;
180 vm = bf_init("", ficlSuppressTextOutput);
181 if (vm != NULL) {
183 * zfs MAXNAMELEN is 256, so we need to pick buf large enough
184 * to contain such names.
186 char buf[MAXNAMELEN * 2];
187 char *kernel_options = NULL;
188 char *kernel = NULL;
189 char *tmp;
190 zpool_handle_t *zph;
193 * just try to interpret following words. on error
194 * we will be missing kernelname, and will get out.
196 (void) snprintf(buf, sizeof (buf), "set currdev=zfs:%s:",
197 node->be_root_ds);
198 ret = ficlVmEvaluate(vm, buf);
199 if (ret != FICL_VM_STATUS_OUT_OF_TEXT) {
200 be_print_err(gettext("be_get_boot_args: error "
201 "interpreting boot config: %d\n"), ret);
202 bf_fini();
203 ret = BE_ERR_NO_MENU;
204 goto cleanup;
206 (void) snprintf(buf, sizeof (buf),
207 "include /boot/forth/loader.4th");
208 ret = ficlVmEvaluate(vm, buf);
209 if (ret != FICL_VM_STATUS_OUT_OF_TEXT) {
210 be_print_err(gettext("be_get_boot_args: error "
211 "interpreting boot config: %d\n"), ret);
212 bf_fini();
213 ret = BE_ERR_NO_MENU;
214 goto cleanup;
216 (void) snprintf(buf, sizeof (buf), "start");
217 ret = ficlVmEvaluate(vm, buf);
218 if (ret != FICL_VM_STATUS_OUT_OF_TEXT) {
219 be_print_err(gettext("be_get_boot_args: error "
220 "interpreting boot config: %d\n"), ret);
221 bf_fini();
222 ret = BE_ERR_NO_MENU;
223 goto cleanup;
225 (void) snprintf(buf, sizeof (buf), "boot");
226 ret = ficlVmEvaluate(vm, buf);
227 bf_fini();
228 if (ret != FICL_VM_STATUS_OUT_OF_TEXT) {
229 be_print_err(gettext("be_get_boot_args: error "
230 "interpreting boot config: %d\n"), ret);
231 ret = BE_ERR_NO_MENU;
232 goto cleanup;
235 kernel_options = getenv("boot-args");
236 kernel = getenv("kernelname");
238 if (kernel == NULL) {
239 be_print_err(gettext("be_get_boot_args: no kernel\n"));
240 ret = BE_ERR_NOENT;
241 goto cleanup;
244 if ((zph = zpool_open(g_zfs, node->be_rpool)) == NULL) {
245 be_print_err(gettext("be_get_boot_args: failed to "
246 "open root pool (%s): %s\n"), node->be_rpool,
247 libzfs_error_description(g_zfs));
248 ret = zfs_err_to_be_err(g_zfs);
249 goto cleanup;
251 ret = zpool_get_physpath(zph, buf, sizeof (buf));
252 zpool_close(zph);
253 if (ret != 0) {
254 be_print_err(gettext("be_get_boot_args: failed to "
255 "get physpath\n"));
256 goto cleanup;
259 /* zpool_get_physpath() can return space separated list */
260 tmp = buf;
261 tmp = strsep(&tmp, " ");
263 if (kernel_options == NULL || *kernel_options == '\0')
264 (void) asprintf(fbarg, "/ %s "
265 "-B zfs-bootfs=%s,bootpath=\"%s\"\n", kernel,
266 node->be_root_ds, tmp);
267 else
268 (void) asprintf(fbarg, "/ %s %s "
269 "-B zfs-bootfs=%s,bootpath=\"%s\"\n", kernel,
270 kernel_options, node->be_root_ds, tmp);
272 if (fbarg == NULL)
273 ret = BE_ERR_NOMEM;
274 else
275 ret = 0;
276 } else
277 ret = BE_ERR_NOMEM;
278 cleanup:
279 if (be_mounted == B_TRUE)
280 (void) _be_unmount(node->be_node_name, BE_UNMOUNT_FLAG_FORCE);
281 be_free_list(be_nodes);
282 done:
283 free(mountpoint);
284 free(bt.obe_name);
285 free(bt.obe_root_ds);
286 free(bt.obe_zpool);
287 free(bt.obe_snap_name);
288 free(bt.obe_altroot);
289 be_zfs_fini();
290 return (ret);
294 * Function: be_max_avail
295 * Description: Returns the available size for the zfs dataset passed in.
296 * Parameters:
297 * dataset - The dataset we want to get the available space for.
298 * ret - The available size will be returned in this.
299 * Returns:
300 * The error returned by the zfs get property function.
301 * Scope:
302 * Public
305 be_max_avail(char *dataset, uint64_t *ret)
307 zfs_handle_t *zhp;
308 int err = 0;
310 /* Initialize libzfs handle */
311 if (!be_zfs_init())
312 return (BE_ERR_INIT);
314 zhp = zfs_open(g_zfs, dataset, ZFS_TYPE_DATASET);
315 if (zhp == NULL) {
317 * The zfs_open failed return an error
319 err = zfs_err_to_be_err(g_zfs);
320 } else {
321 err = be_maxsize_avail(zhp, ret);
323 ZFS_CLOSE(zhp);
324 be_zfs_fini();
325 return (err);
329 * Function: libbe_print_errors
330 * Description: Turns on/off error output for the library.
331 * Parameter:
332 * set_do_print - Boolean that turns library error
333 * printing on or off.
334 * Returns:
335 * None
336 * Scope:
337 * Public;
339 void
340 libbe_print_errors(boolean_t set_do_print)
342 do_print = set_do_print;
345 /* ******************************************************************** */
346 /* Semi-Private Functions */
347 /* ******************************************************************** */
350 * Function: be_zfs_init
351 * Description: Initializes the libary global libzfs handle.
352 * Parameters:
353 * None
354 * Returns:
355 * B_TRUE - Success
356 * B_FALSE - Failure
357 * Scope:
358 * Semi-private (library wide use only)
360 boolean_t
361 be_zfs_init(void)
363 be_zfs_fini();
365 if ((g_zfs = libzfs_init()) == NULL) {
366 be_print_err(gettext("be_zfs_init: failed to initialize ZFS "
367 "library\n"));
368 return (B_FALSE);
371 return (B_TRUE);
375 * Function: be_zfs_fini
376 * Description: Closes the library global libzfs handle if it currently open.
377 * Parameter:
378 * None
379 * Returns:
380 * None
381 * Scope:
382 * Semi-private (library wide use only)
384 void
385 be_zfs_fini(void)
387 if (g_zfs)
388 libzfs_fini(g_zfs);
390 g_zfs = NULL;
394 * Function: be_get_defaults
395 * Description: Open defaults and gets be default paramets
396 * Parameters:
397 * defaults - be defaults struct
398 * Returns:
399 * None
400 * Scope:
401 * Semi-private (library wide use only)
403 void
404 be_get_defaults(struct be_defaults *defaults)
406 void *defp;
408 defaults->be_deflt_grub = B_FALSE;
409 defaults->be_deflt_rpool_container = B_FALSE;
410 defaults->be_deflt_bename_starts_with[0] = '\0';
412 if ((defp = defopen_r(BE_DEFAULTS)) != NULL) {
413 const char *res = defread_r(BE_DFLT_BENAME_STARTS, defp);
414 if (res != NULL && res[0] != '\0') {
415 (void) strlcpy(defaults->be_deflt_bename_starts_with,
416 res, ZFS_MAX_DATASET_NAME_LEN);
417 defaults->be_deflt_rpool_container = B_TRUE;
419 if (be_is_isa("i386")) {
420 res = defread_r(BE_DFLT_BE_HAS_GRUB, defp);
421 if (res != NULL && res[0] != '\0') {
422 if (strcasecmp(res, "true") == 0)
423 defaults->be_deflt_grub = B_TRUE;
426 defclose_r(defp);
431 * Function: be_make_root_ds
432 * Description: Generate string for BE's root dataset given the pool
433 * it lives in and the BE name.
434 * Parameters:
435 * zpool - pointer zpool name.
436 * be_name - pointer to BE name.
437 * be_root_ds - pointer to buffer to return BE root dataset in.
438 * be_root_ds_size - size of be_root_ds
439 * Returns:
440 * None
441 * Scope:
442 * Semi-private (library wide use only)
444 void
445 be_make_root_ds(const char *zpool, const char *be_name, char *be_root_ds,
446 int be_root_ds_size)
448 struct be_defaults be_defaults;
449 be_get_defaults(&be_defaults);
450 char *root_ds = NULL;
452 if (getzoneid() == GLOBAL_ZONEID) {
453 if (be_defaults.be_deflt_rpool_container) {
454 (void) snprintf(be_root_ds, be_root_ds_size,
455 "%s/%s", zpool, be_name);
456 } else {
457 (void) snprintf(be_root_ds, be_root_ds_size,
458 "%s/%s/%s", zpool, BE_CONTAINER_DS_NAME, be_name);
460 } else {
462 * In non-global zone we can use path from mounted root dataset
463 * to generate BE's root dataset string.
465 if ((root_ds = be_get_ds_from_dir("/")) != NULL) {
466 (void) snprintf(be_root_ds, be_root_ds_size, "%s/%s",
467 dirname(root_ds), be_name);
468 } else {
469 be_print_err(gettext("be_make_root_ds: zone root "
470 "dataset is not mounted\n"));
471 return;
477 * Function: be_make_container_ds
478 * Description: Generate string for the BE container dataset given a pool name.
479 * Parameters:
480 * zpool - pointer zpool name.
481 * container_ds - pointer to buffer to return BE container
482 * dataset in.
483 * container_ds_size - size of container_ds
484 * Returns:
485 * None
486 * Scope:
487 * Semi-private (library wide use only)
489 void
490 be_make_container_ds(const char *zpool, char *container_ds,
491 int container_ds_size)
493 struct be_defaults be_defaults;
494 be_get_defaults(&be_defaults);
495 char *root_ds = NULL;
497 if (getzoneid() == GLOBAL_ZONEID) {
498 if (be_defaults.be_deflt_rpool_container) {
499 (void) snprintf(container_ds, container_ds_size,
500 "%s", zpool);
501 } else {
502 (void) snprintf(container_ds, container_ds_size,
503 "%s/%s", zpool, BE_CONTAINER_DS_NAME);
505 } else {
506 if ((root_ds = be_get_ds_from_dir("/")) != NULL) {
507 (void) strlcpy(container_ds, dirname(root_ds),
508 container_ds_size);
509 } else {
510 be_print_err(gettext("be_make_container_ds: zone root "
511 "dataset is not mounted\n"));
512 return;
518 * Function: be_make_name_from_ds
519 * Description: This function takes a dataset name and strips off the
520 * BE container dataset portion from the beginning. The
521 * returned name is allocated in heap storage, so the caller
522 * is responsible for freeing it.
523 * Parameters:
524 * dataset - dataset to get name from.
525 * rc_loc - dataset underwhich the root container dataset lives.
526 * Returns:
527 * name of dataset relative to BE container dataset.
528 * NULL if dataset is not under a BE root dataset.
529 * Scope:
530 * Semi-primate (library wide use only)
532 char *
533 be_make_name_from_ds(const char *dataset, char *rc_loc)
535 char ds[ZFS_MAX_DATASET_NAME_LEN];
536 char *tok = NULL;
537 char *name = NULL;
538 struct be_defaults be_defaults;
539 int rlen = strlen(rc_loc);
541 be_get_defaults(&be_defaults);
544 * First token is the location of where the root container dataset
545 * lives; it must match rc_loc.
547 if (strncmp(dataset, rc_loc, rlen) == 0 && dataset[rlen] == '/')
548 (void) strlcpy(ds, dataset + rlen + 1, sizeof (ds));
549 else
550 return (NULL);
552 if (be_defaults.be_deflt_rpool_container) {
553 if ((name = strdup(ds)) == NULL) {
554 be_print_err(gettext("be_make_name_from_ds: "
555 "memory allocation failed\n"));
556 return (NULL);
558 } else {
559 /* Second token must be BE container dataset name */
560 if ((tok = strtok(ds, "/")) == NULL ||
561 strcmp(tok, BE_CONTAINER_DS_NAME) != 0)
562 return (NULL);
564 /* Return the remaining token if one exists */
565 if ((tok = strtok(NULL, "")) == NULL)
566 return (NULL);
568 if ((name = strdup(tok)) == NULL) {
569 be_print_err(gettext("be_make_name_from_ds: "
570 "memory allocation failed\n"));
571 return (NULL);
575 return (name);
579 * Function: be_maxsize_avail
580 * Description: Returns the available size for the zfs handle passed in.
581 * Parameters:
582 * zhp - A pointer to the open zfs handle.
583 * ret - The available size will be returned in this.
584 * Returns:
585 * The error returned by the zfs get property function.
586 * Scope:
587 * Semi-private (library wide use only)
590 be_maxsize_avail(zfs_handle_t *zhp, uint64_t *ret)
592 return ((*ret = zfs_prop_get_int(zhp, ZFS_PROP_AVAILABLE)));
596 * Function: be_append_menu
597 * Description: Appends an entry for a BE into the menu.lst.
598 * Parameters:
599 * be_name - pointer to name of BE to add boot menu entry for.
600 * be_root_pool - pointer to name of pool BE lives in.
601 * boot_pool - Used if the pool containing the grub menu is
602 * different than the one contaiing the BE. This
603 * will normally be NULL.
604 * be_orig_root_ds - The root dataset for the BE. This is
605 * used to check to see if an entry already exists
606 * for this BE.
607 * description - pointer to description of BE to be added in
608 * the title line for this BEs entry.
609 * Returns:
610 * BE_SUCCESS - Success
611 * be_errno_t - Failure
612 * Scope:
613 * Semi-private (library wide use only)
616 be_append_menu(char *be_name, char *be_root_pool, char *boot_pool,
617 char *be_orig_root_ds, char *description)
619 zfs_handle_t *zhp = NULL;
620 char menu_file[MAXPATHLEN];
621 char be_root_ds[MAXPATHLEN];
622 char line[BUFSIZ];
623 char temp_line[BUFSIZ];
624 char title[MAXPATHLEN];
625 char *entries[BUFSIZ];
626 char *tmp_entries[BUFSIZ];
627 char *pool_mntpnt = NULL;
628 char *ptmp_mntpnt = NULL;
629 char *orig_mntpnt = NULL;
630 boolean_t found_be = B_FALSE;
631 boolean_t found_orig_be = B_FALSE;
632 boolean_t found_title = B_FALSE;
633 boolean_t pool_mounted = B_FALSE;
634 boolean_t collect_lines = B_FALSE;
635 FILE *menu_fp = NULL;
636 int err = 0, ret = BE_SUCCESS;
637 int i, num_tmp_lines = 0, num_lines = 0;
639 if (be_name == NULL || be_root_pool == NULL)
640 return (BE_ERR_INVAL);
642 if (boot_pool == NULL)
643 boot_pool = be_root_pool;
645 if ((zhp = zfs_open(g_zfs, be_root_pool, ZFS_TYPE_DATASET)) == NULL) {
646 be_print_err(gettext("be_append_menu: failed to open "
647 "pool dataset for %s: %s\n"), be_root_pool,
648 libzfs_error_description(g_zfs));
649 return (zfs_err_to_be_err(g_zfs));
653 * Check to see if the pool's dataset is mounted. If it isn't we'll
654 * attempt to mount it.
656 if ((ret = be_mount_pool(zhp, &ptmp_mntpnt, &orig_mntpnt,
657 &pool_mounted)) != BE_SUCCESS) {
658 be_print_err(gettext("be_append_menu: pool dataset "
659 "(%s) could not be mounted\n"), be_root_pool);
660 ZFS_CLOSE(zhp);
661 return (ret);
665 * Get the mountpoint for the root pool dataset.
667 if (!zfs_is_mounted(zhp, &pool_mntpnt)) {
668 be_print_err(gettext("be_append_menu: pool "
669 "dataset (%s) is not mounted. Can't set "
670 "the default BE in the grub menu.\n"), be_root_pool);
671 ret = BE_ERR_NO_MENU;
672 goto cleanup;
676 * Check to see if this system supports grub
678 if (be_has_grub()) {
679 (void) snprintf(menu_file, sizeof (menu_file),
680 "%s%s", pool_mntpnt, BE_GRUB_MENU);
681 } else {
682 (void) snprintf(menu_file, sizeof (menu_file),
683 "%s%s", pool_mntpnt, BE_SPARC_MENU);
686 be_make_root_ds(be_root_pool, be_name, be_root_ds, sizeof (be_root_ds));
689 * Iterate through menu first to make sure the BE doesn't already
690 * have an entry in the menu.
692 * Additionally while iterating through the menu, if we have an
693 * original root dataset for a BE we're cloning from, we need to keep
694 * track of that BE's menu entry. We will then use the lines from
695 * that entry to create the entry for the new BE.
697 if ((ret = be_open_menu(be_root_pool, menu_file,
698 &menu_fp, "r", B_TRUE)) != BE_SUCCESS) {
699 goto cleanup;
700 } else if (menu_fp == NULL) {
701 ret = BE_ERR_NO_MENU;
702 goto cleanup;
705 free(pool_mntpnt);
706 pool_mntpnt = NULL;
708 while (fgets(line, BUFSIZ, menu_fp)) {
709 char *tok = NULL;
711 (void) strlcpy(temp_line, line, BUFSIZ);
712 tok = strtok(line, BE_WHITE_SPACE);
714 if (tok == NULL || tok[0] == '#') {
715 continue;
716 } else if (strcmp(tok, "title") == 0) {
717 collect_lines = B_FALSE;
718 if ((tok = strtok(NULL, "\n")) == NULL)
719 (void) strlcpy(title, "", sizeof (title));
720 else
721 (void) strlcpy(title, tok, sizeof (title));
722 found_title = B_TRUE;
724 if (num_tmp_lines != 0) {
725 for (i = 0; i < num_tmp_lines; i++) {
726 free(tmp_entries[i]);
727 tmp_entries[i] = NULL;
729 num_tmp_lines = 0;
731 } else if (strcmp(tok, "bootfs") == 0) {
732 char *bootfs = strtok(NULL, BE_WHITE_SPACE);
733 found_title = B_FALSE;
734 if (bootfs == NULL)
735 continue;
737 if (strcmp(bootfs, be_root_ds) == 0) {
738 found_be = B_TRUE;
739 break;
742 if (be_orig_root_ds != NULL &&
743 strcmp(bootfs, be_orig_root_ds) == 0 &&
744 !found_orig_be) {
745 char str[BUFSIZ];
746 found_orig_be = B_TRUE;
747 num_lines = 0;
749 * Store the new title line
751 (void) snprintf(str, BUFSIZ, "title %s\n",
752 description ? description : be_name);
753 entries[num_lines] = strdup(str);
754 num_lines++;
756 * If there are any lines between the title
757 * and the bootfs line store these. Also
758 * free the temporary lines.
760 for (i = 0; i < num_tmp_lines; i++) {
761 entries[num_lines] = tmp_entries[i];
762 tmp_entries[i] = NULL;
763 num_lines++;
765 num_tmp_lines = 0;
767 * Store the new bootfs line.
769 (void) snprintf(str, BUFSIZ, "bootfs %s\n",
770 be_root_ds);
771 entries[num_lines] = strdup(str);
772 num_lines++;
773 collect_lines = B_TRUE;
775 } else if (found_orig_be && collect_lines) {
777 * get the rest of the lines for the original BE and
778 * store them.
780 if (strstr(line, BE_GRUB_COMMENT) != NULL ||
781 strstr(line, "BOOTADM") != NULL)
782 continue;
783 if (strcmp(tok, "splashimage") == 0) {
784 entries[num_lines] =
785 strdup("splashimage "
786 "/boot/splashimage.xpm\n");
787 } else {
788 entries[num_lines] = strdup(temp_line);
790 num_lines++;
791 } else if (found_title && !found_orig_be) {
792 tmp_entries[num_tmp_lines] = strdup(temp_line);
793 num_tmp_lines++;
797 (void) fclose(menu_fp);
799 if (found_be) {
801 * If an entry for this BE was already in the menu, then if
802 * that entry's title matches what we would have put in
803 * return success. Otherwise return failure.
805 char *new_title = description ? description : be_name;
807 if (strcmp(title, new_title) == 0) {
808 ret = BE_SUCCESS;
809 goto cleanup;
810 } else {
811 if (be_remove_menu(be_name, be_root_pool,
812 boot_pool) != BE_SUCCESS) {
813 be_print_err(gettext("be_append_menu: "
814 "Failed to remove existing unusable "
815 "entry '%s' in boot menu.\n"), be_name);
816 ret = BE_ERR_BE_EXISTS;
817 goto cleanup;
822 /* Append BE entry to the end of the file */
823 menu_fp = fopen(menu_file, "a+");
824 err = errno;
825 if (menu_fp == NULL) {
826 be_print_err(gettext("be_append_menu: failed "
827 "to open menu.lst file %s\n"), menu_file);
828 ret = errno_to_be_err(err);
829 goto cleanup;
832 if (found_orig_be) {
834 * write out all the stored lines
836 for (i = 0; i < num_lines; i++) {
837 (void) fprintf(menu_fp, "%s", entries[i]);
838 free(entries[i]);
840 num_lines = 0;
843 * Check to see if this system supports grub
845 if (be_has_grub())
846 (void) fprintf(menu_fp, "%s\n", BE_GRUB_COMMENT);
847 ret = BE_SUCCESS;
848 } else {
849 (void) fprintf(menu_fp, "title %s\n",
850 description ? description : be_name);
851 (void) fprintf(menu_fp, "bootfs %s\n", be_root_ds);
854 * Check to see if this system supports grub
856 if (be_has_grub()) {
857 (void) fprintf(menu_fp, "kernel$ "
858 "/platform/i86pc/kernel/$ISADIR/unix -B "
859 "$ZFS-BOOTFS\n");
860 (void) fprintf(menu_fp, "module$ "
861 "/platform/i86pc/$ISADIR/boot_archive\n");
862 (void) fprintf(menu_fp, "%s\n", BE_GRUB_COMMENT);
864 ret = BE_SUCCESS;
866 (void) fclose(menu_fp);
867 cleanup:
868 if (pool_mounted) {
869 int err = BE_SUCCESS;
870 err = be_unmount_pool(zhp, ptmp_mntpnt, orig_mntpnt);
871 if (ret == BE_SUCCESS)
872 ret = err;
873 free(orig_mntpnt);
874 free(ptmp_mntpnt);
876 ZFS_CLOSE(zhp);
877 if (num_tmp_lines > 0) {
878 for (i = 0; i < num_tmp_lines; i++) {
879 free(tmp_entries[i]);
880 tmp_entries[i] = NULL;
883 if (num_lines > 0) {
884 for (i = 0; i < num_lines; i++) {
885 free(entries[i]);
886 entries[i] = NULL;
889 return (ret);
893 * Function: be_remove_menu
894 * Description: Removes a BE's entry from a menu.lst file.
895 * Parameters:
896 * be_name - the name of BE whose entry is to be removed from
897 * the menu.lst file.
898 * be_root_pool - the pool that be_name lives in.
899 * boot_pool - the pool where the BE is, if different than
900 * the pool containing the boot menu. If this is
901 * NULL it will be set to be_root_pool.
902 * Returns:
903 * BE_SUCCESS - Success
904 * be_errno_t - Failure
905 * Scope:
906 * Semi-private (library wide use only)
909 be_remove_menu(char *be_name, char *be_root_pool, char *boot_pool)
911 zfs_handle_t *zhp = NULL;
912 char be_root_ds[MAXPATHLEN];
913 char **buffer = NULL;
914 char menu_buf[BUFSIZ];
915 char menu[MAXPATHLEN];
916 char *pool_mntpnt = NULL;
917 char *ptmp_mntpnt = NULL;
918 char *orig_mntpnt = NULL;
919 char *tmp_menu = NULL;
920 FILE *menu_fp = NULL;
921 FILE *tmp_menu_fp = NULL;
922 struct stat sb;
923 int ret = BE_SUCCESS;
924 int i;
925 int fd;
926 int err = 0;
927 int nlines = 0;
928 int default_entry = 0;
929 int entry_cnt = 0;
930 int entry_del = 0;
931 int num_entry_del = 0;
932 int tmp_menu_len = 0;
933 boolean_t write = B_TRUE;
934 boolean_t do_buffer = B_FALSE;
935 boolean_t pool_mounted = B_FALSE;
937 if (boot_pool == NULL)
938 boot_pool = be_root_pool;
940 /* Get name of BE's root dataset */
941 be_make_root_ds(be_root_pool, be_name, be_root_ds, sizeof (be_root_ds));
943 /* Get handle to pool dataset */
944 if ((zhp = zfs_open(g_zfs, be_root_pool, ZFS_TYPE_DATASET)) == NULL) {
945 be_print_err(gettext("be_remove_menu: "
946 "failed to open pool dataset for %s: %s"),
947 be_root_pool, libzfs_error_description(g_zfs));
948 return (zfs_err_to_be_err(g_zfs));
952 * Check to see if the pool's dataset is mounted. If it isn't we'll
953 * attempt to mount it.
955 if ((ret = be_mount_pool(zhp, &ptmp_mntpnt, &orig_mntpnt,
956 &pool_mounted)) != BE_SUCCESS) {
957 be_print_err(gettext("be_remove_menu: pool dataset "
958 "(%s) could not be mounted\n"), be_root_pool);
959 ZFS_CLOSE(zhp);
960 return (ret);
964 * Get the mountpoint for the root pool dataset.
966 if (!zfs_is_mounted(zhp, &pool_mntpnt)) {
967 be_print_err(gettext("be_remove_menu: pool "
968 "dataset (%s) is not mounted. Can't set "
969 "the default BE in the grub menu.\n"), be_root_pool);
970 ret = BE_ERR_NO_MENU;
971 goto cleanup;
974 /* Get path to boot menu */
975 (void) strlcpy(menu, pool_mntpnt, sizeof (menu));
978 * Check to see if this system supports grub
980 if (be_has_grub())
981 (void) strlcat(menu, BE_GRUB_MENU, sizeof (menu));
982 else
983 (void) strlcat(menu, BE_SPARC_MENU, sizeof (menu));
985 /* Get handle to boot menu file */
986 if ((ret = be_open_menu(be_root_pool, menu, &menu_fp, "r",
987 B_TRUE)) != BE_SUCCESS) {
988 goto cleanup;
989 } else if (menu_fp == NULL) {
990 ret = BE_ERR_NO_MENU;
991 goto cleanup;
994 free(pool_mntpnt);
995 pool_mntpnt = NULL;
997 /* Grab the stats of the original menu file */
998 if (stat(menu, &sb) != 0) {
999 err = errno;
1000 be_print_err(gettext("be_remove_menu: "
1001 "failed to stat file %s: %s\n"), menu, strerror(err));
1002 ret = errno_to_be_err(err);
1003 goto cleanup;
1006 /* Create a tmp file for the modified menu.lst */
1007 tmp_menu_len = strlen(menu) + 7;
1008 if ((tmp_menu = (char *)malloc(tmp_menu_len)) == NULL) {
1009 be_print_err(gettext("be_remove_menu: malloc failed\n"));
1010 ret = BE_ERR_NOMEM;
1011 goto cleanup;
1013 (void) memset(tmp_menu, 0, tmp_menu_len);
1014 (void) strlcpy(tmp_menu, menu, tmp_menu_len);
1015 (void) strlcat(tmp_menu, "XXXXXX", tmp_menu_len);
1016 if ((fd = mkstemp(tmp_menu)) == -1) {
1017 err = errno;
1018 be_print_err(gettext("be_remove_menu: mkstemp failed\n"));
1019 ret = errno_to_be_err(err);
1020 free(tmp_menu);
1021 tmp_menu = NULL;
1022 goto cleanup;
1024 if ((tmp_menu_fp = fdopen(fd, "w")) == NULL) {
1025 err = errno;
1026 be_print_err(gettext("be_remove_menu: "
1027 "could not open tmp file for write: %s\n"), strerror(err));
1028 (void) close(fd);
1029 ret = errno_to_be_err(err);
1030 goto cleanup;
1033 while (fgets(menu_buf, BUFSIZ, menu_fp)) {
1034 char tline [BUFSIZ];
1035 char *tok = NULL;
1037 (void) strlcpy(tline, menu_buf, sizeof (tline));
1039 /* Tokenize line */
1040 tok = strtok(tline, BE_WHITE_SPACE);
1042 if (tok == NULL || tok[0] == '#') {
1043 /* Found empty line or comment line */
1044 if (do_buffer) {
1045 /* Buffer this line */
1046 if ((buffer = (char **)realloc(buffer,
1047 sizeof (char *)*(nlines + 1))) == NULL) {
1048 ret = BE_ERR_NOMEM;
1049 goto cleanup;
1051 if ((buffer[nlines++] = strdup(menu_buf))
1052 == NULL) {
1053 ret = BE_ERR_NOMEM;
1054 goto cleanup;
1057 } else if (write || strncmp(menu_buf, BE_GRUB_COMMENT,
1058 strlen(BE_GRUB_COMMENT)) != 0) {
1059 /* Write this line out */
1060 (void) fputs(menu_buf, tmp_menu_fp);
1062 } else if (strcmp(tok, "default") == 0) {
1064 * Record what 'default' is set to because we might
1065 * need to adjust this upon deleting an entry.
1067 tok = strtok(NULL, BE_WHITE_SPACE);
1069 if (tok != NULL) {
1070 default_entry = atoi(tok);
1073 (void) fputs(menu_buf, tmp_menu_fp);
1074 } else if (strcmp(tok, "title") == 0) {
1076 * If we've reached a 'title' line and do_buffer is
1077 * is true, that means we've just buffered an entire
1078 * entry without finding a 'bootfs' directive. We
1079 * need to write that entry out and keep searching.
1081 if (do_buffer) {
1082 for (i = 0; i < nlines; i++) {
1083 (void) fputs(buffer[i], tmp_menu_fp);
1084 free(buffer[i]);
1086 free(buffer);
1087 buffer = NULL;
1088 nlines = 0;
1092 * Turn writing off and buffering on, and increment
1093 * our entry counter.
1095 write = B_FALSE;
1096 do_buffer = B_TRUE;
1097 entry_cnt++;
1099 /* Buffer this 'title' line */
1100 if ((buffer = (char **)realloc(buffer,
1101 sizeof (char *)*(nlines + 1))) == NULL) {
1102 ret = BE_ERR_NOMEM;
1103 goto cleanup;
1105 if ((buffer[nlines++] = strdup(menu_buf)) == NULL) {
1106 ret = BE_ERR_NOMEM;
1107 goto cleanup;
1110 } else if (strcmp(tok, "bootfs") == 0) {
1111 char *bootfs = NULL;
1114 * Found a 'bootfs' line. See if it matches the
1115 * BE we're looking for.
1117 if ((bootfs = strtok(NULL, BE_WHITE_SPACE)) == NULL ||
1118 strcmp(bootfs, be_root_ds) != 0) {
1120 * Either there's nothing after the 'bootfs'
1121 * or this is not the BE we're looking for,
1122 * write out the line(s) we've buffered since
1123 * finding the title.
1125 for (i = 0; i < nlines; i++) {
1126 (void) fputs(buffer[i], tmp_menu_fp);
1127 free(buffer[i]);
1129 free(buffer);
1130 buffer = NULL;
1131 nlines = 0;
1134 * Turn writing back on, and turn off buffering
1135 * since this isn't the entry we're looking
1136 * for.
1138 write = B_TRUE;
1139 do_buffer = B_FALSE;
1141 /* Write this 'bootfs' line out. */
1142 (void) fputs(menu_buf, tmp_menu_fp);
1143 } else {
1145 * Found the entry we're looking for.
1146 * Record its entry number, increment the
1147 * number of entries we've deleted, and turn
1148 * writing off. Also, throw away the lines
1149 * we've buffered for this entry so far, we
1150 * don't need them.
1152 entry_del = entry_cnt - 1;
1153 num_entry_del++;
1154 write = B_FALSE;
1155 do_buffer = B_FALSE;
1157 for (i = 0; i < nlines; i++) {
1158 free(buffer[i]);
1160 free(buffer);
1161 buffer = NULL;
1162 nlines = 0;
1164 } else {
1165 if (do_buffer) {
1166 /* Buffer this line */
1167 if ((buffer = (char **)realloc(buffer,
1168 sizeof (char *)*(nlines + 1))) == NULL) {
1169 ret = BE_ERR_NOMEM;
1170 goto cleanup;
1172 if ((buffer[nlines++] = strdup(menu_buf))
1173 == NULL) {
1174 ret = BE_ERR_NOMEM;
1175 goto cleanup;
1177 } else if (write) {
1178 /* Write this line out */
1179 (void) fputs(menu_buf, tmp_menu_fp);
1184 (void) fclose(menu_fp);
1185 menu_fp = NULL;
1186 (void) fclose(tmp_menu_fp);
1187 tmp_menu_fp = NULL;
1189 /* Copy the modified menu.lst into place */
1190 if (rename(tmp_menu, menu) != 0) {
1191 err = errno;
1192 be_print_err(gettext("be_remove_menu: "
1193 "failed to rename file %s to %s: %s\n"),
1194 tmp_menu, menu, strerror(err));
1195 ret = errno_to_be_err(err);
1196 goto cleanup;
1198 free(tmp_menu);
1199 tmp_menu = NULL;
1202 * If we've removed an entry, see if we need to
1203 * adjust the default value in the menu.lst. If the
1204 * entry we've deleted comes before the default entry
1205 * we need to adjust the default value accordingly.
1207 * be_has_grub is used here to check to see if this system
1208 * supports grub.
1210 if (be_has_grub() && num_entry_del > 0) {
1211 if (entry_del <= default_entry) {
1212 default_entry = default_entry - num_entry_del;
1213 if (default_entry < 0)
1214 default_entry = 0;
1217 * Adjust the default value by rewriting the
1218 * menu.lst file. This may be overkill, but to
1219 * preserve the location of the 'default' entry
1220 * in the file, we need to do this.
1223 /* Get handle to boot menu file */
1224 if ((menu_fp = fopen(menu, "r")) == NULL) {
1225 err = errno;
1226 be_print_err(gettext("be_remove_menu: "
1227 "failed to open menu.lst (%s): %s\n"),
1228 menu, strerror(err));
1229 ret = errno_to_be_err(err);
1230 goto cleanup;
1233 /* Create a tmp file for the modified menu.lst */
1234 tmp_menu_len = strlen(menu) + 7;
1235 if ((tmp_menu = (char *)malloc(tmp_menu_len))
1236 == NULL) {
1237 be_print_err(gettext("be_remove_menu: "
1238 "malloc failed\n"));
1239 ret = BE_ERR_NOMEM;
1240 goto cleanup;
1242 (void) memset(tmp_menu, 0, tmp_menu_len);
1243 (void) strlcpy(tmp_menu, menu, tmp_menu_len);
1244 (void) strlcat(tmp_menu, "XXXXXX", tmp_menu_len);
1245 if ((fd = mkstemp(tmp_menu)) == -1) {
1246 err = errno;
1247 be_print_err(gettext("be_remove_menu: "
1248 "mkstemp failed: %s\n"), strerror(err));
1249 ret = errno_to_be_err(err);
1250 free(tmp_menu);
1251 tmp_menu = NULL;
1252 goto cleanup;
1254 if ((tmp_menu_fp = fdopen(fd, "w")) == NULL) {
1255 err = errno;
1256 be_print_err(gettext("be_remove_menu: "
1257 "could not open tmp file for write: %s\n"),
1258 strerror(err));
1259 (void) close(fd);
1260 ret = errno_to_be_err(err);
1261 goto cleanup;
1264 while (fgets(menu_buf, BUFSIZ, menu_fp)) {
1265 char tline [BUFSIZ];
1266 char *tok = NULL;
1268 (void) strlcpy(tline, menu_buf, sizeof (tline));
1270 /* Tokenize line */
1271 tok = strtok(tline, BE_WHITE_SPACE);
1273 if (tok == NULL) {
1274 /* Found empty line, write it out */
1275 (void) fputs(menu_buf, tmp_menu_fp);
1276 } else if (strcmp(tok, "default") == 0) {
1277 /* Found the default line, adjust it */
1278 (void) snprintf(tline, sizeof (tline),
1279 "default %d\n", default_entry);
1281 (void) fputs(tline, tmp_menu_fp);
1282 } else {
1283 /* Pass through all other lines */
1284 (void) fputs(menu_buf, tmp_menu_fp);
1288 (void) fclose(menu_fp);
1289 menu_fp = NULL;
1290 (void) fclose(tmp_menu_fp);
1291 tmp_menu_fp = NULL;
1293 /* Copy the modified menu.lst into place */
1294 if (rename(tmp_menu, menu) != 0) {
1295 err = errno;
1296 be_print_err(gettext("be_remove_menu: "
1297 "failed to rename file %s to %s: %s\n"),
1298 tmp_menu, menu, strerror(err));
1299 ret = errno_to_be_err(err);
1300 goto cleanup;
1303 free(tmp_menu);
1304 tmp_menu = NULL;
1308 /* Set the perms and ownership of the updated file */
1309 if (chmod(menu, sb.st_mode) != 0) {
1310 err = errno;
1311 be_print_err(gettext("be_remove_menu: "
1312 "failed to chmod %s: %s\n"), menu, strerror(err));
1313 ret = errno_to_be_err(err);
1314 goto cleanup;
1316 if (chown(menu, sb.st_uid, sb.st_gid) != 0) {
1317 err = errno;
1318 be_print_err(gettext("be_remove_menu: "
1319 "failed to chown %s: %s\n"), menu, strerror(err));
1320 ret = errno_to_be_err(err);
1321 goto cleanup;
1324 cleanup:
1325 if (pool_mounted) {
1326 int err = BE_SUCCESS;
1327 err = be_unmount_pool(zhp, ptmp_mntpnt, orig_mntpnt);
1328 if (ret == BE_SUCCESS)
1329 ret = err;
1330 free(orig_mntpnt);
1331 free(ptmp_mntpnt);
1333 ZFS_CLOSE(zhp);
1335 free(buffer);
1336 if (menu_fp != NULL)
1337 (void) fclose(menu_fp);
1338 if (tmp_menu_fp != NULL)
1339 (void) fclose(tmp_menu_fp);
1340 if (tmp_menu != NULL) {
1341 (void) unlink(tmp_menu);
1342 free(tmp_menu);
1345 return (ret);
1349 * Function: be_default_grub_bootfs
1350 * Description: This function returns the dataset in the default entry of
1351 * the grub menu. If no default entry is found with a valid bootfs
1352 * entry NULL is returned.
1353 * Parameters:
1354 * be_root_pool - This is the name of the root pool where the
1355 * grub menu can be found.
1356 * def_bootfs - This is used to pass back the bootfs string. On
1357 * error NULL is returned here.
1358 * Returns:
1359 * Success - BE_SUCCESS is returned.
1360 * Failure - a be_errno_t is returned.
1361 * Scope:
1362 * Semi-private (library wide use only)
1365 be_default_grub_bootfs(const char *be_root_pool, char **def_bootfs)
1367 zfs_handle_t *zhp = NULL;
1368 char grub_file[MAXPATHLEN];
1369 FILE *menu_fp;
1370 char line[BUFSIZ];
1371 char *pool_mntpnt = NULL;
1372 char *ptmp_mntpnt = NULL;
1373 char *orig_mntpnt = NULL;
1374 int default_entry = 0, entries = 0;
1375 int found_default = 0;
1376 int ret = BE_SUCCESS;
1377 boolean_t pool_mounted = B_FALSE;
1379 errno = 0;
1382 * Check to see if this system supports grub
1384 if (!be_has_grub()) {
1385 be_print_err(gettext("be_default_grub_bootfs: operation "
1386 "not supported on this architecture\n"));
1387 return (BE_ERR_NOTSUP);
1390 *def_bootfs = NULL;
1392 /* Get handle to pool dataset */
1393 if ((zhp = zfs_open(g_zfs, be_root_pool, ZFS_TYPE_DATASET)) == NULL) {
1394 be_print_err(gettext("be_default_grub_bootfs: "
1395 "failed to open pool dataset for %s: %s"),
1396 be_root_pool, libzfs_error_description(g_zfs));
1397 return (zfs_err_to_be_err(g_zfs));
1401 * Check to see if the pool's dataset is mounted. If it isn't we'll
1402 * attempt to mount it.
1404 if ((ret = be_mount_pool(zhp, &ptmp_mntpnt, &orig_mntpnt,
1405 &pool_mounted)) != BE_SUCCESS) {
1406 be_print_err(gettext("be_default_grub_bootfs: pool dataset "
1407 "(%s) could not be mounted\n"), be_root_pool);
1408 ZFS_CLOSE(zhp);
1409 return (ret);
1413 * Get the mountpoint for the root pool dataset.
1415 if (!zfs_is_mounted(zhp, &pool_mntpnt)) {
1416 be_print_err(gettext("be_default_grub_bootfs: failed "
1417 "to get mount point for the root pool. Can't set "
1418 "the default BE in the grub menu.\n"));
1419 ret = BE_ERR_NO_MENU;
1420 goto cleanup;
1423 (void) snprintf(grub_file, MAXPATHLEN, "%s%s",
1424 pool_mntpnt, BE_GRUB_MENU);
1426 if ((ret = be_open_menu((char *)be_root_pool, grub_file,
1427 &menu_fp, "r", B_FALSE)) != BE_SUCCESS) {
1428 goto cleanup;
1429 } else if (menu_fp == NULL) {
1430 ret = BE_ERR_NO_MENU;
1431 goto cleanup;
1434 free(pool_mntpnt);
1435 pool_mntpnt = NULL;
1437 while (fgets(line, BUFSIZ, menu_fp)) {
1438 char *tok = strtok(line, BE_WHITE_SPACE);
1440 if (tok != NULL && tok[0] != '#') {
1441 if (!found_default) {
1442 if (strcmp(tok, "default") == 0) {
1443 tok = strtok(NULL, BE_WHITE_SPACE);
1444 if (tok != NULL) {
1445 default_entry = atoi(tok);
1446 rewind(menu_fp);
1447 found_default = 1;
1450 continue;
1452 if (strcmp(tok, "title") == 0) {
1453 entries++;
1454 } else if (default_entry == entries - 1) {
1455 if (strcmp(tok, "bootfs") == 0) {
1456 tok = strtok(NULL, BE_WHITE_SPACE);
1457 (void) fclose(menu_fp);
1459 if (tok == NULL) {
1460 ret = BE_SUCCESS;
1461 goto cleanup;
1464 if ((*def_bootfs = strdup(tok)) !=
1465 NULL) {
1466 ret = BE_SUCCESS;
1467 goto cleanup;
1469 be_print_err(gettext(
1470 "be_default_grub_bootfs: "
1471 "memory allocation failed\n"));
1472 ret = BE_ERR_NOMEM;
1473 goto cleanup;
1475 } else if (default_entry < entries - 1) {
1477 * no bootfs entry for the default entry.
1479 break;
1483 (void) fclose(menu_fp);
1485 cleanup:
1486 if (pool_mounted) {
1487 int err = BE_SUCCESS;
1488 err = be_unmount_pool(zhp, ptmp_mntpnt, orig_mntpnt);
1489 if (ret == BE_SUCCESS)
1490 ret = err;
1491 free(orig_mntpnt);
1492 free(ptmp_mntpnt);
1494 ZFS_CLOSE(zhp);
1495 return (ret);
1499 * Function: be_change_grub_default
1500 * Description: This function takes two parameters. These are the name of
1501 * the BE we want to have as the default booted in the grub
1502 * menu and the root pool where the path to the grub menu exists.
1503 * The code takes this and finds the BE's entry in the grub menu
1504 * and changes the default entry to point to that entry in the
1505 * list.
1506 * Parameters:
1507 * be_name - This is the name of the BE wanted as the default
1508 * for the next boot.
1509 * be_root_pool - This is the name of the root pool where the
1510 * grub menu can be found.
1511 * Returns:
1512 * BE_SUCCESS - Success
1513 * be_errno_t - Failure
1514 * Scope:
1515 * Semi-private (library wide use only)
1518 be_change_grub_default(char *be_name, char *be_root_pool)
1520 zfs_handle_t *zhp = NULL;
1521 char grub_file[MAXPATHLEN];
1522 char *temp_grub = NULL;
1523 char *pool_mntpnt = NULL;
1524 char *ptmp_mntpnt = NULL;
1525 char *orig_mntpnt = NULL;
1526 char line[BUFSIZ];
1527 char temp_line[BUFSIZ];
1528 char be_root_ds[MAXPATHLEN];
1529 FILE *grub_fp = NULL;
1530 FILE *temp_fp = NULL;
1531 struct stat sb;
1532 int temp_grub_len = 0;
1533 int fd, entries = 0;
1534 int err = 0;
1535 int ret = BE_SUCCESS;
1536 boolean_t found_default = B_FALSE;
1537 boolean_t pool_mounted = B_FALSE;
1539 errno = 0;
1542 * Check to see if this system supports grub
1544 if (!be_has_grub()) {
1545 be_print_err(gettext("be_change_grub_default: operation "
1546 "not supported on this architecture\n"));
1547 return (BE_ERR_NOTSUP);
1550 /* Generate string for BE's root dataset */
1551 be_make_root_ds(be_root_pool, be_name, be_root_ds, sizeof (be_root_ds));
1553 /* Get handle to pool dataset */
1554 if ((zhp = zfs_open(g_zfs, be_root_pool, ZFS_TYPE_DATASET)) == NULL) {
1555 be_print_err(gettext("be_change_grub_default: "
1556 "failed to open pool dataset for %s: %s"),
1557 be_root_pool, libzfs_error_description(g_zfs));
1558 return (zfs_err_to_be_err(g_zfs));
1562 * Check to see if the pool's dataset is mounted. If it isn't we'll
1563 * attempt to mount it.
1565 if ((ret = be_mount_pool(zhp, &ptmp_mntpnt, &orig_mntpnt,
1566 &pool_mounted)) != BE_SUCCESS) {
1567 be_print_err(gettext("be_change_grub_default: pool dataset "
1568 "(%s) could not be mounted\n"), be_root_pool);
1569 ZFS_CLOSE(zhp);
1570 return (ret);
1574 * Get the mountpoint for the root pool dataset.
1576 if (!zfs_is_mounted(zhp, &pool_mntpnt)) {
1577 be_print_err(gettext("be_change_grub_default: pool "
1578 "dataset (%s) is not mounted. Can't set "
1579 "the default BE in the grub menu.\n"), be_root_pool);
1580 ret = BE_ERR_NO_MENU;
1581 goto cleanup;
1584 (void) snprintf(grub_file, MAXPATHLEN, "%s%s",
1585 pool_mntpnt, BE_GRUB_MENU);
1587 if ((ret = be_open_menu(be_root_pool, grub_file,
1588 &grub_fp, "r+", B_TRUE)) != BE_SUCCESS) {
1589 goto cleanup;
1590 } else if (grub_fp == NULL) {
1591 ret = BE_ERR_NO_MENU;
1592 goto cleanup;
1595 free(pool_mntpnt);
1596 pool_mntpnt = NULL;
1598 /* Grab the stats of the original menu file */
1599 if (stat(grub_file, &sb) != 0) {
1600 err = errno;
1601 be_print_err(gettext("be_change_grub_default: "
1602 "failed to stat file %s: %s\n"), grub_file, strerror(err));
1603 ret = errno_to_be_err(err);
1604 goto cleanup;
1607 /* Create a tmp file for the modified menu.lst */
1608 temp_grub_len = strlen(grub_file) + 7;
1609 if ((temp_grub = (char *)malloc(temp_grub_len)) == NULL) {
1610 be_print_err(gettext("be_change_grub_default: "
1611 "malloc failed\n"));
1612 ret = BE_ERR_NOMEM;
1613 goto cleanup;
1615 (void) memset(temp_grub, 0, temp_grub_len);
1616 (void) strlcpy(temp_grub, grub_file, temp_grub_len);
1617 (void) strlcat(temp_grub, "XXXXXX", temp_grub_len);
1618 if ((fd = mkstemp(temp_grub)) == -1) {
1619 err = errno;
1620 be_print_err(gettext("be_change_grub_default: "
1621 "mkstemp failed: %s\n"), strerror(err));
1622 ret = errno_to_be_err(err);
1623 free(temp_grub);
1624 temp_grub = NULL;
1625 goto cleanup;
1627 if ((temp_fp = fdopen(fd, "w")) == NULL) {
1628 err = errno;
1629 be_print_err(gettext("be_change_grub_default: "
1630 "failed to open %s file: %s\n"),
1631 temp_grub, strerror(err));
1632 (void) close(fd);
1633 ret = errno_to_be_err(err);
1634 goto cleanup;
1637 while (fgets(line, BUFSIZ, grub_fp)) {
1638 char *tok = strtok(line, BE_WHITE_SPACE);
1640 if (tok == NULL || tok[0] == '#') {
1641 continue;
1642 } else if (strcmp(tok, "title") == 0) {
1643 entries++;
1644 continue;
1645 } else if (strcmp(tok, "bootfs") == 0) {
1646 char *bootfs = strtok(NULL, BE_WHITE_SPACE);
1647 if (bootfs == NULL)
1648 continue;
1650 if (strcmp(bootfs, be_root_ds) == 0) {
1651 found_default = B_TRUE;
1652 break;
1657 if (!found_default) {
1658 be_print_err(gettext("be_change_grub_default: failed "
1659 "to find entry for %s in the grub menu\n"),
1660 be_name);
1661 ret = BE_ERR_BE_NOENT;
1662 goto cleanup;
1665 rewind(grub_fp);
1667 while (fgets(line, BUFSIZ, grub_fp)) {
1668 char *tok = NULL;
1670 (void) strncpy(temp_line, line, BUFSIZ);
1672 if ((tok = strtok(temp_line, BE_WHITE_SPACE)) != NULL &&
1673 strcmp(tok, "default") == 0) {
1674 (void) snprintf(temp_line, BUFSIZ, "default %d\n",
1675 entries - 1 >= 0 ? entries - 1 : 0);
1676 (void) fputs(temp_line, temp_fp);
1677 } else {
1678 (void) fputs(line, temp_fp);
1682 (void) fclose(grub_fp);
1683 grub_fp = NULL;
1684 (void) fclose(temp_fp);
1685 temp_fp = NULL;
1687 if (rename(temp_grub, grub_file) != 0) {
1688 err = errno;
1689 be_print_err(gettext("be_change_grub_default: "
1690 "failed to rename file %s to %s: %s\n"),
1691 temp_grub, grub_file, strerror(err));
1692 ret = errno_to_be_err(err);
1693 goto cleanup;
1695 free(temp_grub);
1696 temp_grub = NULL;
1698 /* Set the perms and ownership of the updated file */
1699 if (chmod(grub_file, sb.st_mode) != 0) {
1700 err = errno;
1701 be_print_err(gettext("be_change_grub_default: "
1702 "failed to chmod %s: %s\n"), grub_file, strerror(err));
1703 ret = errno_to_be_err(err);
1704 goto cleanup;
1706 if (chown(grub_file, sb.st_uid, sb.st_gid) != 0) {
1707 err = errno;
1708 be_print_err(gettext("be_change_grub_default: "
1709 "failed to chown %s: %s\n"), grub_file, strerror(err));
1710 ret = errno_to_be_err(err);
1713 cleanup:
1714 if (pool_mounted) {
1715 int err = BE_SUCCESS;
1716 err = be_unmount_pool(zhp, ptmp_mntpnt, orig_mntpnt);
1717 if (ret == BE_SUCCESS)
1718 ret = err;
1719 free(orig_mntpnt);
1720 free(ptmp_mntpnt);
1722 ZFS_CLOSE(zhp);
1723 if (grub_fp != NULL)
1724 (void) fclose(grub_fp);
1725 if (temp_fp != NULL)
1726 (void) fclose(temp_fp);
1727 if (temp_grub != NULL) {
1728 (void) unlink(temp_grub);
1729 free(temp_grub);
1732 return (ret);
1736 * Function: be_update_menu
1737 * Description: This function is used by be_rename to change the BE name in
1738 * an existing entry in the grub menu to the new name of the BE.
1739 * Parameters:
1740 * be_orig_name - the original name of the BE
1741 * be_new_name - the new name the BE is being renameed to.
1742 * be_root_pool - The pool which contains the grub menu
1743 * boot_pool - the pool where the BE is, if different than
1744 * the pool containing the boot menu. If this is
1745 * NULL it will be set to be_root_pool.
1746 * Returns:
1747 * BE_SUCCESS - Success
1748 * be_errno_t - Failure
1749 * Scope:
1750 * Semi-private (library wide use only)
1753 be_update_menu(char *be_orig_name, char *be_new_name, char *be_root_pool,
1754 char *boot_pool)
1756 zfs_handle_t *zhp = NULL;
1757 char menu_file[MAXPATHLEN];
1758 char be_root_ds[MAXPATHLEN];
1759 char be_new_root_ds[MAXPATHLEN];
1760 char line[BUFSIZ];
1761 char *pool_mntpnt = NULL;
1762 char *ptmp_mntpnt = NULL;
1763 char *orig_mntpnt = NULL;
1764 char *temp_menu = NULL;
1765 FILE *menu_fp = NULL;
1766 FILE *new_fp = NULL;
1767 struct stat sb;
1768 int temp_menu_len = 0;
1769 int tmp_fd;
1770 int ret = BE_SUCCESS;
1771 int err = 0;
1772 boolean_t pool_mounted = B_FALSE;
1774 errno = 0;
1776 if (boot_pool == NULL)
1777 boot_pool = be_root_pool;
1779 if ((zhp = zfs_open(g_zfs, be_root_pool, ZFS_TYPE_DATASET)) == NULL) {
1780 be_print_err(gettext("be_update_menu: failed to open "
1781 "pool dataset for %s: %s\n"), be_root_pool,
1782 libzfs_error_description(g_zfs));
1783 return (zfs_err_to_be_err(g_zfs));
1787 * Check to see if the pool's dataset is mounted. If it isn't we'll
1788 * attempt to mount it.
1790 if ((ret = be_mount_pool(zhp, &ptmp_mntpnt, &orig_mntpnt,
1791 &pool_mounted)) != BE_SUCCESS) {
1792 be_print_err(gettext("be_update_menu: pool dataset "
1793 "(%s) could not be mounted\n"), be_root_pool);
1794 ZFS_CLOSE(zhp);
1795 return (ret);
1799 * Get the mountpoint for the root pool dataset.
1801 if (!zfs_is_mounted(zhp, &pool_mntpnt)) {
1802 be_print_err(gettext("be_update_menu: failed "
1803 "to get mount point for the root pool. Can't set "
1804 "the default BE in the grub menu.\n"));
1805 ret = BE_ERR_NO_MENU;
1806 goto cleanup;
1810 * Check to see if this system supports grub
1812 if (be_has_grub()) {
1813 (void) snprintf(menu_file, sizeof (menu_file),
1814 "%s%s", pool_mntpnt, BE_GRUB_MENU);
1815 } else {
1816 (void) snprintf(menu_file, sizeof (menu_file),
1817 "%s%s", pool_mntpnt, BE_SPARC_MENU);
1820 be_make_root_ds(be_root_pool, be_orig_name, be_root_ds,
1821 sizeof (be_root_ds));
1822 be_make_root_ds(be_root_pool, be_new_name, be_new_root_ds,
1823 sizeof (be_new_root_ds));
1825 if ((ret = be_open_menu(be_root_pool, menu_file,
1826 &menu_fp, "r", B_TRUE)) != BE_SUCCESS) {
1827 goto cleanup;
1828 } else if (menu_fp == NULL) {
1829 ret = BE_ERR_NO_MENU;
1830 goto cleanup;
1833 free(pool_mntpnt);
1834 pool_mntpnt = NULL;
1836 /* Grab the stat of the original menu file */
1837 if (stat(menu_file, &sb) != 0) {
1838 err = errno;
1839 be_print_err(gettext("be_update_menu: "
1840 "failed to stat file %s: %s\n"), menu_file, strerror(err));
1841 (void) fclose(menu_fp);
1842 ret = errno_to_be_err(err);
1843 goto cleanup;
1846 /* Create tmp file for modified menu.lst */
1847 temp_menu_len = strlen(menu_file) + 7;
1848 if ((temp_menu = (char *)malloc(temp_menu_len))
1849 == NULL) {
1850 be_print_err(gettext("be_update_menu: "
1851 "malloc failed\n"));
1852 (void) fclose(menu_fp);
1853 ret = BE_ERR_NOMEM;
1854 goto cleanup;
1856 (void) memset(temp_menu, 0, temp_menu_len);
1857 (void) strlcpy(temp_menu, menu_file, temp_menu_len);
1858 (void) strlcat(temp_menu, "XXXXXX", temp_menu_len);
1859 if ((tmp_fd = mkstemp(temp_menu)) == -1) {
1860 err = errno;
1861 be_print_err(gettext("be_update_menu: "
1862 "mkstemp failed: %s\n"), strerror(err));
1863 (void) fclose(menu_fp);
1864 free(temp_menu);
1865 ret = errno_to_be_err(err);
1866 goto cleanup;
1868 if ((new_fp = fdopen(tmp_fd, "w")) == NULL) {
1869 err = errno;
1870 be_print_err(gettext("be_update_menu: "
1871 "fdopen failed: %s\n"), strerror(err));
1872 (void) close(tmp_fd);
1873 (void) fclose(menu_fp);
1874 free(temp_menu);
1875 ret = errno_to_be_err(err);
1876 goto cleanup;
1879 while (fgets(line, BUFSIZ, menu_fp)) {
1880 char tline[BUFSIZ];
1881 char new_line[BUFSIZ];
1882 char *c = NULL;
1884 (void) strlcpy(tline, line, sizeof (tline));
1886 /* Tokenize line */
1887 c = strtok(tline, BE_WHITE_SPACE);
1889 if (c == NULL) {
1890 /* Found empty line, write it out. */
1891 (void) fputs(line, new_fp);
1892 } else if (c[0] == '#') {
1893 /* Found a comment line, write it out. */
1894 (void) fputs(line, new_fp);
1895 } else if (strcmp(c, "title") == 0) {
1896 char *name = NULL;
1897 char *desc = NULL;
1900 * Found a 'title' line, parse out BE name or
1901 * the description.
1903 name = strtok(NULL, BE_WHITE_SPACE);
1905 if (name == NULL) {
1907 * Nothing after 'title', just push
1908 * this line through
1910 (void) fputs(line, new_fp);
1911 } else {
1913 * Grab the remainder of the title which
1914 * could be a multi worded description
1916 desc = strtok(NULL, "\n");
1918 if (strcmp(name, be_orig_name) == 0) {
1920 * The first token of the title is
1921 * the old BE name, replace it with
1922 * the new one, and write it out
1923 * along with the remainder of
1924 * description if there is one.
1926 if (desc) {
1927 (void) snprintf(new_line,
1928 sizeof (new_line),
1929 "title %s %s\n",
1930 be_new_name, desc);
1931 } else {
1932 (void) snprintf(new_line,
1933 sizeof (new_line),
1934 "title %s\n", be_new_name);
1937 (void) fputs(new_line, new_fp);
1938 } else {
1939 (void) fputs(line, new_fp);
1942 } else if (strcmp(c, "bootfs") == 0) {
1944 * Found a 'bootfs' line, parse out the BE root
1945 * dataset value.
1947 char *root_ds = strtok(NULL, BE_WHITE_SPACE);
1949 if (root_ds == NULL) {
1951 * Nothing after 'bootfs', just push
1952 * this line through
1954 (void) fputs(line, new_fp);
1955 } else {
1957 * If this bootfs is the one we're renaming,
1958 * write out the new root dataset value
1960 if (strcmp(root_ds, be_root_ds) == 0) {
1961 (void) snprintf(new_line,
1962 sizeof (new_line), "bootfs %s\n",
1963 be_new_root_ds);
1965 (void) fputs(new_line, new_fp);
1966 } else {
1967 (void) fputs(line, new_fp);
1970 } else {
1972 * Found some other line we don't care
1973 * about, write it out.
1975 (void) fputs(line, new_fp);
1979 (void) fclose(menu_fp);
1980 (void) fclose(new_fp);
1981 (void) close(tmp_fd);
1983 if (rename(temp_menu, menu_file) != 0) {
1984 err = errno;
1985 be_print_err(gettext("be_update_menu: "
1986 "failed to rename file %s to %s: %s\n"),
1987 temp_menu, menu_file, strerror(err));
1988 ret = errno_to_be_err(err);
1990 free(temp_menu);
1992 /* Set the perms and ownership of the updated file */
1993 if (chmod(menu_file, sb.st_mode) != 0) {
1994 err = errno;
1995 be_print_err(gettext("be_update_menu: "
1996 "failed to chmod %s: %s\n"), menu_file, strerror(err));
1997 ret = errno_to_be_err(err);
1998 goto cleanup;
2000 if (chown(menu_file, sb.st_uid, sb.st_gid) != 0) {
2001 err = errno;
2002 be_print_err(gettext("be_update_menu: "
2003 "failed to chown %s: %s\n"), menu_file, strerror(err));
2004 ret = errno_to_be_err(err);
2007 cleanup:
2008 if (pool_mounted) {
2009 int err = BE_SUCCESS;
2010 err = be_unmount_pool(zhp, ptmp_mntpnt, orig_mntpnt);
2011 if (ret == BE_SUCCESS)
2012 ret = err;
2013 free(orig_mntpnt);
2014 free(ptmp_mntpnt);
2016 ZFS_CLOSE(zhp);
2017 return (ret);
2021 * Function: be_has_menu_entry
2022 * Description: Checks to see if the BEs root dataset has an entry in the grub
2023 * menu.
2024 * Parameters:
2025 * be_dataset - The root dataset of the BE
2026 * be_root_pool - The pool which contains the boot menu
2027 * entry - A pointer the the entry number of the BE if found.
2028 * Returns:
2029 * B_TRUE - Success
2030 * B_FALSE - Failure
2031 * Scope:
2032 * Semi-private (library wide use only)
2034 boolean_t
2035 be_has_menu_entry(char *be_dataset, char *be_root_pool, int *entry)
2037 zfs_handle_t *zhp = NULL;
2038 char menu_file[MAXPATHLEN];
2039 FILE *menu_fp;
2040 char line[BUFSIZ];
2041 char *last;
2042 char *rpool_mntpnt = NULL;
2043 char *ptmp_mntpnt = NULL;
2044 char *orig_mntpnt = NULL;
2045 int ent_num = 0;
2046 boolean_t ret = 0;
2047 boolean_t pool_mounted = B_FALSE;
2051 * Check to see if this system supports grub
2053 if ((zhp = zfs_open(g_zfs, be_root_pool, ZFS_TYPE_DATASET)) == NULL) {
2054 be_print_err(gettext("be_has_menu_entry: failed to open "
2055 "pool dataset for %s: %s\n"), be_root_pool,
2056 libzfs_error_description(g_zfs));
2057 return (B_FALSE);
2061 * Check to see if the pool's dataset is mounted. If it isn't we'll
2062 * attempt to mount it.
2064 if (be_mount_pool(zhp, &ptmp_mntpnt, &orig_mntpnt,
2065 &pool_mounted) != 0) {
2066 be_print_err(gettext("be_has_menu_entry: pool dataset "
2067 "(%s) could not be mounted\n"), be_root_pool);
2068 ZFS_CLOSE(zhp);
2069 return (B_FALSE);
2073 * Get the mountpoint for the root pool dataset.
2075 if (!zfs_is_mounted(zhp, &rpool_mntpnt)) {
2076 be_print_err(gettext("be_has_menu_entry: pool "
2077 "dataset (%s) is not mounted. Can't set "
2078 "the default BE in the grub menu.\n"), be_root_pool);
2079 ret = B_FALSE;
2080 goto cleanup;
2083 if (be_has_grub()) {
2084 (void) snprintf(menu_file, MAXPATHLEN, "/%s%s",
2085 rpool_mntpnt, BE_GRUB_MENU);
2086 } else {
2087 (void) snprintf(menu_file, MAXPATHLEN, "/%s%s",
2088 rpool_mntpnt, BE_SPARC_MENU);
2091 if (be_open_menu(be_root_pool, menu_file, &menu_fp, "r",
2092 B_FALSE) != 0) {
2093 ret = B_FALSE;
2094 goto cleanup;
2095 } else if (menu_fp == NULL) {
2096 ret = B_FALSE;
2097 goto cleanup;
2100 free(rpool_mntpnt);
2101 rpool_mntpnt = NULL;
2103 while (fgets(line, BUFSIZ, menu_fp)) {
2104 char *tok = strtok_r(line, BE_WHITE_SPACE, &last);
2106 if (tok != NULL && tok[0] != '#') {
2107 if (strcmp(tok, "bootfs") == 0) {
2108 tok = strtok_r(last, BE_WHITE_SPACE, &last);
2109 if (tok != NULL && strcmp(tok,
2110 be_dataset) == 0) {
2111 (void) fclose(menu_fp);
2113 * The entry number needs to be
2114 * decremented here because the title
2115 * will always be the first line for
2116 * an entry. Because of this we'll
2117 * always be off by one entry when we
2118 * check for bootfs.
2120 *entry = ent_num - 1;
2121 ret = B_TRUE;
2122 goto cleanup;
2124 } else if (strcmp(tok, "title") == 0)
2125 ent_num++;
2129 cleanup:
2130 if (pool_mounted) {
2131 (void) be_unmount_pool(zhp, ptmp_mntpnt, orig_mntpnt);
2132 free(orig_mntpnt);
2133 free(ptmp_mntpnt);
2135 ZFS_CLOSE(zhp);
2136 (void) fclose(menu_fp);
2137 return (ret);
2141 * Function: be_update_vfstab
2142 * Description: This function digs into a BE's vfstab and updates all
2143 * entries with file systems listed in be_fs_list_data_t.
2144 * The entry's root container dataset and be_name will be
2145 * updated with the parameters passed in.
2146 * Parameters:
2147 * be_name - name of BE to update
2148 * old_rc_loc - dataset under which the root container dataset
2149 * of the old BE resides in.
2150 * new_rc_loc - dataset under which the root container dataset
2151 * of the new BE resides in.
2152 * fld - be_fs_list_data_t pointer providing the list of
2153 * file systems to look for in vfstab.
2154 * mountpoint - directory of where BE is currently mounted.
2155 * If NULL, then BE is not currently mounted.
2156 * Returns:
2157 * BE_SUCCESS - Success
2158 * be_errno_t - Failure
2159 * Scope:
2160 * Semi-private (library wide use only)
2163 be_update_vfstab(char *be_name, char *old_rc_loc, char *new_rc_loc,
2164 be_fs_list_data_t *fld, char *mountpoint)
2166 char *tmp_mountpoint = NULL;
2167 char alt_vfstab[MAXPATHLEN];
2168 int ret = BE_SUCCESS, err = BE_SUCCESS;
2170 if (fld == NULL || fld->fs_list == NULL || fld->fs_num == 0)
2171 return (BE_SUCCESS);
2173 /* If BE not already mounted, mount the BE */
2174 if (mountpoint == NULL) {
2175 if ((ret = _be_mount(be_name, &tmp_mountpoint,
2176 BE_MOUNT_FLAG_NO_ZONES)) != BE_SUCCESS) {
2177 be_print_err(gettext("be_update_vfstab: "
2178 "failed to mount BE (%s)\n"), be_name);
2179 return (ret);
2181 } else {
2182 tmp_mountpoint = mountpoint;
2185 /* Get string for vfstab in the mounted BE. */
2186 (void) snprintf(alt_vfstab, sizeof (alt_vfstab), "%s/etc/vfstab",
2187 tmp_mountpoint);
2189 /* Update the vfstab */
2190 ret = _update_vfstab(alt_vfstab, be_name, old_rc_loc, new_rc_loc,
2191 fld);
2193 /* Unmount BE if we mounted it */
2194 if (mountpoint == NULL) {
2195 if ((err = _be_unmount(be_name, 0)) == BE_SUCCESS) {
2196 /* Remove temporary mountpoint */
2197 (void) rmdir(tmp_mountpoint);
2198 } else {
2199 be_print_err(gettext("be_update_vfstab: "
2200 "failed to unmount BE %s mounted at %s\n"),
2201 be_name, tmp_mountpoint);
2202 if (ret == BE_SUCCESS)
2203 ret = err;
2206 free(tmp_mountpoint);
2209 return (ret);
2213 * Function: be_update_zone_vfstab
2214 * Description: This function digs into a zone BE's vfstab and updates all
2215 * entries with file systems listed in be_fs_list_data_t.
2216 * The entry's root container dataset and be_name will be
2217 * updated with the parameters passed in.
2218 * Parameters:
2219 * zhp - zfs_handle_t pointer to zone root dataset.
2220 * be_name - name of zone BE to update
2221 * old_rc_loc - dataset under which the root container dataset
2222 * of the old zone BE resides in.
2223 * new_rc_loc - dataset under which the root container dataset
2224 * of the new zone BE resides in.
2225 * fld - be_fs_list_data_t pointer providing the list of
2226 * file systems to look for in vfstab.
2227 * Returns:
2228 * BE_SUCCESS - Success
2229 * be_errno_t - Failure
2230 * Scope:
2231 * Semi-private (library wide use only)
2234 be_update_zone_vfstab(zfs_handle_t *zhp, char *be_name, char *old_rc_loc,
2235 char *new_rc_loc, be_fs_list_data_t *fld)
2237 be_mount_data_t md = { 0 };
2238 be_unmount_data_t ud = { 0 };
2239 char alt_vfstab[MAXPATHLEN];
2240 boolean_t mounted_here = B_FALSE;
2241 int ret = BE_SUCCESS;
2244 * If zone root not already mounted, mount it at a
2245 * temporary location.
2247 if (!zfs_is_mounted(zhp, &md.altroot)) {
2248 /* Generate temporary mountpoint to mount zone root */
2249 if ((ret = be_make_tmp_mountpoint(&md.altroot)) != BE_SUCCESS) {
2250 be_print_err(gettext("be_update_zone_vfstab: "
2251 "failed to make temporary mountpoint to "
2252 "mount zone root\n"));
2253 return (ret);
2256 if (be_mount_zone_root(zhp, &md) != BE_SUCCESS) {
2257 be_print_err(gettext("be_update_zone_vfstab: "
2258 "failed to mount zone root %s\n"),
2259 zfs_get_name(zhp));
2260 free(md.altroot);
2261 return (BE_ERR_MOUNT_ZONEROOT);
2263 mounted_here = B_TRUE;
2266 /* Get string from vfstab in the mounted zone BE */
2267 (void) snprintf(alt_vfstab, sizeof (alt_vfstab), "%s/etc/vfstab",
2268 md.altroot);
2270 /* Update the vfstab */
2271 ret = _update_vfstab(alt_vfstab, be_name, old_rc_loc, new_rc_loc,
2272 fld);
2274 /* Unmount zone root if we mounted it */
2275 if (mounted_here) {
2276 ud.force = B_TRUE;
2278 if (be_unmount_zone_root(zhp, &ud) == BE_SUCCESS) {
2279 /* Remove the temporary mountpoint */
2280 (void) rmdir(md.altroot);
2281 } else {
2282 be_print_err(gettext("be_update_zone_vfstab: "
2283 "failed to unmount zone root %s from %s\n"),
2284 zfs_get_name(zhp), md.altroot);
2285 if (ret == 0)
2286 ret = BE_ERR_UMOUNT_ZONEROOT;
2290 free(md.altroot);
2291 return (ret);
2295 * Function: be_auto_snap_name
2296 * Description: Generate an auto snapshot name constructed based on the
2297 * current date and time. The auto snapshot name is of the form:
2299 * <date>-<time>
2301 * where <date> is in ISO standard format, so the resultant name
2302 * is of the form:
2304 * %Y-%m-%d-%H:%M:%S
2306 * Parameters:
2307 * None
2308 * Returns:
2309 * Success - pointer to auto generated snapshot name. The name
2310 * is allocated in heap storage so the caller is
2311 * responsible for free'ing the name.
2312 * Failure - NULL
2313 * Scope:
2314 * Semi-private (library wide use only)
2316 char *
2317 be_auto_snap_name(void)
2319 time_t utc_tm = NULL;
2320 struct tm *gmt_tm = NULL;
2321 char gmt_time_str[64];
2322 char *auto_snap_name = NULL;
2324 if (time(&utc_tm) == -1) {
2325 be_print_err(gettext("be_auto_snap_name: time() failed\n"));
2326 return (NULL);
2329 if ((gmt_tm = gmtime(&utc_tm)) == NULL) {
2330 be_print_err(gettext("be_auto_snap_name: gmtime() failed\n"));
2331 return (NULL);
2334 (void) strftime(gmt_time_str, sizeof (gmt_time_str), "%F-%T", gmt_tm);
2336 if ((auto_snap_name = strdup(gmt_time_str)) == NULL) {
2337 be_print_err(gettext("be_auto_snap_name: "
2338 "memory allocation failed\n"));
2339 return (NULL);
2342 return (auto_snap_name);
2346 * Function: be_auto_be_name
2347 * Description: Generate an auto BE name constructed based on the BE name
2348 * of the original BE being cloned.
2349 * Parameters:
2350 * obe_name - name of the original BE being cloned.
2351 * Returns:
2352 * Success - pointer to auto generated BE name. The name
2353 * is allocated in heap storage so the caller is
2354 * responsible for free'ing the name.
2355 * Failure - NULL
2356 * Scope:
2357 * Semi-private (library wide use only)
2359 char *
2360 be_auto_be_name(char *obe_name)
2362 return (be_get_auto_name(obe_name, NULL, B_FALSE));
2366 * Function: be_auto_zone_be_name
2367 * Description: Generate an auto BE name for a zone constructed based on
2368 * the BE name of the original zone BE being cloned.
2369 * Parameters:
2370 * container_ds - container dataset for the zone.
2371 * zbe_name - name of the original zone BE being cloned.
2372 * Returns:
2373 * Success - pointer to auto generated BE name. The name
2374 * is allocated in heap storage so the caller is
2375 * responsible for free'ing the name.
2376 * Failure - NULL
2377 * Scope:
2378 * Semi-private (library wide use only)
2380 char *
2381 be_auto_zone_be_name(char *container_ds, char *zbe_name)
2383 return (be_get_auto_name(zbe_name, container_ds, B_TRUE));
2387 * Function: be_valid_be_name
2388 * Description: Validates a BE name.
2389 * Parameters:
2390 * be_name - name of BE to validate
2391 * Returns:
2392 * B_TRUE - be_name is valid
2393 * B_FALSE - be_name is invalid
2394 * Scope:
2395 * Semi-private (library wide use only)
2398 boolean_t
2399 be_valid_be_name(const char *be_name)
2401 const char *c = NULL;
2402 struct be_defaults be_defaults;
2404 if (be_name == NULL)
2405 return (B_FALSE);
2407 be_get_defaults(&be_defaults);
2410 * A BE name must not be a multi-level dataset name. We also check
2411 * that it does not contain the ' ' and '%' characters. The ' ' is
2412 * a valid character for datasets, however we don't allow that in a
2413 * BE name. The '%' is invalid, but zfs_name_valid() allows it for
2414 * internal reasons, so we explicitly check for it here.
2416 c = be_name;
2417 while (*c != '\0' && *c != '/' && *c != ' ' && *c != '%')
2418 c++;
2420 if (*c != '\0')
2421 return (B_FALSE);
2424 * The BE name must comply with a zfs dataset filesystem. We also
2425 * verify its length to be < BE_NAME_MAX_LEN.
2427 if (!zfs_name_valid(be_name, ZFS_TYPE_FILESYSTEM) ||
2428 strlen(be_name) > BE_NAME_MAX_LEN)
2429 return (B_FALSE);
2431 if (be_defaults.be_deflt_bename_starts_with[0] != '\0' &&
2432 strstr(be_name, be_defaults.be_deflt_bename_starts_with) == NULL) {
2433 return (B_FALSE);
2436 return (B_TRUE);
2440 * Function: be_valid_auto_snap_name
2441 * Description: This function checks that a snapshot name is a valid auto
2442 * generated snapshot name. A valid auto generated snapshot
2443 * name is of the form:
2445 * %Y-%m-%d-%H:%M:%S
2447 * An older form of the auto generated snapshot name also
2448 * included the snapshot's BE cleanup policy and a reserved
2449 * field. Those names will also be verified by this function.
2451 * Examples of valid auto snapshot names are:
2453 * 2008-03-31-18:41:30
2454 * 2008-03-31-22:17:24
2455 * <policy>:-:2008:04-05-09:12:55
2456 * <policy>:-:2008:04-06-15:34:12
2458 * Parameters:
2459 * name - name of the snapshot to be validated.
2460 * Returns:
2461 * B_TRUE - the name is a valid auto snapshot name.
2462 * B_FALSE - the name is not a valid auto snapshot name.
2463 * Scope:
2464 * Semi-private (library wide use only)
2466 boolean_t
2467 be_valid_auto_snap_name(char *name)
2469 struct tm gmt_tm;
2471 char *policy = NULL;
2472 char *reserved = NULL;
2473 char *date = NULL;
2474 char *c = NULL;
2476 /* Validate the snapshot name by converting it into utc time */
2477 if (strptime(name, "%Y-%m-%d-%T", &gmt_tm) != NULL &&
2478 (mktime(&gmt_tm) != -1)) {
2479 return (B_TRUE);
2483 * Validate the snapshot name against the older form of an
2484 * auto generated snapshot name.
2486 policy = strdup(name);
2489 * Get the first field from the snapshot name,
2490 * which is the BE policy
2492 c = strchr(policy, ':');
2493 if (c == NULL) {
2494 free(policy);
2495 return (B_FALSE);
2497 c[0] = '\0';
2499 /* Validate the policy name */
2500 if (!valid_be_policy(policy)) {
2501 free(policy);
2502 return (B_FALSE);
2505 /* Get the next field, which is the reserved field. */
2506 if (c[1] == NULL || c[1] == '\0') {
2507 free(policy);
2508 return (B_FALSE);
2510 reserved = c+1;
2511 c = strchr(reserved, ':');
2512 if (c == NULL) {
2513 free(policy);
2514 return (B_FALSE);
2516 c[0] = '\0';
2518 /* Validate the reserved field */
2519 if (strcmp(reserved, "-") != 0) {
2520 free(policy);
2521 return (B_FALSE);
2524 /* The remaining string should be the date field */
2525 if (c[1] == NULL || c[1] == '\0') {
2526 free(policy);
2527 return (B_FALSE);
2529 date = c+1;
2531 /* Validate the date string by converting it into utc time */
2532 if (strptime(date, "%Y-%m-%d-%T", &gmt_tm) == NULL ||
2533 (mktime(&gmt_tm) == -1)) {
2534 be_print_err(gettext("be_valid_auto_snap_name: "
2535 "invalid auto snapshot name\n"));
2536 free(policy);
2537 return (B_FALSE);
2540 free(policy);
2541 return (B_TRUE);
2545 * Function: be_default_policy
2546 * Description: Temporary hardcoded policy support. This function returns
2547 * the default policy type to be used to create a BE or a BE
2548 * snapshot.
2549 * Parameters:
2550 * None
2551 * Returns:
2552 * Name of default BE policy.
2553 * Scope:
2554 * Semi-private (library wide use only)
2556 char *
2557 be_default_policy(void)
2559 return (BE_PLCY_STATIC);
2563 * Function: valid_be_policy
2564 * Description: Temporary hardcoded policy support. This function valids
2565 * whether a policy is a valid known policy or not.
2566 * Paramters:
2567 * policy - name of policy to validate.
2568 * Returns:
2569 * B_TRUE - policy is a valid.
2570 * B_FALSE - policy is invalid.
2571 * Scope:
2572 * Semi-private (library wide use only)
2574 boolean_t
2575 valid_be_policy(char *policy)
2577 if (policy == NULL)
2578 return (B_FALSE);
2580 if (strcmp(policy, BE_PLCY_STATIC) == 0 ||
2581 strcmp(policy, BE_PLCY_VOLATILE) == 0) {
2582 return (B_TRUE);
2585 return (B_FALSE);
2589 * Function: be_print_err
2590 * Description: This function prints out error messages if do_print is
2591 * set to B_TRUE or if the BE_PRINT_ERR environment variable
2592 * is set to true.
2593 * Paramters:
2594 * prnt_str - the string we wish to print and any arguments
2595 * for the format of that string.
2596 * Returns:
2597 * void
2598 * Scope:
2599 * Semi-private (library wide use only)
2601 void
2602 be_print_err(char *prnt_str, ...)
2604 va_list ap;
2605 char buf[BUFSIZ];
2606 char *env_buf;
2607 static boolean_t env_checked = B_FALSE;
2609 if (!env_checked) {
2610 if ((env_buf = getenv("BE_PRINT_ERR")) != NULL) {
2611 if (strcasecmp(env_buf, "true") == 0) {
2612 do_print = B_TRUE;
2615 env_checked = B_TRUE;
2618 if (do_print) {
2619 va_start(ap, prnt_str);
2620 /* LINTED variable format specifier */
2621 (void) vsnprintf(buf, BUFSIZ, prnt_str, ap);
2622 (void) fputs(buf, stderr);
2623 va_end(ap);
2628 * Function: be_find_current_be
2629 * Description: Find the currently "active" BE. Fill in the
2630 * passed in be_transaction_data_t reference with the
2631 * active BE's data.
2632 * Paramters:
2633 * none
2634 * Returns:
2635 * BE_SUCCESS - Success
2636 * be_errnot_t - Failure
2637 * Scope:
2638 * Semi-private (library wide use only)
2639 * Notes:
2640 * The caller is responsible for initializing the libzfs handle
2641 * and freeing the memory used by the active be_name.
2644 be_find_current_be(be_transaction_data_t *bt)
2646 int zret;
2648 if ((zret = zpool_iter(g_zfs, be_zpool_find_current_be_callback,
2649 bt)) == 0) {
2650 be_print_err(gettext("be_find_current_be: failed to "
2651 "find current BE name\n"));
2652 return (BE_ERR_BE_NOENT);
2653 } else if (zret < 0) {
2654 be_print_err(gettext("be_find_current_be: "
2655 "zpool_iter failed: %s\n"),
2656 libzfs_error_description(g_zfs));
2657 return (zfs_err_to_be_err(g_zfs));
2660 return (BE_SUCCESS);
2664 * Function: be_zpool_find_current_be_callback
2665 * Description: Callback function used to iterate through all existing pools
2666 * to find the BE that is the currently booted BE.
2667 * Parameters:
2668 * zlp - zpool_handle_t pointer to the current pool being
2669 * looked at.
2670 * data - be_transaction_data_t pointer.
2671 * Upon successfully finding the current BE, the
2672 * obe_zpool member of this parameter is set to the
2673 * pool it is found in.
2674 * Return:
2675 * 1 - Found current BE in this pool.
2676 * 0 - Did not find current BE in this pool.
2677 * Scope:
2678 * Semi-private (library wide use only)
2681 be_zpool_find_current_be_callback(zpool_handle_t *zlp, void *data)
2683 be_transaction_data_t *bt = data;
2684 zfs_handle_t *zhp = NULL;
2685 const char *zpool = zpool_get_name(zlp);
2686 char be_container_ds[MAXPATHLEN];
2687 char *zpath = NULL;
2690 * Generate string for BE container dataset
2692 if (getzoneid() != GLOBAL_ZONEID) {
2693 if ((zpath = be_get_ds_from_dir("/")) != NULL) {
2694 (void) strlcpy(be_container_ds, dirname(zpath),
2695 sizeof (be_container_ds));
2696 } else {
2697 be_print_err(gettext(
2698 "be_zpool_find_current_be_callback: "
2699 "zone root dataset is not mounted\n"));
2700 return (0);
2702 } else {
2703 be_make_container_ds(zpool, be_container_ds,
2704 sizeof (be_container_ds));
2708 * Check if a BE container dataset exists in this pool.
2710 if (!zfs_dataset_exists(g_zfs, be_container_ds, ZFS_TYPE_FILESYSTEM)) {
2711 zpool_close(zlp);
2712 return (0);
2716 * Get handle to this zpool's BE container dataset.
2718 if ((zhp = zfs_open(g_zfs, be_container_ds, ZFS_TYPE_FILESYSTEM)) ==
2719 NULL) {
2720 be_print_err(gettext("be_zpool_find_current_be_callback: "
2721 "failed to open BE container dataset (%s)\n"),
2722 be_container_ds);
2723 zpool_close(zlp);
2724 return (0);
2728 * Iterate through all potential BEs in this zpool
2730 if (zfs_iter_filesystems(zhp, be_zfs_find_current_be_callback, bt)) {
2732 * Found current BE dataset; set obe_zpool
2734 if ((bt->obe_zpool = strdup(zpool)) == NULL) {
2735 be_print_err(gettext(
2736 "be_zpool_find_current_be_callback: "
2737 "memory allocation failed\n"));
2738 ZFS_CLOSE(zhp);
2739 zpool_close(zlp);
2740 return (0);
2743 ZFS_CLOSE(zhp);
2744 zpool_close(zlp);
2745 return (1);
2748 ZFS_CLOSE(zhp);
2749 zpool_close(zlp);
2751 return (0);
2755 * Function: be_zfs_find_current_be_callback
2756 * Description: Callback function used to iterate through all BEs in a
2757 * pool to find the BE that is the currently booted BE.
2758 * Parameters:
2759 * zhp - zfs_handle_t pointer to current filesystem being checked.
2760 * data - be_transaction-data_t pointer
2761 * Upon successfully finding the current BE, the
2762 * obe_name and obe_root_ds members of this parameter
2763 * are set to the BE name and BE's root dataset
2764 * respectively.
2765 * Return:
2766 * 1 - Found current BE.
2767 * 0 - Did not find current BE.
2768 * Scope:
2769 * Semi-private (library wide use only)
2772 be_zfs_find_current_be_callback(zfs_handle_t *zhp, void *data)
2774 be_transaction_data_t *bt = data;
2775 char *mp = NULL;
2778 * Check if dataset is mounted, and if so where.
2780 if (zfs_is_mounted(zhp, &mp)) {
2782 * If mounted at root, set obe_root_ds and obe_name
2784 if (mp != NULL && strcmp(mp, "/") == 0) {
2785 free(mp);
2787 if ((bt->obe_root_ds = strdup(zfs_get_name(zhp)))
2788 == NULL) {
2789 be_print_err(gettext(
2790 "be_zfs_find_current_be_callback: "
2791 "memory allocation failed\n"));
2792 ZFS_CLOSE(zhp);
2793 return (0);
2796 if ((bt->obe_name = strdup(basename(bt->obe_root_ds)))
2797 == NULL) {
2798 be_print_err(gettext(
2799 "be_zfs_find_current_be_callback: "
2800 "memory allocation failed\n"));
2801 ZFS_CLOSE(zhp);
2802 return (0);
2805 ZFS_CLOSE(zhp);
2806 return (1);
2809 free(mp);
2811 ZFS_CLOSE(zhp);
2813 return (0);
2817 * Function: be_check_be_roots_callback
2818 * Description: This function checks whether or not the dataset name passed
2819 * is hierachically located under the BE root container dataset
2820 * for this pool.
2821 * Parameters:
2822 * zlp - zpool_handle_t pointer to current pool being processed.
2823 * data - name of dataset to check
2824 * Returns:
2825 * 0 - dataset is not in this pool's BE root container dataset
2826 * 1 - dataset is in this pool's BE root container dataset
2827 * Scope:
2828 * Semi-private (library wide use only)
2831 be_check_be_roots_callback(zpool_handle_t *zlp, void *data)
2833 const char *zpool = zpool_get_name(zlp);
2834 char *ds = data;
2835 char be_container_ds[MAXPATHLEN];
2837 /* Generate string for this pool's BE root container dataset */
2838 be_make_container_ds(zpool, be_container_ds, sizeof (be_container_ds));
2841 * If dataset lives under the BE root container dataset
2842 * of this pool, return failure.
2844 if (strncmp(be_container_ds, ds, strlen(be_container_ds)) == 0 &&
2845 ds[strlen(be_container_ds)] == '/') {
2846 zpool_close(zlp);
2847 return (1);
2850 zpool_close(zlp);
2851 return (0);
2855 * Function: zfs_err_to_be_err
2856 * Description: This function takes the error stored in the libzfs handle
2857 * and maps it to an be_errno_t. If there are no matching
2858 * be_errno_t's then BE_ERR_ZFS is returned.
2859 * Paramters:
2860 * zfsh - The libzfs handle containing the error we're looking up.
2861 * Returns:
2862 * be_errno_t
2863 * Scope:
2864 * Semi-private (library wide use only)
2867 zfs_err_to_be_err(libzfs_handle_t *zfsh)
2869 int err = libzfs_errno(zfsh);
2871 switch (err) {
2872 case 0:
2873 return (BE_SUCCESS);
2874 case EZFS_PERM:
2875 return (BE_ERR_PERM);
2876 case EZFS_INTR:
2877 return (BE_ERR_INTR);
2878 case EZFS_NOENT:
2879 return (BE_ERR_NOENT);
2880 case EZFS_NOSPC:
2881 return (BE_ERR_NOSPC);
2882 case EZFS_MOUNTFAILED:
2883 return (BE_ERR_MOUNT);
2884 case EZFS_UMOUNTFAILED:
2885 return (BE_ERR_UMOUNT);
2886 case EZFS_EXISTS:
2887 return (BE_ERR_BE_EXISTS);
2888 case EZFS_BUSY:
2889 return (BE_ERR_DEV_BUSY);
2890 case EZFS_POOLREADONLY:
2891 return (BE_ERR_ROFS);
2892 case EZFS_NAMETOOLONG:
2893 return (BE_ERR_NAMETOOLONG);
2894 case EZFS_NODEVICE:
2895 return (BE_ERR_NODEV);
2896 case EZFS_POOL_INVALARG:
2897 return (BE_ERR_INVAL);
2898 case EZFS_PROPTYPE:
2899 return (BE_ERR_INVALPROP);
2900 case EZFS_BADTYPE:
2901 return (BE_ERR_DSTYPE);
2902 case EZFS_PROPNONINHERIT:
2903 return (BE_ERR_NONINHERIT);
2904 case EZFS_PROPREADONLY:
2905 return (BE_ERR_READONLYPROP);
2906 case EZFS_RESILVERING:
2907 case EZFS_POOLUNAVAIL:
2908 return (BE_ERR_UNAVAIL);
2909 case EZFS_DSREADONLY:
2910 return (BE_ERR_READONLYDS);
2911 default:
2912 return (BE_ERR_ZFS);
2917 * Function: errno_to_be_err
2918 * Description: This function takes an errno and maps it to an be_errno_t.
2919 * If there are no matching be_errno_t's then BE_ERR_UNKNOWN is
2920 * returned.
2921 * Paramters:
2922 * err - The errno we're compairing against.
2923 * Returns:
2924 * be_errno_t
2925 * Scope:
2926 * Semi-private (library wide use only)
2929 errno_to_be_err(int err)
2931 switch (err) {
2932 case EPERM:
2933 return (BE_ERR_PERM);
2934 case EACCES:
2935 return (BE_ERR_ACCESS);
2936 case ECANCELED:
2937 return (BE_ERR_CANCELED);
2938 case EINTR:
2939 return (BE_ERR_INTR);
2940 case ENOENT:
2941 return (BE_ERR_NOENT);
2942 case ENOSPC:
2943 case EDQUOT:
2944 return (BE_ERR_NOSPC);
2945 case EEXIST:
2946 return (BE_ERR_BE_EXISTS);
2947 case EBUSY:
2948 return (BE_ERR_BUSY);
2949 case EROFS:
2950 return (BE_ERR_ROFS);
2951 case ENAMETOOLONG:
2952 return (BE_ERR_NAMETOOLONG);
2953 case ENXIO:
2954 return (BE_ERR_NXIO);
2955 case EINVAL:
2956 return (BE_ERR_INVAL);
2957 case EFAULT:
2958 return (BE_ERR_FAULT);
2959 default:
2960 return (BE_ERR_UNKNOWN);
2965 * Function: be_err_to_str
2966 * Description: This function takes a be_errno_t and maps it to a message.
2967 * If there are no matching be_errno_t's then NULL is returned.
2968 * Paramters:
2969 * be_errno_t - The be_errno_t we're mapping.
2970 * Returns:
2971 * string or NULL if the error code is not known.
2972 * Scope:
2973 * Semi-private (library wide use only)
2975 char *
2976 be_err_to_str(int err)
2978 switch (err) {
2979 case BE_ERR_ACCESS:
2980 return (gettext("Permission denied."));
2981 case BE_ERR_ACTIVATE_CURR:
2982 return (gettext("Activation of current BE failed."));
2983 case BE_ERR_AUTONAME:
2984 return (gettext("Auto naming failed."));
2985 case BE_ERR_BE_NOENT:
2986 return (gettext("No such BE."));
2987 case BE_ERR_BUSY:
2988 return (gettext("Mount busy."));
2989 case BE_ERR_DEV_BUSY:
2990 return (gettext("Device busy."));
2991 case BE_ERR_CANCELED:
2992 return (gettext("Operation canceled."));
2993 case BE_ERR_CLONE:
2994 return (gettext("BE clone failed."));
2995 case BE_ERR_COPY:
2996 return (gettext("BE copy failed."));
2997 case BE_ERR_CREATDS:
2998 return (gettext("Dataset creation failed."));
2999 case BE_ERR_CURR_BE_NOT_FOUND:
3000 return (gettext("Can't find current BE."));
3001 case BE_ERR_DESTROY:
3002 return (gettext("Failed to destroy BE or snapshot."));
3003 case BE_ERR_DESTROY_CURR_BE:
3004 return (gettext("Cannot destroy current BE."));
3005 case BE_ERR_DEMOTE:
3006 return (gettext("BE demotion failed."));
3007 case BE_ERR_DSTYPE:
3008 return (gettext("Invalid dataset type."));
3009 case BE_ERR_BE_EXISTS:
3010 return (gettext("BE exists."));
3011 case BE_ERR_INIT:
3012 return (gettext("be_zfs_init failed."));
3013 case BE_ERR_INTR:
3014 return (gettext("Interupted system call."));
3015 case BE_ERR_INVAL:
3016 return (gettext("Invalid argument."));
3017 case BE_ERR_INVALPROP:
3018 return (gettext("Invalid property for dataset."));
3019 case BE_ERR_INVALMOUNTPOINT:
3020 return (gettext("Unexpected mountpoint."));
3021 case BE_ERR_MOUNT:
3022 return (gettext("Mount failed."));
3023 case BE_ERR_MOUNTED:
3024 return (gettext("Already mounted."));
3025 case BE_ERR_NAMETOOLONG:
3026 return (gettext("name > BUFSIZ."));
3027 case BE_ERR_NOENT:
3028 return (gettext("Doesn't exist."));
3029 case BE_ERR_POOL_NOENT:
3030 return (gettext("No such pool."));
3031 case BE_ERR_NODEV:
3032 return (gettext("No such device."));
3033 case BE_ERR_NOTMOUNTED:
3034 return (gettext("File system not mounted."));
3035 case BE_ERR_NOMEM:
3036 return (gettext("Not enough memory."));
3037 case BE_ERR_NONINHERIT:
3038 return (gettext(
3039 "Property is not inheritable for the BE dataset."));
3040 case BE_ERR_NXIO:
3041 return (gettext("No such device or address."));
3042 case BE_ERR_NOSPC:
3043 return (gettext("No space on device."));
3044 case BE_ERR_NOTSUP:
3045 return (gettext("Operation not supported."));
3046 case BE_ERR_OPEN:
3047 return (gettext("Open failed."));
3048 case BE_ERR_PERM:
3049 return (gettext("Not owner."));
3050 case BE_ERR_UNAVAIL:
3051 return (gettext("The BE is currently unavailable."));
3052 case BE_ERR_PROMOTE:
3053 return (gettext("BE promotion failed."));
3054 case BE_ERR_ROFS:
3055 return (gettext("Read only file system."));
3056 case BE_ERR_READONLYDS:
3057 return (gettext("Read only dataset."));
3058 case BE_ERR_READONLYPROP:
3059 return (gettext("Read only property."));
3060 case BE_ERR_RENAME_ACTIVE:
3061 return (gettext("Renaming the active BE is not supported."));
3062 case BE_ERR_SS_EXISTS:
3063 return (gettext("Snapshot exists."));
3064 case BE_ERR_SS_NOENT:
3065 return (gettext("No such snapshot."));
3066 case BE_ERR_UMOUNT:
3067 return (gettext("Unmount failed."));
3068 case BE_ERR_UMOUNT_CURR_BE:
3069 return (gettext("Can't unmount the current BE."));
3070 case BE_ERR_UMOUNT_SHARED:
3071 return (gettext("Unmount of a shared File System failed."));
3072 case BE_ERR_FAULT:
3073 return (gettext("Bad address."));
3074 case BE_ERR_UNKNOWN:
3075 return (gettext("Unknown error."));
3076 case BE_ERR_ZFS:
3077 return (gettext("ZFS returned an error."));
3078 case BE_ERR_GEN_UUID:
3079 return (gettext("Failed to generate uuid."));
3080 case BE_ERR_PARSE_UUID:
3081 return (gettext("Failed to parse uuid."));
3082 case BE_ERR_NO_UUID:
3083 return (gettext("No uuid"));
3084 case BE_ERR_ZONE_NO_PARENTBE:
3085 return (gettext("No parent uuid"));
3086 case BE_ERR_ZONE_MULTIPLE_ACTIVE:
3087 return (gettext("Multiple active zone roots"));
3088 case BE_ERR_ZONE_NO_ACTIVE_ROOT:
3089 return (gettext("No active zone root"));
3090 case BE_ERR_ZONE_ROOT_NOT_LEGACY:
3091 return (gettext("Zone root not legacy"));
3092 case BE_ERR_MOUNT_ZONEROOT:
3093 return (gettext("Failed to mount a zone root."));
3094 case BE_ERR_UMOUNT_ZONEROOT:
3095 return (gettext("Failed to unmount a zone root."));
3096 case BE_ERR_NO_MOUNTED_ZONE:
3097 return (gettext("Zone is not mounted"));
3098 case BE_ERR_ZONES_UNMOUNT:
3099 return (gettext("Unable to unmount a zone BE."));
3100 case BE_ERR_NO_MENU:
3101 return (gettext("Missing boot menu file."));
3102 case BE_ERR_BAD_MENU_PATH:
3103 return (gettext("Invalid path for menu.lst file"));
3104 case BE_ERR_ZONE_SS_EXISTS:
3105 return (gettext("Zone snapshot exists."));
3106 case BE_ERR_BOOTFILE_INST:
3107 return (gettext("Error installing boot files."));
3108 case BE_ERR_EXTCMD:
3109 return (gettext("Error running an external command."));
3110 default:
3111 return (NULL);
3116 * Function: be_has_grub
3117 * Description: Boolean function indicating whether the current system
3118 * uses grub.
3119 * Return: B_FALSE - the system does not have grub
3120 * B_TRUE - the system does have grub.
3121 * Scope:
3122 * Semi-private (library wide use only)
3124 boolean_t
3125 be_has_grub(void)
3127 static struct be_defaults be_defaults;
3128 static boolean_t be_deflts_set = B_FALSE;
3130 /* Cache the defaults, because be_has_grub is used often. */
3131 if (be_deflts_set == B_FALSE) {
3132 be_get_defaults(&be_defaults);
3133 be_deflts_set = B_TRUE;
3136 return (be_defaults.be_deflt_grub);
3140 * Function: be_is_isa
3141 * Description: Boolean function indicating whether the instruction set
3142 * architecture of the executing system matches the name provided.
3143 * The string must match a system defined architecture (e.g.
3144 * "i386", "sparc") and is case sensitive.
3145 * Parameters: name - string representing the name of instruction set
3146 * architecture being tested
3147 * Returns: B_FALSE - the system instruction set architecture is different
3148 * from the one specified
3149 * B_TRUE - the system instruction set architecture is the same
3150 * as the one specified
3151 * Scope:
3152 * Semi-private (library wide use only)
3154 boolean_t
3155 be_is_isa(char *name)
3157 return ((strcmp((char *)be_get_default_isa(), name) == 0));
3161 * Function: be_get_default_isa
3162 * Description:
3163 * Returns the default instruction set architecture of the
3164 * machine it is executed on. (eg. sparc, i386, ...)
3165 * NOTE: SYS_INST environment variable may override default
3166 * return value
3167 * Parameters:
3168 * none
3169 * Returns:
3170 * NULL - the architecture returned by sysinfo() was too
3171 * long for local variables
3172 * char * - pointer to a string containing the default
3173 * implementation
3174 * Scope:
3175 * Semi-private (library wide use only)
3177 char *
3178 be_get_default_isa(void)
3180 int i;
3181 char *envp;
3182 static char default_inst[ARCH_LENGTH] = "";
3184 if (default_inst[0] == '\0') {
3185 if ((envp = getenv("SYS_INST")) != NULL) {
3186 if ((int)strlen(envp) >= ARCH_LENGTH)
3187 return (NULL);
3188 else
3189 (void) strcpy(default_inst, envp);
3190 } else {
3191 i = sysinfo(SI_ARCHITECTURE, default_inst, ARCH_LENGTH);
3192 if (i < 0 || i > ARCH_LENGTH)
3193 return (NULL);
3196 return (default_inst);
3200 * Function: be_get_platform
3201 * Description:
3202 * Returns the platfom name
3203 * Parameters:
3204 * none
3205 * Returns:
3206 * NULL - the platform name returned by sysinfo() was too
3207 * long for local variables
3208 * char * - pointer to a string containing the platform name
3209 * Scope:
3210 * Semi-private (library wide use only)
3212 char *
3213 be_get_platform(void)
3215 int i;
3216 static char default_inst[ARCH_LENGTH] = "";
3218 if (default_inst[0] == '\0') {
3219 i = sysinfo(SI_PLATFORM, default_inst, ARCH_LENGTH);
3220 if (i < 0 || i > ARCH_LENGTH)
3221 return (NULL);
3223 return (default_inst);
3227 * Function: be_run_cmd
3228 * Description:
3229 * Runs a command in a separate subprocess. Splits out stdout from stderr
3230 * and sends each to its own buffer. Buffers must be pre-allocated and
3231 * passed in as arguments. Buffer sizes are also passed in as arguments.
3233 * Notes / caveats:
3234 * - Command being run is assumed to not have any stdout or stderr
3235 * redirection.
3236 * - Commands which emit total stderr output of greater than PIPE_BUF
3237 * bytes can hang. For such commands, a different implementation
3238 * which uses poll(2) must be used.
3239 * - stdout_buf can be NULL. In this case, stdout_bufsize is ignored, and
3240 * the stream which would have gone to it is sent to the bit
3241 * bucket.
3242 * - stderr_buf cannot be NULL.
3243 * - Only subprocess errors are appended to the stderr_buf. Errors
3244 * running the command are reported through be_print_err().
3245 * - Data which would overflow its respective buffer is sent to the bit
3246 * bucket.
3248 * Parameters:
3249 * command: command to run. Assumed not to have embedded stdout
3250 * or stderr redirection. May have stdin redirection,
3251 * however.
3252 * stderr_buf: buffer returning subprocess stderr data. Errors
3253 * reported by this function are reported through
3254 * be_print_err().
3255 * stderr_bufsize: size of stderr_buf
3256 * stdout_buf: buffer returning subprocess stdout data.
3257 * stdout_bufsize: size of stdout_buf
3258 * Returns:
3259 * BE_SUCCESS - The command ran successfully without returning
3260 * errors.
3261 * BE_ERR_EXTCMD
3262 * - The command could not be run.
3263 * - The command terminated with error status.
3264 * - There were errors extracting or returning subprocess
3265 * data.
3266 * BE_ERR_NOMEM - The command exceeds the command buffer size.
3267 * BE_ERR_INVAL - An invalid argument was specified.
3268 * Scope:
3269 * Semi-private (library wide use only)
3272 be_run_cmd(char *command, char *stderr_buf, int stderr_bufsize,
3273 char *stdout_buf, int stdout_bufsize)
3275 char *temp_filename = strdup(tmpnam(NULL));
3276 FILE *stdout_str = NULL;
3277 FILE *stderr_str = NULL;
3278 char cmdline[BUFSIZ];
3279 char oneline[BUFSIZ];
3280 int exit_status;
3281 int rval = BE_SUCCESS;
3283 if ((command == NULL) || (stderr_buf == NULL) ||
3284 (stderr_bufsize <= 0) || (stdout_bufsize < 0) ||
3285 ((stdout_buf != NULL) ^ (stdout_bufsize != 0))) {
3286 return (BE_ERR_INVAL);
3289 /* Set up command so popen returns stderr, not stdout */
3290 if (snprintf(cmdline, BUFSIZ, "%s 2> %s", command,
3291 temp_filename) >= BUFSIZ) {
3292 rval = BE_ERR_NOMEM;
3293 goto cleanup;
3296 /* Set up the fifo that will make stderr available. */
3297 if (mkfifo(temp_filename, 0600) != 0) {
3298 (void) be_print_err(gettext("be_run_cmd: mkfifo: %s\n"),
3299 strerror(errno));
3300 rval = BE_ERR_EXTCMD;
3301 goto cleanup;
3304 if ((stdout_str = popen(cmdline, "r")) == NULL) {
3305 (void) be_print_err(gettext("be_run_cmd: popen: %s\n"),
3306 strerror(errno));
3307 rval = BE_ERR_EXTCMD;
3308 goto cleanup;
3311 if ((stderr_str = fopen(temp_filename, "r")) == NULL) {
3312 (void) be_print_err(gettext("be_run_cmd: fopen: %s\n"),
3313 strerror(errno));
3314 (void) pclose(stdout_str);
3315 rval = BE_ERR_EXTCMD;
3316 goto cleanup;
3319 /* Read stdout first, as it usually outputs more than stderr. */
3320 oneline[BUFSIZ-1] = '\0';
3321 while (fgets(oneline, BUFSIZ-1, stdout_str) != NULL) {
3322 if (stdout_str != NULL) {
3323 (void) strlcat(stdout_buf, oneline, stdout_bufsize);
3327 while (fgets(oneline, BUFSIZ-1, stderr_str) != NULL) {
3328 (void) strlcat(stderr_buf, oneline, stderr_bufsize);
3331 /* Close pipe, get exit status. */
3332 if ((exit_status = pclose(stdout_str)) == -1) {
3333 (void) be_print_err(gettext("be_run_cmd: pclose: %s\n"),
3334 strerror(errno));
3335 rval = BE_ERR_EXTCMD;
3336 } else if (WIFEXITED(exit_status)) {
3337 exit_status = (int)((char)WEXITSTATUS(exit_status));
3339 * error code BC_NOUPDT means more recent version
3340 * is installed
3342 if (exit_status != BC_SUCCESS && exit_status != BC_NOUPDT) {
3343 (void) snprintf(oneline, BUFSIZ, gettext("be_run_cmd: "
3344 "command terminated with error status: %d\n"),
3345 exit_status);
3346 (void) strlcat(stderr_buf, oneline, stderr_bufsize);
3347 rval = BE_ERR_EXTCMD;
3349 } else {
3350 (void) snprintf(oneline, BUFSIZ, gettext("be_run_cmd: command "
3351 "terminated on signal: %s\n"),
3352 strsignal(WTERMSIG(exit_status)));
3353 (void) strlcat(stderr_buf, oneline, stderr_bufsize);
3354 rval = BE_ERR_EXTCMD;
3357 cleanup:
3358 (void) unlink(temp_filename);
3359 (void) free(temp_filename);
3361 return (rval);
3364 /* ******************************************************************** */
3365 /* Private Functions */
3366 /* ******************************************************************** */
3369 * Function: update_dataset
3370 * Description: This function takes a dataset name and replaces the zpool
3371 * and be_name components of the dataset with the new be_name
3372 * zpool passed in.
3373 * Parameters:
3374 * dataset - name of dataset
3375 * dataset_len - lenth of buffer in which dataset is passed in.
3376 * be_name - name of new BE name to update to.
3377 * old_rc_loc - dataset under which the root container dataset
3378 * for the old BE lives.
3379 * new_rc_loc - dataset under which the root container dataset
3380 * for the new BE lives.
3381 * Returns:
3382 * BE_SUCCESS - Success
3383 * be_errno_t - Failure
3384 * Scope:
3385 * Private
3387 static int
3388 update_dataset(char *dataset, int dataset_len, char *be_name,
3389 char *old_rc_loc, char *new_rc_loc)
3391 char *ds = NULL;
3392 char *sub_ds = NULL;
3394 /* Tear off the BE container dataset */
3395 if ((ds = be_make_name_from_ds(dataset, old_rc_loc)) == NULL) {
3396 return (BE_ERR_INVAL);
3399 /* Get dataset name relative to BE root, if there is one */
3400 sub_ds = strchr(ds, '/');
3402 /* Generate the BE root dataset name */
3403 be_make_root_ds(new_rc_loc, be_name, dataset, dataset_len);
3405 /* If a subordinate dataset name was found, append it */
3406 if (sub_ds != NULL)
3407 (void) strlcat(dataset, sub_ds, dataset_len);
3409 free(ds);
3410 return (BE_SUCCESS);
3414 * Function: _update_vfstab
3415 * Description: This function updates a vfstab file to reflect the new
3416 * root container dataset location and be_name for all
3417 * entries listed in the be_fs_list_data_t structure passed in.
3418 * Parameters:
3419 * vfstab - vfstab file to modify
3420 * be_name - name of BE to update.
3421 * old_rc_loc - dataset under which the root container dataset
3422 * of the old BE resides in.
3423 * new_rc_loc - dataset under which the root container dataset
3424 * of the new BE resides in.
3425 * fld - be_fs_list_data_t pointer providing the list of
3426 * file systems to look for in vfstab.
3427 * Returns:
3428 * BE_SUCCESS - Success
3429 * be_errno_t - Failure
3430 * Scope:
3431 * Private
3433 static int
3434 _update_vfstab(char *vfstab, char *be_name, char *old_rc_loc,
3435 char *new_rc_loc, be_fs_list_data_t *fld)
3437 struct vfstab vp;
3438 char *tmp_vfstab = NULL;
3439 char comments_buf[BUFSIZ];
3440 FILE *comments = NULL;
3441 FILE *vfs_ents = NULL;
3442 FILE *tfile = NULL;
3443 struct stat sb;
3444 char dev[MAXPATHLEN];
3445 char *c;
3446 int fd;
3447 int ret = BE_SUCCESS, err = 0;
3448 int i;
3449 int tmp_vfstab_len = 0;
3451 errno = 0;
3454 * Open vfstab for reading twice. First is for comments,
3455 * second is for actual entries.
3457 if ((comments = fopen(vfstab, "r")) == NULL ||
3458 (vfs_ents = fopen(vfstab, "r")) == NULL) {
3459 err = errno;
3460 be_print_err(gettext("_update_vfstab: "
3461 "failed to open vfstab (%s): %s\n"), vfstab,
3462 strerror(err));
3463 ret = errno_to_be_err(err);
3464 goto cleanup;
3467 /* Grab the stats of the original vfstab file */
3468 if (stat(vfstab, &sb) != 0) {
3469 err = errno;
3470 be_print_err(gettext("_update_vfstab: "
3471 "failed to stat file %s: %s\n"), vfstab,
3472 strerror(err));
3473 ret = errno_to_be_err(err);
3474 goto cleanup;
3477 /* Create tmp file for modified vfstab */
3478 if ((tmp_vfstab = (char *)malloc(strlen(vfstab) + 7))
3479 == NULL) {
3480 be_print_err(gettext("_update_vfstab: "
3481 "malloc failed\n"));
3482 ret = BE_ERR_NOMEM;
3483 goto cleanup;
3485 tmp_vfstab_len = strlen(vfstab) + 7;
3486 (void) memset(tmp_vfstab, 0, tmp_vfstab_len);
3487 (void) strlcpy(tmp_vfstab, vfstab, tmp_vfstab_len);
3488 (void) strlcat(tmp_vfstab, "XXXXXX", tmp_vfstab_len);
3489 if ((fd = mkstemp(tmp_vfstab)) == -1) {
3490 err = errno;
3491 be_print_err(gettext("_update_vfstab: "
3492 "mkstemp failed: %s\n"), strerror(err));
3493 ret = errno_to_be_err(err);
3494 goto cleanup;
3496 if ((tfile = fdopen(fd, "w")) == NULL) {
3497 err = errno;
3498 be_print_err(gettext("_update_vfstab: "
3499 "could not open file for write\n"));
3500 (void) close(fd);
3501 ret = errno_to_be_err(err);
3502 goto cleanup;
3505 while (fgets(comments_buf, BUFSIZ, comments)) {
3506 for (c = comments_buf; *c != '\0' && isspace(*c); c++)
3508 if (*c == '\0') {
3509 continue;
3510 } else if (*c == '#') {
3512 * If line is a comment line, just put
3513 * it through to the tmp vfstab.
3515 (void) fputs(comments_buf, tfile);
3516 } else {
3518 * Else line is a vfstab entry, grab it
3519 * into a vfstab struct.
3521 if (getvfsent(vfs_ents, &vp) != 0) {
3522 err = errno;
3523 be_print_err(gettext("_update_vfstab: "
3524 "getvfsent failed: %s\n"), strerror(err));
3525 ret = errno_to_be_err(err);
3526 goto cleanup;
3529 if (vp.vfs_special == NULL || vp.vfs_mountp == NULL) {
3530 (void) putvfsent(tfile, &vp);
3531 continue;
3535 * If the entry is one of the entries in the list
3536 * of file systems to update, modify it's device
3537 * field to be correct for this BE.
3539 for (i = 0; i < fld->fs_num; i++) {
3540 if (strcmp(vp.vfs_special, fld->fs_list[i])
3541 == 0) {
3543 * Found entry that needs an update.
3544 * Replace the root container dataset
3545 * location and be_name in the
3546 * entry's device.
3548 (void) strlcpy(dev, vp.vfs_special,
3549 sizeof (dev));
3551 if ((ret = update_dataset(dev,
3552 sizeof (dev), be_name, old_rc_loc,
3553 new_rc_loc)) != 0) {
3554 be_print_err(
3555 gettext("_update_vfstab: "
3556 "Failed to update device "
3557 "field for vfstab entry "
3558 "%s\n"), fld->fs_list[i]);
3559 goto cleanup;
3562 vp.vfs_special = dev;
3563 break;
3567 /* Put entry through to tmp vfstab */
3568 (void) putvfsent(tfile, &vp);
3572 (void) fclose(comments);
3573 comments = NULL;
3574 (void) fclose(vfs_ents);
3575 vfs_ents = NULL;
3576 (void) fclose(tfile);
3577 tfile = NULL;
3579 /* Copy tmp vfstab into place */
3580 if (rename(tmp_vfstab, vfstab) != 0) {
3581 err = errno;
3582 be_print_err(gettext("_update_vfstab: "
3583 "failed to rename file %s to %s: %s\n"), tmp_vfstab,
3584 vfstab, strerror(err));
3585 ret = errno_to_be_err(err);
3586 goto cleanup;
3589 /* Set the perms and ownership of the updated file */
3590 if (chmod(vfstab, sb.st_mode) != 0) {
3591 err = errno;
3592 be_print_err(gettext("_update_vfstab: "
3593 "failed to chmod %s: %s\n"), vfstab, strerror(err));
3594 ret = errno_to_be_err(err);
3595 goto cleanup;
3597 if (chown(vfstab, sb.st_uid, sb.st_gid) != 0) {
3598 err = errno;
3599 be_print_err(gettext("_update_vfstab: "
3600 "failed to chown %s: %s\n"), vfstab, strerror(err));
3601 ret = errno_to_be_err(err);
3602 goto cleanup;
3605 cleanup:
3606 if (comments != NULL)
3607 (void) fclose(comments);
3608 if (vfs_ents != NULL)
3609 (void) fclose(vfs_ents);
3610 (void) unlink(tmp_vfstab);
3611 (void) free(tmp_vfstab);
3612 if (tfile != NULL)
3613 (void) fclose(tfile);
3615 return (ret);
3620 * Function: be_get_auto_name
3621 * Description: Generate an auto name constructed based on the BE name
3622 * of the original BE or zone BE being cloned.
3623 * Parameters:
3624 * obe_name - name of the original BE or zone BE being cloned.
3625 * container_ds - container dataset for the zone.
3626 * Note: if zone_be is false this should be
3627 * NULL.
3628 * zone_be - flag that indicates if we are operating on a zone BE.
3629 * Returns:
3630 * Success - pointer to auto generated BE name. The name
3631 * is allocated in heap storage so the caller is
3632 * responsible for free'ing the name.
3633 * Failure - NULL
3634 * Scope:
3635 * Private
3637 static char *
3638 be_get_auto_name(char *obe_name, char *be_container_ds, boolean_t zone_be)
3640 be_node_list_t *be_nodes = NULL;
3641 be_node_list_t *cur_be = NULL;
3642 char auto_be_name[MAXPATHLEN];
3643 char base_be_name[MAXPATHLEN];
3644 char cur_be_name[MAXPATHLEN];
3645 char *num_str = NULL;
3646 char *c = NULL;
3647 int num = 0;
3648 int cur_num = 0;
3650 errno = 0;
3653 * Check if obe_name is already in an auto BE name format.
3654 * If it is, then strip off the increment number to get the
3655 * base name.
3657 (void) strlcpy(base_be_name, obe_name, sizeof (base_be_name));
3659 if ((num_str = strrchr(base_be_name, BE_AUTO_NAME_DELIM))
3660 != NULL) {
3661 /* Make sure remaining string is all digits */
3662 c = num_str + 1;
3663 while (c[0] != '\0' && isdigit(c[0]))
3664 c++;
3666 * If we're now at the end of the string strip off the
3667 * increment number.
3669 if (c[0] == '\0')
3670 num_str[0] = '\0';
3673 if (zone_be) {
3674 if (be_container_ds == NULL)
3675 return (NULL);
3676 if (be_get_zone_be_list(obe_name, be_container_ds,
3677 &be_nodes) != BE_SUCCESS) {
3678 be_print_err(gettext("be_get_auto_name: "
3679 "be_get_zone_be_list failed\n"));
3680 return (NULL);
3682 } else if (_be_list(NULL, &be_nodes) != BE_SUCCESS) {
3683 be_print_err(gettext("be_get_auto_name: be_list failed\n"));
3684 return (NULL);
3687 for (cur_be = be_nodes; cur_be != NULL; cur_be = cur_be->be_next_node) {
3688 (void) strlcpy(cur_be_name, cur_be->be_node_name,
3689 sizeof (cur_be_name));
3691 /* If cur_be_name doesn't match at least base be name, skip. */
3692 if (strncmp(cur_be_name, base_be_name, strlen(base_be_name))
3693 != 0)
3694 continue;
3696 /* Get the string following the base be name */
3697 num_str = cur_be_name + strlen(base_be_name);
3700 * If nothing follows the base be name, this cur_be_name
3701 * is the BE named with the base be name, skip.
3703 if (num_str == NULL || num_str[0] == '\0')
3704 continue;
3707 * Remove the name delimiter. If its not there,
3708 * cur_be_name isn't part of this BE name stream, skip.
3710 if (num_str[0] == BE_AUTO_NAME_DELIM)
3711 num_str++;
3712 else
3713 continue;
3715 /* Make sure remaining string is all digits */
3716 c = num_str;
3717 while (c[0] != '\0' && isdigit(c[0]))
3718 c++;
3719 if (c[0] != '\0')
3720 continue;
3722 /* Convert the number string to an int */
3723 cur_num = atoi(num_str);
3726 * If failed to convert the string, skip it. If its too
3727 * long to be converted to an int, we wouldn't auto generate
3728 * this number anyway so there couldn't be a conflict.
3729 * We treat it as a manually created BE name.
3731 if (cur_num == 0 && errno == EINVAL)
3732 continue;
3735 * Compare current number to current max number,
3736 * take higher of the two.
3738 if (cur_num > num)
3739 num = cur_num;
3743 * Store off a copy of 'num' incase we need it later. If incrementing
3744 * 'num' causes it to roll over, this means 'num' is the largest
3745 * positive int possible; we'll need it later in the loop to determine
3746 * if we've exhausted all possible increment numbers. We store it in
3747 * 'cur_num'.
3749 cur_num = num;
3751 /* Increment 'num' to get new auto BE name number */
3752 if (++num <= 0) {
3753 int ret = 0;
3756 * Since incrementing 'num' caused it to rollover, start
3757 * over at 0 and find the first available number.
3759 for (num = 0; num < cur_num; num++) {
3761 (void) snprintf(cur_be_name, sizeof (cur_be_name),
3762 "%s%c%d", base_be_name, BE_AUTO_NAME_DELIM, num);
3764 ret = zpool_iter(g_zfs, be_exists_callback,
3765 cur_be_name);
3767 if (ret == 0) {
3769 * BE name doesn't exist, break out
3770 * to use 'num'.
3772 break;
3773 } else if (ret == 1) {
3774 /* BE name exists, continue looking */
3775 continue;
3776 } else {
3777 be_print_err(gettext("be_get_auto_name: "
3778 "zpool_iter failed: %s\n"),
3779 libzfs_error_description(g_zfs));
3780 be_free_list(be_nodes);
3781 return (NULL);
3786 * If 'num' equals 'cur_num', we've exhausted all possible
3787 * auto BE names for this base BE name.
3789 if (num == cur_num) {
3790 be_print_err(gettext("be_get_auto_name: "
3791 "No more available auto BE names for base "
3792 "BE name %s\n"), base_be_name);
3793 be_free_list(be_nodes);
3794 return (NULL);
3798 be_free_list(be_nodes);
3801 * Generate string for auto BE name.
3803 (void) snprintf(auto_be_name, sizeof (auto_be_name), "%s%c%d",
3804 base_be_name, BE_AUTO_NAME_DELIM, num);
3806 if ((c = strdup(auto_be_name)) == NULL) {
3807 be_print_err(gettext("be_get_auto_name: "
3808 "memory allocation failed\n"));
3809 return (NULL);
3812 return (c);
3816 * Function: be_get_console_prop
3817 * Description: Determine console device.
3818 * Returns:
3819 * Success - pointer to console setting.
3820 * Failure - NULL
3821 * Scope:
3822 * Private
3824 static char *
3825 be_get_console_prop(void)
3827 di_node_t dn;
3828 char *console = NULL;
3830 if ((dn = di_init("/", DINFOPROP)) == DI_NODE_NIL) {
3831 be_print_err(gettext("be_get_console_prop: "
3832 "di_init() failed\n"));
3833 return (NULL);
3836 if (di_prop_lookup_strings(DDI_DEV_T_ANY, dn,
3837 "console", &console) != -1) {
3838 di_fini(dn);
3839 return (console);
3842 if (console == NULL) {
3843 if (di_prop_lookup_strings(DDI_DEV_T_ANY, dn,
3844 "output-device", &console) != -1) {
3845 di_fini(dn);
3846 if (strncmp(console, "screen", strlen("screen")) == 0)
3847 console = BE_DEFAULT_CONSOLE;
3852 * Default console to text
3854 if (console == NULL) {
3855 console = BE_DEFAULT_CONSOLE;
3858 return (console);
3862 * Function: be_create_menu
3863 * Description:
3864 * This function is used if no menu.lst file exists. In
3865 * this case a new file is created and if needed default
3866 * lines are added to the file.
3867 * Parameters:
3868 * pool - The name of the pool the menu.lst file is on
3869 * menu_file - The name of the file we're creating.
3870 * menu_fp - A pointer to the file pointer of the file we
3871 * created. This is also used to pass back the file
3872 * pointer to the newly created file.
3873 * mode - the original mode used for the failed attempt to
3874 * non-existent file.
3875 * Returns:
3876 * BE_SUCCESS - Success
3877 * be_errno_t - Failure
3878 * Scope:
3879 * Private
3881 static int
3882 be_create_menu(
3883 char *pool,
3884 char *menu_file,
3885 FILE **menu_fp,
3886 char *mode)
3888 be_node_list_t *be_nodes = NULL;
3889 char *menu_path = NULL;
3890 char *be_rpool = NULL;
3891 char *be_name = NULL;
3892 char *console = NULL;
3893 errno = 0;
3895 if (menu_file == NULL || menu_fp == NULL || mode == NULL)
3896 return (BE_ERR_INVAL);
3898 menu_path = strdup(menu_file);
3899 if (menu_path == NULL)
3900 return (BE_ERR_NOMEM);
3902 (void) dirname(menu_path);
3903 if (*menu_path == '.') {
3904 free(menu_path);
3905 return (BE_ERR_BAD_MENU_PATH);
3907 if (mkdirp(menu_path,
3908 S_IRWXU | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH) == -1 &&
3909 errno != EEXIST) {
3910 free(menu_path);
3911 be_print_err(gettext("be_create_menu: Failed to create the %s "
3912 "directory: %s\n"), menu_path, strerror(errno));
3913 return (errno_to_be_err(errno));
3915 free(menu_path);
3918 * Check to see if this system supports grub
3920 if (be_has_grub()) {
3922 * The grub menu is missing so we need to create it
3923 * and fill in the first few lines.
3925 FILE *temp_fp = fopen(menu_file, "a+");
3926 if (temp_fp == NULL) {
3927 *menu_fp = NULL;
3928 return (errno_to_be_err(errno));
3931 if ((console = be_get_console_prop()) != NULL) {
3934 * If console is redirected to serial line,
3935 * GRUB splash screen will not be enabled.
3937 if (strncmp(console, "text", strlen("text")) == 0 ||
3938 strncmp(console, "graphics",
3939 strlen("graphics")) == 0) {
3941 (void) fprintf(temp_fp, "%s\n", BE_GRUB_SPLASH);
3942 (void) fprintf(temp_fp, "%s\n",
3943 BE_GRUB_FOREGROUND);
3944 (void) fprintf(temp_fp, "%s\n",
3945 BE_GRUB_BACKGROUND);
3946 (void) fprintf(temp_fp, "%s\n",
3947 BE_GRUB_DEFAULT);
3948 } else {
3949 be_print_err(gettext("be_create_menu: "
3950 "console on serial line, "
3951 "GRUB splash image will be disabled\n"));
3955 (void) fprintf(temp_fp, "timeout 30\n");
3956 (void) fclose(temp_fp);
3958 } else {
3960 * The menu file doesn't exist so we need to create a
3961 * blank file.
3963 FILE *temp_fp = fopen(menu_file, "w+");
3964 if (temp_fp == NULL) {
3965 *menu_fp = NULL;
3966 return (errno_to_be_err(errno));
3968 (void) fclose(temp_fp);
3972 * Now we need to add all the BE's back into the the file.
3974 if (_be_list(NULL, &be_nodes) == BE_SUCCESS) {
3975 while (be_nodes != NULL) {
3976 if (strcmp(pool, be_nodes->be_rpool) == 0) {
3977 (void) be_append_menu(be_nodes->be_node_name,
3978 be_nodes->be_rpool, NULL, NULL, NULL);
3980 if (be_nodes->be_active_on_boot) {
3981 be_rpool = strdup(be_nodes->be_rpool);
3982 be_name = strdup(be_nodes->be_node_name);
3985 be_nodes = be_nodes->be_next_node;
3988 be_free_list(be_nodes);
3991 * Check to see if this system supports grub
3993 if (be_has_grub()) {
3994 int err = be_change_grub_default(be_name, be_rpool);
3995 if (err != BE_SUCCESS)
3996 return (err);
3998 *menu_fp = fopen(menu_file, mode);
3999 if (*menu_fp == NULL)
4000 return (errno_to_be_err(errno));
4002 return (BE_SUCCESS);
4006 * Function: be_open_menu
4007 * Description:
4008 * This function is used it open the menu.lst file. If this
4009 * file does not exist be_create_menu is called to create it
4010 * and the open file pointer is returned. If the file does
4011 * exist it is simply opened using the mode passed in.
4012 * Parameters:
4013 * pool - The name of the pool the menu.lst file is on
4014 * menu_file - The name of the file we're opening.
4015 * menu_fp - A pointer to the file pointer of the file we're
4016 * opening. This is also used to pass back the file
4017 * pointer.
4018 * mode - the original mode to be used for opening the menu.lst
4019 * file.
4020 * create_menu - If this is true and the menu.lst file does not
4021 * exist we will attempt to re-create it. However
4022 * if it's false the error returned from the fopen
4023 * will be returned.
4024 * Returns:
4025 * BE_SUCCESS - Success
4026 * be_errno_t - Failure
4027 * Scope:
4028 * Private
4030 static int
4031 be_open_menu(
4032 char *pool,
4033 char *menu_file,
4034 FILE **menu_fp,
4035 char *mode,
4036 boolean_t create_menu)
4038 int err = 0;
4039 boolean_t set_print = B_FALSE;
4041 *menu_fp = fopen(menu_file, mode);
4042 err = errno;
4043 if (*menu_fp == NULL) {
4044 if (err == ENOENT && create_menu) {
4045 be_print_err(gettext("be_open_menu: menu.lst "
4046 "file %s does not exist,\n"), menu_file);
4047 if (!do_print) {
4048 set_print = B_TRUE;
4049 do_print = B_TRUE;
4051 be_print_err(gettext("WARNING: menu.lst "
4052 "file %s does not exist,\n generating "
4053 "a new menu.lst file\n"), menu_file);
4054 if (set_print)
4055 do_print = B_FALSE;
4056 err = 0;
4057 if ((err = be_create_menu(pool, menu_file,
4058 menu_fp, mode)) == ENOENT)
4059 return (BE_ERR_NO_MENU);
4060 else if (err != BE_SUCCESS)
4061 return (err);
4062 else if (*menu_fp == NULL)
4063 return (BE_ERR_NO_MENU);
4064 } else {
4065 be_print_err(gettext("be_open_menu: failed "
4066 "to open menu.lst file %s\n"), menu_file);
4067 if (err == ENOENT)
4068 return (BE_ERR_NO_MENU);
4069 else
4070 return (errno_to_be_err(err));
4073 return (BE_SUCCESS);