719 beadm should allow BEs outside of <rpool>/ROOT
[unleashed.git] / usr / src / lib / libbe / common / be_utils.c
blob888a8e2c712df7cc077cf75cf88005dd4535a1af
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.
27 * Copyright 2011 Nexenta Systems, Inc. All rights reserved.
32 * System includes
34 #include <assert.h>
35 #include <errno.h>
36 #include <libgen.h>
37 #include <libintl.h>
38 #include <libnvpair.h>
39 #include <libzfs.h>
40 #include <libgen.h>
41 #include <stdio.h>
42 #include <stdlib.h>
43 #include <string.h>
44 #include <sys/stat.h>
45 #include <sys/types.h>
46 #include <sys/vfstab.h>
47 #include <sys/param.h>
48 #include <sys/systeminfo.h>
49 #include <ctype.h>
50 #include <time.h>
51 #include <unistd.h>
52 #include <fcntl.h>
53 #include <deflt.h>
54 #include <wait.h>
55 #include <libdevinfo.h>
57 #include <libbe.h>
58 #include <libbe_priv.h>
60 /* Private function prototypes */
61 static int update_dataset(char *, int, char *, char *, char *);
62 static int _update_vfstab(char *, char *, char *, char *, be_fs_list_data_t *);
63 static int be_open_menu(char *, char *, FILE **, char *, boolean_t);
64 static int be_create_menu(char *, char *, FILE **, char *);
65 static char *be_get_auto_name(char *, char *, boolean_t);
68 * Global error printing
70 boolean_t do_print = B_FALSE;
73 * Private datatypes
75 typedef struct zone_be_name_cb_data {
76 char *base_be_name;
77 int num;
78 } zone_be_name_cb_data_t;
80 /* ******************************************************************** */
81 /* Public Functions */
82 /* ******************************************************************** */
85 * Function: be_max_avail
86 * Description: Returns the available size for the zfs dataset passed in.
87 * Parameters:
88 * dataset - The dataset we want to get the available space for.
89 * ret - The available size will be returned in this.
90 * Returns:
91 * The error returned by the zfs get property function.
92 * Scope:
93 * Public
95 int
96 be_max_avail(char *dataset, uint64_t *ret)
98 zfs_handle_t *zhp;
99 int err = 0;
101 /* Initialize libzfs handle */
102 if (!be_zfs_init())
103 return (BE_ERR_INIT);
105 zhp = zfs_open(g_zfs, dataset, ZFS_TYPE_DATASET);
106 if (zhp == NULL) {
108 * The zfs_open failed return an error
110 err = zfs_err_to_be_err(g_zfs);
111 } else {
112 err = be_maxsize_avail(zhp, ret);
114 ZFS_CLOSE(zhp);
115 be_zfs_fini();
116 return (err);
120 * Function: libbe_print_errors
121 * Description: Turns on/off error output for the library.
122 * Parameter:
123 * set_do_print - Boolean that turns library error
124 * printing on or off.
125 * Returns:
126 * None
127 * Scope:
128 * Public;
130 void
131 libbe_print_errors(boolean_t set_do_print)
133 do_print = set_do_print;
136 /* ******************************************************************** */
137 /* Semi-Private Functions */
138 /* ******************************************************************** */
141 * Function: be_zfs_init
142 * Description: Initializes the libary global libzfs handle.
143 * Parameters:
144 * None
145 * Returns:
146 * B_TRUE - Success
147 * B_FALSE - Failure
148 * Scope:
149 * Semi-private (library wide use only)
151 boolean_t
152 be_zfs_init(void)
154 be_zfs_fini();
156 if ((g_zfs = libzfs_init()) == NULL) {
157 be_print_err(gettext("be_zfs_init: failed to initialize ZFS "
158 "library\n"));
159 return (B_FALSE);
162 return (B_TRUE);
166 * Function: be_zfs_fini
167 * Description: Closes the library global libzfs handle if it currently open.
168 * Parameter:
169 * None
170 * Returns:
171 * None
172 * Scope:
173 * Semi-private (library wide use only)
175 void
176 be_zfs_fini(void)
178 if (g_zfs)
179 libzfs_fini(g_zfs);
181 g_zfs = NULL;
185 * Function: be_get_defaults
186 * Description: Open defaults and gets be default paramets
187 * Parameters:
188 * defaults - be defaults struct
189 * Returns:
190 * None
191 * Scope:
192 * Semi-private (library wide use only)
194 void
195 be_get_defaults(struct be_defaults *defaults)
197 void *defp;
199 defaults->be_deflt_rpool_container = B_FALSE;
200 defaults->be_deflt_bename_starts_with[0] = '\0';
202 if ((defp = defopen_r(BE_DEFAULTS)) != NULL) {
203 const char *res = defread_r(BE_DFLT_BENAME_STARTS, defp);
204 if (res != NULL && res[0] != NULL) {
205 (void) strlcpy(defaults->be_deflt_bename_starts_with,
206 res, ZFS_MAXNAMELEN);
207 defaults->be_deflt_rpool_container = B_TRUE;
209 defclose_r(defp);
214 * Function: be_make_root_ds
215 * Description: Generate string for BE's root dataset given the pool
216 * it lives in and the BE name.
217 * Parameters:
218 * zpool - pointer zpool name.
219 * be_name - pointer to BE name.
220 * be_root_ds - pointer to buffer to return BE root dataset in.
221 * be_root_ds_size - size of be_root_ds
222 * Returns:
223 * None
224 * Scope:
225 * Semi-private (library wide use only)
227 void
228 be_make_root_ds(const char *zpool, const char *be_name, char *be_root_ds,
229 int be_root_ds_size)
231 struct be_defaults be_defaults;
232 be_get_defaults(&be_defaults);
234 if (be_defaults.be_deflt_rpool_container)
235 (void) snprintf(be_root_ds, be_root_ds_size, "%s/%s", zpool,
236 be_name);
237 else
238 (void) snprintf(be_root_ds, be_root_ds_size, "%s/%s/%s", zpool,
239 BE_CONTAINER_DS_NAME, be_name);
243 * Function: be_make_container_ds
244 * Description: Generate string for the BE container dataset given a pool name.
245 * Parameters:
246 * zpool - pointer zpool name.
247 * container_ds - pointer to buffer to return BE container
248 * dataset in.
249 * container_ds_size - size of container_ds
250 * Returns:
251 * None
252 * Scope:
253 * Semi-private (library wide use only)
255 void
256 be_make_container_ds(const char *zpool, char *container_ds,
257 int container_ds_size)
259 struct be_defaults be_defaults;
260 be_get_defaults(&be_defaults);
262 if (be_defaults.be_deflt_rpool_container)
263 (void) snprintf(container_ds, container_ds_size, "%s", zpool);
264 else
265 (void) snprintf(container_ds, container_ds_size, "%s/%s", zpool,
266 BE_CONTAINER_DS_NAME);
270 * Function: be_make_name_from_ds
271 * Description: This function takes a dataset name and strips off the
272 * BE container dataset portion from the beginning. The
273 * returned name is allocated in heap storage, so the caller
274 * is responsible for freeing it.
275 * Parameters:
276 * dataset - dataset to get name from.
277 * rc_loc - dataset underwhich the root container dataset lives.
278 * Returns:
279 * name of dataset relative to BE container dataset.
280 * NULL if dataset is not under a BE root dataset.
281 * Scope:
282 * Semi-primate (library wide use only)
284 char *
285 be_make_name_from_ds(const char *dataset, char *rc_loc)
287 char ds[ZFS_MAXNAMELEN];
288 char *tok = NULL;
289 char *name = NULL;
290 struct be_defaults be_defaults;
291 int rlen = strlen(rc_loc);
293 be_get_defaults(&be_defaults);
296 * First token is the location of where the root container dataset
297 * lives; it must match rc_loc.
299 if (strncmp(dataset, rc_loc, rlen) == 0 && dataset[rlen] == '/')
300 (void) strlcpy(ds, dataset + rlen + 1, sizeof (ds));
301 else
302 return (NULL);
304 if (be_defaults.be_deflt_rpool_container) {
305 if ((name = strdup(ds)) == NULL) {
306 be_print_err(gettext("be_make_name_from_ds: "
307 "memory allocation failed\n"));
308 return (NULL);
310 } else {
311 /* Second token must be BE container dataset name */
312 if ((tok = strtok(ds, "/")) == NULL ||
313 strcmp(tok, BE_CONTAINER_DS_NAME) != 0)
314 return (NULL);
316 /* Return the remaining token if one exists */
317 if ((tok = strtok(NULL, "")) == NULL)
318 return (NULL);
320 if ((name = strdup(tok)) == NULL) {
321 be_print_err(gettext("be_make_name_from_ds: "
322 "memory allocation failed\n"));
323 return (NULL);
327 return (name);
331 * Function: be_maxsize_avail
332 * Description: Returns the available size for the zfs handle passed in.
333 * Parameters:
334 * zhp - A pointer to the open zfs handle.
335 * ret - The available size will be returned in this.
336 * Returns:
337 * The error returned by the zfs get property function.
338 * Scope:
339 * Semi-private (library wide use only)
342 be_maxsize_avail(zfs_handle_t *zhp, uint64_t *ret)
344 return ((*ret = zfs_prop_get_int(zhp, ZFS_PROP_AVAILABLE)));
348 * Function: be_append_menu
349 * Description: Appends an entry for a BE into the menu.lst.
350 * Parameters:
351 * be_name - pointer to name of BE to add boot menu entry for.
352 * be_root_pool - pointer to name of pool BE lives in.
353 * boot_pool - Used if the pool containing the grub menu is
354 * different than the one contaiing the BE. This
355 * will normally be NULL.
356 * be_orig_root_ds - The root dataset for the BE. This is
357 * used to check to see if an entry already exists
358 * for this BE.
359 * description - pointer to description of BE to be added in
360 * the title line for this BEs entry.
361 * Returns:
362 * BE_SUCCESS - Success
363 * be_errno_t - Failure
364 * Scope:
365 * Semi-private (library wide use only)
368 be_append_menu(char *be_name, char *be_root_pool, char *boot_pool,
369 char *be_orig_root_ds, char *description)
371 zfs_handle_t *zhp = NULL;
372 char menu_file[MAXPATHLEN];
373 char be_root_ds[MAXPATHLEN];
374 char line[BUFSIZ];
375 char temp_line[BUFSIZ];
376 char title[MAXPATHLEN];
377 char *entries[BUFSIZ];
378 char *tmp_entries[BUFSIZ];
379 char *pool_mntpnt = NULL;
380 char *ptmp_mntpnt = NULL;
381 char *orig_mntpnt = NULL;
382 boolean_t found_be = B_FALSE;
383 boolean_t found_orig_be = B_FALSE;
384 boolean_t found_title = B_FALSE;
385 boolean_t pool_mounted = B_FALSE;
386 boolean_t collect_lines = B_FALSE;
387 FILE *menu_fp = NULL;
388 int err = 0, ret = BE_SUCCESS;
389 int i, num_tmp_lines = 0, num_lines = 0;
391 if (be_name == NULL || be_root_pool == NULL)
392 return (BE_ERR_INVAL);
394 if (boot_pool == NULL)
395 boot_pool = be_root_pool;
397 if ((zhp = zfs_open(g_zfs, be_root_pool, ZFS_TYPE_DATASET)) == NULL) {
398 be_print_err(gettext("be_append_menu: failed to open "
399 "pool dataset for %s: %s\n"), be_root_pool,
400 libzfs_error_description(g_zfs));
401 return (zfs_err_to_be_err(g_zfs));
405 * Check to see if the pool's dataset is mounted. If it isn't we'll
406 * attempt to mount it.
408 if ((ret = be_mount_pool(zhp, &ptmp_mntpnt, &orig_mntpnt,
409 &pool_mounted)) != BE_SUCCESS) {
410 be_print_err(gettext("be_append_menu: pool dataset "
411 "(%s) could not be mounted\n"), be_root_pool);
412 ZFS_CLOSE(zhp);
413 return (ret);
417 * Get the mountpoint for the root pool dataset.
419 if (!zfs_is_mounted(zhp, &pool_mntpnt)) {
420 be_print_err(gettext("be_append_menu: pool "
421 "dataset (%s) is not mounted. Can't set "
422 "the default BE in the grub menu.\n"), be_root_pool);
423 ret = BE_ERR_NO_MENU;
424 goto cleanup;
428 * Check to see if this system supports grub
430 if (be_has_grub()) {
431 (void) snprintf(menu_file, sizeof (menu_file),
432 "%s%s", pool_mntpnt, BE_GRUB_MENU);
433 } else {
434 (void) snprintf(menu_file, sizeof (menu_file),
435 "%s%s", pool_mntpnt, BE_SPARC_MENU);
438 be_make_root_ds(be_root_pool, be_name, be_root_ds, sizeof (be_root_ds));
441 * Iterate through menu first to make sure the BE doesn't already
442 * have an entry in the menu.
444 * Additionally while iterating through the menu, if we have an
445 * original root dataset for a BE we're cloning from, we need to keep
446 * track of that BE's menu entry. We will then use the lines from
447 * that entry to create the entry for the new BE.
449 if ((ret = be_open_menu(be_root_pool, menu_file,
450 &menu_fp, "r", B_TRUE)) != BE_SUCCESS) {
451 goto cleanup;
452 } else if (menu_fp == NULL) {
453 ret = BE_ERR_NO_MENU;
454 goto cleanup;
457 free(pool_mntpnt);
458 pool_mntpnt = NULL;
460 while (fgets(line, BUFSIZ, menu_fp)) {
461 char *tok = NULL;
463 (void) strlcpy(temp_line, line, BUFSIZ);
464 tok = strtok(line, BE_WHITE_SPACE);
466 if (tok == NULL || tok[0] == '#') {
467 continue;
468 } else if (strcmp(tok, "title") == 0) {
469 collect_lines = B_FALSE;
470 if ((tok = strtok(NULL, "\n")) == NULL)
471 (void) strlcpy(title, "", sizeof (title));
472 else
473 (void) strlcpy(title, tok, sizeof (title));
474 found_title = B_TRUE;
476 if (num_tmp_lines != 0) {
477 for (i = 0; i < num_tmp_lines; i++) {
478 free(tmp_entries[i]);
479 tmp_entries[i] = NULL;
481 num_tmp_lines = 0;
483 } else if (strcmp(tok, "bootfs") == 0) {
484 char *bootfs = strtok(NULL, BE_WHITE_SPACE);
485 found_title = B_FALSE;
486 if (bootfs == NULL)
487 continue;
489 if (strcmp(bootfs, be_root_ds) == 0) {
490 found_be = B_TRUE;
491 break;
494 if (be_orig_root_ds != NULL &&
495 strcmp(bootfs, be_orig_root_ds) == 0 &&
496 !found_orig_be) {
497 char str[BUFSIZ];
498 found_orig_be = B_TRUE;
499 num_lines = 0;
501 * Store the new title line
503 (void) snprintf(str, BUFSIZ, "title %s\n",
504 description ? description : be_name);
505 entries[num_lines] = strdup(str);
506 num_lines++;
508 * If there are any lines between the title
509 * and the bootfs line store these. Also
510 * free the temporary lines.
512 for (i = 0; i < num_tmp_lines; i++) {
513 entries[num_lines] = tmp_entries[i];
514 tmp_entries[i] = NULL;
515 num_lines++;
517 num_tmp_lines = 0;
519 * Store the new bootfs line.
521 (void) snprintf(str, BUFSIZ, "bootfs %s\n",
522 be_root_ds);
523 entries[num_lines] = strdup(str);
524 num_lines++;
525 collect_lines = B_TRUE;
527 } else if (found_orig_be && collect_lines) {
529 * get the rest of the lines for the original BE and
530 * store them.
532 if (strstr(line, BE_GRUB_COMMENT) != NULL ||
533 strstr(line, "BOOTADM") != NULL)
534 continue;
535 if (strcmp(tok, "splashimage") == 0) {
536 entries[num_lines] =
537 strdup("splashimage "
538 "/boot/splashimage.xpm\n");
539 } else {
540 entries[num_lines] = strdup(temp_line);
542 num_lines++;
543 } else if (found_title && !found_orig_be) {
544 tmp_entries[num_tmp_lines] = strdup(temp_line);
545 num_tmp_lines++;
549 (void) fclose(menu_fp);
551 if (found_be) {
553 * If an entry for this BE was already in the menu, then if
554 * that entry's title matches what we would have put in
555 * return success. Otherwise return failure.
557 char *new_title = description ? description : be_name;
559 if (strcmp(title, new_title) == 0) {
560 ret = BE_SUCCESS;
561 goto cleanup;
562 } else {
563 if (be_remove_menu(be_name, be_root_pool,
564 boot_pool) != BE_SUCCESS) {
565 be_print_err(gettext("be_append_menu: "
566 "Failed to remove existing unusable "
567 "entry '%s' in boot menu.\n"), be_name);
568 ret = BE_ERR_BE_EXISTS;
569 goto cleanup;
574 /* Append BE entry to the end of the file */
575 menu_fp = fopen(menu_file, "a+");
576 err = errno;
577 if (menu_fp == NULL) {
578 be_print_err(gettext("be_append_menu: failed "
579 "to open menu.lst file %s\n"), menu_file);
580 ret = errno_to_be_err(err);
581 goto cleanup;
584 if (found_orig_be) {
586 * write out all the stored lines
588 for (i = 0; i < num_lines; i++) {
589 (void) fprintf(menu_fp, "%s", entries[i]);
590 free(entries[i]);
592 num_lines = 0;
595 * Check to see if this system supports grub
597 if (be_has_grub())
598 (void) fprintf(menu_fp, "%s\n", BE_GRUB_COMMENT);
599 ret = BE_SUCCESS;
600 } else {
601 (void) fprintf(menu_fp, "title %s\n",
602 description ? description : be_name);
603 (void) fprintf(menu_fp, "bootfs %s\n", be_root_ds);
606 * Check to see if this system supports grub
608 if (be_has_grub()) {
609 (void) fprintf(menu_fp, "kernel$ "
610 "/platform/i86pc/kernel/$ISADIR/unix -B "
611 "$ZFS-BOOTFS\n");
612 (void) fprintf(menu_fp, "module$ "
613 "/platform/i86pc/$ISADIR/boot_archive\n");
614 (void) fprintf(menu_fp, "%s\n", BE_GRUB_COMMENT);
616 ret = BE_SUCCESS;
618 (void) fclose(menu_fp);
619 cleanup:
620 if (pool_mounted) {
621 int err = BE_SUCCESS;
622 err = be_unmount_pool(zhp, ptmp_mntpnt, orig_mntpnt);
623 if (ret == BE_SUCCESS)
624 ret = err;
625 free(orig_mntpnt);
626 free(ptmp_mntpnt);
628 ZFS_CLOSE(zhp);
629 if (num_tmp_lines > 0) {
630 for (i = 0; i < num_tmp_lines; i++) {
631 free(tmp_entries[i]);
632 tmp_entries[i] = NULL;
635 if (num_lines > 0) {
636 for (i = 0; i < num_lines; i++) {
637 free(entries[i]);
638 entries[i] = NULL;
641 return (ret);
645 * Function: be_remove_menu
646 * Description: Removes a BE's entry from a menu.lst file.
647 * Parameters:
648 * be_name - the name of BE whose entry is to be removed from
649 * the menu.lst file.
650 * be_root_pool - the pool that be_name lives in.
651 * boot_pool - the pool where the BE is, if different than
652 * the pool containing the boot menu. If this is
653 * NULL it will be set to be_root_pool.
654 * Returns:
655 * BE_SUCCESS - Success
656 * be_errno_t - Failure
657 * Scope:
658 * Semi-private (library wide use only)
661 be_remove_menu(char *be_name, char *be_root_pool, char *boot_pool)
663 zfs_handle_t *zhp = NULL;
664 char be_root_ds[MAXPATHLEN];
665 char **buffer = NULL;
666 char menu_buf[BUFSIZ];
667 char menu[MAXPATHLEN];
668 char *pool_mntpnt = NULL;
669 char *ptmp_mntpnt = NULL;
670 char *orig_mntpnt = NULL;
671 char *tmp_menu = NULL;
672 FILE *menu_fp = NULL;
673 FILE *tmp_menu_fp = NULL;
674 struct stat sb;
675 int ret = BE_SUCCESS;
676 int i;
677 int fd;
678 int err = 0;
679 int nlines = 0;
680 int default_entry = 0;
681 int entry_cnt = 0;
682 int entry_del = 0;
683 int num_entry_del = 0;
684 int tmp_menu_len = 0;
685 boolean_t write = B_TRUE;
686 boolean_t do_buffer = B_FALSE;
687 boolean_t pool_mounted = B_FALSE;
689 if (boot_pool == NULL)
690 boot_pool = be_root_pool;
692 /* Get name of BE's root dataset */
693 be_make_root_ds(be_root_pool, be_name, be_root_ds, sizeof (be_root_ds));
695 /* Get handle to pool dataset */
696 if ((zhp = zfs_open(g_zfs, be_root_pool, ZFS_TYPE_DATASET)) == NULL) {
697 be_print_err(gettext("be_remove_menu: "
698 "failed to open pool dataset for %s: %s"),
699 be_root_pool, libzfs_error_description(g_zfs));
700 return (zfs_err_to_be_err(g_zfs));
704 * Check to see if the pool's dataset is mounted. If it isn't we'll
705 * attempt to mount it.
707 if ((ret = be_mount_pool(zhp, &ptmp_mntpnt, &orig_mntpnt,
708 &pool_mounted)) != BE_SUCCESS) {
709 be_print_err(gettext("be_remove_menu: pool dataset "
710 "(%s) could not be mounted\n"), be_root_pool);
711 ZFS_CLOSE(zhp);
712 return (ret);
716 * Get the mountpoint for the root pool dataset.
718 if (!zfs_is_mounted(zhp, &pool_mntpnt)) {
719 be_print_err(gettext("be_remove_menu: pool "
720 "dataset (%s) is not mounted. Can't set "
721 "the default BE in the grub menu.\n"), be_root_pool);
722 ret = BE_ERR_NO_MENU;
723 goto cleanup;
726 /* Get path to boot menu */
727 (void) strlcpy(menu, pool_mntpnt, sizeof (menu));
730 * Check to see if this system supports grub
732 if (be_has_grub())
733 (void) strlcat(menu, BE_GRUB_MENU, sizeof (menu));
734 else
735 (void) strlcat(menu, BE_SPARC_MENU, sizeof (menu));
737 /* Get handle to boot menu file */
738 if ((ret = be_open_menu(be_root_pool, menu, &menu_fp, "r",
739 B_TRUE)) != BE_SUCCESS) {
740 goto cleanup;
741 } else if (menu_fp == NULL) {
742 ret = BE_ERR_NO_MENU;
743 goto cleanup;
746 free(pool_mntpnt);
747 pool_mntpnt = NULL;
749 /* Grab the stats of the original menu file */
750 if (stat(menu, &sb) != 0) {
751 err = errno;
752 be_print_err(gettext("be_remove_menu: "
753 "failed to stat file %s: %s\n"), menu, strerror(err));
754 ret = errno_to_be_err(err);
755 goto cleanup;
758 /* Create a tmp file for the modified menu.lst */
759 tmp_menu_len = strlen(menu) + 7;
760 if ((tmp_menu = (char *)malloc(tmp_menu_len)) == NULL) {
761 be_print_err(gettext("be_remove_menu: malloc failed\n"));
762 ret = BE_ERR_NOMEM;
763 goto cleanup;
765 (void) memset(tmp_menu, 0, tmp_menu_len);
766 (void) strlcpy(tmp_menu, menu, tmp_menu_len);
767 (void) strlcat(tmp_menu, "XXXXXX", tmp_menu_len);
768 if ((fd = mkstemp(tmp_menu)) == -1) {
769 err = errno;
770 be_print_err(gettext("be_remove_menu: mkstemp failed\n"));
771 ret = errno_to_be_err(err);
772 free(tmp_menu);
773 tmp_menu = NULL;
774 goto cleanup;
776 if ((tmp_menu_fp = fdopen(fd, "w")) == NULL) {
777 err = errno;
778 be_print_err(gettext("be_remove_menu: "
779 "could not open tmp file for write: %s\n"), strerror(err));
780 (void) close(fd);
781 ret = errno_to_be_err(err);
782 goto cleanup;
785 while (fgets(menu_buf, BUFSIZ, menu_fp)) {
786 char tline [BUFSIZ];
787 char *tok = NULL;
789 (void) strlcpy(tline, menu_buf, sizeof (tline));
791 /* Tokenize line */
792 tok = strtok(tline, BE_WHITE_SPACE);
794 if (tok == NULL || tok[0] == '#') {
795 /* Found empty line or comment line */
796 if (do_buffer) {
797 /* Buffer this line */
798 if ((buffer = (char **)realloc(buffer,
799 sizeof (char *)*(nlines + 1))) == NULL) {
800 ret = BE_ERR_NOMEM;
801 goto cleanup;
803 if ((buffer[nlines++] = strdup(menu_buf))
804 == NULL) {
805 ret = BE_ERR_NOMEM;
806 goto cleanup;
809 } else if (write || strncmp(menu_buf, BE_GRUB_COMMENT,
810 strlen(BE_GRUB_COMMENT)) != 0) {
811 /* Write this line out */
812 (void) fputs(menu_buf, tmp_menu_fp);
814 } else if (strcmp(tok, "default") == 0) {
816 * Record what 'default' is set to because we might
817 * need to adjust this upon deleting an entry.
819 tok = strtok(NULL, BE_WHITE_SPACE);
821 if (tok != NULL) {
822 default_entry = atoi(tok);
825 (void) fputs(menu_buf, tmp_menu_fp);
826 } else if (strcmp(tok, "title") == 0) {
828 * If we've reached a 'title' line and do_buffer is
829 * is true, that means we've just buffered an entire
830 * entry without finding a 'bootfs' directive. We
831 * need to write that entry out and keep searching.
833 if (do_buffer) {
834 for (i = 0; i < nlines; i++) {
835 (void) fputs(buffer[i], tmp_menu_fp);
836 free(buffer[i]);
838 free(buffer);
839 buffer = NULL;
840 nlines = 0;
844 * Turn writing off and buffering on, and increment
845 * our entry counter.
847 write = B_FALSE;
848 do_buffer = B_TRUE;
849 entry_cnt++;
851 /* Buffer this 'title' line */
852 if ((buffer = (char **)realloc(buffer,
853 sizeof (char *)*(nlines + 1))) == NULL) {
854 ret = BE_ERR_NOMEM;
855 goto cleanup;
857 if ((buffer[nlines++] = strdup(menu_buf)) == NULL) {
858 ret = BE_ERR_NOMEM;
859 goto cleanup;
862 } else if (strcmp(tok, "bootfs") == 0) {
863 char *bootfs = NULL;
866 * Found a 'bootfs' line. See if it matches the
867 * BE we're looking for.
869 if ((bootfs = strtok(NULL, BE_WHITE_SPACE)) == NULL ||
870 strcmp(bootfs, be_root_ds) != 0) {
872 * Either there's nothing after the 'bootfs'
873 * or this is not the BE we're looking for,
874 * write out the line(s) we've buffered since
875 * finding the title.
877 for (i = 0; i < nlines; i++) {
878 (void) fputs(buffer[i], tmp_menu_fp);
879 free(buffer[i]);
881 free(buffer);
882 buffer = NULL;
883 nlines = 0;
886 * Turn writing back on, and turn off buffering
887 * since this isn't the entry we're looking
888 * for.
890 write = B_TRUE;
891 do_buffer = B_FALSE;
893 /* Write this 'bootfs' line out. */
894 (void) fputs(menu_buf, tmp_menu_fp);
895 } else {
897 * Found the entry we're looking for.
898 * Record its entry number, increment the
899 * number of entries we've deleted, and turn
900 * writing off. Also, throw away the lines
901 * we've buffered for this entry so far, we
902 * don't need them.
904 entry_del = entry_cnt - 1;
905 num_entry_del++;
906 write = B_FALSE;
907 do_buffer = B_FALSE;
909 for (i = 0; i < nlines; i++) {
910 free(buffer[i]);
912 free(buffer);
913 buffer = NULL;
914 nlines = 0;
916 } else {
917 if (do_buffer) {
918 /* Buffer this line */
919 if ((buffer = (char **)realloc(buffer,
920 sizeof (char *)*(nlines + 1))) == NULL) {
921 ret = BE_ERR_NOMEM;
922 goto cleanup;
924 if ((buffer[nlines++] = strdup(menu_buf))
925 == NULL) {
926 ret = BE_ERR_NOMEM;
927 goto cleanup;
929 } else if (write) {
930 /* Write this line out */
931 (void) fputs(menu_buf, tmp_menu_fp);
936 (void) fclose(menu_fp);
937 menu_fp = NULL;
938 (void) fclose(tmp_menu_fp);
939 tmp_menu_fp = NULL;
941 /* Copy the modified menu.lst into place */
942 if (rename(tmp_menu, menu) != 0) {
943 err = errno;
944 be_print_err(gettext("be_remove_menu: "
945 "failed to rename file %s to %s: %s\n"),
946 tmp_menu, menu, strerror(err));
947 ret = errno_to_be_err(err);
948 goto cleanup;
950 free(tmp_menu);
951 tmp_menu = NULL;
954 * If we've removed an entry, see if we need to
955 * adjust the default value in the menu.lst. If the
956 * entry we've deleted comes before the default entry
957 * we need to adjust the default value accordingly.
959 * be_has_grub is used here to check to see if this system
960 * supports grub.
962 if (be_has_grub() && num_entry_del > 0) {
963 if (entry_del <= default_entry) {
964 default_entry = default_entry - num_entry_del;
965 if (default_entry < 0)
966 default_entry = 0;
969 * Adjust the default value by rewriting the
970 * menu.lst file. This may be overkill, but to
971 * preserve the location of the 'default' entry
972 * in the file, we need to do this.
975 /* Get handle to boot menu file */
976 if ((menu_fp = fopen(menu, "r")) == NULL) {
977 err = errno;
978 be_print_err(gettext("be_remove_menu: "
979 "failed to open menu.lst (%s): %s\n"),
980 menu, strerror(err));
981 ret = errno_to_be_err(err);
982 goto cleanup;
985 /* Create a tmp file for the modified menu.lst */
986 tmp_menu_len = strlen(menu) + 7;
987 if ((tmp_menu = (char *)malloc(tmp_menu_len))
988 == NULL) {
989 be_print_err(gettext("be_remove_menu: "
990 "malloc failed\n"));
991 ret = BE_ERR_NOMEM;
992 goto cleanup;
994 (void) memset(tmp_menu, 0, tmp_menu_len);
995 (void) strlcpy(tmp_menu, menu, tmp_menu_len);
996 (void) strlcat(tmp_menu, "XXXXXX", tmp_menu_len);
997 if ((fd = mkstemp(tmp_menu)) == -1) {
998 err = errno;
999 be_print_err(gettext("be_remove_menu: "
1000 "mkstemp failed: %s\n"), strerror(err));
1001 ret = errno_to_be_err(err);
1002 free(tmp_menu);
1003 tmp_menu = NULL;
1004 goto cleanup;
1006 if ((tmp_menu_fp = fdopen(fd, "w")) == NULL) {
1007 err = errno;
1008 be_print_err(gettext("be_remove_menu: "
1009 "could not open tmp file for write: %s\n"),
1010 strerror(err));
1011 (void) close(fd);
1012 ret = errno_to_be_err(err);
1013 goto cleanup;
1016 while (fgets(menu_buf, BUFSIZ, menu_fp)) {
1017 char tline [BUFSIZ];
1018 char *tok = NULL;
1020 (void) strlcpy(tline, menu_buf, sizeof (tline));
1022 /* Tokenize line */
1023 tok = strtok(tline, BE_WHITE_SPACE);
1025 if (tok == NULL) {
1026 /* Found empty line, write it out */
1027 (void) fputs(menu_buf, tmp_menu_fp);
1028 } else if (strcmp(tok, "default") == 0) {
1029 /* Found the default line, adjust it */
1030 (void) snprintf(tline, sizeof (tline),
1031 "default %d\n", default_entry);
1033 (void) fputs(tline, tmp_menu_fp);
1034 } else {
1035 /* Pass through all other lines */
1036 (void) fputs(menu_buf, tmp_menu_fp);
1040 (void) fclose(menu_fp);
1041 menu_fp = NULL;
1042 (void) fclose(tmp_menu_fp);
1043 tmp_menu_fp = NULL;
1045 /* Copy the modified menu.lst into place */
1046 if (rename(tmp_menu, menu) != 0) {
1047 err = errno;
1048 be_print_err(gettext("be_remove_menu: "
1049 "failed to rename file %s to %s: %s\n"),
1050 tmp_menu, menu, strerror(err));
1051 ret = errno_to_be_err(err);
1052 goto cleanup;
1055 free(tmp_menu);
1056 tmp_menu = NULL;
1060 /* Set the perms and ownership of the updated file */
1061 if (chmod(menu, sb.st_mode) != 0) {
1062 err = errno;
1063 be_print_err(gettext("be_remove_menu: "
1064 "failed to chmod %s: %s\n"), menu, strerror(err));
1065 ret = errno_to_be_err(err);
1066 goto cleanup;
1068 if (chown(menu, sb.st_uid, sb.st_gid) != 0) {
1069 err = errno;
1070 be_print_err(gettext("be_remove_menu: "
1071 "failed to chown %s: %s\n"), menu, strerror(err));
1072 ret = errno_to_be_err(err);
1073 goto cleanup;
1076 cleanup:
1077 if (pool_mounted) {
1078 int err = BE_SUCCESS;
1079 err = be_unmount_pool(zhp, ptmp_mntpnt, orig_mntpnt);
1080 if (ret == BE_SUCCESS)
1081 ret = err;
1082 free(orig_mntpnt);
1083 free(ptmp_mntpnt);
1085 ZFS_CLOSE(zhp);
1087 free(buffer);
1088 if (menu_fp != NULL)
1089 (void) fclose(menu_fp);
1090 if (tmp_menu_fp != NULL)
1091 (void) fclose(tmp_menu_fp);
1092 if (tmp_menu != NULL) {
1093 (void) unlink(tmp_menu);
1094 free(tmp_menu);
1097 return (ret);
1101 * Function: be_default_grub_bootfs
1102 * Description: This function returns the dataset in the default entry of
1103 * the grub menu. If no default entry is found with a valid bootfs
1104 * entry NULL is returned.
1105 * Parameters:
1106 * be_root_pool - This is the name of the root pool where the
1107 * grub menu can be found.
1108 * def_bootfs - This is used to pass back the bootfs string. On
1109 * error NULL is returned here.
1110 * Returns:
1111 * Success - BE_SUCCESS is returned.
1112 * Failure - a be_errno_t is returned.
1113 * Scope:
1114 * Semi-private (library wide use only)
1117 be_default_grub_bootfs(const char *be_root_pool, char **def_bootfs)
1119 zfs_handle_t *zhp = NULL;
1120 char grub_file[MAXPATHLEN];
1121 FILE *menu_fp;
1122 char line[BUFSIZ];
1123 char *pool_mntpnt = NULL;
1124 char *ptmp_mntpnt = NULL;
1125 char *orig_mntpnt = NULL;
1126 int default_entry = 0, entries = 0;
1127 int found_default = 0;
1128 int ret = BE_SUCCESS;
1129 boolean_t pool_mounted = B_FALSE;
1131 errno = 0;
1134 * Check to see if this system supports grub
1136 if (!be_has_grub()) {
1137 be_print_err(gettext("be_default_grub_bootfs: operation "
1138 "not supported on this architecture\n"));
1139 return (BE_ERR_NOTSUP);
1142 *def_bootfs = NULL;
1144 /* Get handle to pool dataset */
1145 if ((zhp = zfs_open(g_zfs, be_root_pool, ZFS_TYPE_DATASET)) == NULL) {
1146 be_print_err(gettext("be_default_grub_bootfs: "
1147 "failed to open pool dataset for %s: %s"),
1148 be_root_pool, libzfs_error_description(g_zfs));
1149 return (zfs_err_to_be_err(g_zfs));
1153 * Check to see if the pool's dataset is mounted. If it isn't we'll
1154 * attempt to mount it.
1156 if ((ret = be_mount_pool(zhp, &ptmp_mntpnt, &orig_mntpnt,
1157 &pool_mounted)) != BE_SUCCESS) {
1158 be_print_err(gettext("be_default_grub_bootfs: pool dataset "
1159 "(%s) could not be mounted\n"), be_root_pool);
1160 ZFS_CLOSE(zhp);
1161 return (ret);
1165 * Get the mountpoint for the root pool dataset.
1167 if (!zfs_is_mounted(zhp, &pool_mntpnt)) {
1168 be_print_err(gettext("be_default_grub_bootfs: failed "
1169 "to get mount point for the root pool. Can't set "
1170 "the default BE in the grub menu.\n"));
1171 ret = BE_ERR_NO_MENU;
1172 goto cleanup;
1175 (void) snprintf(grub_file, MAXPATHLEN, "%s%s",
1176 pool_mntpnt, BE_GRUB_MENU);
1178 if ((ret = be_open_menu((char *)be_root_pool, grub_file,
1179 &menu_fp, "r", B_FALSE)) != BE_SUCCESS) {
1180 goto cleanup;
1181 } else if (menu_fp == NULL) {
1182 ret = BE_ERR_NO_MENU;
1183 goto cleanup;
1186 free(pool_mntpnt);
1187 pool_mntpnt = NULL;
1189 while (fgets(line, BUFSIZ, menu_fp)) {
1190 char *tok = strtok(line, BE_WHITE_SPACE);
1192 if (tok != NULL && tok[0] != '#') {
1193 if (!found_default) {
1194 if (strcmp(tok, "default") == 0) {
1195 tok = strtok(NULL, BE_WHITE_SPACE);
1196 if (tok != NULL) {
1197 default_entry = atoi(tok);
1198 rewind(menu_fp);
1199 found_default = 1;
1202 continue;
1204 if (strcmp(tok, "title") == 0) {
1205 entries++;
1206 } else if (default_entry == entries - 1) {
1207 if (strcmp(tok, "bootfs") == 0) {
1208 tok = strtok(NULL, BE_WHITE_SPACE);
1209 (void) fclose(menu_fp);
1211 if (tok == NULL) {
1212 ret = BE_SUCCESS;
1213 goto cleanup;
1216 if ((*def_bootfs = strdup(tok)) !=
1217 NULL) {
1218 ret = BE_SUCCESS;
1219 goto cleanup;
1221 be_print_err(gettext(
1222 "be_default_grub_bootfs: "
1223 "memory allocation failed\n"));
1224 ret = BE_ERR_NOMEM;
1225 goto cleanup;
1227 } else if (default_entry < entries - 1) {
1229 * no bootfs entry for the default entry.
1231 break;
1235 (void) fclose(menu_fp);
1237 cleanup:
1238 if (pool_mounted) {
1239 int err = BE_SUCCESS;
1240 err = be_unmount_pool(zhp, ptmp_mntpnt, orig_mntpnt);
1241 if (ret == BE_SUCCESS)
1242 ret = err;
1243 free(orig_mntpnt);
1244 free(ptmp_mntpnt);
1246 ZFS_CLOSE(zhp);
1247 return (ret);
1251 * Function: be_change_grub_default
1252 * Description: This function takes two parameters. These are the name of
1253 * the BE we want to have as the default booted in the grub
1254 * menu and the root pool where the path to the grub menu exists.
1255 * The code takes this and finds the BE's entry in the grub menu
1256 * and changes the default entry to point to that entry in the
1257 * list.
1258 * Parameters:
1259 * be_name - This is the name of the BE wanted as the default
1260 * for the next boot.
1261 * be_root_pool - This is the name of the root pool where the
1262 * grub menu can be found.
1263 * Returns:
1264 * BE_SUCCESS - Success
1265 * be_errno_t - Failure
1266 * Scope:
1267 * Semi-private (library wide use only)
1270 be_change_grub_default(char *be_name, char *be_root_pool)
1272 zfs_handle_t *zhp = NULL;
1273 char grub_file[MAXPATHLEN];
1274 char *temp_grub;
1275 char *pool_mntpnt = NULL;
1276 char *ptmp_mntpnt = NULL;
1277 char *orig_mntpnt = NULL;
1278 char line[BUFSIZ];
1279 char temp_line[BUFSIZ];
1280 char be_root_ds[MAXPATHLEN];
1281 FILE *grub_fp = NULL;
1282 FILE *temp_fp = NULL;
1283 struct stat sb;
1284 int temp_grub_len = 0;
1285 int fd, entries = 0;
1286 int err = 0;
1287 int ret = BE_SUCCESS;
1288 boolean_t found_default = B_FALSE;
1289 boolean_t pool_mounted = B_FALSE;
1291 errno = 0;
1294 * Check to see if this system supports grub
1296 if (!be_has_grub()) {
1297 be_print_err(gettext("be_change_grub_default: operation "
1298 "not supported on this architecture\n"));
1299 return (BE_ERR_NOTSUP);
1302 /* Generate string for BE's root dataset */
1303 be_make_root_ds(be_root_pool, be_name, be_root_ds, sizeof (be_root_ds));
1305 /* Get handle to pool dataset */
1306 if ((zhp = zfs_open(g_zfs, be_root_pool, ZFS_TYPE_DATASET)) == NULL) {
1307 be_print_err(gettext("be_change_grub_default: "
1308 "failed to open pool dataset for %s: %s"),
1309 be_root_pool, libzfs_error_description(g_zfs));
1310 return (zfs_err_to_be_err(g_zfs));
1314 * Check to see if the pool's dataset is mounted. If it isn't we'll
1315 * attempt to mount it.
1317 if ((ret = be_mount_pool(zhp, &ptmp_mntpnt, &orig_mntpnt,
1318 &pool_mounted)) != BE_SUCCESS) {
1319 be_print_err(gettext("be_change_grub_default: pool dataset "
1320 "(%s) could not be mounted\n"), be_root_pool);
1321 ZFS_CLOSE(zhp);
1322 return (ret);
1326 * Get the mountpoint for the root pool dataset.
1328 if (!zfs_is_mounted(zhp, &pool_mntpnt)) {
1329 be_print_err(gettext("be_change_grub_default: pool "
1330 "dataset (%s) is not mounted. Can't set "
1331 "the default BE in the grub menu.\n"), be_root_pool);
1332 ret = BE_ERR_NO_MENU;
1333 goto cleanup;
1336 (void) snprintf(grub_file, MAXPATHLEN, "%s%s",
1337 pool_mntpnt, BE_GRUB_MENU);
1339 if ((ret = be_open_menu(be_root_pool, grub_file,
1340 &grub_fp, "r+", B_TRUE)) != BE_SUCCESS) {
1341 goto cleanup;
1342 } else if (grub_fp == NULL) {
1343 ret = BE_ERR_NO_MENU;
1344 goto cleanup;
1347 free(pool_mntpnt);
1348 pool_mntpnt = NULL;
1350 /* Grab the stats of the original menu file */
1351 if (stat(grub_file, &sb) != 0) {
1352 err = errno;
1353 be_print_err(gettext("be_change_grub_default: "
1354 "failed to stat file %s: %s\n"), grub_file, strerror(err));
1355 ret = errno_to_be_err(err);
1356 goto cleanup;
1359 /* Create a tmp file for the modified menu.lst */
1360 temp_grub_len = strlen(grub_file) + 7;
1361 if ((temp_grub = (char *)malloc(temp_grub_len)) == NULL) {
1362 be_print_err(gettext("be_change_grub_default: "
1363 "malloc failed\n"));
1364 ret = BE_ERR_NOMEM;
1365 goto cleanup;
1367 (void) memset(temp_grub, 0, temp_grub_len);
1368 (void) strlcpy(temp_grub, grub_file, temp_grub_len);
1369 (void) strlcat(temp_grub, "XXXXXX", temp_grub_len);
1370 if ((fd = mkstemp(temp_grub)) == -1) {
1371 err = errno;
1372 be_print_err(gettext("be_change_grub_default: "
1373 "mkstemp failed: %s\n"), strerror(err));
1374 ret = errno_to_be_err(err);
1375 free(temp_grub);
1376 temp_grub = NULL;
1377 goto cleanup;
1379 if ((temp_fp = fdopen(fd, "w")) == NULL) {
1380 err = errno;
1381 be_print_err(gettext("be_change_grub_default: "
1382 "failed to open %s file: %s\n"),
1383 temp_grub, strerror(err));
1384 (void) close(fd);
1385 ret = errno_to_be_err(err);
1386 goto cleanup;
1389 while (fgets(line, BUFSIZ, grub_fp)) {
1390 char *tok = strtok(line, BE_WHITE_SPACE);
1392 if (tok == NULL || tok[0] == '#') {
1393 continue;
1394 } else if (strcmp(tok, "title") == 0) {
1395 entries++;
1396 continue;
1397 } else if (strcmp(tok, "bootfs") == 0) {
1398 char *bootfs = strtok(NULL, BE_WHITE_SPACE);
1399 if (bootfs == NULL)
1400 continue;
1402 if (strcmp(bootfs, be_root_ds) == 0) {
1403 found_default = B_TRUE;
1404 break;
1409 if (!found_default) {
1410 be_print_err(gettext("be_change_grub_default: failed "
1411 "to find entry for %s in the grub menu\n"),
1412 be_name);
1413 ret = BE_ERR_BE_NOENT;
1414 goto cleanup;
1417 rewind(grub_fp);
1419 while (fgets(line, BUFSIZ, grub_fp)) {
1420 char *tok = NULL;
1422 (void) strncpy(temp_line, line, BUFSIZ);
1424 if ((tok = strtok(temp_line, BE_WHITE_SPACE)) != NULL &&
1425 strcmp(tok, "default") == 0) {
1426 (void) snprintf(temp_line, BUFSIZ, "default %d\n",
1427 entries - 1 >= 0 ? entries - 1 : 0);
1428 (void) fputs(temp_line, temp_fp);
1429 } else {
1430 (void) fputs(line, temp_fp);
1434 (void) fclose(grub_fp);
1435 grub_fp = NULL;
1436 (void) fclose(temp_fp);
1437 temp_fp = NULL;
1439 if (rename(temp_grub, grub_file) != 0) {
1440 err = errno;
1441 be_print_err(gettext("be_change_grub_default: "
1442 "failed to rename file %s to %s: %s\n"),
1443 temp_grub, grub_file, strerror(err));
1444 ret = errno_to_be_err(err);
1445 goto cleanup;
1447 free(temp_grub);
1448 temp_grub = NULL;
1450 /* Set the perms and ownership of the updated file */
1451 if (chmod(grub_file, sb.st_mode) != 0) {
1452 err = errno;
1453 be_print_err(gettext("be_change_grub_default: "
1454 "failed to chmod %s: %s\n"), grub_file, strerror(err));
1455 ret = errno_to_be_err(err);
1456 goto cleanup;
1458 if (chown(grub_file, sb.st_uid, sb.st_gid) != 0) {
1459 err = errno;
1460 be_print_err(gettext("be_change_grub_default: "
1461 "failed to chown %s: %s\n"), grub_file, strerror(err));
1462 ret = errno_to_be_err(err);
1465 cleanup:
1466 if (pool_mounted) {
1467 int err = BE_SUCCESS;
1468 err = be_unmount_pool(zhp, ptmp_mntpnt, orig_mntpnt);
1469 if (ret == BE_SUCCESS)
1470 ret = err;
1471 free(orig_mntpnt);
1472 free(ptmp_mntpnt);
1474 ZFS_CLOSE(zhp);
1475 if (grub_fp != NULL)
1476 (void) fclose(grub_fp);
1477 if (temp_fp != NULL)
1478 (void) fclose(temp_fp);
1479 if (temp_grub != NULL) {
1480 (void) unlink(temp_grub);
1481 free(temp_grub);
1484 return (ret);
1488 * Function: be_update_menu
1489 * Description: This function is used by be_rename to change the BE name in
1490 * an existing entry in the grub menu to the new name of the BE.
1491 * Parameters:
1492 * be_orig_name - the original name of the BE
1493 * be_new_name - the new name the BE is being renameed to.
1494 * be_root_pool - The pool which contains the grub menu
1495 * boot_pool - the pool where the BE is, if different than
1496 * the pool containing the boot menu. If this is
1497 * NULL it will be set to be_root_pool.
1498 * Returns:
1499 * BE_SUCCESS - Success
1500 * be_errno_t - Failure
1501 * Scope:
1502 * Semi-private (library wide use only)
1505 be_update_menu(char *be_orig_name, char *be_new_name, char *be_root_pool,
1506 char *boot_pool)
1508 zfs_handle_t *zhp = NULL;
1509 char menu_file[MAXPATHLEN];
1510 char be_root_ds[MAXPATHLEN];
1511 char be_new_root_ds[MAXPATHLEN];
1512 char line[BUFSIZ];
1513 char *pool_mntpnt = NULL;
1514 char *ptmp_mntpnt = NULL;
1515 char *orig_mntpnt = NULL;
1516 char *temp_menu = NULL;
1517 FILE *menu_fp = NULL;
1518 FILE *new_fp = NULL;
1519 struct stat sb;
1520 int temp_menu_len = 0;
1521 int tmp_fd;
1522 int ret = BE_SUCCESS;
1523 int err = 0;
1524 boolean_t pool_mounted = B_FALSE;
1526 errno = 0;
1528 if (boot_pool == NULL)
1529 boot_pool = be_root_pool;
1531 if ((zhp = zfs_open(g_zfs, be_root_pool, ZFS_TYPE_DATASET)) == NULL) {
1532 be_print_err(gettext("be_update_menu: failed to open "
1533 "pool dataset for %s: %s\n"), be_root_pool,
1534 libzfs_error_description(g_zfs));
1535 return (zfs_err_to_be_err(g_zfs));
1539 * Check to see if the pool's dataset is mounted. If it isn't we'll
1540 * attempt to mount it.
1542 if ((ret = be_mount_pool(zhp, &ptmp_mntpnt, &orig_mntpnt,
1543 &pool_mounted)) != BE_SUCCESS) {
1544 be_print_err(gettext("be_update_menu: pool dataset "
1545 "(%s) could not be mounted\n"), be_root_pool);
1546 ZFS_CLOSE(zhp);
1547 return (ret);
1551 * Get the mountpoint for the root pool dataset.
1553 if (!zfs_is_mounted(zhp, &pool_mntpnt)) {
1554 be_print_err(gettext("be_update_menu: failed "
1555 "to get mount point for the root pool. Can't set "
1556 "the default BE in the grub menu.\n"));
1557 ret = BE_ERR_NO_MENU;
1558 goto cleanup;
1562 * Check to see if this system supports grub
1564 if (be_has_grub()) {
1565 (void) snprintf(menu_file, sizeof (menu_file),
1566 "%s%s", pool_mntpnt, BE_GRUB_MENU);
1567 } else {
1568 (void) snprintf(menu_file, sizeof (menu_file),
1569 "%s%s", pool_mntpnt, BE_SPARC_MENU);
1572 be_make_root_ds(be_root_pool, be_orig_name, be_root_ds,
1573 sizeof (be_root_ds));
1574 be_make_root_ds(be_root_pool, be_new_name, be_new_root_ds,
1575 sizeof (be_new_root_ds));
1577 if ((ret = be_open_menu(be_root_pool, menu_file,
1578 &menu_fp, "r", B_TRUE)) != BE_SUCCESS) {
1579 goto cleanup;
1580 } else if (menu_fp == NULL) {
1581 ret = BE_ERR_NO_MENU;
1582 goto cleanup;
1585 free(pool_mntpnt);
1586 pool_mntpnt = NULL;
1588 /* Grab the stat of the original menu file */
1589 if (stat(menu_file, &sb) != 0) {
1590 err = errno;
1591 be_print_err(gettext("be_update_menu: "
1592 "failed to stat file %s: %s\n"), menu_file, strerror(err));
1593 (void) fclose(menu_fp);
1594 ret = errno_to_be_err(err);
1595 goto cleanup;
1598 /* Create tmp file for modified menu.lst */
1599 temp_menu_len = strlen(menu_file) + 7;
1600 if ((temp_menu = (char *)malloc(temp_menu_len))
1601 == NULL) {
1602 be_print_err(gettext("be_update_menu: "
1603 "malloc failed\n"));
1604 (void) fclose(menu_fp);
1605 ret = BE_ERR_NOMEM;
1606 goto cleanup;
1608 (void) memset(temp_menu, 0, temp_menu_len);
1609 (void) strlcpy(temp_menu, menu_file, temp_menu_len);
1610 (void) strlcat(temp_menu, "XXXXXX", temp_menu_len);
1611 if ((tmp_fd = mkstemp(temp_menu)) == -1) {
1612 err = errno;
1613 be_print_err(gettext("be_update_menu: "
1614 "mkstemp failed: %s\n"), strerror(err));
1615 (void) fclose(menu_fp);
1616 free(temp_menu);
1617 ret = errno_to_be_err(err);
1618 goto cleanup;
1620 if ((new_fp = fdopen(tmp_fd, "w")) == NULL) {
1621 err = errno;
1622 be_print_err(gettext("be_update_menu: "
1623 "fdopen failed: %s\n"), strerror(err));
1624 (void) close(tmp_fd);
1625 (void) fclose(menu_fp);
1626 free(temp_menu);
1627 ret = errno_to_be_err(err);
1628 goto cleanup;
1631 while (fgets(line, BUFSIZ, menu_fp)) {
1632 char tline[BUFSIZ];
1633 char new_line[BUFSIZ];
1634 char *c = NULL;
1636 (void) strlcpy(tline, line, sizeof (tline));
1638 /* Tokenize line */
1639 c = strtok(tline, BE_WHITE_SPACE);
1641 if (c == NULL) {
1642 /* Found empty line, write it out. */
1643 (void) fputs(line, new_fp);
1644 } else if (c[0] == '#') {
1645 /* Found a comment line, write it out. */
1646 (void) fputs(line, new_fp);
1647 } else if (strcmp(c, "title") == 0) {
1648 char *name = NULL;
1649 char *desc = NULL;
1652 * Found a 'title' line, parse out BE name or
1653 * the description.
1655 name = strtok(NULL, BE_WHITE_SPACE);
1657 if (name == NULL) {
1659 * Nothing after 'title', just push
1660 * this line through
1662 (void) fputs(line, new_fp);
1663 } else {
1665 * Grab the remainder of the title which
1666 * could be a multi worded description
1668 desc = strtok(NULL, "\n");
1670 if (strcmp(name, be_orig_name) == 0) {
1672 * The first token of the title is
1673 * the old BE name, replace it with
1674 * the new one, and write it out
1675 * along with the remainder of
1676 * description if there is one.
1678 if (desc) {
1679 (void) snprintf(new_line,
1680 sizeof (new_line),
1681 "title %s %s\n",
1682 be_new_name, desc);
1683 } else {
1684 (void) snprintf(new_line,
1685 sizeof (new_line),
1686 "title %s\n", be_new_name);
1689 (void) fputs(new_line, new_fp);
1690 } else {
1691 (void) fputs(line, new_fp);
1694 } else if (strcmp(c, "bootfs") == 0) {
1696 * Found a 'bootfs' line, parse out the BE root
1697 * dataset value.
1699 char *root_ds = strtok(NULL, BE_WHITE_SPACE);
1701 if (root_ds == NULL) {
1703 * Nothing after 'bootfs', just push
1704 * this line through
1706 (void) fputs(line, new_fp);
1707 } else {
1709 * If this bootfs is the one we're renaming,
1710 * write out the new root dataset value
1712 if (strcmp(root_ds, be_root_ds) == 0) {
1713 (void) snprintf(new_line,
1714 sizeof (new_line), "bootfs %s\n",
1715 be_new_root_ds);
1717 (void) fputs(new_line, new_fp);
1718 } else {
1719 (void) fputs(line, new_fp);
1722 } else {
1724 * Found some other line we don't care
1725 * about, write it out.
1727 (void) fputs(line, new_fp);
1731 (void) fclose(menu_fp);
1732 (void) fclose(new_fp);
1733 (void) close(tmp_fd);
1735 if (rename(temp_menu, menu_file) != 0) {
1736 err = errno;
1737 be_print_err(gettext("be_update_menu: "
1738 "failed to rename file %s to %s: %s\n"),
1739 temp_menu, menu_file, strerror(err));
1740 ret = errno_to_be_err(err);
1742 free(temp_menu);
1744 /* Set the perms and ownership of the updated file */
1745 if (chmod(menu_file, sb.st_mode) != 0) {
1746 err = errno;
1747 be_print_err(gettext("be_update_menu: "
1748 "failed to chmod %s: %s\n"), menu_file, strerror(err));
1749 ret = errno_to_be_err(err);
1750 goto cleanup;
1752 if (chown(menu_file, sb.st_uid, sb.st_gid) != 0) {
1753 err = errno;
1754 be_print_err(gettext("be_update_menu: "
1755 "failed to chown %s: %s\n"), menu_file, strerror(err));
1756 ret = errno_to_be_err(err);
1759 cleanup:
1760 if (pool_mounted) {
1761 int err = BE_SUCCESS;
1762 err = be_unmount_pool(zhp, ptmp_mntpnt, orig_mntpnt);
1763 if (ret == BE_SUCCESS)
1764 ret = err;
1765 free(orig_mntpnt);
1766 free(ptmp_mntpnt);
1768 ZFS_CLOSE(zhp);
1769 return (ret);
1773 * Function: be_has_menu_entry
1774 * Description: Checks to see if the BEs root dataset has an entry in the grub
1775 * menu.
1776 * Parameters:
1777 * be_dataset - The root dataset of the BE
1778 * be_root_pool - The pool which contains the boot menu
1779 * entry - A pointer the the entry number of the BE if found.
1780 * Returns:
1781 * B_TRUE - Success
1782 * B_FALSE - Failure
1783 * Scope:
1784 * Semi-private (library wide use only)
1786 boolean_t
1787 be_has_menu_entry(char *be_dataset, char *be_root_pool, int *entry)
1789 zfs_handle_t *zhp = NULL;
1790 char menu_file[MAXPATHLEN];
1791 FILE *menu_fp;
1792 char line[BUFSIZ];
1793 char *last;
1794 char *rpool_mntpnt = NULL;
1795 char *ptmp_mntpnt = NULL;
1796 char *orig_mntpnt = NULL;
1797 int ent_num = 0;
1798 boolean_t ret = 0;
1799 boolean_t pool_mounted = B_FALSE;
1803 * Check to see if this system supports grub
1805 if ((zhp = zfs_open(g_zfs, be_root_pool, ZFS_TYPE_DATASET)) == NULL) {
1806 be_print_err(gettext("be_has_menu_entry: failed to open "
1807 "pool dataset for %s: %s\n"), be_root_pool,
1808 libzfs_error_description(g_zfs));
1809 return (B_FALSE);
1813 * Check to see if the pool's dataset is mounted. If it isn't we'll
1814 * attempt to mount it.
1816 if (be_mount_pool(zhp, &ptmp_mntpnt, &orig_mntpnt,
1817 &pool_mounted) != 0) {
1818 be_print_err(gettext("be_has_menu_entry: pool dataset "
1819 "(%s) could not be mounted\n"), be_root_pool);
1820 ZFS_CLOSE(zhp);
1821 return (B_FALSE);
1825 * Get the mountpoint for the root pool dataset.
1827 if (!zfs_is_mounted(zhp, &rpool_mntpnt)) {
1828 be_print_err(gettext("be_has_menu_entry: pool "
1829 "dataset (%s) is not mounted. Can't set "
1830 "the default BE in the grub menu.\n"), be_root_pool);
1831 ret = B_FALSE;
1832 goto cleanup;
1835 if (be_has_grub()) {
1836 (void) snprintf(menu_file, MAXPATHLEN, "/%s%s",
1837 rpool_mntpnt, BE_GRUB_MENU);
1838 } else {
1839 (void) snprintf(menu_file, MAXPATHLEN, "/%s%s",
1840 rpool_mntpnt, BE_SPARC_MENU);
1843 if (be_open_menu(be_root_pool, menu_file, &menu_fp, "r",
1844 B_FALSE) != 0) {
1845 ret = B_FALSE;
1846 goto cleanup;
1847 } else if (menu_fp == NULL) {
1848 ret = B_FALSE;
1849 goto cleanup;
1852 free(rpool_mntpnt);
1853 rpool_mntpnt = NULL;
1855 while (fgets(line, BUFSIZ, menu_fp)) {
1856 char *tok = strtok_r(line, BE_WHITE_SPACE, &last);
1858 if (tok != NULL && tok[0] != '#') {
1859 if (strcmp(tok, "bootfs") == 0) {
1860 tok = strtok_r(last, BE_WHITE_SPACE, &last);
1861 if (tok != NULL && strcmp(tok,
1862 be_dataset) == 0) {
1863 (void) fclose(menu_fp);
1865 * The entry number needs to be
1866 * decremented here because the title
1867 * will always be the first line for
1868 * an entry. Because of this we'll
1869 * always be off by one entry when we
1870 * check for bootfs.
1872 *entry = ent_num - 1;
1873 ret = B_TRUE;
1874 goto cleanup;
1876 } else if (strcmp(tok, "title") == 0)
1877 ent_num++;
1881 cleanup:
1882 if (pool_mounted) {
1883 (void) be_unmount_pool(zhp, ptmp_mntpnt, orig_mntpnt);
1884 free(orig_mntpnt);
1885 free(ptmp_mntpnt);
1887 ZFS_CLOSE(zhp);
1888 (void) fclose(menu_fp);
1889 return (ret);
1893 * Function: be_update_vfstab
1894 * Description: This function digs into a BE's vfstab and updates all
1895 * entries with file systems listed in be_fs_list_data_t.
1896 * The entry's root container dataset and be_name will be
1897 * updated with the parameters passed in.
1898 * Parameters:
1899 * be_name - name of BE to update
1900 * old_rc_loc - dataset under which the root container dataset
1901 * of the old BE resides in.
1902 * new_rc_loc - dataset under which the root container dataset
1903 * of the new BE resides in.
1904 * fld - be_fs_list_data_t pointer providing the list of
1905 * file systems to look for in vfstab.
1906 * mountpoint - directory of where BE is currently mounted.
1907 * If NULL, then BE is not currently mounted.
1908 * Returns:
1909 * BE_SUCCESS - Success
1910 * be_errno_t - Failure
1911 * Scope:
1912 * Semi-private (library wide use only)
1915 be_update_vfstab(char *be_name, char *old_rc_loc, char *new_rc_loc,
1916 be_fs_list_data_t *fld, char *mountpoint)
1918 char *tmp_mountpoint = NULL;
1919 char alt_vfstab[MAXPATHLEN];
1920 int ret = BE_SUCCESS, err = BE_SUCCESS;
1922 if (fld == NULL || fld->fs_list == NULL || fld->fs_num == 0)
1923 return (BE_SUCCESS);
1925 /* If BE not already mounted, mount the BE */
1926 if (mountpoint == NULL) {
1927 if ((ret = _be_mount(be_name, &tmp_mountpoint,
1928 BE_MOUNT_FLAG_NO_ZONES)) != BE_SUCCESS) {
1929 be_print_err(gettext("be_update_vfstab: "
1930 "failed to mount BE (%s)\n"), be_name);
1931 return (ret);
1933 } else {
1934 tmp_mountpoint = mountpoint;
1937 /* Get string for vfstab in the mounted BE. */
1938 (void) snprintf(alt_vfstab, sizeof (alt_vfstab), "%s/etc/vfstab",
1939 tmp_mountpoint);
1941 /* Update the vfstab */
1942 ret = _update_vfstab(alt_vfstab, be_name, old_rc_loc, new_rc_loc,
1943 fld);
1945 /* Unmount BE if we mounted it */
1946 if (mountpoint == NULL) {
1947 if ((err = _be_unmount(be_name, 0)) == BE_SUCCESS) {
1948 /* Remove temporary mountpoint */
1949 (void) rmdir(tmp_mountpoint);
1950 } else {
1951 be_print_err(gettext("be_update_vfstab: "
1952 "failed to unmount BE %s mounted at %s\n"),
1953 be_name, tmp_mountpoint);
1954 if (ret == BE_SUCCESS)
1955 ret = err;
1958 free(tmp_mountpoint);
1961 return (ret);
1965 * Function: be_update_zone_vfstab
1966 * Description: This function digs into a zone BE's vfstab and updates all
1967 * entries with file systems listed in be_fs_list_data_t.
1968 * The entry's root container dataset and be_name will be
1969 * updated with the parameters passed in.
1970 * Parameters:
1971 * zhp - zfs_handle_t pointer to zone root dataset.
1972 * be_name - name of zone BE to update
1973 * old_rc_loc - dataset under which the root container dataset
1974 * of the old zone BE resides in.
1975 * new_rc_loc - dataset under which the root container dataset
1976 * of the new zone BE resides in.
1977 * fld - be_fs_list_data_t pointer providing the list of
1978 * file systems to look for in vfstab.
1979 * Returns:
1980 * BE_SUCCESS - Success
1981 * be_errno_t - Failure
1982 * Scope:
1983 * Semi-private (library wide use only)
1986 be_update_zone_vfstab(zfs_handle_t *zhp, char *be_name, char *old_rc_loc,
1987 char *new_rc_loc, be_fs_list_data_t *fld)
1989 be_mount_data_t md = { 0 };
1990 be_unmount_data_t ud = { 0 };
1991 char alt_vfstab[MAXPATHLEN];
1992 boolean_t mounted_here = B_FALSE;
1993 int ret = BE_SUCCESS;
1996 * If zone root not already mounted, mount it at a
1997 * temporary location.
1999 if (!zfs_is_mounted(zhp, &md.altroot)) {
2000 /* Generate temporary mountpoint to mount zone root */
2001 if ((ret = be_make_tmp_mountpoint(&md.altroot)) != BE_SUCCESS) {
2002 be_print_err(gettext("be_update_zone_vfstab: "
2003 "failed to make temporary mountpoint to "
2004 "mount zone root\n"));
2005 return (ret);
2008 if (be_mount_zone_root(zhp, &md) != BE_SUCCESS) {
2009 be_print_err(gettext("be_update_zone_vfstab: "
2010 "failed to mount zone root %s\n"),
2011 zfs_get_name(zhp));
2012 free(md.altroot);
2013 return (BE_ERR_MOUNT_ZONEROOT);
2015 mounted_here = B_TRUE;
2018 /* Get string from vfstab in the mounted zone BE */
2019 (void) snprintf(alt_vfstab, sizeof (alt_vfstab), "%s/etc/vfstab",
2020 md.altroot);
2022 /* Update the vfstab */
2023 ret = _update_vfstab(alt_vfstab, be_name, old_rc_loc, new_rc_loc,
2024 fld);
2026 /* Unmount zone root if we mounted it */
2027 if (mounted_here) {
2028 ud.force = B_TRUE;
2030 if (be_unmount_zone_root(zhp, &ud) == BE_SUCCESS) {
2031 /* Remove the temporary mountpoint */
2032 (void) rmdir(md.altroot);
2033 } else {
2034 be_print_err(gettext("be_update_zone_vfstab: "
2035 "failed to unmount zone root %s from %s\n"),
2036 zfs_get_name(zhp), md.altroot);
2037 if (ret == 0)
2038 ret = BE_ERR_UMOUNT_ZONEROOT;
2042 free(md.altroot);
2043 return (ret);
2047 * Function: be_auto_snap_name
2048 * Description: Generate an auto snapshot name constructed based on the
2049 * current date and time. The auto snapshot name is of the form:
2051 * <date>-<time>
2053 * where <date> is in ISO standard format, so the resultant name
2054 * is of the form:
2056 * %Y-%m-%d-%H:%M:%S
2058 * Parameters:
2059 * None
2060 * Returns:
2061 * Success - pointer to auto generated snapshot name. The name
2062 * is allocated in heap storage so the caller is
2063 * responsible for free'ing the name.
2064 * Failure - NULL
2065 * Scope:
2066 * Semi-private (library wide use only)
2068 char *
2069 be_auto_snap_name(void)
2071 time_t utc_tm = NULL;
2072 struct tm *gmt_tm = NULL;
2073 char gmt_time_str[64];
2074 char *auto_snap_name = NULL;
2076 if (time(&utc_tm) == -1) {
2077 be_print_err(gettext("be_auto_snap_name: time() failed\n"));
2078 return (NULL);
2081 if ((gmt_tm = gmtime(&utc_tm)) == NULL) {
2082 be_print_err(gettext("be_auto_snap_name: gmtime() failed\n"));
2083 return (NULL);
2086 (void) strftime(gmt_time_str, sizeof (gmt_time_str), "%F-%T", gmt_tm);
2088 if ((auto_snap_name = strdup(gmt_time_str)) == NULL) {
2089 be_print_err(gettext("be_auto_snap_name: "
2090 "memory allocation failed\n"));
2091 return (NULL);
2094 return (auto_snap_name);
2098 * Function: be_auto_be_name
2099 * Description: Generate an auto BE name constructed based on the BE name
2100 * of the original BE being cloned.
2101 * Parameters:
2102 * obe_name - name of the original BE being cloned.
2103 * Returns:
2104 * Success - pointer to auto generated BE name. The name
2105 * is allocated in heap storage so the caller is
2106 * responsible for free'ing the name.
2107 * Failure - NULL
2108 * Scope:
2109 * Semi-private (library wide use only)
2111 char *
2112 be_auto_be_name(char *obe_name)
2114 return (be_get_auto_name(obe_name, NULL, B_FALSE));
2118 * Function: be_auto_zone_be_name
2119 * Description: Generate an auto BE name for a zone constructed based on
2120 * the BE name of the original zone BE being cloned.
2121 * Parameters:
2122 * container_ds - container dataset for the zone.
2123 * zbe_name - name of the original zone BE being cloned.
2124 * Returns:
2125 * Success - pointer to auto generated BE name. The name
2126 * is allocated in heap storage so the caller is
2127 * responsible for free'ing the name.
2128 * Failure - NULL
2129 * Scope:
2130 * Semi-private (library wide use only)
2132 char *
2133 be_auto_zone_be_name(char *container_ds, char *zbe_name)
2135 return (be_get_auto_name(zbe_name, container_ds, B_TRUE));
2139 * Function: be_valid_be_name
2140 * Description: Validates a BE name.
2141 * Parameters:
2142 * be_name - name of BE to validate
2143 * Returns:
2144 * B_TRUE - be_name is valid
2145 * B_FALSE - be_name is invalid
2146 * Scope:
2147 * Semi-private (library wide use only)
2150 boolean_t
2151 be_valid_be_name(const char *be_name)
2153 const char *c = NULL;
2154 struct be_defaults be_defaults;
2156 if (be_name == NULL)
2157 return (B_FALSE);
2159 be_get_defaults(&be_defaults);
2162 * A BE name must not be a multi-level dataset name. We also check
2163 * that it does not contain the ' ' and '%' characters. The ' ' is
2164 * a valid character for datasets, however we don't allow that in a
2165 * BE name. The '%' is invalid, but zfs_name_valid() allows it for
2166 * internal reasons, so we explicitly check for it here.
2168 c = be_name;
2169 while (*c != '\0' && *c != '/' && *c != ' ' && *c != '%')
2170 c++;
2172 if (*c != '\0')
2173 return (B_FALSE);
2176 * The BE name must comply with a zfs dataset filesystem. We also
2177 * verify its length to be < BE_NAME_MAX_LEN.
2179 if (!zfs_name_valid(be_name, ZFS_TYPE_FILESYSTEM) ||
2180 strlen(be_name) > BE_NAME_MAX_LEN)
2181 return (B_FALSE);
2183 if (be_defaults.be_deflt_bename_starts_with[0] != '\0' &&
2184 strstr(be_name, be_defaults.be_deflt_bename_starts_with) == NULL) {
2185 return (B_FALSE);
2188 return (B_TRUE);
2192 * Function: be_valid_auto_snap_name
2193 * Description: This function checks that a snapshot name is a valid auto
2194 * generated snapshot name. A valid auto generated snapshot
2195 * name is of the form:
2197 * %Y-%m-%d-%H:%M:%S
2199 * An older form of the auto generated snapshot name also
2200 * included the snapshot's BE cleanup policy and a reserved
2201 * field. Those names will also be verified by this function.
2203 * Examples of valid auto snapshot names are:
2205 * 2008-03-31-18:41:30
2206 * 2008-03-31-22:17:24
2207 * <policy>:-:2008:04-05-09:12:55
2208 * <policy>:-:2008:04-06-15:34:12
2210 * Parameters:
2211 * name - name of the snapshot to be validated.
2212 * Returns:
2213 * B_TRUE - the name is a valid auto snapshot name.
2214 * B_FALSE - the name is not a valid auto snapshot name.
2215 * Scope:
2216 * Semi-private (library wide use only)
2218 boolean_t
2219 be_valid_auto_snap_name(char *name)
2221 struct tm gmt_tm;
2223 char *policy = NULL;
2224 char *reserved = NULL;
2225 char *date = NULL;
2226 char *c = NULL;
2228 /* Validate the snapshot name by converting it into utc time */
2229 if (strptime(name, "%Y-%m-%d-%T", &gmt_tm) != NULL &&
2230 (mktime(&gmt_tm) != -1)) {
2231 return (B_TRUE);
2235 * Validate the snapshot name against the older form of an
2236 * auto generated snapshot name.
2238 policy = strdup(name);
2241 * Get the first field from the snapshot name,
2242 * which is the BE policy
2244 c = strchr(policy, ':');
2245 if (c == NULL) {
2246 free(policy);
2247 return (B_FALSE);
2249 c[0] = '\0';
2251 /* Validate the policy name */
2252 if (!valid_be_policy(policy)) {
2253 free(policy);
2254 return (B_FALSE);
2257 /* Get the next field, which is the reserved field. */
2258 if (c[1] == NULL || c[1] == '\0') {
2259 free(policy);
2260 return (B_FALSE);
2262 reserved = c+1;
2263 c = strchr(reserved, ':');
2264 if (c == NULL) {
2265 free(policy);
2266 return (B_FALSE);
2268 c[0] = '\0';
2270 /* Validate the reserved field */
2271 if (strcmp(reserved, "-") != 0) {
2272 free(policy);
2273 return (B_FALSE);
2276 /* The remaining string should be the date field */
2277 if (c[1] == NULL || c[1] == '\0') {
2278 free(policy);
2279 return (B_FALSE);
2281 date = c+1;
2283 /* Validate the date string by converting it into utc time */
2284 if (strptime(date, "%Y-%m-%d-%T", &gmt_tm) == NULL ||
2285 (mktime(&gmt_tm) == -1)) {
2286 be_print_err(gettext("be_valid_auto_snap_name: "
2287 "invalid auto snapshot name\n"));
2288 free(policy);
2289 return (B_FALSE);
2292 free(policy);
2293 return (B_TRUE);
2297 * Function: be_default_policy
2298 * Description: Temporary hardcoded policy support. This function returns
2299 * the default policy type to be used to create a BE or a BE
2300 * snapshot.
2301 * Parameters:
2302 * None
2303 * Returns:
2304 * Name of default BE policy.
2305 * Scope:
2306 * Semi-private (library wide use only)
2308 char *
2309 be_default_policy(void)
2311 return (BE_PLCY_STATIC);
2315 * Function: valid_be_policy
2316 * Description: Temporary hardcoded policy support. This function valids
2317 * whether a policy is a valid known policy or not.
2318 * Paramters:
2319 * policy - name of policy to validate.
2320 * Returns:
2321 * B_TRUE - policy is a valid.
2322 * B_FALSE - policy is invalid.
2323 * Scope:
2324 * Semi-private (library wide use only)
2326 boolean_t
2327 valid_be_policy(char *policy)
2329 if (policy == NULL)
2330 return (B_FALSE);
2332 if (strcmp(policy, BE_PLCY_STATIC) == 0 ||
2333 strcmp(policy, BE_PLCY_VOLATILE) == 0) {
2334 return (B_TRUE);
2337 return (B_FALSE);
2341 * Function: be_print_err
2342 * Description: This function prints out error messages if do_print is
2343 * set to B_TRUE or if the BE_PRINT_ERR environment variable
2344 * is set to true.
2345 * Paramters:
2346 * prnt_str - the string we wish to print and any arguments
2347 * for the format of that string.
2348 * Returns:
2349 * void
2350 * Scope:
2351 * Semi-private (library wide use only)
2353 void
2354 be_print_err(char *prnt_str, ...)
2356 va_list ap;
2357 char buf[BUFSIZ];
2358 char *env_buf;
2359 static boolean_t env_checked = B_FALSE;
2361 if (!env_checked) {
2362 if ((env_buf = getenv("BE_PRINT_ERR")) != NULL) {
2363 if (strcasecmp(env_buf, "true") == 0) {
2364 do_print = B_TRUE;
2367 env_checked = B_TRUE;
2370 if (do_print) {
2371 va_start(ap, prnt_str);
2372 /* LINTED variable format specifier */
2373 (void) vsnprintf(buf, BUFSIZ, prnt_str, ap);
2374 (void) fputs(buf, stderr);
2375 va_end(ap);
2380 * Function: be_find_current_be
2381 * Description: Find the currently "active" BE. Fill in the
2382 * passed in be_transaction_data_t reference with the
2383 * active BE's data.
2384 * Paramters:
2385 * none
2386 * Returns:
2387 * BE_SUCCESS - Success
2388 * be_errnot_t - Failure
2389 * Scope:
2390 * Semi-private (library wide use only)
2391 * Notes:
2392 * The caller is responsible for initializing the libzfs handle
2393 * and freeing the memory used by the active be_name.
2396 be_find_current_be(be_transaction_data_t *bt)
2398 int zret;
2400 if ((zret = zpool_iter(g_zfs, be_zpool_find_current_be_callback,
2401 bt)) == 0) {
2402 be_print_err(gettext("be_find_current_be: failed to "
2403 "find current BE name\n"));
2404 return (BE_ERR_BE_NOENT);
2405 } else if (zret < 0) {
2406 be_print_err(gettext("be_find_current_be: "
2407 "zpool_iter failed: %s\n"),
2408 libzfs_error_description(g_zfs));
2409 return (zfs_err_to_be_err(g_zfs));
2412 return (BE_SUCCESS);
2416 * Function: be_zpool_find_current_be_callback
2417 * Description: Callback function used to iterate through all existing pools
2418 * to find the BE that is the currently booted BE.
2419 * Parameters:
2420 * zlp - zpool_handle_t pointer to the current pool being
2421 * looked at.
2422 * data - be_transaction_data_t pointer.
2423 * Upon successfully finding the current BE, the
2424 * obe_zpool member of this parameter is set to the
2425 * pool it is found in.
2426 * Return:
2427 * 1 - Found current BE in this pool.
2428 * 0 - Did not find current BE in this pool.
2429 * Scope:
2430 * Semi-private (library wide use only)
2433 be_zpool_find_current_be_callback(zpool_handle_t *zlp, void *data)
2435 be_transaction_data_t *bt = data;
2436 zfs_handle_t *zhp = NULL;
2437 const char *zpool = zpool_get_name(zlp);
2438 char be_container_ds[MAXPATHLEN];
2441 * Generate string for BE container dataset
2443 be_make_container_ds(zpool, be_container_ds, sizeof (be_container_ds));
2446 * Check if a BE container dataset exists in this pool.
2448 if (!zfs_dataset_exists(g_zfs, be_container_ds, ZFS_TYPE_FILESYSTEM)) {
2449 zpool_close(zlp);
2450 return (0);
2454 * Get handle to this zpool's BE container dataset.
2456 if ((zhp = zfs_open(g_zfs, be_container_ds, ZFS_TYPE_FILESYSTEM)) ==
2457 NULL) {
2458 be_print_err(gettext("be_zpool_find_current_be_callback: "
2459 "failed to open BE container dataset (%s)\n"),
2460 be_container_ds);
2461 zpool_close(zlp);
2462 return (0);
2466 * Iterate through all potential BEs in this zpool
2468 if (zfs_iter_filesystems(zhp, be_zfs_find_current_be_callback, bt)) {
2470 * Found current BE dataset; set obe_zpool
2472 if ((bt->obe_zpool = strdup(zpool)) == NULL) {
2473 be_print_err(gettext(
2474 "be_zpool_find_current_be_callback: "
2475 "memory allocation failed\n"));
2476 ZFS_CLOSE(zhp);
2477 zpool_close(zlp);
2478 return (0);
2481 ZFS_CLOSE(zhp);
2482 zpool_close(zlp);
2483 return (1);
2486 ZFS_CLOSE(zhp);
2487 zpool_close(zlp);
2489 return (0);
2493 * Function: be_zfs_find_current_be_callback
2494 * Description: Callback function used to iterate through all BEs in a
2495 * pool to find the BE that is the currently booted BE.
2496 * Parameters:
2497 * zhp - zfs_handle_t pointer to current filesystem being checked.
2498 * data - be_transaction-data_t pointer
2499 * Upon successfully finding the current BE, the
2500 * obe_name and obe_root_ds members of this parameter
2501 * are set to the BE name and BE's root dataset
2502 * respectively.
2503 * Return:
2504 * 1 - Found current BE.
2505 * 0 - Did not find current BE.
2506 * Scope:
2507 * Semi-private (library wide use only)
2510 be_zfs_find_current_be_callback(zfs_handle_t *zhp, void *data)
2512 be_transaction_data_t *bt = data;
2513 char *mp = NULL;
2516 * Check if dataset is mounted, and if so where.
2518 if (zfs_is_mounted(zhp, &mp)) {
2520 * If mounted at root, set obe_root_ds and obe_name
2522 if (mp != NULL && strcmp(mp, "/") == 0) {
2523 free(mp);
2525 if ((bt->obe_root_ds = strdup(zfs_get_name(zhp)))
2526 == NULL) {
2527 be_print_err(gettext(
2528 "be_zfs_find_current_be_callback: "
2529 "memory allocation failed\n"));
2530 ZFS_CLOSE(zhp);
2531 return (0);
2534 if ((bt->obe_name = strdup(basename(bt->obe_root_ds)))
2535 == NULL) {
2536 be_print_err(gettext(
2537 "be_zfs_find_current_be_callback: "
2538 "memory allocation failed\n"));
2539 ZFS_CLOSE(zhp);
2540 return (0);
2543 ZFS_CLOSE(zhp);
2544 return (1);
2547 free(mp);
2549 ZFS_CLOSE(zhp);
2551 return (0);
2555 * Function: be_check_be_roots_callback
2556 * Description: This function checks whether or not the dataset name passed
2557 * is hierachically located under the BE root container dataset
2558 * for this pool.
2559 * Parameters:
2560 * zlp - zpool_handle_t pointer to current pool being processed.
2561 * data - name of dataset to check
2562 * Returns:
2563 * 0 - dataset is not in this pool's BE root container dataset
2564 * 1 - dataset is in this pool's BE root container dataset
2565 * Scope:
2566 * Semi-private (library wide use only)
2569 be_check_be_roots_callback(zpool_handle_t *zlp, void *data)
2571 const char *zpool = zpool_get_name(zlp);
2572 char *ds = data;
2573 char be_container_ds[MAXPATHLEN];
2575 /* Generate string for this pool's BE root container dataset */
2576 be_make_container_ds(zpool, be_container_ds, sizeof (be_container_ds));
2579 * If dataset lives under the BE root container dataset
2580 * of this pool, return failure.
2582 if (strncmp(be_container_ds, ds, strlen(be_container_ds)) == 0 &&
2583 ds[strlen(be_container_ds)] == '/') {
2584 zpool_close(zlp);
2585 return (1);
2588 zpool_close(zlp);
2589 return (0);
2593 * Function: zfs_err_to_be_err
2594 * Description: This function takes the error stored in the libzfs handle
2595 * and maps it to an be_errno_t. If there are no matching
2596 * be_errno_t's then BE_ERR_ZFS is returned.
2597 * Paramters:
2598 * zfsh - The libzfs handle containing the error we're looking up.
2599 * Returns:
2600 * be_errno_t
2601 * Scope:
2602 * Semi-private (library wide use only)
2605 zfs_err_to_be_err(libzfs_handle_t *zfsh)
2607 int err = libzfs_errno(zfsh);
2609 switch (err) {
2610 case 0:
2611 return (BE_SUCCESS);
2612 case EZFS_PERM:
2613 return (BE_ERR_PERM);
2614 case EZFS_INTR:
2615 return (BE_ERR_INTR);
2616 case EZFS_NOENT:
2617 return (BE_ERR_NOENT);
2618 case EZFS_NOSPC:
2619 return (BE_ERR_NOSPC);
2620 case EZFS_MOUNTFAILED:
2621 return (BE_ERR_MOUNT);
2622 case EZFS_UMOUNTFAILED:
2623 return (BE_ERR_UMOUNT);
2624 case EZFS_EXISTS:
2625 return (BE_ERR_BE_EXISTS);
2626 case EZFS_BUSY:
2627 return (BE_ERR_DEV_BUSY);
2628 case EZFS_POOLREADONLY:
2629 return (BE_ERR_ROFS);
2630 case EZFS_NAMETOOLONG:
2631 return (BE_ERR_NAMETOOLONG);
2632 case EZFS_NODEVICE:
2633 return (BE_ERR_NODEV);
2634 case EZFS_POOL_INVALARG:
2635 return (BE_ERR_INVAL);
2636 case EZFS_PROPTYPE:
2637 return (BE_ERR_INVALPROP);
2638 case EZFS_BADTYPE:
2639 return (BE_ERR_DSTYPE);
2640 case EZFS_PROPNONINHERIT:
2641 return (BE_ERR_NONINHERIT);
2642 case EZFS_PROPREADONLY:
2643 return (BE_ERR_READONLYPROP);
2644 case EZFS_RESILVERING:
2645 case EZFS_POOLUNAVAIL:
2646 return (BE_ERR_UNAVAIL);
2647 case EZFS_DSREADONLY:
2648 return (BE_ERR_READONLYDS);
2649 default:
2650 return (BE_ERR_ZFS);
2655 * Function: errno_to_be_err
2656 * Description: This function takes an errno and maps it to an be_errno_t.
2657 * If there are no matching be_errno_t's then BE_ERR_UNKNOWN is
2658 * returned.
2659 * Paramters:
2660 * err - The errno we're compairing against.
2661 * Returns:
2662 * be_errno_t
2663 * Scope:
2664 * Semi-private (library wide use only)
2667 errno_to_be_err(int err)
2669 switch (err) {
2670 case EPERM:
2671 return (BE_ERR_PERM);
2672 case EACCES:
2673 return (BE_ERR_ACCESS);
2674 case ECANCELED:
2675 return (BE_ERR_CANCELED);
2676 case EINTR:
2677 return (BE_ERR_INTR);
2678 case ENOENT:
2679 return (BE_ERR_NOENT);
2680 case ENOSPC:
2681 case EDQUOT:
2682 return (BE_ERR_NOSPC);
2683 case EEXIST:
2684 return (BE_ERR_BE_EXISTS);
2685 case EBUSY:
2686 return (BE_ERR_BUSY);
2687 case EROFS:
2688 return (BE_ERR_ROFS);
2689 case ENAMETOOLONG:
2690 return (BE_ERR_NAMETOOLONG);
2691 case ENXIO:
2692 return (BE_ERR_NXIO);
2693 case EINVAL:
2694 return (BE_ERR_INVAL);
2695 case EFAULT:
2696 return (BE_ERR_FAULT);
2697 default:
2698 return (BE_ERR_UNKNOWN);
2703 * Function: be_err_to_str
2704 * Description: This function takes a be_errno_t and maps it to a message.
2705 * If there are no matching be_errno_t's then NULL is returned.
2706 * Paramters:
2707 * be_errno_t - The be_errno_t we're mapping.
2708 * Returns:
2709 * string or NULL if the error code is not known.
2710 * Scope:
2711 * Semi-private (library wide use only)
2713 char *
2714 be_err_to_str(int err)
2716 switch (err) {
2717 case BE_ERR_ACCESS:
2718 return (gettext("Permission denied."));
2719 case BE_ERR_ACTIVATE_CURR:
2720 return (gettext("Activation of current BE failed."));
2721 case BE_ERR_AUTONAME:
2722 return (gettext("Auto naming failed."));
2723 case BE_ERR_BE_NOENT:
2724 return (gettext("No such BE."));
2725 case BE_ERR_BUSY:
2726 return (gettext("Mount busy."));
2727 case BE_ERR_DEV_BUSY:
2728 return (gettext("Device busy."));
2729 case BE_ERR_CANCELED:
2730 return (gettext("Operation canceled."));
2731 case BE_ERR_CLONE:
2732 return (gettext("BE clone failed."));
2733 case BE_ERR_COPY:
2734 return (gettext("BE copy failed."));
2735 case BE_ERR_CREATDS:
2736 return (gettext("Dataset creation failed."));
2737 case BE_ERR_CURR_BE_NOT_FOUND:
2738 return (gettext("Can't find current BE."));
2739 case BE_ERR_DESTROY:
2740 return (gettext("Failed to destroy BE or snapshot."));
2741 case BE_ERR_DESTROY_CURR_BE:
2742 return (gettext("Cannot destroy current BE."));
2743 case BE_ERR_DEMOTE:
2744 return (gettext("BE demotion failed."));
2745 case BE_ERR_DSTYPE:
2746 return (gettext("Invalid dataset type."));
2747 case BE_ERR_BE_EXISTS:
2748 return (gettext("BE exists."));
2749 case BE_ERR_INIT:
2750 return (gettext("be_zfs_init failed."));
2751 case BE_ERR_INTR:
2752 return (gettext("Interupted system call."));
2753 case BE_ERR_INVAL:
2754 return (gettext("Invalid argument."));
2755 case BE_ERR_INVALPROP:
2756 return (gettext("Invalid property for dataset."));
2757 case BE_ERR_INVALMOUNTPOINT:
2758 return (gettext("Unexpected mountpoint."));
2759 case BE_ERR_MOUNT:
2760 return (gettext("Mount failed."));
2761 case BE_ERR_MOUNTED:
2762 return (gettext("Already mounted."));
2763 case BE_ERR_NAMETOOLONG:
2764 return (gettext("name > BUFSIZ."));
2765 case BE_ERR_NOENT:
2766 return (gettext("Doesn't exist."));
2767 case BE_ERR_POOL_NOENT:
2768 return (gettext("No such pool."));
2769 case BE_ERR_NODEV:
2770 return (gettext("No such device."));
2771 case BE_ERR_NOTMOUNTED:
2772 return (gettext("File system not mounted."));
2773 case BE_ERR_NOMEM:
2774 return (gettext("Not enough memory."));
2775 case BE_ERR_NONINHERIT:
2776 return (gettext(
2777 "Property is not inheritable for the BE dataset."));
2778 case BE_ERR_NXIO:
2779 return (gettext("No such device or address."));
2780 case BE_ERR_NOSPC:
2781 return (gettext("No space on device."));
2782 case BE_ERR_NOTSUP:
2783 return (gettext("Operation not supported."));
2784 case BE_ERR_OPEN:
2785 return (gettext("Open failed."));
2786 case BE_ERR_PERM:
2787 return (gettext("Not owner."));
2788 case BE_ERR_UNAVAIL:
2789 return (gettext("The BE is currently unavailable."));
2790 case BE_ERR_PROMOTE:
2791 return (gettext("BE promotion failed."));
2792 case BE_ERR_ROFS:
2793 return (gettext("Read only file system."));
2794 case BE_ERR_READONLYDS:
2795 return (gettext("Read only dataset."));
2796 case BE_ERR_READONLYPROP:
2797 return (gettext("Read only property."));
2798 case BE_ERR_RENAME_ACTIVE:
2799 return (gettext("Renaming the active BE is not supported."));
2800 case BE_ERR_SS_EXISTS:
2801 return (gettext("Snapshot exists."));
2802 case BE_ERR_SS_NOENT:
2803 return (gettext("No such snapshot."));
2804 case BE_ERR_UMOUNT:
2805 return (gettext("Unmount failed."));
2806 case BE_ERR_UMOUNT_CURR_BE:
2807 return (gettext("Can't unmount the current BE."));
2808 case BE_ERR_UMOUNT_SHARED:
2809 return (gettext("Unmount of a shared File System failed."));
2810 case BE_ERR_FAULT:
2811 return (gettext("Bad address."));
2812 case BE_ERR_UNKNOWN:
2813 return (gettext("Unknown error."));
2814 case BE_ERR_ZFS:
2815 return (gettext("ZFS returned an error."));
2816 case BE_ERR_GEN_UUID:
2817 return (gettext("Failed to generate uuid."));
2818 case BE_ERR_PARSE_UUID:
2819 return (gettext("Failed to parse uuid."));
2820 case BE_ERR_NO_UUID:
2821 return (gettext("No uuid"));
2822 case BE_ERR_ZONE_NO_PARENTBE:
2823 return (gettext("No parent uuid"));
2824 case BE_ERR_ZONE_MULTIPLE_ACTIVE:
2825 return (gettext("Multiple active zone roots"));
2826 case BE_ERR_ZONE_NO_ACTIVE_ROOT:
2827 return (gettext("No active zone root"));
2828 case BE_ERR_ZONE_ROOT_NOT_LEGACY:
2829 return (gettext("Zone root not legacy"));
2830 case BE_ERR_MOUNT_ZONEROOT:
2831 return (gettext("Failed to mount a zone root."));
2832 case BE_ERR_UMOUNT_ZONEROOT:
2833 return (gettext("Failed to unmount a zone root."));
2834 case BE_ERR_NO_MOUNTED_ZONE:
2835 return (gettext("Zone is not mounted"));
2836 case BE_ERR_ZONES_UNMOUNT:
2837 return (gettext("Unable to unmount a zone BE."));
2838 case BE_ERR_NO_MENU:
2839 return (gettext("Missing boot menu file."));
2840 case BE_ERR_BAD_MENU_PATH:
2841 return (gettext("Invalid path for menu.lst file"));
2842 case BE_ERR_ZONE_SS_EXISTS:
2843 return (gettext("Zone snapshot exists."));
2844 case BE_ERR_BOOTFILE_INST:
2845 return (gettext("Error installing boot files."));
2846 case BE_ERR_EXTCMD:
2847 return (gettext("Error running an external command."));
2848 default:
2849 return (NULL);
2854 * Function: be_has_grub
2855 * Description: Boolean function indicating whether the current system
2856 * uses grub.
2857 * Return: B_FALSE - the system does not have grub
2858 * B_TRUE - the system does have grub.
2859 * Scope:
2860 * Semi-private (library wide use only)
2862 boolean_t
2863 be_has_grub(void)
2866 * TODO: This will need to be expanded to check for the existence of
2867 * grub if and when there is grub support for SPARC.
2869 return (be_is_isa("i386"));
2873 * Function: be_is_isa
2874 * Description: Boolean function indicating whether the instruction set
2875 * architecture of the executing system matches the name provided.
2876 * The string must match a system defined architecture (e.g.
2877 * "i386", "sparc") and is case sensitive.
2878 * Parameters: name - string representing the name of instruction set
2879 * architecture being tested
2880 * Returns: B_FALSE - the system instruction set architecture is different
2881 * from the one specified
2882 * B_TRUE - the system instruction set architecture is the same
2883 * as the one specified
2884 * Scope:
2885 * Semi-private (library wide use only)
2887 boolean_t
2888 be_is_isa(char *name)
2890 return ((strcmp((char *)be_get_default_isa(), name) == 0));
2894 * Function: be_get_default_isa
2895 * Description:
2896 * Returns the default instruction set architecture of the
2897 * machine it is executed on. (eg. sparc, i386, ...)
2898 * NOTE: SYS_INST environment variable may override default
2899 * return value
2900 * Parameters:
2901 * none
2902 * Returns:
2903 * NULL - the architecture returned by sysinfo() was too
2904 * long for local variables
2905 * char * - pointer to a string containing the default
2906 * implementation
2907 * Scope:
2908 * Semi-private (library wide use only)
2910 char *
2911 be_get_default_isa(void)
2913 int i;
2914 char *envp;
2915 static char default_inst[ARCH_LENGTH] = "";
2917 if (default_inst[0] == '\0') {
2918 if ((envp = getenv("SYS_INST")) != NULL) {
2919 if ((int)strlen(envp) >= ARCH_LENGTH)
2920 return (NULL);
2921 else
2922 (void) strcpy(default_inst, envp);
2923 } else {
2924 i = sysinfo(SI_ARCHITECTURE, default_inst, ARCH_LENGTH);
2925 if (i < 0 || i > ARCH_LENGTH)
2926 return (NULL);
2929 return (default_inst);
2933 * Function: be_run_cmd
2934 * Description:
2935 * Runs a command in a separate subprocess. Splits out stdout from stderr
2936 * and sends each to its own buffer. Buffers must be pre-allocated and
2937 * passed in as arguments. Buffer sizes are also passed in as arguments.
2939 * Notes / caveats:
2940 * - Command being run is assumed to not have any stdout or stderr
2941 * redirection.
2942 * - Commands which emit total stderr output of greater than PIPE_BUF
2943 * bytes can hang. For such commands, a different implementation
2944 * which uses poll(2) must be used.
2945 * - stdout_buf can be NULL. In this case, stdout_bufsize is ignored, and
2946 * the stream which would have gone to it is sent to the bit
2947 * bucket.
2948 * - stderr_buf cannot be NULL.
2949 * - Only subprocess errors are appended to the stderr_buf. Errors
2950 * running the command are reported through be_print_err().
2951 * - Data which would overflow its respective buffer is sent to the bit
2952 * bucket.
2954 * Parameters:
2955 * command: command to run. Assumed not to have embedded stdout
2956 * or stderr redirection. May have stdin redirection,
2957 * however.
2958 * stderr_buf: buffer returning subprocess stderr data. Errors
2959 * reported by this function are reported through
2960 * be_print_err().
2961 * stderr_bufsize: size of stderr_buf
2962 * stdout_buf: buffer returning subprocess stdout data.
2963 * stdout_bufsize: size of stdout_buf
2964 * Returns:
2965 * BE_SUCCESS - The command ran successfully without returning
2966 * errors.
2967 * BE_ERR_EXTCMD
2968 * - The command could not be run.
2969 * - The command terminated with error status.
2970 * - There were errors extracting or returning subprocess
2971 * data.
2972 * BE_ERR_NOMEM - The command exceeds the command buffer size.
2973 * BE_ERR_INVAL - An invalid argument was specified.
2974 * Scope:
2975 * Semi-private (library wide use only)
2978 be_run_cmd(char *command, char *stderr_buf, int stderr_bufsize,
2979 char *stdout_buf, int stdout_bufsize)
2981 char *temp_filename = strdup(tmpnam(NULL));
2982 FILE *stdout_str = NULL;
2983 FILE *stderr_str = NULL;
2984 char cmdline[BUFSIZ];
2985 char oneline[BUFSIZ];
2986 int exit_status;
2987 int rval = BE_SUCCESS;
2989 if ((command == NULL) || (stderr_buf == NULL) ||
2990 (stderr_bufsize <= 0) || (stdout_bufsize < 0) ||
2991 ((stdout_buf != NULL) ^ (stdout_bufsize != 0))) {
2992 return (BE_ERR_INVAL);
2995 /* Set up command so popen returns stderr, not stdout */
2996 if (snprintf(cmdline, BUFSIZ, "%s 2> %s", command,
2997 temp_filename) >= BUFSIZ) {
2998 rval = BE_ERR_NOMEM;
2999 goto cleanup;
3002 /* Set up the fifo that will make stderr available. */
3003 if (mkfifo(temp_filename, 0600) != 0) {
3004 (void) be_print_err(gettext("be_run_cmd: mkfifo: %s\n"),
3005 strerror(errno));
3006 rval = BE_ERR_EXTCMD;
3007 goto cleanup;
3010 if ((stdout_str = popen(cmdline, "r")) == NULL) {
3011 (void) be_print_err(gettext("be_run_cmd: popen: %s\n"),
3012 strerror(errno));
3013 rval = BE_ERR_EXTCMD;
3014 goto cleanup;
3017 if ((stderr_str = fopen(temp_filename, "r")) == NULL) {
3018 (void) be_print_err(gettext("be_run_cmd: fopen: %s\n"),
3019 strerror(errno));
3020 (void) pclose(stdout_str);
3021 rval = BE_ERR_EXTCMD;
3022 goto cleanup;
3025 /* Read stdout first, as it usually outputs more than stderr. */
3026 oneline[BUFSIZ-1] = '\0';
3027 while (fgets(oneline, BUFSIZ-1, stdout_str) != NULL) {
3028 if (stdout_str != NULL) {
3029 (void) strlcat(stdout_buf, oneline, stdout_bufsize);
3033 while (fgets(oneline, BUFSIZ-1, stderr_str) != NULL) {
3034 (void) strlcat(stderr_buf, oneline, stderr_bufsize);
3037 /* Close pipe, get exit status. */
3038 if ((exit_status = pclose(stdout_str)) == -1) {
3039 (void) be_print_err(gettext("be_run_cmd: pclose: %s\n"),
3040 strerror(errno));
3041 rval = BE_ERR_EXTCMD;
3042 } else if (WIFEXITED(exit_status)) {
3043 exit_status = (int)((char)WEXITSTATUS(exit_status));
3044 if (exit_status != 0) {
3045 (void) snprintf(oneline, BUFSIZ, gettext("be_run_cmd: "
3046 "command terminated with error status: %d\n"),
3047 exit_status);
3048 (void) strlcat(stderr_buf, oneline, stderr_bufsize);
3049 rval = BE_ERR_EXTCMD;
3051 } else {
3052 (void) snprintf(oneline, BUFSIZ, gettext("be_run_cmd: command "
3053 "terminated on signal: %s\n"),
3054 strsignal(WTERMSIG(exit_status)));
3055 (void) strlcat(stderr_buf, oneline, stderr_bufsize);
3056 rval = BE_ERR_EXTCMD;
3059 cleanup:
3060 (void) unlink(temp_filename);
3061 (void) free(temp_filename);
3063 return (rval);
3066 /* ******************************************************************** */
3067 /* Private Functions */
3068 /* ******************************************************************** */
3071 * Function: update_dataset
3072 * Description: This function takes a dataset name and replaces the zpool
3073 * and be_name components of the dataset with the new be_name
3074 * zpool passed in.
3075 * Parameters:
3076 * dataset - name of dataset
3077 * dataset_len - lenth of buffer in which dataset is passed in.
3078 * be_name - name of new BE name to update to.
3079 * old_rc_loc - dataset under which the root container dataset
3080 * for the old BE lives.
3081 * new_rc_loc - dataset under which the root container dataset
3082 * for the new BE lives.
3083 * Returns:
3084 * BE_SUCCESS - Success
3085 * be_errno_t - Failure
3086 * Scope:
3087 * Private
3089 static int
3090 update_dataset(char *dataset, int dataset_len, char *be_name,
3091 char *old_rc_loc, char *new_rc_loc)
3093 char *ds = NULL;
3094 char *sub_ds = NULL;
3096 /* Tear off the BE container dataset */
3097 if ((ds = be_make_name_from_ds(dataset, old_rc_loc)) == NULL) {
3098 return (BE_ERR_INVAL);
3101 /* Get dataset name relative to BE root, if there is one */
3102 sub_ds = strchr(ds, '/');
3104 /* Generate the BE root dataset name */
3105 be_make_root_ds(new_rc_loc, be_name, dataset, dataset_len);
3107 /* If a subordinate dataset name was found, append it */
3108 if (sub_ds != NULL)
3109 (void) strlcat(dataset, sub_ds, dataset_len);
3111 free(ds);
3112 return (BE_SUCCESS);
3116 * Function: _update_vfstab
3117 * Description: This function updates a vfstab file to reflect the new
3118 * root container dataset location and be_name for all
3119 * entries listed in the be_fs_list_data_t structure passed in.
3120 * Parameters:
3121 * vfstab - vfstab file to modify
3122 * be_name - name of BE to update.
3123 * old_rc_loc - dataset under which the root container dataset
3124 * of the old BE resides in.
3125 * new_rc_loc - dataset under which the root container dataset
3126 * of the new BE resides in.
3127 * fld - be_fs_list_data_t pointer providing the list of
3128 * file systems to look for in vfstab.
3129 * Returns:
3130 * BE_SUCCESS - Success
3131 * be_errno_t - Failure
3132 * Scope:
3133 * Private
3135 static int
3136 _update_vfstab(char *vfstab, char *be_name, char *old_rc_loc,
3137 char *new_rc_loc, be_fs_list_data_t *fld)
3139 struct vfstab vp;
3140 char *tmp_vfstab = NULL;
3141 char comments_buf[BUFSIZ];
3142 FILE *comments = NULL;
3143 FILE *vfs_ents = NULL;
3144 FILE *tfile = NULL;
3145 struct stat sb;
3146 char dev[MAXPATHLEN];
3147 char *c;
3148 int fd;
3149 int ret = BE_SUCCESS, err = 0;
3150 int i;
3151 int tmp_vfstab_len = 0;
3153 errno = 0;
3156 * Open vfstab for reading twice. First is for comments,
3157 * second is for actual entries.
3159 if ((comments = fopen(vfstab, "r")) == NULL ||
3160 (vfs_ents = fopen(vfstab, "r")) == NULL) {
3161 err = errno;
3162 be_print_err(gettext("_update_vfstab: "
3163 "failed to open vfstab (%s): %s\n"), vfstab,
3164 strerror(err));
3165 ret = errno_to_be_err(err);
3166 goto cleanup;
3169 /* Grab the stats of the original vfstab file */
3170 if (stat(vfstab, &sb) != 0) {
3171 err = errno;
3172 be_print_err(gettext("_update_vfstab: "
3173 "failed to stat file %s: %s\n"), vfstab,
3174 strerror(err));
3175 ret = errno_to_be_err(err);
3176 goto cleanup;
3179 /* Create tmp file for modified vfstab */
3180 if ((tmp_vfstab = (char *)malloc(strlen(vfstab) + 7))
3181 == NULL) {
3182 be_print_err(gettext("_update_vfstab: "
3183 "malloc failed\n"));
3184 ret = BE_ERR_NOMEM;
3185 goto cleanup;
3187 tmp_vfstab_len = strlen(vfstab) + 7;
3188 (void) memset(tmp_vfstab, 0, tmp_vfstab_len);
3189 (void) strlcpy(tmp_vfstab, vfstab, tmp_vfstab_len);
3190 (void) strlcat(tmp_vfstab, "XXXXXX", tmp_vfstab_len);
3191 if ((fd = mkstemp(tmp_vfstab)) == -1) {
3192 err = errno;
3193 be_print_err(gettext("_update_vfstab: "
3194 "mkstemp failed: %s\n"), strerror(err));
3195 ret = errno_to_be_err(err);
3196 goto cleanup;
3198 if ((tfile = fdopen(fd, "w")) == NULL) {
3199 err = errno;
3200 be_print_err(gettext("_update_vfstab: "
3201 "could not open file for write\n"));
3202 (void) close(fd);
3203 ret = errno_to_be_err(err);
3204 goto cleanup;
3207 while (fgets(comments_buf, BUFSIZ, comments)) {
3208 for (c = comments_buf; *c != '\0' && isspace(*c); c++)
3210 if (*c == '\0') {
3211 continue;
3212 } else if (*c == '#') {
3214 * If line is a comment line, just put
3215 * it through to the tmp vfstab.
3217 (void) fputs(comments_buf, tfile);
3218 } else {
3220 * Else line is a vfstab entry, grab it
3221 * into a vfstab struct.
3223 if (getvfsent(vfs_ents, &vp) != 0) {
3224 err = errno;
3225 be_print_err(gettext("_update_vfstab: "
3226 "getvfsent failed: %s\n"), strerror(err));
3227 ret = errno_to_be_err(err);
3228 goto cleanup;
3231 if (vp.vfs_special == NULL || vp.vfs_mountp == NULL) {
3232 (void) putvfsent(tfile, &vp);
3233 continue;
3237 * If the entry is one of the entries in the list
3238 * of file systems to update, modify it's device
3239 * field to be correct for this BE.
3241 for (i = 0; i < fld->fs_num; i++) {
3242 if (strcmp(vp.vfs_special, fld->fs_list[i])
3243 == 0) {
3245 * Found entry that needs an update.
3246 * Replace the root container dataset
3247 * location and be_name in the
3248 * entry's device.
3250 (void) strlcpy(dev, vp.vfs_special,
3251 sizeof (dev));
3253 if ((ret = update_dataset(dev,
3254 sizeof (dev), be_name, old_rc_loc,
3255 new_rc_loc)) != 0) {
3256 be_print_err(
3257 gettext("_update_vfstab: "
3258 "Failed to update device "
3259 "field for vfstab entry "
3260 "%s\n"), fld->fs_list[i]);
3261 goto cleanup;
3264 vp.vfs_special = dev;
3265 break;
3269 /* Put entry through to tmp vfstab */
3270 (void) putvfsent(tfile, &vp);
3274 (void) fclose(comments);
3275 comments = NULL;
3276 (void) fclose(vfs_ents);
3277 vfs_ents = NULL;
3278 (void) fclose(tfile);
3279 tfile = NULL;
3281 /* Copy tmp vfstab into place */
3282 if (rename(tmp_vfstab, vfstab) != 0) {
3283 err = errno;
3284 be_print_err(gettext("_update_vfstab: "
3285 "failed to rename file %s to %s: %s\n"), tmp_vfstab,
3286 vfstab, strerror(err));
3287 ret = errno_to_be_err(err);
3288 goto cleanup;
3291 /* Set the perms and ownership of the updated file */
3292 if (chmod(vfstab, sb.st_mode) != 0) {
3293 err = errno;
3294 be_print_err(gettext("_update_vfstab: "
3295 "failed to chmod %s: %s\n"), vfstab, strerror(err));
3296 ret = errno_to_be_err(err);
3297 goto cleanup;
3299 if (chown(vfstab, sb.st_uid, sb.st_gid) != 0) {
3300 err = errno;
3301 be_print_err(gettext("_update_vfstab: "
3302 "failed to chown %s: %s\n"), vfstab, strerror(err));
3303 ret = errno_to_be_err(err);
3304 goto cleanup;
3307 cleanup:
3308 if (comments != NULL)
3309 (void) fclose(comments);
3310 if (vfs_ents != NULL)
3311 (void) fclose(vfs_ents);
3312 (void) unlink(tmp_vfstab);
3313 (void) free(tmp_vfstab);
3314 if (tfile != NULL)
3315 (void) fclose(tfile);
3317 return (ret);
3322 * Function: be_get_auto_name
3323 * Description: Generate an auto name constructed based on the BE name
3324 * of the original BE or zone BE being cloned.
3325 * Parameters:
3326 * obe_name - name of the original BE or zone BE being cloned.
3327 * container_ds - container dataset for the zone.
3328 * Note: if zone_be is false this should be
3329 * NULL.
3330 * zone_be - flag that indicates if we are operating on a zone BE.
3331 * Returns:
3332 * Success - pointer to auto generated BE name. The name
3333 * is allocated in heap storage so the caller is
3334 * responsible for free'ing the name.
3335 * Failure - NULL
3336 * Scope:
3337 * Private
3339 static char *
3340 be_get_auto_name(char *obe_name, char *be_container_ds, boolean_t zone_be)
3342 be_node_list_t *be_nodes = NULL;
3343 be_node_list_t *cur_be = NULL;
3344 char auto_be_name[MAXPATHLEN];
3345 char base_be_name[MAXPATHLEN];
3346 char cur_be_name[MAXPATHLEN];
3347 char *num_str = NULL;
3348 char *c = NULL;
3349 int num = 0;
3350 int cur_num = 0;
3352 errno = 0;
3355 * Check if obe_name is already in an auto BE name format.
3356 * If it is, then strip off the increment number to get the
3357 * base name.
3359 (void) strlcpy(base_be_name, obe_name, sizeof (base_be_name));
3361 if ((num_str = strrchr(base_be_name, BE_AUTO_NAME_DELIM))
3362 != NULL) {
3363 /* Make sure remaining string is all digits */
3364 c = num_str + 1;
3365 while (c[0] != '\0' && isdigit(c[0]))
3366 c++;
3368 * If we're now at the end of the string strip off the
3369 * increment number.
3371 if (c[0] == '\0')
3372 num_str[0] = '\0';
3375 if (zone_be) {
3376 if (be_container_ds == NULL)
3377 return (NULL);
3378 if (be_get_zone_be_list(obe_name, be_container_ds,
3379 &be_nodes) != BE_SUCCESS) {
3380 be_print_err(gettext("be_get_auto_name: "
3381 "be_get_zone_be_list failed\n"));
3382 return (NULL);
3384 } else if (_be_list(NULL, &be_nodes) != BE_SUCCESS) {
3385 be_print_err(gettext("be_get_auto_name: be_list failed\n"));
3386 return (NULL);
3389 for (cur_be = be_nodes; cur_be != NULL; cur_be = cur_be->be_next_node) {
3390 (void) strlcpy(cur_be_name, cur_be->be_node_name,
3391 sizeof (cur_be_name));
3393 /* If cur_be_name doesn't match at least base be name, skip. */
3394 if (strncmp(cur_be_name, base_be_name, strlen(base_be_name))
3395 != 0)
3396 continue;
3398 /* Get the string following the base be name */
3399 num_str = cur_be_name + strlen(base_be_name);
3402 * If nothing follows the base be name, this cur_be_name
3403 * is the BE named with the base be name, skip.
3405 if (num_str == NULL || num_str[0] == '\0')
3406 continue;
3409 * Remove the name delimiter. If its not there,
3410 * cur_be_name isn't part of this BE name stream, skip.
3412 if (num_str[0] == BE_AUTO_NAME_DELIM)
3413 num_str++;
3414 else
3415 continue;
3417 /* Make sure remaining string is all digits */
3418 c = num_str;
3419 while (c[0] != '\0' && isdigit(c[0]))
3420 c++;
3421 if (c[0] != '\0')
3422 continue;
3424 /* Convert the number string to an int */
3425 cur_num = atoi(num_str);
3428 * If failed to convert the string, skip it. If its too
3429 * long to be converted to an int, we wouldn't auto generate
3430 * this number anyway so there couldn't be a conflict.
3431 * We treat it as a manually created BE name.
3433 if (cur_num == 0 && errno == EINVAL)
3434 continue;
3437 * Compare current number to current max number,
3438 * take higher of the two.
3440 if (cur_num > num)
3441 num = cur_num;
3445 * Store off a copy of 'num' incase we need it later. If incrementing
3446 * 'num' causes it to roll over, this means 'num' is the largest
3447 * positive int possible; we'll need it later in the loop to determine
3448 * if we've exhausted all possible increment numbers. We store it in
3449 * 'cur_num'.
3451 cur_num = num;
3453 /* Increment 'num' to get new auto BE name number */
3454 if (++num <= 0) {
3455 int ret = 0;
3458 * Since incrementing 'num' caused it to rollover, start
3459 * over at 0 and find the first available number.
3461 for (num = 0; num < cur_num; num++) {
3463 (void) snprintf(cur_be_name, sizeof (cur_be_name),
3464 "%s%c%d", base_be_name, BE_AUTO_NAME_DELIM, num);
3466 ret = zpool_iter(g_zfs, be_exists_callback,
3467 cur_be_name);
3469 if (ret == 0) {
3471 * BE name doesn't exist, break out
3472 * to use 'num'.
3474 break;
3475 } else if (ret == 1) {
3476 /* BE name exists, continue looking */
3477 continue;
3478 } else {
3479 be_print_err(gettext("be_get_auto_name: "
3480 "zpool_iter failed: %s\n"),
3481 libzfs_error_description(g_zfs));
3482 be_free_list(be_nodes);
3483 return (NULL);
3488 * If 'num' equals 'cur_num', we've exhausted all possible
3489 * auto BE names for this base BE name.
3491 if (num == cur_num) {
3492 be_print_err(gettext("be_get_auto_name: "
3493 "No more available auto BE names for base "
3494 "BE name %s\n"), base_be_name);
3495 be_free_list(be_nodes);
3496 return (NULL);
3500 be_free_list(be_nodes);
3503 * Generate string for auto BE name.
3505 (void) snprintf(auto_be_name, sizeof (auto_be_name), "%s%c%d",
3506 base_be_name, BE_AUTO_NAME_DELIM, num);
3508 if ((c = strdup(auto_be_name)) == NULL) {
3509 be_print_err(gettext("be_get_auto_name: "
3510 "memory allocation failed\n"));
3511 return (NULL);
3514 return (c);
3518 * Function: be_get_console_prop
3519 * Description: Determine console device.
3520 * Returns:
3521 * Success - pointer to console setting.
3522 * Failure - NULL
3523 * Scope:
3524 * Private
3526 static char *
3527 be_get_console_prop(void)
3529 di_node_t dn;
3530 char *console = NULL;
3532 if ((dn = di_init("/", DINFOPROP)) == DI_NODE_NIL) {
3533 be_print_err(gettext("be_get_console_prop: "
3534 "di_init() failed\n"));
3535 return (NULL);
3538 if (di_prop_lookup_strings(DDI_DEV_T_ANY, dn,
3539 "console", &console) != -1) {
3540 di_fini(dn);
3541 return (console);
3544 if (console == NULL) {
3545 if (di_prop_lookup_strings(DDI_DEV_T_ANY, dn,
3546 "output-device", &console) != -1) {
3547 di_fini(dn);
3548 if (strncmp(console, "screen", strlen("screen")) == 0)
3549 console = BE_DEFAULT_CONSOLE;
3554 * Default console to text
3556 if (console == NULL) {
3557 console = BE_DEFAULT_CONSOLE;
3560 return (console);
3564 * Function: be_create_menu
3565 * Description:
3566 * This function is used if no menu.lst file exists. In
3567 * this case a new file is created and if needed default
3568 * lines are added to the file.
3569 * Parameters:
3570 * pool - The name of the pool the menu.lst file is on
3571 * menu_file - The name of the file we're creating.
3572 * menu_fp - A pointer to the file pointer of the file we
3573 * created. This is also used to pass back the file
3574 * pointer to the newly created file.
3575 * mode - the original mode used for the failed attempt to
3576 * non-existent file.
3577 * Returns:
3578 * BE_SUCCESS - Success
3579 * be_errno_t - Failure
3580 * Scope:
3581 * Private
3583 static int
3584 be_create_menu(
3585 char *pool,
3586 char *menu_file,
3587 FILE **menu_fp,
3588 char *mode)
3590 be_node_list_t *be_nodes = NULL;
3591 char *menu_path = NULL;
3592 char *be_rpool = NULL;
3593 char *be_name = NULL;
3594 char *console = NULL;
3595 errno = 0;
3597 if (menu_file == NULL || menu_fp == NULL || mode == NULL)
3598 return (BE_ERR_INVAL);
3600 menu_path = strdup(menu_file);
3601 if (menu_path == NULL)
3602 return (BE_ERR_NOMEM);
3604 (void) dirname(menu_path);
3605 if (*menu_path == '.') {
3606 free(menu_path);
3607 return (BE_ERR_BAD_MENU_PATH);
3609 if (mkdirp(menu_path,
3610 S_IRWXU | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH) == -1 &&
3611 errno != EEXIST) {
3612 free(menu_path);
3613 be_print_err(gettext("be_create_menu: Failed to create the %s "
3614 "directory: %s\n"), menu_path, strerror(errno));
3615 return (errno_to_be_err(errno));
3617 free(menu_path);
3620 * Check to see if this system supports grub
3622 if (be_has_grub()) {
3624 * The grub menu is missing so we need to create it
3625 * and fill in the first few lines.
3627 FILE *temp_fp = fopen(menu_file, "a+");
3628 if (temp_fp == NULL) {
3629 *menu_fp = NULL;
3630 return (errno_to_be_err(errno));
3633 if ((console = be_get_console_prop()) != NULL) {
3636 * If console is redirected to serial line,
3637 * GRUB splash screen will not be enabled.
3639 if (strncmp(console, "text", strlen("text")) == 0 ||
3640 strncmp(console, "graphics",
3641 strlen("graphics")) == 0) {
3643 (void) fprintf(temp_fp, "%s\n", BE_GRUB_SPLASH);
3644 (void) fprintf(temp_fp, "%s\n",
3645 BE_GRUB_FOREGROUND);
3646 (void) fprintf(temp_fp, "%s\n",
3647 BE_GRUB_BACKGROUND);
3648 (void) fprintf(temp_fp, "%s\n",
3649 BE_GRUB_DEFAULT);
3650 } else {
3651 be_print_err(gettext("be_create_menu: "
3652 "console on serial line, "
3653 "GRUB splash image will be disabled\n"));
3657 (void) fprintf(temp_fp, "timeout 30\n");
3658 (void) fclose(temp_fp);
3660 } else {
3662 * The menu file doesn't exist so we need to create a
3663 * blank file.
3665 FILE *temp_fp = fopen(menu_file, "w+");
3666 if (temp_fp == NULL) {
3667 *menu_fp = NULL;
3668 return (errno_to_be_err(errno));
3670 (void) fclose(temp_fp);
3674 * Now we need to add all the BE's back into the the file.
3676 if (_be_list(NULL, &be_nodes) == BE_SUCCESS) {
3677 while (be_nodes != NULL) {
3678 if (strcmp(pool, be_nodes->be_rpool) == 0) {
3679 (void) be_append_menu(be_nodes->be_node_name,
3680 be_nodes->be_rpool, NULL, NULL, NULL);
3682 if (be_nodes->be_active_on_boot) {
3683 be_rpool = strdup(be_nodes->be_rpool);
3684 be_name = strdup(be_nodes->be_node_name);
3687 be_nodes = be_nodes->be_next_node;
3690 be_free_list(be_nodes);
3693 * Check to see if this system supports grub
3695 if (be_has_grub()) {
3696 int err = be_change_grub_default(be_name, be_rpool);
3697 if (err != BE_SUCCESS)
3698 return (err);
3700 *menu_fp = fopen(menu_file, mode);
3701 if (*menu_fp == NULL)
3702 return (errno_to_be_err(errno));
3704 return (BE_SUCCESS);
3708 * Function: be_open_menu
3709 * Description:
3710 * This function is used it open the menu.lst file. If this
3711 * file does not exist be_create_menu is called to create it
3712 * and the open file pointer is returned. If the file does
3713 * exist it is simply opened using the mode passed in.
3714 * Parameters:
3715 * pool - The name of the pool the menu.lst file is on
3716 * menu_file - The name of the file we're opening.
3717 * menu_fp - A pointer to the file pointer of the file we're
3718 * opening. This is also used to pass back the file
3719 * pointer.
3720 * mode - the original mode to be used for opening the menu.lst
3721 * file.
3722 * create_menu - If this is true and the menu.lst file does not
3723 * exist we will attempt to re-create it. However
3724 * if it's false the error returned from the fopen
3725 * will be returned.
3726 * Returns:
3727 * BE_SUCCESS - Success
3728 * be_errno_t - Failure
3729 * Scope:
3730 * Private
3732 static int
3733 be_open_menu(
3734 char *pool,
3735 char *menu_file,
3736 FILE **menu_fp,
3737 char *mode,
3738 boolean_t create_menu)
3740 int err = 0;
3741 boolean_t set_print = B_FALSE;
3743 *menu_fp = fopen(menu_file, mode);
3744 err = errno;
3745 if (*menu_fp == NULL) {
3746 if (err == ENOENT && create_menu) {
3747 be_print_err(gettext("be_open_menu: menu.lst "
3748 "file %s does not exist,\n"), menu_file);
3749 if (!do_print) {
3750 set_print = B_TRUE;
3751 do_print = B_TRUE;
3753 be_print_err(gettext("WARNING: menu.lst "
3754 "file %s does not exist,\n generating "
3755 "a new menu.lst file\n"), menu_file);
3756 if (set_print)
3757 do_print = B_FALSE;
3758 err = 0;
3759 if ((err = be_create_menu(pool, menu_file,
3760 menu_fp, mode)) == ENOENT)
3761 return (BE_ERR_NO_MENU);
3762 else if (err != BE_SUCCESS)
3763 return (err);
3764 else if (*menu_fp == NULL)
3765 return (BE_ERR_NO_MENU);
3766 } else {
3767 be_print_err(gettext("be_open_menu: failed "
3768 "to open menu.lst file %s\n"), menu_file);
3769 if (err == ENOENT)
3770 return (BE_ERR_NO_MENU);
3771 else
3772 return (errno_to_be_err(err));
3775 return (BE_SUCCESS);