6073 libbe be_run_cmd() bad indentation in function body
[unleashed.git] / usr / src / lib / libbe / common / be_utils.c
blob681e72366dc1f07cf86bd92b494f5812ad73c64d
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 2013 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>
56 #include <libgen.h>
58 #include <libbe.h>
59 #include <libbe_priv.h>
61 /* Private function prototypes */
62 static int update_dataset(char *, int, char *, char *, char *);
63 static int _update_vfstab(char *, char *, char *, char *, be_fs_list_data_t *);
64 static int be_open_menu(char *, char *, FILE **, char *, boolean_t);
65 static int be_create_menu(char *, char *, FILE **, char *);
66 static char *be_get_auto_name(char *, char *, boolean_t);
69 * Global error printing
71 boolean_t do_print = B_FALSE;
74 * Private datatypes
76 typedef struct zone_be_name_cb_data {
77 char *base_be_name;
78 int num;
79 } zone_be_name_cb_data_t;
81 /* ******************************************************************** */
82 /* Public Functions */
83 /* ******************************************************************** */
86 * Function: be_max_avail
87 * Description: Returns the available size for the zfs dataset passed in.
88 * Parameters:
89 * dataset - The dataset we want to get the available space for.
90 * ret - The available size will be returned in this.
91 * Returns:
92 * The error returned by the zfs get property function.
93 * Scope:
94 * Public
96 int
97 be_max_avail(char *dataset, uint64_t *ret)
99 zfs_handle_t *zhp;
100 int err = 0;
102 /* Initialize libzfs handle */
103 if (!be_zfs_init())
104 return (BE_ERR_INIT);
106 zhp = zfs_open(g_zfs, dataset, ZFS_TYPE_DATASET);
107 if (zhp == NULL) {
109 * The zfs_open failed return an error
111 err = zfs_err_to_be_err(g_zfs);
112 } else {
113 err = be_maxsize_avail(zhp, ret);
115 ZFS_CLOSE(zhp);
116 be_zfs_fini();
117 return (err);
121 * Function: libbe_print_errors
122 * Description: Turns on/off error output for the library.
123 * Parameter:
124 * set_do_print - Boolean that turns library error
125 * printing on or off.
126 * Returns:
127 * None
128 * Scope:
129 * Public;
131 void
132 libbe_print_errors(boolean_t set_do_print)
134 do_print = set_do_print;
137 /* ******************************************************************** */
138 /* Semi-Private Functions */
139 /* ******************************************************************** */
142 * Function: be_zfs_init
143 * Description: Initializes the libary global libzfs handle.
144 * Parameters:
145 * None
146 * Returns:
147 * B_TRUE - Success
148 * B_FALSE - Failure
149 * Scope:
150 * Semi-private (library wide use only)
152 boolean_t
153 be_zfs_init(void)
155 be_zfs_fini();
157 if ((g_zfs = libzfs_init()) == NULL) {
158 be_print_err(gettext("be_zfs_init: failed to initialize ZFS "
159 "library\n"));
160 return (B_FALSE);
163 return (B_TRUE);
167 * Function: be_zfs_fini
168 * Description: Closes the library global libzfs handle if it currently open.
169 * Parameter:
170 * None
171 * Returns:
172 * None
173 * Scope:
174 * Semi-private (library wide use only)
176 void
177 be_zfs_fini(void)
179 if (g_zfs)
180 libzfs_fini(g_zfs);
182 g_zfs = NULL;
186 * Function: be_get_defaults
187 * Description: Open defaults and gets be default paramets
188 * Parameters:
189 * defaults - be defaults struct
190 * Returns:
191 * None
192 * Scope:
193 * Semi-private (library wide use only)
195 void
196 be_get_defaults(struct be_defaults *defaults)
198 void *defp;
200 defaults->be_deflt_rpool_container = B_FALSE;
201 defaults->be_deflt_bename_starts_with[0] = '\0';
203 if ((defp = defopen_r(BE_DEFAULTS)) != NULL) {
204 const char *res = defread_r(BE_DFLT_BENAME_STARTS, defp);
205 if (res != NULL && res[0] != NULL) {
206 (void) strlcpy(defaults->be_deflt_bename_starts_with,
207 res, ZFS_MAXNAMELEN);
208 defaults->be_deflt_rpool_container = B_TRUE;
210 defclose_r(defp);
215 * Function: be_make_root_ds
216 * Description: Generate string for BE's root dataset given the pool
217 * it lives in and the BE name.
218 * Parameters:
219 * zpool - pointer zpool name.
220 * be_name - pointer to BE name.
221 * be_root_ds - pointer to buffer to return BE root dataset in.
222 * be_root_ds_size - size of be_root_ds
223 * Returns:
224 * None
225 * Scope:
226 * Semi-private (library wide use only)
228 void
229 be_make_root_ds(const char *zpool, const char *be_name, char *be_root_ds,
230 int be_root_ds_size)
232 struct be_defaults be_defaults;
233 be_get_defaults(&be_defaults);
234 char *root_ds = NULL;
236 if (getzoneid() == GLOBAL_ZONEID) {
237 if (be_defaults.be_deflt_rpool_container) {
238 (void) snprintf(be_root_ds, be_root_ds_size,
239 "%s/%s", zpool, be_name);
240 } else {
241 (void) snprintf(be_root_ds, be_root_ds_size,
242 "%s/%s/%s", zpool, BE_CONTAINER_DS_NAME, be_name);
244 } else {
246 * In non-global zone we can use path from mounted root dataset
247 * to generate BE's root dataset string.
249 if ((root_ds = be_get_ds_from_dir("/")) != NULL) {
250 (void) snprintf(be_root_ds, be_root_ds_size, "%s/%s",
251 dirname(root_ds), be_name);
252 } else {
253 be_print_err(gettext("be_make_root_ds: zone root "
254 "dataset is not mounted\n"));
255 return;
261 * Function: be_make_container_ds
262 * Description: Generate string for the BE container dataset given a pool name.
263 * Parameters:
264 * zpool - pointer zpool name.
265 * container_ds - pointer to buffer to return BE container
266 * dataset in.
267 * container_ds_size - size of container_ds
268 * Returns:
269 * None
270 * Scope:
271 * Semi-private (library wide use only)
273 void
274 be_make_container_ds(const char *zpool, char *container_ds,
275 int container_ds_size)
277 struct be_defaults be_defaults;
278 be_get_defaults(&be_defaults);
279 char *root_ds = NULL;
281 if (getzoneid() == GLOBAL_ZONEID) {
282 if (be_defaults.be_deflt_rpool_container) {
283 (void) snprintf(container_ds, container_ds_size,
284 "%s", zpool);
285 } else {
286 (void) snprintf(container_ds, container_ds_size,
287 "%s/%s", zpool, BE_CONTAINER_DS_NAME);
289 } else {
290 if ((root_ds = be_get_ds_from_dir("/")) != NULL) {
291 (void) strlcpy(container_ds, dirname(root_ds),
292 container_ds_size);
293 } else {
294 be_print_err(gettext("be_make_container_ds: zone root "
295 "dataset is not mounted\n"));
296 return;
302 * Function: be_make_name_from_ds
303 * Description: This function takes a dataset name and strips off the
304 * BE container dataset portion from the beginning. The
305 * returned name is allocated in heap storage, so the caller
306 * is responsible for freeing it.
307 * Parameters:
308 * dataset - dataset to get name from.
309 * rc_loc - dataset underwhich the root container dataset lives.
310 * Returns:
311 * name of dataset relative to BE container dataset.
312 * NULL if dataset is not under a BE root dataset.
313 * Scope:
314 * Semi-primate (library wide use only)
316 char *
317 be_make_name_from_ds(const char *dataset, char *rc_loc)
319 char ds[ZFS_MAXNAMELEN];
320 char *tok = NULL;
321 char *name = NULL;
322 struct be_defaults be_defaults;
323 int rlen = strlen(rc_loc);
325 be_get_defaults(&be_defaults);
328 * First token is the location of where the root container dataset
329 * lives; it must match rc_loc.
331 if (strncmp(dataset, rc_loc, rlen) == 0 && dataset[rlen] == '/')
332 (void) strlcpy(ds, dataset + rlen + 1, sizeof (ds));
333 else
334 return (NULL);
336 if (be_defaults.be_deflt_rpool_container) {
337 if ((name = strdup(ds)) == NULL) {
338 be_print_err(gettext("be_make_name_from_ds: "
339 "memory allocation failed\n"));
340 return (NULL);
342 } else {
343 /* Second token must be BE container dataset name */
344 if ((tok = strtok(ds, "/")) == NULL ||
345 strcmp(tok, BE_CONTAINER_DS_NAME) != 0)
346 return (NULL);
348 /* Return the remaining token if one exists */
349 if ((tok = strtok(NULL, "")) == NULL)
350 return (NULL);
352 if ((name = strdup(tok)) == NULL) {
353 be_print_err(gettext("be_make_name_from_ds: "
354 "memory allocation failed\n"));
355 return (NULL);
359 return (name);
363 * Function: be_maxsize_avail
364 * Description: Returns the available size for the zfs handle passed in.
365 * Parameters:
366 * zhp - A pointer to the open zfs handle.
367 * ret - The available size will be returned in this.
368 * Returns:
369 * The error returned by the zfs get property function.
370 * Scope:
371 * Semi-private (library wide use only)
374 be_maxsize_avail(zfs_handle_t *zhp, uint64_t *ret)
376 return ((*ret = zfs_prop_get_int(zhp, ZFS_PROP_AVAILABLE)));
380 * Function: be_append_menu
381 * Description: Appends an entry for a BE into the menu.lst.
382 * Parameters:
383 * be_name - pointer to name of BE to add boot menu entry for.
384 * be_root_pool - pointer to name of pool BE lives in.
385 * boot_pool - Used if the pool containing the grub menu is
386 * different than the one contaiing the BE. This
387 * will normally be NULL.
388 * be_orig_root_ds - The root dataset for the BE. This is
389 * used to check to see if an entry already exists
390 * for this BE.
391 * description - pointer to description of BE to be added in
392 * the title line for this BEs entry.
393 * Returns:
394 * BE_SUCCESS - Success
395 * be_errno_t - Failure
396 * Scope:
397 * Semi-private (library wide use only)
400 be_append_menu(char *be_name, char *be_root_pool, char *boot_pool,
401 char *be_orig_root_ds, char *description)
403 zfs_handle_t *zhp = NULL;
404 char menu_file[MAXPATHLEN];
405 char be_root_ds[MAXPATHLEN];
406 char line[BUFSIZ];
407 char temp_line[BUFSIZ];
408 char title[MAXPATHLEN];
409 char *entries[BUFSIZ];
410 char *tmp_entries[BUFSIZ];
411 char *pool_mntpnt = NULL;
412 char *ptmp_mntpnt = NULL;
413 char *orig_mntpnt = NULL;
414 boolean_t found_be = B_FALSE;
415 boolean_t found_orig_be = B_FALSE;
416 boolean_t found_title = B_FALSE;
417 boolean_t pool_mounted = B_FALSE;
418 boolean_t collect_lines = B_FALSE;
419 FILE *menu_fp = NULL;
420 int err = 0, ret = BE_SUCCESS;
421 int i, num_tmp_lines = 0, num_lines = 0;
423 if (be_name == NULL || be_root_pool == NULL)
424 return (BE_ERR_INVAL);
426 if (boot_pool == NULL)
427 boot_pool = be_root_pool;
429 if ((zhp = zfs_open(g_zfs, be_root_pool, ZFS_TYPE_DATASET)) == NULL) {
430 be_print_err(gettext("be_append_menu: failed to open "
431 "pool dataset for %s: %s\n"), be_root_pool,
432 libzfs_error_description(g_zfs));
433 return (zfs_err_to_be_err(g_zfs));
437 * Check to see if the pool's dataset is mounted. If it isn't we'll
438 * attempt to mount it.
440 if ((ret = be_mount_pool(zhp, &ptmp_mntpnt, &orig_mntpnt,
441 &pool_mounted)) != BE_SUCCESS) {
442 be_print_err(gettext("be_append_menu: pool dataset "
443 "(%s) could not be mounted\n"), be_root_pool);
444 ZFS_CLOSE(zhp);
445 return (ret);
449 * Get the mountpoint for the root pool dataset.
451 if (!zfs_is_mounted(zhp, &pool_mntpnt)) {
452 be_print_err(gettext("be_append_menu: pool "
453 "dataset (%s) is not mounted. Can't set "
454 "the default BE in the grub menu.\n"), be_root_pool);
455 ret = BE_ERR_NO_MENU;
456 goto cleanup;
460 * Check to see if this system supports grub
462 if (be_has_grub()) {
463 (void) snprintf(menu_file, sizeof (menu_file),
464 "%s%s", pool_mntpnt, BE_GRUB_MENU);
465 } else {
466 (void) snprintf(menu_file, sizeof (menu_file),
467 "%s%s", pool_mntpnt, BE_SPARC_MENU);
470 be_make_root_ds(be_root_pool, be_name, be_root_ds, sizeof (be_root_ds));
473 * Iterate through menu first to make sure the BE doesn't already
474 * have an entry in the menu.
476 * Additionally while iterating through the menu, if we have an
477 * original root dataset for a BE we're cloning from, we need to keep
478 * track of that BE's menu entry. We will then use the lines from
479 * that entry to create the entry for the new BE.
481 if ((ret = be_open_menu(be_root_pool, menu_file,
482 &menu_fp, "r", B_TRUE)) != BE_SUCCESS) {
483 goto cleanup;
484 } else if (menu_fp == NULL) {
485 ret = BE_ERR_NO_MENU;
486 goto cleanup;
489 free(pool_mntpnt);
490 pool_mntpnt = NULL;
492 while (fgets(line, BUFSIZ, menu_fp)) {
493 char *tok = NULL;
495 (void) strlcpy(temp_line, line, BUFSIZ);
496 tok = strtok(line, BE_WHITE_SPACE);
498 if (tok == NULL || tok[0] == '#') {
499 continue;
500 } else if (strcmp(tok, "title") == 0) {
501 collect_lines = B_FALSE;
502 if ((tok = strtok(NULL, "\n")) == NULL)
503 (void) strlcpy(title, "", sizeof (title));
504 else
505 (void) strlcpy(title, tok, sizeof (title));
506 found_title = B_TRUE;
508 if (num_tmp_lines != 0) {
509 for (i = 0; i < num_tmp_lines; i++) {
510 free(tmp_entries[i]);
511 tmp_entries[i] = NULL;
513 num_tmp_lines = 0;
515 } else if (strcmp(tok, "bootfs") == 0) {
516 char *bootfs = strtok(NULL, BE_WHITE_SPACE);
517 found_title = B_FALSE;
518 if (bootfs == NULL)
519 continue;
521 if (strcmp(bootfs, be_root_ds) == 0) {
522 found_be = B_TRUE;
523 break;
526 if (be_orig_root_ds != NULL &&
527 strcmp(bootfs, be_orig_root_ds) == 0 &&
528 !found_orig_be) {
529 char str[BUFSIZ];
530 found_orig_be = B_TRUE;
531 num_lines = 0;
533 * Store the new title line
535 (void) snprintf(str, BUFSIZ, "title %s\n",
536 description ? description : be_name);
537 entries[num_lines] = strdup(str);
538 num_lines++;
540 * If there are any lines between the title
541 * and the bootfs line store these. Also
542 * free the temporary lines.
544 for (i = 0; i < num_tmp_lines; i++) {
545 entries[num_lines] = tmp_entries[i];
546 tmp_entries[i] = NULL;
547 num_lines++;
549 num_tmp_lines = 0;
551 * Store the new bootfs line.
553 (void) snprintf(str, BUFSIZ, "bootfs %s\n",
554 be_root_ds);
555 entries[num_lines] = strdup(str);
556 num_lines++;
557 collect_lines = B_TRUE;
559 } else if (found_orig_be && collect_lines) {
561 * get the rest of the lines for the original BE and
562 * store them.
564 if (strstr(line, BE_GRUB_COMMENT) != NULL ||
565 strstr(line, "BOOTADM") != NULL)
566 continue;
567 if (strcmp(tok, "splashimage") == 0) {
568 entries[num_lines] =
569 strdup("splashimage "
570 "/boot/splashimage.xpm\n");
571 } else {
572 entries[num_lines] = strdup(temp_line);
574 num_lines++;
575 } else if (found_title && !found_orig_be) {
576 tmp_entries[num_tmp_lines] = strdup(temp_line);
577 num_tmp_lines++;
581 (void) fclose(menu_fp);
583 if (found_be) {
585 * If an entry for this BE was already in the menu, then if
586 * that entry's title matches what we would have put in
587 * return success. Otherwise return failure.
589 char *new_title = description ? description : be_name;
591 if (strcmp(title, new_title) == 0) {
592 ret = BE_SUCCESS;
593 goto cleanup;
594 } else {
595 if (be_remove_menu(be_name, be_root_pool,
596 boot_pool) != BE_SUCCESS) {
597 be_print_err(gettext("be_append_menu: "
598 "Failed to remove existing unusable "
599 "entry '%s' in boot menu.\n"), be_name);
600 ret = BE_ERR_BE_EXISTS;
601 goto cleanup;
606 /* Append BE entry to the end of the file */
607 menu_fp = fopen(menu_file, "a+");
608 err = errno;
609 if (menu_fp == NULL) {
610 be_print_err(gettext("be_append_menu: failed "
611 "to open menu.lst file %s\n"), menu_file);
612 ret = errno_to_be_err(err);
613 goto cleanup;
616 if (found_orig_be) {
618 * write out all the stored lines
620 for (i = 0; i < num_lines; i++) {
621 (void) fprintf(menu_fp, "%s", entries[i]);
622 free(entries[i]);
624 num_lines = 0;
627 * Check to see if this system supports grub
629 if (be_has_grub())
630 (void) fprintf(menu_fp, "%s\n", BE_GRUB_COMMENT);
631 ret = BE_SUCCESS;
632 } else {
633 (void) fprintf(menu_fp, "title %s\n",
634 description ? description : be_name);
635 (void) fprintf(menu_fp, "bootfs %s\n", be_root_ds);
638 * Check to see if this system supports grub
640 if (be_has_grub()) {
641 (void) fprintf(menu_fp, "kernel$ "
642 "/platform/i86pc/kernel/$ISADIR/unix -B "
643 "$ZFS-BOOTFS\n");
644 (void) fprintf(menu_fp, "module$ "
645 "/platform/i86pc/$ISADIR/boot_archive\n");
646 (void) fprintf(menu_fp, "%s\n", BE_GRUB_COMMENT);
648 ret = BE_SUCCESS;
650 (void) fclose(menu_fp);
651 cleanup:
652 if (pool_mounted) {
653 int err = BE_SUCCESS;
654 err = be_unmount_pool(zhp, ptmp_mntpnt, orig_mntpnt);
655 if (ret == BE_SUCCESS)
656 ret = err;
657 free(orig_mntpnt);
658 free(ptmp_mntpnt);
660 ZFS_CLOSE(zhp);
661 if (num_tmp_lines > 0) {
662 for (i = 0; i < num_tmp_lines; i++) {
663 free(tmp_entries[i]);
664 tmp_entries[i] = NULL;
667 if (num_lines > 0) {
668 for (i = 0; i < num_lines; i++) {
669 free(entries[i]);
670 entries[i] = NULL;
673 return (ret);
677 * Function: be_remove_menu
678 * Description: Removes a BE's entry from a menu.lst file.
679 * Parameters:
680 * be_name - the name of BE whose entry is to be removed from
681 * the menu.lst file.
682 * be_root_pool - the pool that be_name lives in.
683 * boot_pool - the pool where the BE is, if different than
684 * the pool containing the boot menu. If this is
685 * NULL it will be set to be_root_pool.
686 * Returns:
687 * BE_SUCCESS - Success
688 * be_errno_t - Failure
689 * Scope:
690 * Semi-private (library wide use only)
693 be_remove_menu(char *be_name, char *be_root_pool, char *boot_pool)
695 zfs_handle_t *zhp = NULL;
696 char be_root_ds[MAXPATHLEN];
697 char **buffer = NULL;
698 char menu_buf[BUFSIZ];
699 char menu[MAXPATHLEN];
700 char *pool_mntpnt = NULL;
701 char *ptmp_mntpnt = NULL;
702 char *orig_mntpnt = NULL;
703 char *tmp_menu = NULL;
704 FILE *menu_fp = NULL;
705 FILE *tmp_menu_fp = NULL;
706 struct stat sb;
707 int ret = BE_SUCCESS;
708 int i;
709 int fd;
710 int err = 0;
711 int nlines = 0;
712 int default_entry = 0;
713 int entry_cnt = 0;
714 int entry_del = 0;
715 int num_entry_del = 0;
716 int tmp_menu_len = 0;
717 boolean_t write = B_TRUE;
718 boolean_t do_buffer = B_FALSE;
719 boolean_t pool_mounted = B_FALSE;
721 if (boot_pool == NULL)
722 boot_pool = be_root_pool;
724 /* Get name of BE's root dataset */
725 be_make_root_ds(be_root_pool, be_name, be_root_ds, sizeof (be_root_ds));
727 /* Get handle to pool dataset */
728 if ((zhp = zfs_open(g_zfs, be_root_pool, ZFS_TYPE_DATASET)) == NULL) {
729 be_print_err(gettext("be_remove_menu: "
730 "failed to open pool dataset for %s: %s"),
731 be_root_pool, libzfs_error_description(g_zfs));
732 return (zfs_err_to_be_err(g_zfs));
736 * Check to see if the pool's dataset is mounted. If it isn't we'll
737 * attempt to mount it.
739 if ((ret = be_mount_pool(zhp, &ptmp_mntpnt, &orig_mntpnt,
740 &pool_mounted)) != BE_SUCCESS) {
741 be_print_err(gettext("be_remove_menu: pool dataset "
742 "(%s) could not be mounted\n"), be_root_pool);
743 ZFS_CLOSE(zhp);
744 return (ret);
748 * Get the mountpoint for the root pool dataset.
750 if (!zfs_is_mounted(zhp, &pool_mntpnt)) {
751 be_print_err(gettext("be_remove_menu: pool "
752 "dataset (%s) is not mounted. Can't set "
753 "the default BE in the grub menu.\n"), be_root_pool);
754 ret = BE_ERR_NO_MENU;
755 goto cleanup;
758 /* Get path to boot menu */
759 (void) strlcpy(menu, pool_mntpnt, sizeof (menu));
762 * Check to see if this system supports grub
764 if (be_has_grub())
765 (void) strlcat(menu, BE_GRUB_MENU, sizeof (menu));
766 else
767 (void) strlcat(menu, BE_SPARC_MENU, sizeof (menu));
769 /* Get handle to boot menu file */
770 if ((ret = be_open_menu(be_root_pool, menu, &menu_fp, "r",
771 B_TRUE)) != BE_SUCCESS) {
772 goto cleanup;
773 } else if (menu_fp == NULL) {
774 ret = BE_ERR_NO_MENU;
775 goto cleanup;
778 free(pool_mntpnt);
779 pool_mntpnt = NULL;
781 /* Grab the stats of the original menu file */
782 if (stat(menu, &sb) != 0) {
783 err = errno;
784 be_print_err(gettext("be_remove_menu: "
785 "failed to stat file %s: %s\n"), menu, strerror(err));
786 ret = errno_to_be_err(err);
787 goto cleanup;
790 /* Create a tmp file for the modified menu.lst */
791 tmp_menu_len = strlen(menu) + 7;
792 if ((tmp_menu = (char *)malloc(tmp_menu_len)) == NULL) {
793 be_print_err(gettext("be_remove_menu: malloc failed\n"));
794 ret = BE_ERR_NOMEM;
795 goto cleanup;
797 (void) memset(tmp_menu, 0, tmp_menu_len);
798 (void) strlcpy(tmp_menu, menu, tmp_menu_len);
799 (void) strlcat(tmp_menu, "XXXXXX", tmp_menu_len);
800 if ((fd = mkstemp(tmp_menu)) == -1) {
801 err = errno;
802 be_print_err(gettext("be_remove_menu: mkstemp failed\n"));
803 ret = errno_to_be_err(err);
804 free(tmp_menu);
805 tmp_menu = NULL;
806 goto cleanup;
808 if ((tmp_menu_fp = fdopen(fd, "w")) == NULL) {
809 err = errno;
810 be_print_err(gettext("be_remove_menu: "
811 "could not open tmp file for write: %s\n"), strerror(err));
812 (void) close(fd);
813 ret = errno_to_be_err(err);
814 goto cleanup;
817 while (fgets(menu_buf, BUFSIZ, menu_fp)) {
818 char tline [BUFSIZ];
819 char *tok = NULL;
821 (void) strlcpy(tline, menu_buf, sizeof (tline));
823 /* Tokenize line */
824 tok = strtok(tline, BE_WHITE_SPACE);
826 if (tok == NULL || tok[0] == '#') {
827 /* Found empty line or comment line */
828 if (do_buffer) {
829 /* Buffer this line */
830 if ((buffer = (char **)realloc(buffer,
831 sizeof (char *)*(nlines + 1))) == NULL) {
832 ret = BE_ERR_NOMEM;
833 goto cleanup;
835 if ((buffer[nlines++] = strdup(menu_buf))
836 == NULL) {
837 ret = BE_ERR_NOMEM;
838 goto cleanup;
841 } else if (write || strncmp(menu_buf, BE_GRUB_COMMENT,
842 strlen(BE_GRUB_COMMENT)) != 0) {
843 /* Write this line out */
844 (void) fputs(menu_buf, tmp_menu_fp);
846 } else if (strcmp(tok, "default") == 0) {
848 * Record what 'default' is set to because we might
849 * need to adjust this upon deleting an entry.
851 tok = strtok(NULL, BE_WHITE_SPACE);
853 if (tok != NULL) {
854 default_entry = atoi(tok);
857 (void) fputs(menu_buf, tmp_menu_fp);
858 } else if (strcmp(tok, "title") == 0) {
860 * If we've reached a 'title' line and do_buffer is
861 * is true, that means we've just buffered an entire
862 * entry without finding a 'bootfs' directive. We
863 * need to write that entry out and keep searching.
865 if (do_buffer) {
866 for (i = 0; i < nlines; i++) {
867 (void) fputs(buffer[i], tmp_menu_fp);
868 free(buffer[i]);
870 free(buffer);
871 buffer = NULL;
872 nlines = 0;
876 * Turn writing off and buffering on, and increment
877 * our entry counter.
879 write = B_FALSE;
880 do_buffer = B_TRUE;
881 entry_cnt++;
883 /* Buffer this 'title' line */
884 if ((buffer = (char **)realloc(buffer,
885 sizeof (char *)*(nlines + 1))) == NULL) {
886 ret = BE_ERR_NOMEM;
887 goto cleanup;
889 if ((buffer[nlines++] = strdup(menu_buf)) == NULL) {
890 ret = BE_ERR_NOMEM;
891 goto cleanup;
894 } else if (strcmp(tok, "bootfs") == 0) {
895 char *bootfs = NULL;
898 * Found a 'bootfs' line. See if it matches the
899 * BE we're looking for.
901 if ((bootfs = strtok(NULL, BE_WHITE_SPACE)) == NULL ||
902 strcmp(bootfs, be_root_ds) != 0) {
904 * Either there's nothing after the 'bootfs'
905 * or this is not the BE we're looking for,
906 * write out the line(s) we've buffered since
907 * finding the title.
909 for (i = 0; i < nlines; i++) {
910 (void) fputs(buffer[i], tmp_menu_fp);
911 free(buffer[i]);
913 free(buffer);
914 buffer = NULL;
915 nlines = 0;
918 * Turn writing back on, and turn off buffering
919 * since this isn't the entry we're looking
920 * for.
922 write = B_TRUE;
923 do_buffer = B_FALSE;
925 /* Write this 'bootfs' line out. */
926 (void) fputs(menu_buf, tmp_menu_fp);
927 } else {
929 * Found the entry we're looking for.
930 * Record its entry number, increment the
931 * number of entries we've deleted, and turn
932 * writing off. Also, throw away the lines
933 * we've buffered for this entry so far, we
934 * don't need them.
936 entry_del = entry_cnt - 1;
937 num_entry_del++;
938 write = B_FALSE;
939 do_buffer = B_FALSE;
941 for (i = 0; i < nlines; i++) {
942 free(buffer[i]);
944 free(buffer);
945 buffer = NULL;
946 nlines = 0;
948 } else {
949 if (do_buffer) {
950 /* Buffer this line */
951 if ((buffer = (char **)realloc(buffer,
952 sizeof (char *)*(nlines + 1))) == NULL) {
953 ret = BE_ERR_NOMEM;
954 goto cleanup;
956 if ((buffer[nlines++] = strdup(menu_buf))
957 == NULL) {
958 ret = BE_ERR_NOMEM;
959 goto cleanup;
961 } else if (write) {
962 /* Write this line out */
963 (void) fputs(menu_buf, tmp_menu_fp);
968 (void) fclose(menu_fp);
969 menu_fp = NULL;
970 (void) fclose(tmp_menu_fp);
971 tmp_menu_fp = NULL;
973 /* Copy the modified menu.lst into place */
974 if (rename(tmp_menu, menu) != 0) {
975 err = errno;
976 be_print_err(gettext("be_remove_menu: "
977 "failed to rename file %s to %s: %s\n"),
978 tmp_menu, menu, strerror(err));
979 ret = errno_to_be_err(err);
980 goto cleanup;
982 free(tmp_menu);
983 tmp_menu = NULL;
986 * If we've removed an entry, see if we need to
987 * adjust the default value in the menu.lst. If the
988 * entry we've deleted comes before the default entry
989 * we need to adjust the default value accordingly.
991 * be_has_grub is used here to check to see if this system
992 * supports grub.
994 if (be_has_grub() && num_entry_del > 0) {
995 if (entry_del <= default_entry) {
996 default_entry = default_entry - num_entry_del;
997 if (default_entry < 0)
998 default_entry = 0;
1001 * Adjust the default value by rewriting the
1002 * menu.lst file. This may be overkill, but to
1003 * preserve the location of the 'default' entry
1004 * in the file, we need to do this.
1007 /* Get handle to boot menu file */
1008 if ((menu_fp = fopen(menu, "r")) == NULL) {
1009 err = errno;
1010 be_print_err(gettext("be_remove_menu: "
1011 "failed to open menu.lst (%s): %s\n"),
1012 menu, strerror(err));
1013 ret = errno_to_be_err(err);
1014 goto cleanup;
1017 /* Create a tmp file for the modified menu.lst */
1018 tmp_menu_len = strlen(menu) + 7;
1019 if ((tmp_menu = (char *)malloc(tmp_menu_len))
1020 == NULL) {
1021 be_print_err(gettext("be_remove_menu: "
1022 "malloc failed\n"));
1023 ret = BE_ERR_NOMEM;
1024 goto cleanup;
1026 (void) memset(tmp_menu, 0, tmp_menu_len);
1027 (void) strlcpy(tmp_menu, menu, tmp_menu_len);
1028 (void) strlcat(tmp_menu, "XXXXXX", tmp_menu_len);
1029 if ((fd = mkstemp(tmp_menu)) == -1) {
1030 err = errno;
1031 be_print_err(gettext("be_remove_menu: "
1032 "mkstemp failed: %s\n"), strerror(err));
1033 ret = errno_to_be_err(err);
1034 free(tmp_menu);
1035 tmp_menu = NULL;
1036 goto cleanup;
1038 if ((tmp_menu_fp = fdopen(fd, "w")) == NULL) {
1039 err = errno;
1040 be_print_err(gettext("be_remove_menu: "
1041 "could not open tmp file for write: %s\n"),
1042 strerror(err));
1043 (void) close(fd);
1044 ret = errno_to_be_err(err);
1045 goto cleanup;
1048 while (fgets(menu_buf, BUFSIZ, menu_fp)) {
1049 char tline [BUFSIZ];
1050 char *tok = NULL;
1052 (void) strlcpy(tline, menu_buf, sizeof (tline));
1054 /* Tokenize line */
1055 tok = strtok(tline, BE_WHITE_SPACE);
1057 if (tok == NULL) {
1058 /* Found empty line, write it out */
1059 (void) fputs(menu_buf, tmp_menu_fp);
1060 } else if (strcmp(tok, "default") == 0) {
1061 /* Found the default line, adjust it */
1062 (void) snprintf(tline, sizeof (tline),
1063 "default %d\n", default_entry);
1065 (void) fputs(tline, tmp_menu_fp);
1066 } else {
1067 /* Pass through all other lines */
1068 (void) fputs(menu_buf, tmp_menu_fp);
1072 (void) fclose(menu_fp);
1073 menu_fp = NULL;
1074 (void) fclose(tmp_menu_fp);
1075 tmp_menu_fp = NULL;
1077 /* Copy the modified menu.lst into place */
1078 if (rename(tmp_menu, menu) != 0) {
1079 err = errno;
1080 be_print_err(gettext("be_remove_menu: "
1081 "failed to rename file %s to %s: %s\n"),
1082 tmp_menu, menu, strerror(err));
1083 ret = errno_to_be_err(err);
1084 goto cleanup;
1087 free(tmp_menu);
1088 tmp_menu = NULL;
1092 /* Set the perms and ownership of the updated file */
1093 if (chmod(menu, sb.st_mode) != 0) {
1094 err = errno;
1095 be_print_err(gettext("be_remove_menu: "
1096 "failed to chmod %s: %s\n"), menu, strerror(err));
1097 ret = errno_to_be_err(err);
1098 goto cleanup;
1100 if (chown(menu, sb.st_uid, sb.st_gid) != 0) {
1101 err = errno;
1102 be_print_err(gettext("be_remove_menu: "
1103 "failed to chown %s: %s\n"), menu, strerror(err));
1104 ret = errno_to_be_err(err);
1105 goto cleanup;
1108 cleanup:
1109 if (pool_mounted) {
1110 int err = BE_SUCCESS;
1111 err = be_unmount_pool(zhp, ptmp_mntpnt, orig_mntpnt);
1112 if (ret == BE_SUCCESS)
1113 ret = err;
1114 free(orig_mntpnt);
1115 free(ptmp_mntpnt);
1117 ZFS_CLOSE(zhp);
1119 free(buffer);
1120 if (menu_fp != NULL)
1121 (void) fclose(menu_fp);
1122 if (tmp_menu_fp != NULL)
1123 (void) fclose(tmp_menu_fp);
1124 if (tmp_menu != NULL) {
1125 (void) unlink(tmp_menu);
1126 free(tmp_menu);
1129 return (ret);
1133 * Function: be_default_grub_bootfs
1134 * Description: This function returns the dataset in the default entry of
1135 * the grub menu. If no default entry is found with a valid bootfs
1136 * entry NULL is returned.
1137 * Parameters:
1138 * be_root_pool - This is the name of the root pool where the
1139 * grub menu can be found.
1140 * def_bootfs - This is used to pass back the bootfs string. On
1141 * error NULL is returned here.
1142 * Returns:
1143 * Success - BE_SUCCESS is returned.
1144 * Failure - a be_errno_t is returned.
1145 * Scope:
1146 * Semi-private (library wide use only)
1149 be_default_grub_bootfs(const char *be_root_pool, char **def_bootfs)
1151 zfs_handle_t *zhp = NULL;
1152 char grub_file[MAXPATHLEN];
1153 FILE *menu_fp;
1154 char line[BUFSIZ];
1155 char *pool_mntpnt = NULL;
1156 char *ptmp_mntpnt = NULL;
1157 char *orig_mntpnt = NULL;
1158 int default_entry = 0, entries = 0;
1159 int found_default = 0;
1160 int ret = BE_SUCCESS;
1161 boolean_t pool_mounted = B_FALSE;
1163 errno = 0;
1166 * Check to see if this system supports grub
1168 if (!be_has_grub()) {
1169 be_print_err(gettext("be_default_grub_bootfs: operation "
1170 "not supported on this architecture\n"));
1171 return (BE_ERR_NOTSUP);
1174 *def_bootfs = NULL;
1176 /* Get handle to pool dataset */
1177 if ((zhp = zfs_open(g_zfs, be_root_pool, ZFS_TYPE_DATASET)) == NULL) {
1178 be_print_err(gettext("be_default_grub_bootfs: "
1179 "failed to open pool dataset for %s: %s"),
1180 be_root_pool, libzfs_error_description(g_zfs));
1181 return (zfs_err_to_be_err(g_zfs));
1185 * Check to see if the pool's dataset is mounted. If it isn't we'll
1186 * attempt to mount it.
1188 if ((ret = be_mount_pool(zhp, &ptmp_mntpnt, &orig_mntpnt,
1189 &pool_mounted)) != BE_SUCCESS) {
1190 be_print_err(gettext("be_default_grub_bootfs: pool dataset "
1191 "(%s) could not be mounted\n"), be_root_pool);
1192 ZFS_CLOSE(zhp);
1193 return (ret);
1197 * Get the mountpoint for the root pool dataset.
1199 if (!zfs_is_mounted(zhp, &pool_mntpnt)) {
1200 be_print_err(gettext("be_default_grub_bootfs: failed "
1201 "to get mount point for the root pool. Can't set "
1202 "the default BE in the grub menu.\n"));
1203 ret = BE_ERR_NO_MENU;
1204 goto cleanup;
1207 (void) snprintf(grub_file, MAXPATHLEN, "%s%s",
1208 pool_mntpnt, BE_GRUB_MENU);
1210 if ((ret = be_open_menu((char *)be_root_pool, grub_file,
1211 &menu_fp, "r", B_FALSE)) != BE_SUCCESS) {
1212 goto cleanup;
1213 } else if (menu_fp == NULL) {
1214 ret = BE_ERR_NO_MENU;
1215 goto cleanup;
1218 free(pool_mntpnt);
1219 pool_mntpnt = NULL;
1221 while (fgets(line, BUFSIZ, menu_fp)) {
1222 char *tok = strtok(line, BE_WHITE_SPACE);
1224 if (tok != NULL && tok[0] != '#') {
1225 if (!found_default) {
1226 if (strcmp(tok, "default") == 0) {
1227 tok = strtok(NULL, BE_WHITE_SPACE);
1228 if (tok != NULL) {
1229 default_entry = atoi(tok);
1230 rewind(menu_fp);
1231 found_default = 1;
1234 continue;
1236 if (strcmp(tok, "title") == 0) {
1237 entries++;
1238 } else if (default_entry == entries - 1) {
1239 if (strcmp(tok, "bootfs") == 0) {
1240 tok = strtok(NULL, BE_WHITE_SPACE);
1241 (void) fclose(menu_fp);
1243 if (tok == NULL) {
1244 ret = BE_SUCCESS;
1245 goto cleanup;
1248 if ((*def_bootfs = strdup(tok)) !=
1249 NULL) {
1250 ret = BE_SUCCESS;
1251 goto cleanup;
1253 be_print_err(gettext(
1254 "be_default_grub_bootfs: "
1255 "memory allocation failed\n"));
1256 ret = BE_ERR_NOMEM;
1257 goto cleanup;
1259 } else if (default_entry < entries - 1) {
1261 * no bootfs entry for the default entry.
1263 break;
1267 (void) fclose(menu_fp);
1269 cleanup:
1270 if (pool_mounted) {
1271 int err = BE_SUCCESS;
1272 err = be_unmount_pool(zhp, ptmp_mntpnt, orig_mntpnt);
1273 if (ret == BE_SUCCESS)
1274 ret = err;
1275 free(orig_mntpnt);
1276 free(ptmp_mntpnt);
1278 ZFS_CLOSE(zhp);
1279 return (ret);
1283 * Function: be_change_grub_default
1284 * Description: This function takes two parameters. These are the name of
1285 * the BE we want to have as the default booted in the grub
1286 * menu and the root pool where the path to the grub menu exists.
1287 * The code takes this and finds the BE's entry in the grub menu
1288 * and changes the default entry to point to that entry in the
1289 * list.
1290 * Parameters:
1291 * be_name - This is the name of the BE wanted as the default
1292 * for the next boot.
1293 * be_root_pool - This is the name of the root pool where the
1294 * grub menu can be found.
1295 * Returns:
1296 * BE_SUCCESS - Success
1297 * be_errno_t - Failure
1298 * Scope:
1299 * Semi-private (library wide use only)
1302 be_change_grub_default(char *be_name, char *be_root_pool)
1304 zfs_handle_t *zhp = NULL;
1305 char grub_file[MAXPATHLEN];
1306 char *temp_grub;
1307 char *pool_mntpnt = NULL;
1308 char *ptmp_mntpnt = NULL;
1309 char *orig_mntpnt = NULL;
1310 char line[BUFSIZ];
1311 char temp_line[BUFSIZ];
1312 char be_root_ds[MAXPATHLEN];
1313 FILE *grub_fp = NULL;
1314 FILE *temp_fp = NULL;
1315 struct stat sb;
1316 int temp_grub_len = 0;
1317 int fd, entries = 0;
1318 int err = 0;
1319 int ret = BE_SUCCESS;
1320 boolean_t found_default = B_FALSE;
1321 boolean_t pool_mounted = B_FALSE;
1323 errno = 0;
1326 * Check to see if this system supports grub
1328 if (!be_has_grub()) {
1329 be_print_err(gettext("be_change_grub_default: operation "
1330 "not supported on this architecture\n"));
1331 return (BE_ERR_NOTSUP);
1334 /* Generate string for BE's root dataset */
1335 be_make_root_ds(be_root_pool, be_name, be_root_ds, sizeof (be_root_ds));
1337 /* Get handle to pool dataset */
1338 if ((zhp = zfs_open(g_zfs, be_root_pool, ZFS_TYPE_DATASET)) == NULL) {
1339 be_print_err(gettext("be_change_grub_default: "
1340 "failed to open pool dataset for %s: %s"),
1341 be_root_pool, libzfs_error_description(g_zfs));
1342 return (zfs_err_to_be_err(g_zfs));
1346 * Check to see if the pool's dataset is mounted. If it isn't we'll
1347 * attempt to mount it.
1349 if ((ret = be_mount_pool(zhp, &ptmp_mntpnt, &orig_mntpnt,
1350 &pool_mounted)) != BE_SUCCESS) {
1351 be_print_err(gettext("be_change_grub_default: pool dataset "
1352 "(%s) could not be mounted\n"), be_root_pool);
1353 ZFS_CLOSE(zhp);
1354 return (ret);
1358 * Get the mountpoint for the root pool dataset.
1360 if (!zfs_is_mounted(zhp, &pool_mntpnt)) {
1361 be_print_err(gettext("be_change_grub_default: pool "
1362 "dataset (%s) is not mounted. Can't set "
1363 "the default BE in the grub menu.\n"), be_root_pool);
1364 ret = BE_ERR_NO_MENU;
1365 goto cleanup;
1368 (void) snprintf(grub_file, MAXPATHLEN, "%s%s",
1369 pool_mntpnt, BE_GRUB_MENU);
1371 if ((ret = be_open_menu(be_root_pool, grub_file,
1372 &grub_fp, "r+", B_TRUE)) != BE_SUCCESS) {
1373 goto cleanup;
1374 } else if (grub_fp == NULL) {
1375 ret = BE_ERR_NO_MENU;
1376 goto cleanup;
1379 free(pool_mntpnt);
1380 pool_mntpnt = NULL;
1382 /* Grab the stats of the original menu file */
1383 if (stat(grub_file, &sb) != 0) {
1384 err = errno;
1385 be_print_err(gettext("be_change_grub_default: "
1386 "failed to stat file %s: %s\n"), grub_file, strerror(err));
1387 ret = errno_to_be_err(err);
1388 goto cleanup;
1391 /* Create a tmp file for the modified menu.lst */
1392 temp_grub_len = strlen(grub_file) + 7;
1393 if ((temp_grub = (char *)malloc(temp_grub_len)) == NULL) {
1394 be_print_err(gettext("be_change_grub_default: "
1395 "malloc failed\n"));
1396 ret = BE_ERR_NOMEM;
1397 goto cleanup;
1399 (void) memset(temp_grub, 0, temp_grub_len);
1400 (void) strlcpy(temp_grub, grub_file, temp_grub_len);
1401 (void) strlcat(temp_grub, "XXXXXX", temp_grub_len);
1402 if ((fd = mkstemp(temp_grub)) == -1) {
1403 err = errno;
1404 be_print_err(gettext("be_change_grub_default: "
1405 "mkstemp failed: %s\n"), strerror(err));
1406 ret = errno_to_be_err(err);
1407 free(temp_grub);
1408 temp_grub = NULL;
1409 goto cleanup;
1411 if ((temp_fp = fdopen(fd, "w")) == NULL) {
1412 err = errno;
1413 be_print_err(gettext("be_change_grub_default: "
1414 "failed to open %s file: %s\n"),
1415 temp_grub, strerror(err));
1416 (void) close(fd);
1417 ret = errno_to_be_err(err);
1418 goto cleanup;
1421 while (fgets(line, BUFSIZ, grub_fp)) {
1422 char *tok = strtok(line, BE_WHITE_SPACE);
1424 if (tok == NULL || tok[0] == '#') {
1425 continue;
1426 } else if (strcmp(tok, "title") == 0) {
1427 entries++;
1428 continue;
1429 } else if (strcmp(tok, "bootfs") == 0) {
1430 char *bootfs = strtok(NULL, BE_WHITE_SPACE);
1431 if (bootfs == NULL)
1432 continue;
1434 if (strcmp(bootfs, be_root_ds) == 0) {
1435 found_default = B_TRUE;
1436 break;
1441 if (!found_default) {
1442 be_print_err(gettext("be_change_grub_default: failed "
1443 "to find entry for %s in the grub menu\n"),
1444 be_name);
1445 ret = BE_ERR_BE_NOENT;
1446 goto cleanup;
1449 rewind(grub_fp);
1451 while (fgets(line, BUFSIZ, grub_fp)) {
1452 char *tok = NULL;
1454 (void) strncpy(temp_line, line, BUFSIZ);
1456 if ((tok = strtok(temp_line, BE_WHITE_SPACE)) != NULL &&
1457 strcmp(tok, "default") == 0) {
1458 (void) snprintf(temp_line, BUFSIZ, "default %d\n",
1459 entries - 1 >= 0 ? entries - 1 : 0);
1460 (void) fputs(temp_line, temp_fp);
1461 } else {
1462 (void) fputs(line, temp_fp);
1466 (void) fclose(grub_fp);
1467 grub_fp = NULL;
1468 (void) fclose(temp_fp);
1469 temp_fp = NULL;
1471 if (rename(temp_grub, grub_file) != 0) {
1472 err = errno;
1473 be_print_err(gettext("be_change_grub_default: "
1474 "failed to rename file %s to %s: %s\n"),
1475 temp_grub, grub_file, strerror(err));
1476 ret = errno_to_be_err(err);
1477 goto cleanup;
1479 free(temp_grub);
1480 temp_grub = NULL;
1482 /* Set the perms and ownership of the updated file */
1483 if (chmod(grub_file, sb.st_mode) != 0) {
1484 err = errno;
1485 be_print_err(gettext("be_change_grub_default: "
1486 "failed to chmod %s: %s\n"), grub_file, strerror(err));
1487 ret = errno_to_be_err(err);
1488 goto cleanup;
1490 if (chown(grub_file, sb.st_uid, sb.st_gid) != 0) {
1491 err = errno;
1492 be_print_err(gettext("be_change_grub_default: "
1493 "failed to chown %s: %s\n"), grub_file, strerror(err));
1494 ret = errno_to_be_err(err);
1497 cleanup:
1498 if (pool_mounted) {
1499 int err = BE_SUCCESS;
1500 err = be_unmount_pool(zhp, ptmp_mntpnt, orig_mntpnt);
1501 if (ret == BE_SUCCESS)
1502 ret = err;
1503 free(orig_mntpnt);
1504 free(ptmp_mntpnt);
1506 ZFS_CLOSE(zhp);
1507 if (grub_fp != NULL)
1508 (void) fclose(grub_fp);
1509 if (temp_fp != NULL)
1510 (void) fclose(temp_fp);
1511 if (temp_grub != NULL) {
1512 (void) unlink(temp_grub);
1513 free(temp_grub);
1516 return (ret);
1520 * Function: be_update_menu
1521 * Description: This function is used by be_rename to change the BE name in
1522 * an existing entry in the grub menu to the new name of the BE.
1523 * Parameters:
1524 * be_orig_name - the original name of the BE
1525 * be_new_name - the new name the BE is being renameed to.
1526 * be_root_pool - The pool which contains the grub menu
1527 * boot_pool - the pool where the BE is, if different than
1528 * the pool containing the boot menu. If this is
1529 * NULL it will be set to be_root_pool.
1530 * Returns:
1531 * BE_SUCCESS - Success
1532 * be_errno_t - Failure
1533 * Scope:
1534 * Semi-private (library wide use only)
1537 be_update_menu(char *be_orig_name, char *be_new_name, char *be_root_pool,
1538 char *boot_pool)
1540 zfs_handle_t *zhp = NULL;
1541 char menu_file[MAXPATHLEN];
1542 char be_root_ds[MAXPATHLEN];
1543 char be_new_root_ds[MAXPATHLEN];
1544 char line[BUFSIZ];
1545 char *pool_mntpnt = NULL;
1546 char *ptmp_mntpnt = NULL;
1547 char *orig_mntpnt = NULL;
1548 char *temp_menu = NULL;
1549 FILE *menu_fp = NULL;
1550 FILE *new_fp = NULL;
1551 struct stat sb;
1552 int temp_menu_len = 0;
1553 int tmp_fd;
1554 int ret = BE_SUCCESS;
1555 int err = 0;
1556 boolean_t pool_mounted = B_FALSE;
1558 errno = 0;
1560 if (boot_pool == NULL)
1561 boot_pool = be_root_pool;
1563 if ((zhp = zfs_open(g_zfs, be_root_pool, ZFS_TYPE_DATASET)) == NULL) {
1564 be_print_err(gettext("be_update_menu: failed to open "
1565 "pool dataset for %s: %s\n"), be_root_pool,
1566 libzfs_error_description(g_zfs));
1567 return (zfs_err_to_be_err(g_zfs));
1571 * Check to see if the pool's dataset is mounted. If it isn't we'll
1572 * attempt to mount it.
1574 if ((ret = be_mount_pool(zhp, &ptmp_mntpnt, &orig_mntpnt,
1575 &pool_mounted)) != BE_SUCCESS) {
1576 be_print_err(gettext("be_update_menu: pool dataset "
1577 "(%s) could not be mounted\n"), be_root_pool);
1578 ZFS_CLOSE(zhp);
1579 return (ret);
1583 * Get the mountpoint for the root pool dataset.
1585 if (!zfs_is_mounted(zhp, &pool_mntpnt)) {
1586 be_print_err(gettext("be_update_menu: failed "
1587 "to get mount point for the root pool. Can't set "
1588 "the default BE in the grub menu.\n"));
1589 ret = BE_ERR_NO_MENU;
1590 goto cleanup;
1594 * Check to see if this system supports grub
1596 if (be_has_grub()) {
1597 (void) snprintf(menu_file, sizeof (menu_file),
1598 "%s%s", pool_mntpnt, BE_GRUB_MENU);
1599 } else {
1600 (void) snprintf(menu_file, sizeof (menu_file),
1601 "%s%s", pool_mntpnt, BE_SPARC_MENU);
1604 be_make_root_ds(be_root_pool, be_orig_name, be_root_ds,
1605 sizeof (be_root_ds));
1606 be_make_root_ds(be_root_pool, be_new_name, be_new_root_ds,
1607 sizeof (be_new_root_ds));
1609 if ((ret = be_open_menu(be_root_pool, menu_file,
1610 &menu_fp, "r", B_TRUE)) != BE_SUCCESS) {
1611 goto cleanup;
1612 } else if (menu_fp == NULL) {
1613 ret = BE_ERR_NO_MENU;
1614 goto cleanup;
1617 free(pool_mntpnt);
1618 pool_mntpnt = NULL;
1620 /* Grab the stat of the original menu file */
1621 if (stat(menu_file, &sb) != 0) {
1622 err = errno;
1623 be_print_err(gettext("be_update_menu: "
1624 "failed to stat file %s: %s\n"), menu_file, strerror(err));
1625 (void) fclose(menu_fp);
1626 ret = errno_to_be_err(err);
1627 goto cleanup;
1630 /* Create tmp file for modified menu.lst */
1631 temp_menu_len = strlen(menu_file) + 7;
1632 if ((temp_menu = (char *)malloc(temp_menu_len))
1633 == NULL) {
1634 be_print_err(gettext("be_update_menu: "
1635 "malloc failed\n"));
1636 (void) fclose(menu_fp);
1637 ret = BE_ERR_NOMEM;
1638 goto cleanup;
1640 (void) memset(temp_menu, 0, temp_menu_len);
1641 (void) strlcpy(temp_menu, menu_file, temp_menu_len);
1642 (void) strlcat(temp_menu, "XXXXXX", temp_menu_len);
1643 if ((tmp_fd = mkstemp(temp_menu)) == -1) {
1644 err = errno;
1645 be_print_err(gettext("be_update_menu: "
1646 "mkstemp failed: %s\n"), strerror(err));
1647 (void) fclose(menu_fp);
1648 free(temp_menu);
1649 ret = errno_to_be_err(err);
1650 goto cleanup;
1652 if ((new_fp = fdopen(tmp_fd, "w")) == NULL) {
1653 err = errno;
1654 be_print_err(gettext("be_update_menu: "
1655 "fdopen failed: %s\n"), strerror(err));
1656 (void) close(tmp_fd);
1657 (void) fclose(menu_fp);
1658 free(temp_menu);
1659 ret = errno_to_be_err(err);
1660 goto cleanup;
1663 while (fgets(line, BUFSIZ, menu_fp)) {
1664 char tline[BUFSIZ];
1665 char new_line[BUFSIZ];
1666 char *c = NULL;
1668 (void) strlcpy(tline, line, sizeof (tline));
1670 /* Tokenize line */
1671 c = strtok(tline, BE_WHITE_SPACE);
1673 if (c == NULL) {
1674 /* Found empty line, write it out. */
1675 (void) fputs(line, new_fp);
1676 } else if (c[0] == '#') {
1677 /* Found a comment line, write it out. */
1678 (void) fputs(line, new_fp);
1679 } else if (strcmp(c, "title") == 0) {
1680 char *name = NULL;
1681 char *desc = NULL;
1684 * Found a 'title' line, parse out BE name or
1685 * the description.
1687 name = strtok(NULL, BE_WHITE_SPACE);
1689 if (name == NULL) {
1691 * Nothing after 'title', just push
1692 * this line through
1694 (void) fputs(line, new_fp);
1695 } else {
1697 * Grab the remainder of the title which
1698 * could be a multi worded description
1700 desc = strtok(NULL, "\n");
1702 if (strcmp(name, be_orig_name) == 0) {
1704 * The first token of the title is
1705 * the old BE name, replace it with
1706 * the new one, and write it out
1707 * along with the remainder of
1708 * description if there is one.
1710 if (desc) {
1711 (void) snprintf(new_line,
1712 sizeof (new_line),
1713 "title %s %s\n",
1714 be_new_name, desc);
1715 } else {
1716 (void) snprintf(new_line,
1717 sizeof (new_line),
1718 "title %s\n", be_new_name);
1721 (void) fputs(new_line, new_fp);
1722 } else {
1723 (void) fputs(line, new_fp);
1726 } else if (strcmp(c, "bootfs") == 0) {
1728 * Found a 'bootfs' line, parse out the BE root
1729 * dataset value.
1731 char *root_ds = strtok(NULL, BE_WHITE_SPACE);
1733 if (root_ds == NULL) {
1735 * Nothing after 'bootfs', just push
1736 * this line through
1738 (void) fputs(line, new_fp);
1739 } else {
1741 * If this bootfs is the one we're renaming,
1742 * write out the new root dataset value
1744 if (strcmp(root_ds, be_root_ds) == 0) {
1745 (void) snprintf(new_line,
1746 sizeof (new_line), "bootfs %s\n",
1747 be_new_root_ds);
1749 (void) fputs(new_line, new_fp);
1750 } else {
1751 (void) fputs(line, new_fp);
1754 } else {
1756 * Found some other line we don't care
1757 * about, write it out.
1759 (void) fputs(line, new_fp);
1763 (void) fclose(menu_fp);
1764 (void) fclose(new_fp);
1765 (void) close(tmp_fd);
1767 if (rename(temp_menu, menu_file) != 0) {
1768 err = errno;
1769 be_print_err(gettext("be_update_menu: "
1770 "failed to rename file %s to %s: %s\n"),
1771 temp_menu, menu_file, strerror(err));
1772 ret = errno_to_be_err(err);
1774 free(temp_menu);
1776 /* Set the perms and ownership of the updated file */
1777 if (chmod(menu_file, sb.st_mode) != 0) {
1778 err = errno;
1779 be_print_err(gettext("be_update_menu: "
1780 "failed to chmod %s: %s\n"), menu_file, strerror(err));
1781 ret = errno_to_be_err(err);
1782 goto cleanup;
1784 if (chown(menu_file, sb.st_uid, sb.st_gid) != 0) {
1785 err = errno;
1786 be_print_err(gettext("be_update_menu: "
1787 "failed to chown %s: %s\n"), menu_file, strerror(err));
1788 ret = errno_to_be_err(err);
1791 cleanup:
1792 if (pool_mounted) {
1793 int err = BE_SUCCESS;
1794 err = be_unmount_pool(zhp, ptmp_mntpnt, orig_mntpnt);
1795 if (ret == BE_SUCCESS)
1796 ret = err;
1797 free(orig_mntpnt);
1798 free(ptmp_mntpnt);
1800 ZFS_CLOSE(zhp);
1801 return (ret);
1805 * Function: be_has_menu_entry
1806 * Description: Checks to see if the BEs root dataset has an entry in the grub
1807 * menu.
1808 * Parameters:
1809 * be_dataset - The root dataset of the BE
1810 * be_root_pool - The pool which contains the boot menu
1811 * entry - A pointer the the entry number of the BE if found.
1812 * Returns:
1813 * B_TRUE - Success
1814 * B_FALSE - Failure
1815 * Scope:
1816 * Semi-private (library wide use only)
1818 boolean_t
1819 be_has_menu_entry(char *be_dataset, char *be_root_pool, int *entry)
1821 zfs_handle_t *zhp = NULL;
1822 char menu_file[MAXPATHLEN];
1823 FILE *menu_fp;
1824 char line[BUFSIZ];
1825 char *last;
1826 char *rpool_mntpnt = NULL;
1827 char *ptmp_mntpnt = NULL;
1828 char *orig_mntpnt = NULL;
1829 int ent_num = 0;
1830 boolean_t ret = 0;
1831 boolean_t pool_mounted = B_FALSE;
1835 * Check to see if this system supports grub
1837 if ((zhp = zfs_open(g_zfs, be_root_pool, ZFS_TYPE_DATASET)) == NULL) {
1838 be_print_err(gettext("be_has_menu_entry: failed to open "
1839 "pool dataset for %s: %s\n"), be_root_pool,
1840 libzfs_error_description(g_zfs));
1841 return (B_FALSE);
1845 * Check to see if the pool's dataset is mounted. If it isn't we'll
1846 * attempt to mount it.
1848 if (be_mount_pool(zhp, &ptmp_mntpnt, &orig_mntpnt,
1849 &pool_mounted) != 0) {
1850 be_print_err(gettext("be_has_menu_entry: pool dataset "
1851 "(%s) could not be mounted\n"), be_root_pool);
1852 ZFS_CLOSE(zhp);
1853 return (B_FALSE);
1857 * Get the mountpoint for the root pool dataset.
1859 if (!zfs_is_mounted(zhp, &rpool_mntpnt)) {
1860 be_print_err(gettext("be_has_menu_entry: pool "
1861 "dataset (%s) is not mounted. Can't set "
1862 "the default BE in the grub menu.\n"), be_root_pool);
1863 ret = B_FALSE;
1864 goto cleanup;
1867 if (be_has_grub()) {
1868 (void) snprintf(menu_file, MAXPATHLEN, "/%s%s",
1869 rpool_mntpnt, BE_GRUB_MENU);
1870 } else {
1871 (void) snprintf(menu_file, MAXPATHLEN, "/%s%s",
1872 rpool_mntpnt, BE_SPARC_MENU);
1875 if (be_open_menu(be_root_pool, menu_file, &menu_fp, "r",
1876 B_FALSE) != 0) {
1877 ret = B_FALSE;
1878 goto cleanup;
1879 } else if (menu_fp == NULL) {
1880 ret = B_FALSE;
1881 goto cleanup;
1884 free(rpool_mntpnt);
1885 rpool_mntpnt = NULL;
1887 while (fgets(line, BUFSIZ, menu_fp)) {
1888 char *tok = strtok_r(line, BE_WHITE_SPACE, &last);
1890 if (tok != NULL && tok[0] != '#') {
1891 if (strcmp(tok, "bootfs") == 0) {
1892 tok = strtok_r(last, BE_WHITE_SPACE, &last);
1893 if (tok != NULL && strcmp(tok,
1894 be_dataset) == 0) {
1895 (void) fclose(menu_fp);
1897 * The entry number needs to be
1898 * decremented here because the title
1899 * will always be the first line for
1900 * an entry. Because of this we'll
1901 * always be off by one entry when we
1902 * check for bootfs.
1904 *entry = ent_num - 1;
1905 ret = B_TRUE;
1906 goto cleanup;
1908 } else if (strcmp(tok, "title") == 0)
1909 ent_num++;
1913 cleanup:
1914 if (pool_mounted) {
1915 (void) be_unmount_pool(zhp, ptmp_mntpnt, orig_mntpnt);
1916 free(orig_mntpnt);
1917 free(ptmp_mntpnt);
1919 ZFS_CLOSE(zhp);
1920 (void) fclose(menu_fp);
1921 return (ret);
1925 * Function: be_update_vfstab
1926 * Description: This function digs into a BE's vfstab and updates all
1927 * entries with file systems listed in be_fs_list_data_t.
1928 * The entry's root container dataset and be_name will be
1929 * updated with the parameters passed in.
1930 * Parameters:
1931 * be_name - name of BE to update
1932 * old_rc_loc - dataset under which the root container dataset
1933 * of the old BE resides in.
1934 * new_rc_loc - dataset under which the root container dataset
1935 * of the new BE resides in.
1936 * fld - be_fs_list_data_t pointer providing the list of
1937 * file systems to look for in vfstab.
1938 * mountpoint - directory of where BE is currently mounted.
1939 * If NULL, then BE is not currently mounted.
1940 * Returns:
1941 * BE_SUCCESS - Success
1942 * be_errno_t - Failure
1943 * Scope:
1944 * Semi-private (library wide use only)
1947 be_update_vfstab(char *be_name, char *old_rc_loc, char *new_rc_loc,
1948 be_fs_list_data_t *fld, char *mountpoint)
1950 char *tmp_mountpoint = NULL;
1951 char alt_vfstab[MAXPATHLEN];
1952 int ret = BE_SUCCESS, err = BE_SUCCESS;
1954 if (fld == NULL || fld->fs_list == NULL || fld->fs_num == 0)
1955 return (BE_SUCCESS);
1957 /* If BE not already mounted, mount the BE */
1958 if (mountpoint == NULL) {
1959 if ((ret = _be_mount(be_name, &tmp_mountpoint,
1960 BE_MOUNT_FLAG_NO_ZONES)) != BE_SUCCESS) {
1961 be_print_err(gettext("be_update_vfstab: "
1962 "failed to mount BE (%s)\n"), be_name);
1963 return (ret);
1965 } else {
1966 tmp_mountpoint = mountpoint;
1969 /* Get string for vfstab in the mounted BE. */
1970 (void) snprintf(alt_vfstab, sizeof (alt_vfstab), "%s/etc/vfstab",
1971 tmp_mountpoint);
1973 /* Update the vfstab */
1974 ret = _update_vfstab(alt_vfstab, be_name, old_rc_loc, new_rc_loc,
1975 fld);
1977 /* Unmount BE if we mounted it */
1978 if (mountpoint == NULL) {
1979 if ((err = _be_unmount(be_name, 0)) == BE_SUCCESS) {
1980 /* Remove temporary mountpoint */
1981 (void) rmdir(tmp_mountpoint);
1982 } else {
1983 be_print_err(gettext("be_update_vfstab: "
1984 "failed to unmount BE %s mounted at %s\n"),
1985 be_name, tmp_mountpoint);
1986 if (ret == BE_SUCCESS)
1987 ret = err;
1990 free(tmp_mountpoint);
1993 return (ret);
1997 * Function: be_update_zone_vfstab
1998 * Description: This function digs into a zone BE's vfstab and updates all
1999 * entries with file systems listed in be_fs_list_data_t.
2000 * The entry's root container dataset and be_name will be
2001 * updated with the parameters passed in.
2002 * Parameters:
2003 * zhp - zfs_handle_t pointer to zone root dataset.
2004 * be_name - name of zone BE to update
2005 * old_rc_loc - dataset under which the root container dataset
2006 * of the old zone BE resides in.
2007 * new_rc_loc - dataset under which the root container dataset
2008 * of the new zone BE resides in.
2009 * fld - be_fs_list_data_t pointer providing the list of
2010 * file systems to look for in vfstab.
2011 * Returns:
2012 * BE_SUCCESS - Success
2013 * be_errno_t - Failure
2014 * Scope:
2015 * Semi-private (library wide use only)
2018 be_update_zone_vfstab(zfs_handle_t *zhp, char *be_name, char *old_rc_loc,
2019 char *new_rc_loc, be_fs_list_data_t *fld)
2021 be_mount_data_t md = { 0 };
2022 be_unmount_data_t ud = { 0 };
2023 char alt_vfstab[MAXPATHLEN];
2024 boolean_t mounted_here = B_FALSE;
2025 int ret = BE_SUCCESS;
2028 * If zone root not already mounted, mount it at a
2029 * temporary location.
2031 if (!zfs_is_mounted(zhp, &md.altroot)) {
2032 /* Generate temporary mountpoint to mount zone root */
2033 if ((ret = be_make_tmp_mountpoint(&md.altroot)) != BE_SUCCESS) {
2034 be_print_err(gettext("be_update_zone_vfstab: "
2035 "failed to make temporary mountpoint to "
2036 "mount zone root\n"));
2037 return (ret);
2040 if (be_mount_zone_root(zhp, &md) != BE_SUCCESS) {
2041 be_print_err(gettext("be_update_zone_vfstab: "
2042 "failed to mount zone root %s\n"),
2043 zfs_get_name(zhp));
2044 free(md.altroot);
2045 return (BE_ERR_MOUNT_ZONEROOT);
2047 mounted_here = B_TRUE;
2050 /* Get string from vfstab in the mounted zone BE */
2051 (void) snprintf(alt_vfstab, sizeof (alt_vfstab), "%s/etc/vfstab",
2052 md.altroot);
2054 /* Update the vfstab */
2055 ret = _update_vfstab(alt_vfstab, be_name, old_rc_loc, new_rc_loc,
2056 fld);
2058 /* Unmount zone root if we mounted it */
2059 if (mounted_here) {
2060 ud.force = B_TRUE;
2062 if (be_unmount_zone_root(zhp, &ud) == BE_SUCCESS) {
2063 /* Remove the temporary mountpoint */
2064 (void) rmdir(md.altroot);
2065 } else {
2066 be_print_err(gettext("be_update_zone_vfstab: "
2067 "failed to unmount zone root %s from %s\n"),
2068 zfs_get_name(zhp), md.altroot);
2069 if (ret == 0)
2070 ret = BE_ERR_UMOUNT_ZONEROOT;
2074 free(md.altroot);
2075 return (ret);
2079 * Function: be_auto_snap_name
2080 * Description: Generate an auto snapshot name constructed based on the
2081 * current date and time. The auto snapshot name is of the form:
2083 * <date>-<time>
2085 * where <date> is in ISO standard format, so the resultant name
2086 * is of the form:
2088 * %Y-%m-%d-%H:%M:%S
2090 * Parameters:
2091 * None
2092 * Returns:
2093 * Success - pointer to auto generated snapshot name. The name
2094 * is allocated in heap storage so the caller is
2095 * responsible for free'ing the name.
2096 * Failure - NULL
2097 * Scope:
2098 * Semi-private (library wide use only)
2100 char *
2101 be_auto_snap_name(void)
2103 time_t utc_tm = NULL;
2104 struct tm *gmt_tm = NULL;
2105 char gmt_time_str[64];
2106 char *auto_snap_name = NULL;
2108 if (time(&utc_tm) == -1) {
2109 be_print_err(gettext("be_auto_snap_name: time() failed\n"));
2110 return (NULL);
2113 if ((gmt_tm = gmtime(&utc_tm)) == NULL) {
2114 be_print_err(gettext("be_auto_snap_name: gmtime() failed\n"));
2115 return (NULL);
2118 (void) strftime(gmt_time_str, sizeof (gmt_time_str), "%F-%T", gmt_tm);
2120 if ((auto_snap_name = strdup(gmt_time_str)) == NULL) {
2121 be_print_err(gettext("be_auto_snap_name: "
2122 "memory allocation failed\n"));
2123 return (NULL);
2126 return (auto_snap_name);
2130 * Function: be_auto_be_name
2131 * Description: Generate an auto BE name constructed based on the BE name
2132 * of the original BE being cloned.
2133 * Parameters:
2134 * obe_name - name of the original BE being cloned.
2135 * Returns:
2136 * Success - pointer to auto generated BE name. The name
2137 * is allocated in heap storage so the caller is
2138 * responsible for free'ing the name.
2139 * Failure - NULL
2140 * Scope:
2141 * Semi-private (library wide use only)
2143 char *
2144 be_auto_be_name(char *obe_name)
2146 return (be_get_auto_name(obe_name, NULL, B_FALSE));
2150 * Function: be_auto_zone_be_name
2151 * Description: Generate an auto BE name for a zone constructed based on
2152 * the BE name of the original zone BE being cloned.
2153 * Parameters:
2154 * container_ds - container dataset for the zone.
2155 * zbe_name - name of the original zone BE being cloned.
2156 * Returns:
2157 * Success - pointer to auto generated BE name. The name
2158 * is allocated in heap storage so the caller is
2159 * responsible for free'ing the name.
2160 * Failure - NULL
2161 * Scope:
2162 * Semi-private (library wide use only)
2164 char *
2165 be_auto_zone_be_name(char *container_ds, char *zbe_name)
2167 return (be_get_auto_name(zbe_name, container_ds, B_TRUE));
2171 * Function: be_valid_be_name
2172 * Description: Validates a BE name.
2173 * Parameters:
2174 * be_name - name of BE to validate
2175 * Returns:
2176 * B_TRUE - be_name is valid
2177 * B_FALSE - be_name is invalid
2178 * Scope:
2179 * Semi-private (library wide use only)
2182 boolean_t
2183 be_valid_be_name(const char *be_name)
2185 const char *c = NULL;
2186 struct be_defaults be_defaults;
2188 if (be_name == NULL)
2189 return (B_FALSE);
2191 be_get_defaults(&be_defaults);
2194 * A BE name must not be a multi-level dataset name. We also check
2195 * that it does not contain the ' ' and '%' characters. The ' ' is
2196 * a valid character for datasets, however we don't allow that in a
2197 * BE name. The '%' is invalid, but zfs_name_valid() allows it for
2198 * internal reasons, so we explicitly check for it here.
2200 c = be_name;
2201 while (*c != '\0' && *c != '/' && *c != ' ' && *c != '%')
2202 c++;
2204 if (*c != '\0')
2205 return (B_FALSE);
2208 * The BE name must comply with a zfs dataset filesystem. We also
2209 * verify its length to be < BE_NAME_MAX_LEN.
2211 if (!zfs_name_valid(be_name, ZFS_TYPE_FILESYSTEM) ||
2212 strlen(be_name) > BE_NAME_MAX_LEN)
2213 return (B_FALSE);
2215 if (be_defaults.be_deflt_bename_starts_with[0] != '\0' &&
2216 strstr(be_name, be_defaults.be_deflt_bename_starts_with) == NULL) {
2217 return (B_FALSE);
2220 return (B_TRUE);
2224 * Function: be_valid_auto_snap_name
2225 * Description: This function checks that a snapshot name is a valid auto
2226 * generated snapshot name. A valid auto generated snapshot
2227 * name is of the form:
2229 * %Y-%m-%d-%H:%M:%S
2231 * An older form of the auto generated snapshot name also
2232 * included the snapshot's BE cleanup policy and a reserved
2233 * field. Those names will also be verified by this function.
2235 * Examples of valid auto snapshot names are:
2237 * 2008-03-31-18:41:30
2238 * 2008-03-31-22:17:24
2239 * <policy>:-:2008:04-05-09:12:55
2240 * <policy>:-:2008:04-06-15:34:12
2242 * Parameters:
2243 * name - name of the snapshot to be validated.
2244 * Returns:
2245 * B_TRUE - the name is a valid auto snapshot name.
2246 * B_FALSE - the name is not a valid auto snapshot name.
2247 * Scope:
2248 * Semi-private (library wide use only)
2250 boolean_t
2251 be_valid_auto_snap_name(char *name)
2253 struct tm gmt_tm;
2255 char *policy = NULL;
2256 char *reserved = NULL;
2257 char *date = NULL;
2258 char *c = NULL;
2260 /* Validate the snapshot name by converting it into utc time */
2261 if (strptime(name, "%Y-%m-%d-%T", &gmt_tm) != NULL &&
2262 (mktime(&gmt_tm) != -1)) {
2263 return (B_TRUE);
2267 * Validate the snapshot name against the older form of an
2268 * auto generated snapshot name.
2270 policy = strdup(name);
2273 * Get the first field from the snapshot name,
2274 * which is the BE policy
2276 c = strchr(policy, ':');
2277 if (c == NULL) {
2278 free(policy);
2279 return (B_FALSE);
2281 c[0] = '\0';
2283 /* Validate the policy name */
2284 if (!valid_be_policy(policy)) {
2285 free(policy);
2286 return (B_FALSE);
2289 /* Get the next field, which is the reserved field. */
2290 if (c[1] == NULL || c[1] == '\0') {
2291 free(policy);
2292 return (B_FALSE);
2294 reserved = c+1;
2295 c = strchr(reserved, ':');
2296 if (c == NULL) {
2297 free(policy);
2298 return (B_FALSE);
2300 c[0] = '\0';
2302 /* Validate the reserved field */
2303 if (strcmp(reserved, "-") != 0) {
2304 free(policy);
2305 return (B_FALSE);
2308 /* The remaining string should be the date field */
2309 if (c[1] == NULL || c[1] == '\0') {
2310 free(policy);
2311 return (B_FALSE);
2313 date = c+1;
2315 /* Validate the date string by converting it into utc time */
2316 if (strptime(date, "%Y-%m-%d-%T", &gmt_tm) == NULL ||
2317 (mktime(&gmt_tm) == -1)) {
2318 be_print_err(gettext("be_valid_auto_snap_name: "
2319 "invalid auto snapshot name\n"));
2320 free(policy);
2321 return (B_FALSE);
2324 free(policy);
2325 return (B_TRUE);
2329 * Function: be_default_policy
2330 * Description: Temporary hardcoded policy support. This function returns
2331 * the default policy type to be used to create a BE or a BE
2332 * snapshot.
2333 * Parameters:
2334 * None
2335 * Returns:
2336 * Name of default BE policy.
2337 * Scope:
2338 * Semi-private (library wide use only)
2340 char *
2341 be_default_policy(void)
2343 return (BE_PLCY_STATIC);
2347 * Function: valid_be_policy
2348 * Description: Temporary hardcoded policy support. This function valids
2349 * whether a policy is a valid known policy or not.
2350 * Paramters:
2351 * policy - name of policy to validate.
2352 * Returns:
2353 * B_TRUE - policy is a valid.
2354 * B_FALSE - policy is invalid.
2355 * Scope:
2356 * Semi-private (library wide use only)
2358 boolean_t
2359 valid_be_policy(char *policy)
2361 if (policy == NULL)
2362 return (B_FALSE);
2364 if (strcmp(policy, BE_PLCY_STATIC) == 0 ||
2365 strcmp(policy, BE_PLCY_VOLATILE) == 0) {
2366 return (B_TRUE);
2369 return (B_FALSE);
2373 * Function: be_print_err
2374 * Description: This function prints out error messages if do_print is
2375 * set to B_TRUE or if the BE_PRINT_ERR environment variable
2376 * is set to true.
2377 * Paramters:
2378 * prnt_str - the string we wish to print and any arguments
2379 * for the format of that string.
2380 * Returns:
2381 * void
2382 * Scope:
2383 * Semi-private (library wide use only)
2385 void
2386 be_print_err(char *prnt_str, ...)
2388 va_list ap;
2389 char buf[BUFSIZ];
2390 char *env_buf;
2391 static boolean_t env_checked = B_FALSE;
2393 if (!env_checked) {
2394 if ((env_buf = getenv("BE_PRINT_ERR")) != NULL) {
2395 if (strcasecmp(env_buf, "true") == 0) {
2396 do_print = B_TRUE;
2399 env_checked = B_TRUE;
2402 if (do_print) {
2403 va_start(ap, prnt_str);
2404 /* LINTED variable format specifier */
2405 (void) vsnprintf(buf, BUFSIZ, prnt_str, ap);
2406 (void) fputs(buf, stderr);
2407 va_end(ap);
2412 * Function: be_find_current_be
2413 * Description: Find the currently "active" BE. Fill in the
2414 * passed in be_transaction_data_t reference with the
2415 * active BE's data.
2416 * Paramters:
2417 * none
2418 * Returns:
2419 * BE_SUCCESS - Success
2420 * be_errnot_t - Failure
2421 * Scope:
2422 * Semi-private (library wide use only)
2423 * Notes:
2424 * The caller is responsible for initializing the libzfs handle
2425 * and freeing the memory used by the active be_name.
2428 be_find_current_be(be_transaction_data_t *bt)
2430 int zret;
2432 if ((zret = zpool_iter(g_zfs, be_zpool_find_current_be_callback,
2433 bt)) == 0) {
2434 be_print_err(gettext("be_find_current_be: failed to "
2435 "find current BE name\n"));
2436 return (BE_ERR_BE_NOENT);
2437 } else if (zret < 0) {
2438 be_print_err(gettext("be_find_current_be: "
2439 "zpool_iter failed: %s\n"),
2440 libzfs_error_description(g_zfs));
2441 return (zfs_err_to_be_err(g_zfs));
2444 return (BE_SUCCESS);
2448 * Function: be_zpool_find_current_be_callback
2449 * Description: Callback function used to iterate through all existing pools
2450 * to find the BE that is the currently booted BE.
2451 * Parameters:
2452 * zlp - zpool_handle_t pointer to the current pool being
2453 * looked at.
2454 * data - be_transaction_data_t pointer.
2455 * Upon successfully finding the current BE, the
2456 * obe_zpool member of this parameter is set to the
2457 * pool it is found in.
2458 * Return:
2459 * 1 - Found current BE in this pool.
2460 * 0 - Did not find current BE in this pool.
2461 * Scope:
2462 * Semi-private (library wide use only)
2465 be_zpool_find_current_be_callback(zpool_handle_t *zlp, void *data)
2467 be_transaction_data_t *bt = data;
2468 zfs_handle_t *zhp = NULL;
2469 const char *zpool = zpool_get_name(zlp);
2470 char be_container_ds[MAXPATHLEN];
2471 char *zpath = NULL;
2474 * Generate string for BE container dataset
2476 if (getzoneid() != GLOBAL_ZONEID) {
2477 if ((zpath = be_get_ds_from_dir("/")) != NULL) {
2478 (void) strlcpy(be_container_ds, dirname(zpath),
2479 sizeof (be_container_ds));
2480 } else {
2481 be_print_err(gettext(
2482 "be_zpool_find_current_be_callback: "
2483 "zone root dataset is not mounted\n"));
2484 return (0);
2486 } else {
2487 be_make_container_ds(zpool, be_container_ds,
2488 sizeof (be_container_ds));
2492 * Check if a BE container dataset exists in this pool.
2494 if (!zfs_dataset_exists(g_zfs, be_container_ds, ZFS_TYPE_FILESYSTEM)) {
2495 zpool_close(zlp);
2496 return (0);
2500 * Get handle to this zpool's BE container dataset.
2502 if ((zhp = zfs_open(g_zfs, be_container_ds, ZFS_TYPE_FILESYSTEM)) ==
2503 NULL) {
2504 be_print_err(gettext("be_zpool_find_current_be_callback: "
2505 "failed to open BE container dataset (%s)\n"),
2506 be_container_ds);
2507 zpool_close(zlp);
2508 return (0);
2512 * Iterate through all potential BEs in this zpool
2514 if (zfs_iter_filesystems(zhp, be_zfs_find_current_be_callback, bt)) {
2516 * Found current BE dataset; set obe_zpool
2518 if ((bt->obe_zpool = strdup(zpool)) == NULL) {
2519 be_print_err(gettext(
2520 "be_zpool_find_current_be_callback: "
2521 "memory allocation failed\n"));
2522 ZFS_CLOSE(zhp);
2523 zpool_close(zlp);
2524 return (0);
2527 ZFS_CLOSE(zhp);
2528 zpool_close(zlp);
2529 return (1);
2532 ZFS_CLOSE(zhp);
2533 zpool_close(zlp);
2535 return (0);
2539 * Function: be_zfs_find_current_be_callback
2540 * Description: Callback function used to iterate through all BEs in a
2541 * pool to find the BE that is the currently booted BE.
2542 * Parameters:
2543 * zhp - zfs_handle_t pointer to current filesystem being checked.
2544 * data - be_transaction-data_t pointer
2545 * Upon successfully finding the current BE, the
2546 * obe_name and obe_root_ds members of this parameter
2547 * are set to the BE name and BE's root dataset
2548 * respectively.
2549 * Return:
2550 * 1 - Found current BE.
2551 * 0 - Did not find current BE.
2552 * Scope:
2553 * Semi-private (library wide use only)
2556 be_zfs_find_current_be_callback(zfs_handle_t *zhp, void *data)
2558 be_transaction_data_t *bt = data;
2559 char *mp = NULL;
2562 * Check if dataset is mounted, and if so where.
2564 if (zfs_is_mounted(zhp, &mp)) {
2566 * If mounted at root, set obe_root_ds and obe_name
2568 if (mp != NULL && strcmp(mp, "/") == 0) {
2569 free(mp);
2571 if ((bt->obe_root_ds = strdup(zfs_get_name(zhp)))
2572 == NULL) {
2573 be_print_err(gettext(
2574 "be_zfs_find_current_be_callback: "
2575 "memory allocation failed\n"));
2576 ZFS_CLOSE(zhp);
2577 return (0);
2580 if ((bt->obe_name = strdup(basename(bt->obe_root_ds)))
2581 == NULL) {
2582 be_print_err(gettext(
2583 "be_zfs_find_current_be_callback: "
2584 "memory allocation failed\n"));
2585 ZFS_CLOSE(zhp);
2586 return (0);
2589 ZFS_CLOSE(zhp);
2590 return (1);
2593 free(mp);
2595 ZFS_CLOSE(zhp);
2597 return (0);
2601 * Function: be_check_be_roots_callback
2602 * Description: This function checks whether or not the dataset name passed
2603 * is hierachically located under the BE root container dataset
2604 * for this pool.
2605 * Parameters:
2606 * zlp - zpool_handle_t pointer to current pool being processed.
2607 * data - name of dataset to check
2608 * Returns:
2609 * 0 - dataset is not in this pool's BE root container dataset
2610 * 1 - dataset is in this pool's BE root container dataset
2611 * Scope:
2612 * Semi-private (library wide use only)
2615 be_check_be_roots_callback(zpool_handle_t *zlp, void *data)
2617 const char *zpool = zpool_get_name(zlp);
2618 char *ds = data;
2619 char be_container_ds[MAXPATHLEN];
2621 /* Generate string for this pool's BE root container dataset */
2622 be_make_container_ds(zpool, be_container_ds, sizeof (be_container_ds));
2625 * If dataset lives under the BE root container dataset
2626 * of this pool, return failure.
2628 if (strncmp(be_container_ds, ds, strlen(be_container_ds)) == 0 &&
2629 ds[strlen(be_container_ds)] == '/') {
2630 zpool_close(zlp);
2631 return (1);
2634 zpool_close(zlp);
2635 return (0);
2639 * Function: zfs_err_to_be_err
2640 * Description: This function takes the error stored in the libzfs handle
2641 * and maps it to an be_errno_t. If there are no matching
2642 * be_errno_t's then BE_ERR_ZFS is returned.
2643 * Paramters:
2644 * zfsh - The libzfs handle containing the error we're looking up.
2645 * Returns:
2646 * be_errno_t
2647 * Scope:
2648 * Semi-private (library wide use only)
2651 zfs_err_to_be_err(libzfs_handle_t *zfsh)
2653 int err = libzfs_errno(zfsh);
2655 switch (err) {
2656 case 0:
2657 return (BE_SUCCESS);
2658 case EZFS_PERM:
2659 return (BE_ERR_PERM);
2660 case EZFS_INTR:
2661 return (BE_ERR_INTR);
2662 case EZFS_NOENT:
2663 return (BE_ERR_NOENT);
2664 case EZFS_NOSPC:
2665 return (BE_ERR_NOSPC);
2666 case EZFS_MOUNTFAILED:
2667 return (BE_ERR_MOUNT);
2668 case EZFS_UMOUNTFAILED:
2669 return (BE_ERR_UMOUNT);
2670 case EZFS_EXISTS:
2671 return (BE_ERR_BE_EXISTS);
2672 case EZFS_BUSY:
2673 return (BE_ERR_DEV_BUSY);
2674 case EZFS_POOLREADONLY:
2675 return (BE_ERR_ROFS);
2676 case EZFS_NAMETOOLONG:
2677 return (BE_ERR_NAMETOOLONG);
2678 case EZFS_NODEVICE:
2679 return (BE_ERR_NODEV);
2680 case EZFS_POOL_INVALARG:
2681 return (BE_ERR_INVAL);
2682 case EZFS_PROPTYPE:
2683 return (BE_ERR_INVALPROP);
2684 case EZFS_BADTYPE:
2685 return (BE_ERR_DSTYPE);
2686 case EZFS_PROPNONINHERIT:
2687 return (BE_ERR_NONINHERIT);
2688 case EZFS_PROPREADONLY:
2689 return (BE_ERR_READONLYPROP);
2690 case EZFS_RESILVERING:
2691 case EZFS_POOLUNAVAIL:
2692 return (BE_ERR_UNAVAIL);
2693 case EZFS_DSREADONLY:
2694 return (BE_ERR_READONLYDS);
2695 default:
2696 return (BE_ERR_ZFS);
2701 * Function: errno_to_be_err
2702 * Description: This function takes an errno and maps it to an be_errno_t.
2703 * If there are no matching be_errno_t's then BE_ERR_UNKNOWN is
2704 * returned.
2705 * Paramters:
2706 * err - The errno we're compairing against.
2707 * Returns:
2708 * be_errno_t
2709 * Scope:
2710 * Semi-private (library wide use only)
2713 errno_to_be_err(int err)
2715 switch (err) {
2716 case EPERM:
2717 return (BE_ERR_PERM);
2718 case EACCES:
2719 return (BE_ERR_ACCESS);
2720 case ECANCELED:
2721 return (BE_ERR_CANCELED);
2722 case EINTR:
2723 return (BE_ERR_INTR);
2724 case ENOENT:
2725 return (BE_ERR_NOENT);
2726 case ENOSPC:
2727 case EDQUOT:
2728 return (BE_ERR_NOSPC);
2729 case EEXIST:
2730 return (BE_ERR_BE_EXISTS);
2731 case EBUSY:
2732 return (BE_ERR_BUSY);
2733 case EROFS:
2734 return (BE_ERR_ROFS);
2735 case ENAMETOOLONG:
2736 return (BE_ERR_NAMETOOLONG);
2737 case ENXIO:
2738 return (BE_ERR_NXIO);
2739 case EINVAL:
2740 return (BE_ERR_INVAL);
2741 case EFAULT:
2742 return (BE_ERR_FAULT);
2743 default:
2744 return (BE_ERR_UNKNOWN);
2749 * Function: be_err_to_str
2750 * Description: This function takes a be_errno_t and maps it to a message.
2751 * If there are no matching be_errno_t's then NULL is returned.
2752 * Paramters:
2753 * be_errno_t - The be_errno_t we're mapping.
2754 * Returns:
2755 * string or NULL if the error code is not known.
2756 * Scope:
2757 * Semi-private (library wide use only)
2759 char *
2760 be_err_to_str(int err)
2762 switch (err) {
2763 case BE_ERR_ACCESS:
2764 return (gettext("Permission denied."));
2765 case BE_ERR_ACTIVATE_CURR:
2766 return (gettext("Activation of current BE failed."));
2767 case BE_ERR_AUTONAME:
2768 return (gettext("Auto naming failed."));
2769 case BE_ERR_BE_NOENT:
2770 return (gettext("No such BE."));
2771 case BE_ERR_BUSY:
2772 return (gettext("Mount busy."));
2773 case BE_ERR_DEV_BUSY:
2774 return (gettext("Device busy."));
2775 case BE_ERR_CANCELED:
2776 return (gettext("Operation canceled."));
2777 case BE_ERR_CLONE:
2778 return (gettext("BE clone failed."));
2779 case BE_ERR_COPY:
2780 return (gettext("BE copy failed."));
2781 case BE_ERR_CREATDS:
2782 return (gettext("Dataset creation failed."));
2783 case BE_ERR_CURR_BE_NOT_FOUND:
2784 return (gettext("Can't find current BE."));
2785 case BE_ERR_DESTROY:
2786 return (gettext("Failed to destroy BE or snapshot."));
2787 case BE_ERR_DESTROY_CURR_BE:
2788 return (gettext("Cannot destroy current BE."));
2789 case BE_ERR_DEMOTE:
2790 return (gettext("BE demotion failed."));
2791 case BE_ERR_DSTYPE:
2792 return (gettext("Invalid dataset type."));
2793 case BE_ERR_BE_EXISTS:
2794 return (gettext("BE exists."));
2795 case BE_ERR_INIT:
2796 return (gettext("be_zfs_init failed."));
2797 case BE_ERR_INTR:
2798 return (gettext("Interupted system call."));
2799 case BE_ERR_INVAL:
2800 return (gettext("Invalid argument."));
2801 case BE_ERR_INVALPROP:
2802 return (gettext("Invalid property for dataset."));
2803 case BE_ERR_INVALMOUNTPOINT:
2804 return (gettext("Unexpected mountpoint."));
2805 case BE_ERR_MOUNT:
2806 return (gettext("Mount failed."));
2807 case BE_ERR_MOUNTED:
2808 return (gettext("Already mounted."));
2809 case BE_ERR_NAMETOOLONG:
2810 return (gettext("name > BUFSIZ."));
2811 case BE_ERR_NOENT:
2812 return (gettext("Doesn't exist."));
2813 case BE_ERR_POOL_NOENT:
2814 return (gettext("No such pool."));
2815 case BE_ERR_NODEV:
2816 return (gettext("No such device."));
2817 case BE_ERR_NOTMOUNTED:
2818 return (gettext("File system not mounted."));
2819 case BE_ERR_NOMEM:
2820 return (gettext("Not enough memory."));
2821 case BE_ERR_NONINHERIT:
2822 return (gettext(
2823 "Property is not inheritable for the BE dataset."));
2824 case BE_ERR_NXIO:
2825 return (gettext("No such device or address."));
2826 case BE_ERR_NOSPC:
2827 return (gettext("No space on device."));
2828 case BE_ERR_NOTSUP:
2829 return (gettext("Operation not supported."));
2830 case BE_ERR_OPEN:
2831 return (gettext("Open failed."));
2832 case BE_ERR_PERM:
2833 return (gettext("Not owner."));
2834 case BE_ERR_UNAVAIL:
2835 return (gettext("The BE is currently unavailable."));
2836 case BE_ERR_PROMOTE:
2837 return (gettext("BE promotion failed."));
2838 case BE_ERR_ROFS:
2839 return (gettext("Read only file system."));
2840 case BE_ERR_READONLYDS:
2841 return (gettext("Read only dataset."));
2842 case BE_ERR_READONLYPROP:
2843 return (gettext("Read only property."));
2844 case BE_ERR_RENAME_ACTIVE:
2845 return (gettext("Renaming the active BE is not supported."));
2846 case BE_ERR_SS_EXISTS:
2847 return (gettext("Snapshot exists."));
2848 case BE_ERR_SS_NOENT:
2849 return (gettext("No such snapshot."));
2850 case BE_ERR_UMOUNT:
2851 return (gettext("Unmount failed."));
2852 case BE_ERR_UMOUNT_CURR_BE:
2853 return (gettext("Can't unmount the current BE."));
2854 case BE_ERR_UMOUNT_SHARED:
2855 return (gettext("Unmount of a shared File System failed."));
2856 case BE_ERR_FAULT:
2857 return (gettext("Bad address."));
2858 case BE_ERR_UNKNOWN:
2859 return (gettext("Unknown error."));
2860 case BE_ERR_ZFS:
2861 return (gettext("ZFS returned an error."));
2862 case BE_ERR_GEN_UUID:
2863 return (gettext("Failed to generate uuid."));
2864 case BE_ERR_PARSE_UUID:
2865 return (gettext("Failed to parse uuid."));
2866 case BE_ERR_NO_UUID:
2867 return (gettext("No uuid"));
2868 case BE_ERR_ZONE_NO_PARENTBE:
2869 return (gettext("No parent uuid"));
2870 case BE_ERR_ZONE_MULTIPLE_ACTIVE:
2871 return (gettext("Multiple active zone roots"));
2872 case BE_ERR_ZONE_NO_ACTIVE_ROOT:
2873 return (gettext("No active zone root"));
2874 case BE_ERR_ZONE_ROOT_NOT_LEGACY:
2875 return (gettext("Zone root not legacy"));
2876 case BE_ERR_MOUNT_ZONEROOT:
2877 return (gettext("Failed to mount a zone root."));
2878 case BE_ERR_UMOUNT_ZONEROOT:
2879 return (gettext("Failed to unmount a zone root."));
2880 case BE_ERR_NO_MOUNTED_ZONE:
2881 return (gettext("Zone is not mounted"));
2882 case BE_ERR_ZONES_UNMOUNT:
2883 return (gettext("Unable to unmount a zone BE."));
2884 case BE_ERR_NO_MENU:
2885 return (gettext("Missing boot menu file."));
2886 case BE_ERR_BAD_MENU_PATH:
2887 return (gettext("Invalid path for menu.lst file"));
2888 case BE_ERR_ZONE_SS_EXISTS:
2889 return (gettext("Zone snapshot exists."));
2890 case BE_ERR_BOOTFILE_INST:
2891 return (gettext("Error installing boot files."));
2892 case BE_ERR_EXTCMD:
2893 return (gettext("Error running an external command."));
2894 default:
2895 return (NULL);
2900 * Function: be_has_grub
2901 * Description: Boolean function indicating whether the current system
2902 * uses grub.
2903 * Return: B_FALSE - the system does not have grub
2904 * B_TRUE - the system does have grub.
2905 * Scope:
2906 * Semi-private (library wide use only)
2908 boolean_t
2909 be_has_grub(void)
2912 * TODO: This will need to be expanded to check for the existence of
2913 * grub if and when there is grub support for SPARC.
2915 return (be_is_isa("i386"));
2919 * Function: be_is_isa
2920 * Description: Boolean function indicating whether the instruction set
2921 * architecture of the executing system matches the name provided.
2922 * The string must match a system defined architecture (e.g.
2923 * "i386", "sparc") and is case sensitive.
2924 * Parameters: name - string representing the name of instruction set
2925 * architecture being tested
2926 * Returns: B_FALSE - the system instruction set architecture is different
2927 * from the one specified
2928 * B_TRUE - the system instruction set architecture is the same
2929 * as the one specified
2930 * Scope:
2931 * Semi-private (library wide use only)
2933 boolean_t
2934 be_is_isa(char *name)
2936 return ((strcmp((char *)be_get_default_isa(), name) == 0));
2940 * Function: be_get_default_isa
2941 * Description:
2942 * Returns the default instruction set architecture of the
2943 * machine it is executed on. (eg. sparc, i386, ...)
2944 * NOTE: SYS_INST environment variable may override default
2945 * return value
2946 * Parameters:
2947 * none
2948 * Returns:
2949 * NULL - the architecture returned by sysinfo() was too
2950 * long for local variables
2951 * char * - pointer to a string containing the default
2952 * implementation
2953 * Scope:
2954 * Semi-private (library wide use only)
2956 char *
2957 be_get_default_isa(void)
2959 int i;
2960 char *envp;
2961 static char default_inst[ARCH_LENGTH] = "";
2963 if (default_inst[0] == '\0') {
2964 if ((envp = getenv("SYS_INST")) != NULL) {
2965 if ((int)strlen(envp) >= ARCH_LENGTH)
2966 return (NULL);
2967 else
2968 (void) strcpy(default_inst, envp);
2969 } else {
2970 i = sysinfo(SI_ARCHITECTURE, default_inst, ARCH_LENGTH);
2971 if (i < 0 || i > ARCH_LENGTH)
2972 return (NULL);
2975 return (default_inst);
2979 * Function: be_run_cmd
2980 * Description:
2981 * Runs a command in a separate subprocess. Splits out stdout from stderr
2982 * and sends each to its own buffer. Buffers must be pre-allocated and
2983 * passed in as arguments. Buffer sizes are also passed in as arguments.
2985 * Notes / caveats:
2986 * - Command being run is assumed to not have any stdout or stderr
2987 * redirection.
2988 * - Commands which emit total stderr output of greater than PIPE_BUF
2989 * bytes can hang. For such commands, a different implementation
2990 * which uses poll(2) must be used.
2991 * - stdout_buf can be NULL. In this case, stdout_bufsize is ignored, and
2992 * the stream which would have gone to it is sent to the bit
2993 * bucket.
2994 * - stderr_buf cannot be NULL.
2995 * - Only subprocess errors are appended to the stderr_buf. Errors
2996 * running the command are reported through be_print_err().
2997 * - Data which would overflow its respective buffer is sent to the bit
2998 * bucket.
3000 * Parameters:
3001 * command: command to run. Assumed not to have embedded stdout
3002 * or stderr redirection. May have stdin redirection,
3003 * however.
3004 * stderr_buf: buffer returning subprocess stderr data. Errors
3005 * reported by this function are reported through
3006 * be_print_err().
3007 * stderr_bufsize: size of stderr_buf
3008 * stdout_buf: buffer returning subprocess stdout data.
3009 * stdout_bufsize: size of stdout_buf
3010 * Returns:
3011 * BE_SUCCESS - The command ran successfully without returning
3012 * errors.
3013 * BE_ERR_EXTCMD
3014 * - The command could not be run.
3015 * - The command terminated with error status.
3016 * - There were errors extracting or returning subprocess
3017 * data.
3018 * BE_ERR_NOMEM - The command exceeds the command buffer size.
3019 * BE_ERR_INVAL - An invalid argument was specified.
3020 * Scope:
3021 * Semi-private (library wide use only)
3024 be_run_cmd(char *command, char *stderr_buf, int stderr_bufsize,
3025 char *stdout_buf, int stdout_bufsize)
3027 char *temp_filename = strdup(tmpnam(NULL));
3028 FILE *stdout_str = NULL;
3029 FILE *stderr_str = NULL;
3030 char cmdline[BUFSIZ];
3031 char oneline[BUFSIZ];
3032 int exit_status;
3033 int rval = BE_SUCCESS;
3035 if ((command == NULL) || (stderr_buf == NULL) ||
3036 (stderr_bufsize <= 0) || (stdout_bufsize < 0) ||
3037 ((stdout_buf != NULL) ^ (stdout_bufsize != 0))) {
3038 return (BE_ERR_INVAL);
3041 /* Set up command so popen returns stderr, not stdout */
3042 if (snprintf(cmdline, BUFSIZ, "%s 2> %s", command,
3043 temp_filename) >= BUFSIZ) {
3044 rval = BE_ERR_NOMEM;
3045 goto cleanup;
3048 /* Set up the fifo that will make stderr available. */
3049 if (mkfifo(temp_filename, 0600) != 0) {
3050 (void) be_print_err(gettext("be_run_cmd: mkfifo: %s\n"),
3051 strerror(errno));
3052 rval = BE_ERR_EXTCMD;
3053 goto cleanup;
3056 if ((stdout_str = popen(cmdline, "r")) == NULL) {
3057 (void) be_print_err(gettext("be_run_cmd: popen: %s\n"),
3058 strerror(errno));
3059 rval = BE_ERR_EXTCMD;
3060 goto cleanup;
3063 if ((stderr_str = fopen(temp_filename, "r")) == NULL) {
3064 (void) be_print_err(gettext("be_run_cmd: fopen: %s\n"),
3065 strerror(errno));
3066 (void) pclose(stdout_str);
3067 rval = BE_ERR_EXTCMD;
3068 goto cleanup;
3071 /* Read stdout first, as it usually outputs more than stderr. */
3072 oneline[BUFSIZ-1] = '\0';
3073 while (fgets(oneline, BUFSIZ-1, stdout_str) != NULL) {
3074 if (stdout_str != NULL) {
3075 (void) strlcat(stdout_buf, oneline, stdout_bufsize);
3079 while (fgets(oneline, BUFSIZ-1, stderr_str) != NULL) {
3080 (void) strlcat(stderr_buf, oneline, stderr_bufsize);
3083 /* Close pipe, get exit status. */
3084 if ((exit_status = pclose(stdout_str)) == -1) {
3085 (void) be_print_err(gettext("be_run_cmd: pclose: %s\n"),
3086 strerror(errno));
3087 rval = BE_ERR_EXTCMD;
3088 } else if (WIFEXITED(exit_status)) {
3089 exit_status = (int)((char)WEXITSTATUS(exit_status));
3090 if (exit_status != 0) {
3091 (void) snprintf(oneline, BUFSIZ, gettext("be_run_cmd: "
3092 "command terminated with error status: %d\n"),
3093 exit_status);
3094 (void) strlcat(stderr_buf, oneline, stderr_bufsize);
3095 rval = BE_ERR_EXTCMD;
3097 } else {
3098 (void) snprintf(oneline, BUFSIZ, gettext("be_run_cmd: command "
3099 "terminated on signal: %s\n"),
3100 strsignal(WTERMSIG(exit_status)));
3101 (void) strlcat(stderr_buf, oneline, stderr_bufsize);
3102 rval = BE_ERR_EXTCMD;
3105 cleanup:
3106 (void) unlink(temp_filename);
3107 (void) free(temp_filename);
3109 return (rval);
3112 /* ******************************************************************** */
3113 /* Private Functions */
3114 /* ******************************************************************** */
3117 * Function: update_dataset
3118 * Description: This function takes a dataset name and replaces the zpool
3119 * and be_name components of the dataset with the new be_name
3120 * zpool passed in.
3121 * Parameters:
3122 * dataset - name of dataset
3123 * dataset_len - lenth of buffer in which dataset is passed in.
3124 * be_name - name of new BE name to update to.
3125 * old_rc_loc - dataset under which the root container dataset
3126 * for the old BE lives.
3127 * new_rc_loc - dataset under which the root container dataset
3128 * for the new BE lives.
3129 * Returns:
3130 * BE_SUCCESS - Success
3131 * be_errno_t - Failure
3132 * Scope:
3133 * Private
3135 static int
3136 update_dataset(char *dataset, int dataset_len, char *be_name,
3137 char *old_rc_loc, char *new_rc_loc)
3139 char *ds = NULL;
3140 char *sub_ds = NULL;
3142 /* Tear off the BE container dataset */
3143 if ((ds = be_make_name_from_ds(dataset, old_rc_loc)) == NULL) {
3144 return (BE_ERR_INVAL);
3147 /* Get dataset name relative to BE root, if there is one */
3148 sub_ds = strchr(ds, '/');
3150 /* Generate the BE root dataset name */
3151 be_make_root_ds(new_rc_loc, be_name, dataset, dataset_len);
3153 /* If a subordinate dataset name was found, append it */
3154 if (sub_ds != NULL)
3155 (void) strlcat(dataset, sub_ds, dataset_len);
3157 free(ds);
3158 return (BE_SUCCESS);
3162 * Function: _update_vfstab
3163 * Description: This function updates a vfstab file to reflect the new
3164 * root container dataset location and be_name for all
3165 * entries listed in the be_fs_list_data_t structure passed in.
3166 * Parameters:
3167 * vfstab - vfstab file to modify
3168 * be_name - name of BE to update.
3169 * old_rc_loc - dataset under which the root container dataset
3170 * of the old BE resides in.
3171 * new_rc_loc - dataset under which the root container dataset
3172 * of the new BE resides in.
3173 * fld - be_fs_list_data_t pointer providing the list of
3174 * file systems to look for in vfstab.
3175 * Returns:
3176 * BE_SUCCESS - Success
3177 * be_errno_t - Failure
3178 * Scope:
3179 * Private
3181 static int
3182 _update_vfstab(char *vfstab, char *be_name, char *old_rc_loc,
3183 char *new_rc_loc, be_fs_list_data_t *fld)
3185 struct vfstab vp;
3186 char *tmp_vfstab = NULL;
3187 char comments_buf[BUFSIZ];
3188 FILE *comments = NULL;
3189 FILE *vfs_ents = NULL;
3190 FILE *tfile = NULL;
3191 struct stat sb;
3192 char dev[MAXPATHLEN];
3193 char *c;
3194 int fd;
3195 int ret = BE_SUCCESS, err = 0;
3196 int i;
3197 int tmp_vfstab_len = 0;
3199 errno = 0;
3202 * Open vfstab for reading twice. First is for comments,
3203 * second is for actual entries.
3205 if ((comments = fopen(vfstab, "r")) == NULL ||
3206 (vfs_ents = fopen(vfstab, "r")) == NULL) {
3207 err = errno;
3208 be_print_err(gettext("_update_vfstab: "
3209 "failed to open vfstab (%s): %s\n"), vfstab,
3210 strerror(err));
3211 ret = errno_to_be_err(err);
3212 goto cleanup;
3215 /* Grab the stats of the original vfstab file */
3216 if (stat(vfstab, &sb) != 0) {
3217 err = errno;
3218 be_print_err(gettext("_update_vfstab: "
3219 "failed to stat file %s: %s\n"), vfstab,
3220 strerror(err));
3221 ret = errno_to_be_err(err);
3222 goto cleanup;
3225 /* Create tmp file for modified vfstab */
3226 if ((tmp_vfstab = (char *)malloc(strlen(vfstab) + 7))
3227 == NULL) {
3228 be_print_err(gettext("_update_vfstab: "
3229 "malloc failed\n"));
3230 ret = BE_ERR_NOMEM;
3231 goto cleanup;
3233 tmp_vfstab_len = strlen(vfstab) + 7;
3234 (void) memset(tmp_vfstab, 0, tmp_vfstab_len);
3235 (void) strlcpy(tmp_vfstab, vfstab, tmp_vfstab_len);
3236 (void) strlcat(tmp_vfstab, "XXXXXX", tmp_vfstab_len);
3237 if ((fd = mkstemp(tmp_vfstab)) == -1) {
3238 err = errno;
3239 be_print_err(gettext("_update_vfstab: "
3240 "mkstemp failed: %s\n"), strerror(err));
3241 ret = errno_to_be_err(err);
3242 goto cleanup;
3244 if ((tfile = fdopen(fd, "w")) == NULL) {
3245 err = errno;
3246 be_print_err(gettext("_update_vfstab: "
3247 "could not open file for write\n"));
3248 (void) close(fd);
3249 ret = errno_to_be_err(err);
3250 goto cleanup;
3253 while (fgets(comments_buf, BUFSIZ, comments)) {
3254 for (c = comments_buf; *c != '\0' && isspace(*c); c++)
3256 if (*c == '\0') {
3257 continue;
3258 } else if (*c == '#') {
3260 * If line is a comment line, just put
3261 * it through to the tmp vfstab.
3263 (void) fputs(comments_buf, tfile);
3264 } else {
3266 * Else line is a vfstab entry, grab it
3267 * into a vfstab struct.
3269 if (getvfsent(vfs_ents, &vp) != 0) {
3270 err = errno;
3271 be_print_err(gettext("_update_vfstab: "
3272 "getvfsent failed: %s\n"), strerror(err));
3273 ret = errno_to_be_err(err);
3274 goto cleanup;
3277 if (vp.vfs_special == NULL || vp.vfs_mountp == NULL) {
3278 (void) putvfsent(tfile, &vp);
3279 continue;
3283 * If the entry is one of the entries in the list
3284 * of file systems to update, modify it's device
3285 * field to be correct for this BE.
3287 for (i = 0; i < fld->fs_num; i++) {
3288 if (strcmp(vp.vfs_special, fld->fs_list[i])
3289 == 0) {
3291 * Found entry that needs an update.
3292 * Replace the root container dataset
3293 * location and be_name in the
3294 * entry's device.
3296 (void) strlcpy(dev, vp.vfs_special,
3297 sizeof (dev));
3299 if ((ret = update_dataset(dev,
3300 sizeof (dev), be_name, old_rc_loc,
3301 new_rc_loc)) != 0) {
3302 be_print_err(
3303 gettext("_update_vfstab: "
3304 "Failed to update device "
3305 "field for vfstab entry "
3306 "%s\n"), fld->fs_list[i]);
3307 goto cleanup;
3310 vp.vfs_special = dev;
3311 break;
3315 /* Put entry through to tmp vfstab */
3316 (void) putvfsent(tfile, &vp);
3320 (void) fclose(comments);
3321 comments = NULL;
3322 (void) fclose(vfs_ents);
3323 vfs_ents = NULL;
3324 (void) fclose(tfile);
3325 tfile = NULL;
3327 /* Copy tmp vfstab into place */
3328 if (rename(tmp_vfstab, vfstab) != 0) {
3329 err = errno;
3330 be_print_err(gettext("_update_vfstab: "
3331 "failed to rename file %s to %s: %s\n"), tmp_vfstab,
3332 vfstab, strerror(err));
3333 ret = errno_to_be_err(err);
3334 goto cleanup;
3337 /* Set the perms and ownership of the updated file */
3338 if (chmod(vfstab, sb.st_mode) != 0) {
3339 err = errno;
3340 be_print_err(gettext("_update_vfstab: "
3341 "failed to chmod %s: %s\n"), vfstab, strerror(err));
3342 ret = errno_to_be_err(err);
3343 goto cleanup;
3345 if (chown(vfstab, sb.st_uid, sb.st_gid) != 0) {
3346 err = errno;
3347 be_print_err(gettext("_update_vfstab: "
3348 "failed to chown %s: %s\n"), vfstab, strerror(err));
3349 ret = errno_to_be_err(err);
3350 goto cleanup;
3353 cleanup:
3354 if (comments != NULL)
3355 (void) fclose(comments);
3356 if (vfs_ents != NULL)
3357 (void) fclose(vfs_ents);
3358 (void) unlink(tmp_vfstab);
3359 (void) free(tmp_vfstab);
3360 if (tfile != NULL)
3361 (void) fclose(tfile);
3363 return (ret);
3368 * Function: be_get_auto_name
3369 * Description: Generate an auto name constructed based on the BE name
3370 * of the original BE or zone BE being cloned.
3371 * Parameters:
3372 * obe_name - name of the original BE or zone BE being cloned.
3373 * container_ds - container dataset for the zone.
3374 * Note: if zone_be is false this should be
3375 * NULL.
3376 * zone_be - flag that indicates if we are operating on a zone BE.
3377 * Returns:
3378 * Success - pointer to auto generated BE name. The name
3379 * is allocated in heap storage so the caller is
3380 * responsible for free'ing the name.
3381 * Failure - NULL
3382 * Scope:
3383 * Private
3385 static char *
3386 be_get_auto_name(char *obe_name, char *be_container_ds, boolean_t zone_be)
3388 be_node_list_t *be_nodes = NULL;
3389 be_node_list_t *cur_be = NULL;
3390 char auto_be_name[MAXPATHLEN];
3391 char base_be_name[MAXPATHLEN];
3392 char cur_be_name[MAXPATHLEN];
3393 char *num_str = NULL;
3394 char *c = NULL;
3395 int num = 0;
3396 int cur_num = 0;
3398 errno = 0;
3401 * Check if obe_name is already in an auto BE name format.
3402 * If it is, then strip off the increment number to get the
3403 * base name.
3405 (void) strlcpy(base_be_name, obe_name, sizeof (base_be_name));
3407 if ((num_str = strrchr(base_be_name, BE_AUTO_NAME_DELIM))
3408 != NULL) {
3409 /* Make sure remaining string is all digits */
3410 c = num_str + 1;
3411 while (c[0] != '\0' && isdigit(c[0]))
3412 c++;
3414 * If we're now at the end of the string strip off the
3415 * increment number.
3417 if (c[0] == '\0')
3418 num_str[0] = '\0';
3421 if (zone_be) {
3422 if (be_container_ds == NULL)
3423 return (NULL);
3424 if (be_get_zone_be_list(obe_name, be_container_ds,
3425 &be_nodes) != BE_SUCCESS) {
3426 be_print_err(gettext("be_get_auto_name: "
3427 "be_get_zone_be_list failed\n"));
3428 return (NULL);
3430 } else if (_be_list(NULL, &be_nodes) != BE_SUCCESS) {
3431 be_print_err(gettext("be_get_auto_name: be_list failed\n"));
3432 return (NULL);
3435 for (cur_be = be_nodes; cur_be != NULL; cur_be = cur_be->be_next_node) {
3436 (void) strlcpy(cur_be_name, cur_be->be_node_name,
3437 sizeof (cur_be_name));
3439 /* If cur_be_name doesn't match at least base be name, skip. */
3440 if (strncmp(cur_be_name, base_be_name, strlen(base_be_name))
3441 != 0)
3442 continue;
3444 /* Get the string following the base be name */
3445 num_str = cur_be_name + strlen(base_be_name);
3448 * If nothing follows the base be name, this cur_be_name
3449 * is the BE named with the base be name, skip.
3451 if (num_str == NULL || num_str[0] == '\0')
3452 continue;
3455 * Remove the name delimiter. If its not there,
3456 * cur_be_name isn't part of this BE name stream, skip.
3458 if (num_str[0] == BE_AUTO_NAME_DELIM)
3459 num_str++;
3460 else
3461 continue;
3463 /* Make sure remaining string is all digits */
3464 c = num_str;
3465 while (c[0] != '\0' && isdigit(c[0]))
3466 c++;
3467 if (c[0] != '\0')
3468 continue;
3470 /* Convert the number string to an int */
3471 cur_num = atoi(num_str);
3474 * If failed to convert the string, skip it. If its too
3475 * long to be converted to an int, we wouldn't auto generate
3476 * this number anyway so there couldn't be a conflict.
3477 * We treat it as a manually created BE name.
3479 if (cur_num == 0 && errno == EINVAL)
3480 continue;
3483 * Compare current number to current max number,
3484 * take higher of the two.
3486 if (cur_num > num)
3487 num = cur_num;
3491 * Store off a copy of 'num' incase we need it later. If incrementing
3492 * 'num' causes it to roll over, this means 'num' is the largest
3493 * positive int possible; we'll need it later in the loop to determine
3494 * if we've exhausted all possible increment numbers. We store it in
3495 * 'cur_num'.
3497 cur_num = num;
3499 /* Increment 'num' to get new auto BE name number */
3500 if (++num <= 0) {
3501 int ret = 0;
3504 * Since incrementing 'num' caused it to rollover, start
3505 * over at 0 and find the first available number.
3507 for (num = 0; num < cur_num; num++) {
3509 (void) snprintf(cur_be_name, sizeof (cur_be_name),
3510 "%s%c%d", base_be_name, BE_AUTO_NAME_DELIM, num);
3512 ret = zpool_iter(g_zfs, be_exists_callback,
3513 cur_be_name);
3515 if (ret == 0) {
3517 * BE name doesn't exist, break out
3518 * to use 'num'.
3520 break;
3521 } else if (ret == 1) {
3522 /* BE name exists, continue looking */
3523 continue;
3524 } else {
3525 be_print_err(gettext("be_get_auto_name: "
3526 "zpool_iter failed: %s\n"),
3527 libzfs_error_description(g_zfs));
3528 be_free_list(be_nodes);
3529 return (NULL);
3534 * If 'num' equals 'cur_num', we've exhausted all possible
3535 * auto BE names for this base BE name.
3537 if (num == cur_num) {
3538 be_print_err(gettext("be_get_auto_name: "
3539 "No more available auto BE names for base "
3540 "BE name %s\n"), base_be_name);
3541 be_free_list(be_nodes);
3542 return (NULL);
3546 be_free_list(be_nodes);
3549 * Generate string for auto BE name.
3551 (void) snprintf(auto_be_name, sizeof (auto_be_name), "%s%c%d",
3552 base_be_name, BE_AUTO_NAME_DELIM, num);
3554 if ((c = strdup(auto_be_name)) == NULL) {
3555 be_print_err(gettext("be_get_auto_name: "
3556 "memory allocation failed\n"));
3557 return (NULL);
3560 return (c);
3564 * Function: be_get_console_prop
3565 * Description: Determine console device.
3566 * Returns:
3567 * Success - pointer to console setting.
3568 * Failure - NULL
3569 * Scope:
3570 * Private
3572 static char *
3573 be_get_console_prop(void)
3575 di_node_t dn;
3576 char *console = NULL;
3578 if ((dn = di_init("/", DINFOPROP)) == DI_NODE_NIL) {
3579 be_print_err(gettext("be_get_console_prop: "
3580 "di_init() failed\n"));
3581 return (NULL);
3584 if (di_prop_lookup_strings(DDI_DEV_T_ANY, dn,
3585 "console", &console) != -1) {
3586 di_fini(dn);
3587 return (console);
3590 if (console == NULL) {
3591 if (di_prop_lookup_strings(DDI_DEV_T_ANY, dn,
3592 "output-device", &console) != -1) {
3593 di_fini(dn);
3594 if (strncmp(console, "screen", strlen("screen")) == 0)
3595 console = BE_DEFAULT_CONSOLE;
3600 * Default console to text
3602 if (console == NULL) {
3603 console = BE_DEFAULT_CONSOLE;
3606 return (console);
3610 * Function: be_create_menu
3611 * Description:
3612 * This function is used if no menu.lst file exists. In
3613 * this case a new file is created and if needed default
3614 * lines are added to the file.
3615 * Parameters:
3616 * pool - The name of the pool the menu.lst file is on
3617 * menu_file - The name of the file we're creating.
3618 * menu_fp - A pointer to the file pointer of the file we
3619 * created. This is also used to pass back the file
3620 * pointer to the newly created file.
3621 * mode - the original mode used for the failed attempt to
3622 * non-existent file.
3623 * Returns:
3624 * BE_SUCCESS - Success
3625 * be_errno_t - Failure
3626 * Scope:
3627 * Private
3629 static int
3630 be_create_menu(
3631 char *pool,
3632 char *menu_file,
3633 FILE **menu_fp,
3634 char *mode)
3636 be_node_list_t *be_nodes = NULL;
3637 char *menu_path = NULL;
3638 char *be_rpool = NULL;
3639 char *be_name = NULL;
3640 char *console = NULL;
3641 errno = 0;
3643 if (menu_file == NULL || menu_fp == NULL || mode == NULL)
3644 return (BE_ERR_INVAL);
3646 menu_path = strdup(menu_file);
3647 if (menu_path == NULL)
3648 return (BE_ERR_NOMEM);
3650 (void) dirname(menu_path);
3651 if (*menu_path == '.') {
3652 free(menu_path);
3653 return (BE_ERR_BAD_MENU_PATH);
3655 if (mkdirp(menu_path,
3656 S_IRWXU | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH) == -1 &&
3657 errno != EEXIST) {
3658 free(menu_path);
3659 be_print_err(gettext("be_create_menu: Failed to create the %s "
3660 "directory: %s\n"), menu_path, strerror(errno));
3661 return (errno_to_be_err(errno));
3663 free(menu_path);
3666 * Check to see if this system supports grub
3668 if (be_has_grub()) {
3670 * The grub menu is missing so we need to create it
3671 * and fill in the first few lines.
3673 FILE *temp_fp = fopen(menu_file, "a+");
3674 if (temp_fp == NULL) {
3675 *menu_fp = NULL;
3676 return (errno_to_be_err(errno));
3679 if ((console = be_get_console_prop()) != NULL) {
3682 * If console is redirected to serial line,
3683 * GRUB splash screen will not be enabled.
3685 if (strncmp(console, "text", strlen("text")) == 0 ||
3686 strncmp(console, "graphics",
3687 strlen("graphics")) == 0) {
3689 (void) fprintf(temp_fp, "%s\n", BE_GRUB_SPLASH);
3690 (void) fprintf(temp_fp, "%s\n",
3691 BE_GRUB_FOREGROUND);
3692 (void) fprintf(temp_fp, "%s\n",
3693 BE_GRUB_BACKGROUND);
3694 (void) fprintf(temp_fp, "%s\n",
3695 BE_GRUB_DEFAULT);
3696 } else {
3697 be_print_err(gettext("be_create_menu: "
3698 "console on serial line, "
3699 "GRUB splash image will be disabled\n"));
3703 (void) fprintf(temp_fp, "timeout 30\n");
3704 (void) fclose(temp_fp);
3706 } else {
3708 * The menu file doesn't exist so we need to create a
3709 * blank file.
3711 FILE *temp_fp = fopen(menu_file, "w+");
3712 if (temp_fp == NULL) {
3713 *menu_fp = NULL;
3714 return (errno_to_be_err(errno));
3716 (void) fclose(temp_fp);
3720 * Now we need to add all the BE's back into the the file.
3722 if (_be_list(NULL, &be_nodes) == BE_SUCCESS) {
3723 while (be_nodes != NULL) {
3724 if (strcmp(pool, be_nodes->be_rpool) == 0) {
3725 (void) be_append_menu(be_nodes->be_node_name,
3726 be_nodes->be_rpool, NULL, NULL, NULL);
3728 if (be_nodes->be_active_on_boot) {
3729 be_rpool = strdup(be_nodes->be_rpool);
3730 be_name = strdup(be_nodes->be_node_name);
3733 be_nodes = be_nodes->be_next_node;
3736 be_free_list(be_nodes);
3739 * Check to see if this system supports grub
3741 if (be_has_grub()) {
3742 int err = be_change_grub_default(be_name, be_rpool);
3743 if (err != BE_SUCCESS)
3744 return (err);
3746 *menu_fp = fopen(menu_file, mode);
3747 if (*menu_fp == NULL)
3748 return (errno_to_be_err(errno));
3750 return (BE_SUCCESS);
3754 * Function: be_open_menu
3755 * Description:
3756 * This function is used it open the menu.lst file. If this
3757 * file does not exist be_create_menu is called to create it
3758 * and the open file pointer is returned. If the file does
3759 * exist it is simply opened using the mode passed in.
3760 * Parameters:
3761 * pool - The name of the pool the menu.lst file is on
3762 * menu_file - The name of the file we're opening.
3763 * menu_fp - A pointer to the file pointer of the file we're
3764 * opening. This is also used to pass back the file
3765 * pointer.
3766 * mode - the original mode to be used for opening the menu.lst
3767 * file.
3768 * create_menu - If this is true and the menu.lst file does not
3769 * exist we will attempt to re-create it. However
3770 * if it's false the error returned from the fopen
3771 * will be returned.
3772 * Returns:
3773 * BE_SUCCESS - Success
3774 * be_errno_t - Failure
3775 * Scope:
3776 * Private
3778 static int
3779 be_open_menu(
3780 char *pool,
3781 char *menu_file,
3782 FILE **menu_fp,
3783 char *mode,
3784 boolean_t create_menu)
3786 int err = 0;
3787 boolean_t set_print = B_FALSE;
3789 *menu_fp = fopen(menu_file, mode);
3790 err = errno;
3791 if (*menu_fp == NULL) {
3792 if (err == ENOENT && create_menu) {
3793 be_print_err(gettext("be_open_menu: menu.lst "
3794 "file %s does not exist,\n"), menu_file);
3795 if (!do_print) {
3796 set_print = B_TRUE;
3797 do_print = B_TRUE;
3799 be_print_err(gettext("WARNING: menu.lst "
3800 "file %s does not exist,\n generating "
3801 "a new menu.lst file\n"), menu_file);
3802 if (set_print)
3803 do_print = B_FALSE;
3804 err = 0;
3805 if ((err = be_create_menu(pool, menu_file,
3806 menu_fp, mode)) == ENOENT)
3807 return (BE_ERR_NO_MENU);
3808 else if (err != BE_SUCCESS)
3809 return (err);
3810 else if (*menu_fp == NULL)
3811 return (BE_ERR_NO_MENU);
3812 } else {
3813 be_print_err(gettext("be_open_menu: failed "
3814 "to open menu.lst file %s\n"), menu_file);
3815 if (err == ENOENT)
3816 return (BE_ERR_NO_MENU);
3817 else
3818 return (errno_to_be_err(err));
3821 return (BE_SUCCESS);