1 /*
2 * CDDL HEADER START
3 *
4 * The contents of this file are subject to the terms of the
5 * Common Development and Distribution License (the "License").
6 * You may not use this file except in compliance with the License.
7 *
8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9 * or http://www.opensolaris.org/os/licensing.
10 * See the License for the specific language governing permissions
11 * and limitations under the License.
12 *
13 * When distributing Covered Code, include this CDDL HEADER in each
14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15 * If applicable, add the following below this CDDL HEADER, with the
16 * fields enclosed by brackets "[]" replaced with your own identifying
17 * information: Portions Copyright [yyyy] [name of copyright owner]
18 *
19 * CDDL HEADER END
20 */
21
22 /*
23 * Copyright (c) 2008, 2010, Oracle and/or its affiliates. All rights reserved.
24 */
25
26 /*
27 * Copyright 2013 Nexenta Systems, Inc. All rights reserved.
28 */
29
30 #include <assert.h>
31 #include <libintl.h>
32 #include <libnvpair.h>
33 #include <libzfs.h>
34 #include <stdio.h>
35 #include <stdlib.h>
36 #include <string.h>
37 #include <errno.h>
38 #include <sys/mnttab.h>
39 #include <sys/types.h>
40 #include <sys/stat.h>
41 #include <unistd.h>
42
43 #include <libbe.h>
44 #include <libbe_priv.h>
45
46 char *mnttab = MNTTAB;
47
48 /*
49 * Private function prototypes
50 */
51 static int set_bootfs(char *boot_rpool, char *be_root_ds);
52 static int set_canmount(be_node_list_t *, char *);
53 static int be_do_installgrub(be_transaction_data_t *);
54 static int be_get_grub_vers(be_transaction_data_t *, char **, char **);
55 static int get_ver_from_capfile(char *, char **);
56 static int be_promote_zone_ds(char *, char *);
57 static int be_promote_ds_callback(zfs_handle_t *, void *);
58
59 /* ******************************************************************** */
60 /* Public Functions */
61 /* ******************************************************************** */
62
63 /*
64 * Function: be_activate
65 * Description: Calls _be_activate which activates the BE named in the
66 * attributes passed in through be_attrs. The process of
67 * activation sets the bootfs property of the root pool, resets
68 * the canmount property to noauto, and sets the default in the
69 * grub menu to the entry corresponding to the entry for the named
70 * BE.
71 * Parameters:
72 * be_attrs - pointer to nvlist_t of attributes being passed in.
73 * The follow attribute values are used by this function:
74 *
75 * BE_ATTR_ORIG_BE_NAME *required
76 * Return:
77 * BE_SUCCESS - Success
78 * be_errno_t - Failure
79 * Scope:
80 * Public
81 */
82 int
83 be_activate(nvlist_t *be_attrs)
84 {
85 int ret = BE_SUCCESS;
86 char *be_name = NULL;
87
88 /* Initialize libzfs handle */
89 if (!be_zfs_init())
90 return (BE_ERR_INIT);
91
92 /* Get the BE name to activate */
93 if (nvlist_lookup_string(be_attrs, BE_ATTR_ORIG_BE_NAME, &be_name)
94 != 0) {
95 be_print_err(gettext("be_activate: failed to "
96 "lookup BE_ATTR_ORIG_BE_NAME attribute\n"));
97 be_zfs_fini();
98 return (BE_ERR_INVAL);
99 }
100
101 /* Validate BE name */
102 if (!be_valid_be_name(be_name)) {
103 be_print_err(gettext("be_activate: invalid BE name %s\n"),
104 be_name);
105 be_zfs_fini();
106 return (BE_ERR_INVAL);
107 }
108
109 ret = _be_activate(be_name);
110
111 be_zfs_fini();
112
113 return (ret);
114 }
115
116 /* ******************************************************************** */
117 /* Semi Private Functions */
118 /* ******************************************************************** */
119
120 /*
121 * Function: _be_activate
122 * Description: This does the actual work described in be_activate.
123 * Parameters:
124 * be_name - pointer to the name of BE to activate.
125 *
126 * Return:
127 * BE_SUCCESS - Success
128 * be_errnot_t - Failure
129 * Scope:
130 * Public
131 */
132 int
133 _be_activate(char *be_name)
134 {
135 be_transaction_data_t cb = { 0 };
136 zfs_handle_t *zhp = NULL;
137 char root_ds[MAXPATHLEN];
138 char active_ds[MAXPATHLEN];
139 char *cur_vers = NULL, *new_vers = NULL;
140 be_node_list_t *be_nodes = NULL;
141 uuid_t uu = {0};
142 int entry, ret = BE_SUCCESS;
143 int zret = 0;
144
145 /*
146 * TODO: The BE needs to be validated to make sure that it is actually
147 * a bootable BE.
148 */
149
150 if (be_name == NULL)
151 return (BE_ERR_INVAL);
152
153 /* Set obe_name to be_name in the cb structure */
154 cb.obe_name = be_name;
155
156 /* find which zpool the be is in */
157 if ((zret = zpool_iter(g_zfs, be_find_zpool_callback, &cb)) == 0) {
158 be_print_err(gettext("be_activate: failed to "
159 "find zpool for BE (%s)\n"), cb.obe_name);
160 return (BE_ERR_BE_NOENT);
161 } else if (zret < 0) {
162 be_print_err(gettext("be_activate: "
163 "zpool_iter failed: %s\n"),
164 libzfs_error_description(g_zfs));
165 ret = zfs_err_to_be_err(g_zfs);
166 return (ret);
167 }
168
169 be_make_root_ds(cb.obe_zpool, cb.obe_name, root_ds, sizeof (root_ds));
170 cb.obe_root_ds = strdup(root_ds);
171
172 if (getzoneid() == GLOBAL_ZONEID) {
173 if (be_has_grub() && (ret = be_get_grub_vers(&cb, &cur_vers,
174 &new_vers)) != BE_SUCCESS) {
175 be_print_err(gettext("be_activate: failed to get grub "
176 "versions from capability files.\n"));
177 return (ret);
178 }
179 if (cur_vers != NULL) {
180 /*
181 * We need to check to see if the version number from
182 * the BE being activated is greater than the current
183 * one.
184 */
185 if (new_vers != NULL &&
186 atof(cur_vers) < atof(new_vers)) {
187 if ((ret = be_do_installgrub(&cb))
188 != BE_SUCCESS) {
189 free(new_vers);
190 free(cur_vers);
191 return (ret);
192 }
193 free(new_vers);
194 }
195 free(cur_vers);
196 } else if (new_vers != NULL) {
197 if ((ret = be_do_installgrub(&cb)) != BE_SUCCESS) {
198 free(new_vers);
199 return (ret);
200 }
201 free(new_vers);
202 }
203 if (!be_has_menu_entry(root_ds, cb.obe_zpool, &entry)) {
204 if ((ret = be_append_menu(cb.obe_name, cb.obe_zpool,
205 NULL, NULL, NULL)) != BE_SUCCESS) {
206 be_print_err(gettext("be_activate: Failed to "
207 "add BE (%s) to the GRUB menu\n"),
208 cb.obe_name);
209 goto done;
210 }
211 }
212 if (be_has_grub()) {
213 if ((ret = be_change_grub_default(cb.obe_name,
214 cb.obe_zpool)) != BE_SUCCESS) {
215 be_print_err(gettext("be_activate: failed to "
216 "change the default entry in menu.lst\n"));
217 goto done;
218 }
219 }
220 }
221
222 if ((ret = _be_list(cb.obe_name, &be_nodes)) != BE_SUCCESS) {
223 return (ret);
224 }
225
226 if ((ret = set_canmount(be_nodes, "noauto")) != BE_SUCCESS) {
227 be_print_err(gettext("be_activate: failed to set "
228 "canmount dataset property\n"));
229 goto done;
230 }
231
232 if (getzoneid() == GLOBAL_ZONEID) {
233 if ((ret = set_bootfs(be_nodes->be_rpool,
234 root_ds)) != BE_SUCCESS) {
235 be_print_err(gettext("be_activate: failed to set "
236 "bootfs pool property for %s\n"), root_ds);
237 goto done;
238 }
239 }
240
241 if ((zhp = zfs_open(g_zfs, root_ds, ZFS_TYPE_FILESYSTEM)) != NULL) {
242 /*
243 * We don't need to close the zfs handle at this
244 * point because The callback funtion
245 * be_promote_ds_callback() will close it for us.
246 */
247 if (be_promote_ds_callback(zhp, NULL) != 0) {
248 be_print_err(gettext("be_activate: "
249 "failed to activate the "
250 "datasets for %s: %s\n"),
251 root_ds,
252 libzfs_error_description(g_zfs));
253 ret = BE_ERR_PROMOTE;
254 goto done;
255 }
256 } else {
257 be_print_err(gettext("be_activate: failed to open "
258 "dataset (%s): %s\n"), root_ds,
259 libzfs_error_description(g_zfs));
260 ret = zfs_err_to_be_err(g_zfs);
261 goto done;
262 }
263
264 if (getzoneid() == GLOBAL_ZONEID &&
265 be_get_uuid(cb.obe_root_ds, &uu) == BE_SUCCESS &&
266 (ret = be_promote_zone_ds(cb.obe_name, cb.obe_root_ds))
267 != BE_SUCCESS) {
268 be_print_err(gettext("be_activate: failed to promote "
269 "the active zonepath datasets for zones in BE %s\n"),
270 cb.obe_name);
271 }
272
273 if (getzoneid() != GLOBAL_ZONEID) {
274 if (!be_zone_compare_uuids(root_ds)) {
275 be_print_err(gettext("be_activate: activating zone "
276 "root dataset from non-active global BE is not "
277 "supported\n"));
278 ret = BE_ERR_NOTSUP;
279 goto done;
280 }
281 if ((zhp = zfs_open(g_zfs, root_ds,
282 ZFS_TYPE_FILESYSTEM)) == NULL) {
283 be_print_err(gettext("be_activate: failed to open "
284 "dataset (%s): %s\n"), root_ds,
285 libzfs_error_description(g_zfs));
286 ret = zfs_err_to_be_err(g_zfs);
287 goto done;
288 }
289 /* Find current active zone root dataset */
290 if ((ret = be_find_active_zone_root(zhp, cb.obe_zpool,
291 active_ds, sizeof (active_ds))) != BE_SUCCESS) {
292 be_print_err(gettext("be_activate: failed to find "
293 "active zone root dataset\n"));
294 ZFS_CLOSE(zhp);
295 goto done;
296 }
297 /* Do nothing if requested BE is already active */
298 if (strcmp(root_ds, active_ds) == 0) {
299 ret = BE_SUCCESS;
300 ZFS_CLOSE(zhp);
301 goto done;
302 }
303
304 /* Set active property for BE */
305 if (zfs_prop_set(zhp, BE_ZONE_ACTIVE_PROPERTY, "on") != 0) {
306 be_print_err(gettext("be_activate: failed to set "
307 "active property (%s): %s\n"), root_ds,
308 libzfs_error_description(g_zfs));
309 ret = zfs_err_to_be_err(g_zfs);
310 ZFS_CLOSE(zhp);
311 goto done;
312 }
313 ZFS_CLOSE(zhp);
314
315 /* Unset active property for old active root dataset */
316 if ((zhp = zfs_open(g_zfs, active_ds,
317 ZFS_TYPE_FILESYSTEM)) == NULL) {
318 be_print_err(gettext("be_activate: failed to open "
319 "dataset (%s): %s\n"), active_ds,
320 libzfs_error_description(g_zfs));
321 ret = zfs_err_to_be_err(g_zfs);
322 goto done;
323 }
324 if (zfs_prop_set(zhp, BE_ZONE_ACTIVE_PROPERTY, "off") != 0) {
325 be_print_err(gettext("be_activate: failed to unset "
326 "active property (%s): %s\n"), active_ds,
327 libzfs_error_description(g_zfs));
328 ret = zfs_err_to_be_err(g_zfs);
329 ZFS_CLOSE(zhp);
330 goto done;
331 }
332 ZFS_CLOSE(zhp);
333 }
334 done:
335 be_free_list(be_nodes);
336 return (ret);
337 }
338
339 /*
340 * Function: be_activate_current_be
341 * Description: Set the currently "active" BE to be "active on boot"
342 * Paramters:
343 * none
344 * Returns:
345 * BE_SUCCESS - Success
346 * be_errnot_t - Failure
347 * Scope:
348 * Semi-private (library wide use only)
349 */
350 int
351 be_activate_current_be(void)
352 {
353 int ret = BE_SUCCESS;
354 be_transaction_data_t bt = { 0 };
355
356 if ((ret = be_find_current_be(&bt)) != BE_SUCCESS) {
357 return (ret);
358 }
359
360 if ((ret = _be_activate(bt.obe_name)) != BE_SUCCESS) {
361 be_print_err(gettext("be_activate_current_be: failed to "
362 "activate %s\n"), bt.obe_name);
363 return (ret);
364 }
365
366 return (BE_SUCCESS);
367 }
368
369 /*
370 * Function: be_is_active_on_boot
371 * Description: Checks if the BE name passed in has the "active on boot"
372 * property set to B_TRUE.
373 * Paramters:
374 * be_name - the name of the BE to check
375 * Returns:
376 * B_TRUE - if active on boot.
377 * B_FALSE - if not active on boot.
378 * Scope:
379 * Semi-private (library wide use only)
380 */
381 boolean_t
382 be_is_active_on_boot(char *be_name)
383 {
384 be_node_list_t *be_node = NULL;
385
386 if (be_name == NULL) {
387 be_print_err(gettext("be_is_active_on_boot: "
388 "be_name must not be NULL\n"));
389 return (B_FALSE);
390 }
391
392 if (_be_list(be_name, &be_node) != BE_SUCCESS) {
393 return (B_FALSE);
394 }
395
396 if (be_node == NULL) {
397 return (B_FALSE);
398 }
399
400 if (be_node->be_active_on_boot) {
401 be_free_list(be_node);
402 return (B_TRUE);
403 } else {
404 be_free_list(be_node);
405 return (B_FALSE);
406 }
407 }
408
409 /* ******************************************************************** */
410 /* Private Functions */
411 /* ******************************************************************** */
412
413 /*
414 * Function: set_bootfs
415 * Description: Sets the bootfs property on the boot pool to be the
416 * root dataset of the activated BE.
417 * Parameters:
418 * boot_pool - The pool we're setting bootfs in.
419 * be_root_ds - The main dataset for the BE.
420 * Return:
421 * BE_SUCCESS - Success
422 * be_errno_t - Failure
423 * Scope:
424 * Private
425 */
426 static int
427 set_bootfs(char *boot_rpool, char *be_root_ds)
428 {
429 zpool_handle_t *zhp;
430 int err = BE_SUCCESS;
431
432 if ((zhp = zpool_open(g_zfs, boot_rpool)) == NULL) {
433 be_print_err(gettext("set_bootfs: failed to open pool "
434 "(%s): %s\n"), boot_rpool, libzfs_error_description(g_zfs));
435 err = zfs_err_to_be_err(g_zfs);
436 return (err);
437 }
438
439 err = zpool_set_prop(zhp, "bootfs", be_root_ds);
440 if (err) {
441 be_print_err(gettext("set_bootfs: failed to set "
442 "bootfs property for pool %s: %s\n"), boot_rpool,
443 libzfs_error_description(g_zfs));
444 err = zfs_err_to_be_err(g_zfs);
445 zpool_close(zhp);
446 return (err);
447 }
448
449 zpool_close(zhp);
450 return (BE_SUCCESS);
451 }
452
453 /*
454 * Function: set_canmount
455 * Description: Sets the canmount property on the datasets of the
456 * activated BE.
457 * Parameters:
458 * be_nodes - The be_node_t returned from be_list
459 * value - The value of canmount we setting, on|off|noauto.
460 * Return:
461 * BE_SUCCESS - Success
462 * be_errno_t - Failure
463 * Scope:
464 * Private
465 */
466 static int
467 set_canmount(be_node_list_t *be_nodes, char *value)
468 {
469 char ds_path[MAXPATHLEN];
470 zfs_handle_t *zhp = NULL;
471 be_node_list_t *list = be_nodes;
472 int err = BE_SUCCESS;
473
474 while (list != NULL) {
475 be_dataset_list_t *datasets = list->be_node_datasets;
476
477 be_make_root_ds(list->be_rpool, list->be_node_name, ds_path,
478 sizeof (ds_path));
479
480 if ((zhp = zfs_open(g_zfs, ds_path, ZFS_TYPE_DATASET)) ==
481 NULL) {
482 be_print_err(gettext("set_canmount: failed to open "
483 "dataset (%s): %s\n"), ds_path,
484 libzfs_error_description(g_zfs));
485 err = zfs_err_to_be_err(g_zfs);
486 return (err);
487 }
488 if (zfs_prop_get_int(zhp, ZFS_PROP_MOUNTED)) {
489 /*
490 * it's already mounted so we can't change the
491 * canmount property anyway.
492 */
493 err = BE_SUCCESS;
494 } else {
495 err = zfs_prop_set(zhp,
496 zfs_prop_to_name(ZFS_PROP_CANMOUNT), value);
497 if (err) {
498 ZFS_CLOSE(zhp);
499 be_print_err(gettext("set_canmount: failed to "
500 "set dataset property (%s): %s\n"),
501 ds_path, libzfs_error_description(g_zfs));
502 err = zfs_err_to_be_err(g_zfs);
503 return (err);
504 }
505 }
506 ZFS_CLOSE(zhp);
507
508 while (datasets != NULL) {
509 be_make_root_ds(list->be_rpool,
510 datasets->be_dataset_name, ds_path,
511 sizeof (ds_path));
512
513 if ((zhp = zfs_open(g_zfs, ds_path, ZFS_TYPE_DATASET))
514 == NULL) {
515 be_print_err(gettext("set_canmount: failed to "
516 "open dataset %s: %s\n"), ds_path,
517 libzfs_error_description(g_zfs));
518 err = zfs_err_to_be_err(g_zfs);
519 return (err);
520 }
521 if (zfs_prop_get_int(zhp, ZFS_PROP_MOUNTED)) {
522 /*
523 * it's already mounted so we can't change the
524 * canmount property anyway.
525 */
526 err = BE_SUCCESS;
527 ZFS_CLOSE(zhp);
528 break;
529 }
530 err = zfs_prop_set(zhp,
531 zfs_prop_to_name(ZFS_PROP_CANMOUNT), value);
532 if (err) {
533 ZFS_CLOSE(zhp);
534 be_print_err(gettext("set_canmount: "
535 "Failed to set property value %s "
536 "for dataset %s: %s\n"), value, ds_path,
537 libzfs_error_description(g_zfs));
538 err = zfs_err_to_be_err(g_zfs);
539 return (err);
540 }
541 ZFS_CLOSE(zhp);
542 datasets = datasets->be_next_dataset;
543 }
544 list = list->be_next_node;
545 }
546 return (err);
547 }
548
549 /*
550 * Function: be_get_grub_vers
551 * Description: Gets the grub version number from /boot/grub/capability. If
552 * capability file doesn't exist NULL is returned.
553 * Parameters:
554 * bt - The transaction data for the BE we're getting the grub
555 * version for.
556 * cur_vers - used to return the current version of grub from
557 * the root pool.
558 * new_vers - used to return the grub version of the BE we're
559 * activating.
560 * Return:
561 * BE_SUCCESS - Success
562 * be_errno_t - Failed to find version
563 * Scope:
564 * Private
565 */
566 static int
567 be_get_grub_vers(be_transaction_data_t *bt, char **cur_vers, char **new_vers)
568 {
569 zfs_handle_t *zhp = NULL;
570 zfs_handle_t *pool_zhp = NULL;
571 int ret = BE_SUCCESS;
572 char cap_file[MAXPATHLEN];
573 char *temp_mntpnt = NULL;
574 char *zpool_mntpt = NULL;
575 char *ptmp_mntpnt = NULL;
576 char *orig_mntpnt = NULL;
577 boolean_t be_mounted = B_FALSE;
578 boolean_t pool_mounted = B_FALSE;
579
580 if (!be_has_grub()) {
581 be_print_err(gettext("be_get_grub_vers: Not supported on "
582 "this architecture\n"));
583 return (BE_ERR_NOTSUP);
584 }
585
586 if (bt == NULL || bt->obe_name == NULL || bt->obe_zpool == NULL ||
587 bt->obe_root_ds == NULL) {
588 be_print_err(gettext("be_get_grub_vers: Invalid BE\n"));
589 return (BE_ERR_INVAL);
590 }
591
592 if ((pool_zhp = zfs_open(g_zfs, bt->obe_zpool, ZFS_TYPE_FILESYSTEM)) ==
593 NULL) {
594 be_print_err(gettext("be_get_grub_vers: zfs_open failed: %s\n"),
595 libzfs_error_description(g_zfs));
596 return (zfs_err_to_be_err(g_zfs));
597 }
598
599 /*
600 * Check to see if the pool's dataset is mounted. If it isn't we'll
601 * attempt to mount it.
602 */
603 if ((ret = be_mount_pool(pool_zhp, &ptmp_mntpnt,
604 &orig_mntpnt, &pool_mounted)) != BE_SUCCESS) {
605 be_print_err(gettext("be_get_grub_vers: pool dataset "
606 "(%s) could not be mounted\n"), bt->obe_zpool);
607 ZFS_CLOSE(pool_zhp);
608 return (ret);
609 }
610
611 /*
612 * Get the mountpoint for the root pool dataset.
613 */
614 if (!zfs_is_mounted(pool_zhp, &zpool_mntpt)) {
615 be_print_err(gettext("be_get_grub_vers: pool "
616 "dataset (%s) is not mounted. Can't set the "
617 "default BE in the grub menu.\n"), bt->obe_zpool);
618 ret = BE_ERR_NO_MENU;
619 goto cleanup;
620 }
621
622 /*
623 * get the version of the most recent grub update.
624 */
625 (void) snprintf(cap_file, sizeof (cap_file), "%s%s",
626 zpool_mntpt, BE_CAP_FILE);
627 free(zpool_mntpt);
628 zpool_mntpt = NULL;
629
630 if ((ret = get_ver_from_capfile(cap_file, cur_vers)) != BE_SUCCESS)
631 goto cleanup;
632
633 if ((zhp = zfs_open(g_zfs, bt->obe_root_ds, ZFS_TYPE_FILESYSTEM)) ==
634 NULL) {
635 be_print_err(gettext("be_get_grub_vers: failed to "
636 "open BE root dataset (%s): %s\n"), bt->obe_root_ds,
637 libzfs_error_description(g_zfs));
638 free(cur_vers);
639 ret = zfs_err_to_be_err(g_zfs);
640 goto cleanup;
641 }
642 if (!zfs_is_mounted(zhp, &temp_mntpnt)) {
643 if ((ret = _be_mount(bt->obe_name, &temp_mntpnt,
644 BE_MOUNT_FLAG_NO_ZONES)) != BE_SUCCESS) {
645 be_print_err(gettext("be_get_grub_vers: failed to "
646 "mount BE (%s)\n"), bt->obe_name);
647 free(*cur_vers);
648 *cur_vers = NULL;
649 ZFS_CLOSE(zhp);
650 goto cleanup;
651 }
652 be_mounted = B_TRUE;
653 }
654 ZFS_CLOSE(zhp);
655
656 /*
657 * Now get the grub version for the BE being activated.
658 */
659 (void) snprintf(cap_file, sizeof (cap_file), "%s%s", temp_mntpnt,
660 BE_CAP_FILE);
661 ret = get_ver_from_capfile(cap_file, new_vers);
662 if (ret != BE_SUCCESS) {
663 free(*cur_vers);
664 *cur_vers = NULL;
665 }
666 if (be_mounted)
667 (void) _be_unmount(bt->obe_name, 0);
668
669 cleanup:
670 if (pool_mounted) {
671 int iret = BE_SUCCESS;
672 iret = be_unmount_pool(pool_zhp, ptmp_mntpnt, orig_mntpnt);
673 if (ret == BE_SUCCESS)
674 ret = iret;
675 free(orig_mntpnt);
676 free(ptmp_mntpnt);
677 }
678 ZFS_CLOSE(pool_zhp);
679
680 free(temp_mntpnt);
681 return (ret);
682 }
683
684 /*
685 * Function: get_ver_from_capfile
686 * Description: Parses the capability file passed in looking for the VERSION
687 * line. If found the version is returned in vers, if not then
688 * NULL is returned in vers.
689 *
690 * Parameters:
691 * file - the path to the capability file we want to parse.
692 * vers - the version string that will be passed back.
693 * Return:
694 * BE_SUCCESS - Success
695 * be_errno_t - Failed to find version
696 * Scope:
697 * Private
698 */
699 static int
700 get_ver_from_capfile(char *file, char **vers)
701 {
702 FILE *fp = NULL;
703 char line[BUFSIZ];
704 char *last = NULL;
705 int err = BE_SUCCESS;
706 errno = 0;
707
708 if (!be_has_grub()) {
709 be_print_err(gettext("get_ver_from_capfile: Not supported "
710 "on this architecture\n"));
711 return (BE_ERR_NOTSUP);
712 }
713
714 /*
715 * Set version string to NULL; the only case this shouldn't be set
716 * to be NULL is when we've actually found a version in the capability
717 * file, which is set below.
718 */
719 *vers = NULL;
720
721 /*
722 * If the capability file doesn't exist, we're returning success
723 * because on older releases, the capability file did not exist
724 * so this is a valid scenario.
725 */
726 if (access(file, F_OK) == 0) {
727 if ((fp = fopen(file, "r")) == NULL) {
728 err = errno;
729 be_print_err(gettext("get_ver_from_capfile: failed to "
730 "open file %s with error %s\n"), file,
731 strerror(err));
732 err = errno_to_be_err(err);
733 return (err);
734 }
735
736 while (fgets(line, BUFSIZ, fp)) {
737 char *tok = strtok_r(line, "=", &last);
738
739 if (tok == NULL || tok[0] == '#') {
740 continue;
741 } else if (strcmp(tok, "VERSION") == 0) {
742 *vers = strdup(last);
743 break;
744 }
745 }
746 (void) fclose(fp);
747 }
748
749 return (BE_SUCCESS);
750 }
751
752 /*
753 * Function: be_do_installgrub
754 * Description: This function runs installgrub using the grub loader files
755 * from the BE we're activating and installing them on the
756 * pool the BE lives in.
757 *
758 * Parameters:
759 * bt - The transaction data for the BE we're activating.
760 * Return:
761 * BE_SUCCESS - Success
762 * be_errno_t - Failure
763 *
764 * Scope:
765 * Private
766 */
767 static int
768 be_do_installgrub(be_transaction_data_t *bt)
769 {
770 zpool_handle_t *zphp = NULL;
771 zfs_handle_t *zhp = NULL;
772 nvlist_t **child, *nv, *config;
773 uint_t c, children = 0;
774 char *tmp_mntpt = NULL;
775 char *pool_mntpnt = NULL;
776 char *ptmp_mntpnt = NULL;
777 char *orig_mntpnt = NULL;
778 FILE *cap_fp = NULL;
779 FILE *zpool_cap_fp = NULL;
780 char line[BUFSIZ];
781 char cap_file[MAXPATHLEN];
782 char zpool_cap_file[MAXPATHLEN];
783 char stage1[MAXPATHLEN];
784 char stage2[MAXPATHLEN];
785 char installgrub_cmd[MAXPATHLEN];
786 char *vname;
787 char be_run_cmd_errbuf[BUFSIZ];
788 int ret = BE_SUCCESS;
789 int err = 0;
790 boolean_t be_mounted = B_FALSE;
791 boolean_t pool_mounted = B_FALSE;
792
793 if (!be_has_grub()) {
794 be_print_err(gettext("be_do_installgrub: Not supported "
795 "on this architecture\n"));
796 return (BE_ERR_NOTSUP);
797 }
798
799 if ((zhp = zfs_open(g_zfs, bt->obe_root_ds, ZFS_TYPE_FILESYSTEM)) ==
800 NULL) {
801 be_print_err(gettext("be_do_installgrub: failed to "
802 "open BE root dataset (%s): %s\n"), bt->obe_root_ds,
803 libzfs_error_description(g_zfs));
804 ret = zfs_err_to_be_err(g_zfs);
805 return (ret);
806 }
807 if (!zfs_is_mounted(zhp, &tmp_mntpt)) {
808 if ((ret = _be_mount(bt->obe_name, &tmp_mntpt,
809 BE_MOUNT_FLAG_NO_ZONES)) != BE_SUCCESS) {
810 be_print_err(gettext("be_do_installgrub: failed to "
811 "mount BE (%s)\n"), bt->obe_name);
812 ZFS_CLOSE(zhp);
813 return (ret);
814 }
815 be_mounted = B_TRUE;
816 }
817 ZFS_CLOSE(zhp);
818
819 (void) snprintf(stage1, sizeof (stage1), "%s%s", tmp_mntpt, BE_STAGE_1);
820 (void) snprintf(stage2, sizeof (stage2), "%s%s", tmp_mntpt, BE_STAGE_2);
821
822 if ((zphp = zpool_open(g_zfs, bt->obe_zpool)) == NULL) {
823 be_print_err(gettext("be_do_installgrub: failed to open "
824 "pool (%s): %s\n"), bt->obe_zpool,
825 libzfs_error_description(g_zfs));
826 ret = zfs_err_to_be_err(g_zfs);
827 if (be_mounted)
828 (void) _be_unmount(bt->obe_name, 0);
829 free(tmp_mntpt);
830 return (ret);
831 }
832
833 if ((config = zpool_get_config(zphp, NULL)) == NULL) {
834 be_print_err(gettext("be_do_installgrub: failed to get zpool "
835 "configuration information. %s\n"),
836 libzfs_error_description(g_zfs));
837 ret = zfs_err_to_be_err(g_zfs);
838 goto done;
839 }
840
841 /*
842 * Get the vdev tree
843 */
844 if (nvlist_lookup_nvlist(config, ZPOOL_CONFIG_VDEV_TREE, &nv) != 0) {
845 be_print_err(gettext("be_do_installgrub: failed to get vdev "
846 "tree: %s\n"), libzfs_error_description(g_zfs));
847 ret = zfs_err_to_be_err(g_zfs);
848 goto done;
849 }
850
851 if (nvlist_lookup_nvlist_array(nv, ZPOOL_CONFIG_CHILDREN, &child,
852 &children) != 0) {
853 be_print_err(gettext("be_do_installgrub: failed to traverse "
854 "the vdev tree: %s\n"), libzfs_error_description(g_zfs));
855 ret = zfs_err_to_be_err(g_zfs);
856 goto done;
857 }
858 for (c = 0; c < children; c++) {
859 uint_t i, nchildren = 0;
860 nvlist_t **nvchild;
861 vname = zpool_vdev_name(g_zfs, zphp, child[c], B_FALSE);
862 if (vname == NULL) {
863 be_print_err(gettext(
864 "be_do_installgrub: "
865 "failed to get device name: %s\n"),
866 libzfs_error_description(g_zfs));
867 ret = zfs_err_to_be_err(g_zfs);
868 goto done;
869 }
870 if (strcmp(vname, "mirror") == 0 || vname[0] != 'c') {
871
872 if (nvlist_lookup_nvlist_array(child[c],
873 ZPOOL_CONFIG_CHILDREN, &nvchild, &nchildren) != 0) {
874 be_print_err(gettext("be_do_installgrub: "
875 "failed to traverse the vdev tree: %s\n"),
876 libzfs_error_description(g_zfs));
877 ret = zfs_err_to_be_err(g_zfs);
878 goto done;
879 }
880
881 for (i = 0; i < nchildren; i++) {
882 vname = zpool_vdev_name(g_zfs, zphp,
883 nvchild[i], B_FALSE);
884 if (vname == NULL) {
885 be_print_err(gettext(
886 "be_do_installgrub: "
887 "failed to get device name: %s\n"),
888 libzfs_error_description(g_zfs));
889 ret = zfs_err_to_be_err(g_zfs);
890 goto done;
891 }
892
893 (void) snprintf(installgrub_cmd,
894 sizeof (installgrub_cmd),
895 "%s %s %s /dev/rdsk/%s",
896 BE_INSTALL_GRUB, stage1, stage2, vname);
897 if (be_run_cmd(installgrub_cmd,
898 be_run_cmd_errbuf, BUFSIZ, NULL, 0) !=
899 BE_SUCCESS) {
900 be_print_err(gettext(
901 "be_do_installgrub: installgrub "
902 "failed for device %s.\n"), vname);
903 /* Assume localized cmd err output. */
904 be_print_err(gettext(
905 " Command: \"%s\"\n"),
906 installgrub_cmd);
907 be_print_err("%s", be_run_cmd_errbuf);
908 free(vname);
909 ret = BE_ERR_BOOTFILE_INST;
910 goto done;
911 }
912 free(vname);
913 }
914 } else {
915 (void) snprintf(installgrub_cmd,
916 sizeof (installgrub_cmd), "%s %s %s /dev/rdsk/%s",
917 BE_INSTALL_GRUB, stage1, stage2, vname);
918 if (be_run_cmd(installgrub_cmd, be_run_cmd_errbuf,
919 BUFSIZ, NULL, 0) != BE_SUCCESS) {
920 be_print_err(gettext(
921 "be_do_installgrub: installgrub "
922 "failed for device %s.\n"), vname);
923 /* Assume localized cmd err output. */
924 be_print_err(gettext(" Command: \"%s\"\n"),
925 installgrub_cmd);
926 be_print_err("%s", be_run_cmd_errbuf);
927 free(vname);
928 ret = BE_ERR_BOOTFILE_INST;
929 goto done;
930 }
931 free(vname);
932 }
933 }
934
935 /*
936 * Copy the grub capability file from the BE we're activating into
937 * the root pool.
938 */
939 (void) snprintf(cap_file, sizeof (cap_file), "%s%s", tmp_mntpt,
940 BE_CAP_FILE);
941
942 if ((zhp = zfs_open(g_zfs, bt->obe_zpool, ZFS_TYPE_FILESYSTEM)) ==
943 NULL) {
944 be_print_err(gettext("be_do_installgrub: zfs_open "
945 "failed: %s\n"), libzfs_error_description(g_zfs));
946 zpool_close(zphp);
947 return (zfs_err_to_be_err(g_zfs));
948 }
949
950 /*
951 * Check to see if the pool's dataset is mounted. If it isn't we'll
952 * attempt to mount it.
953 */
954 if ((ret = be_mount_pool(zhp, &ptmp_mntpnt,
955 &orig_mntpnt, &pool_mounted)) != BE_SUCCESS) {
956 be_print_err(gettext("be_do_installgrub: pool dataset "
957 "(%s) could not be mounted\n"), bt->obe_zpool);
958 ZFS_CLOSE(zhp);
959 zpool_close(zphp);
960 return (ret);
961 }
962
963 /*
964 * Get the mountpoint for the root pool dataset.
965 */
966 if (!zfs_is_mounted(zhp, &pool_mntpnt)) {
967 be_print_err(gettext("be_do_installgrub: pool "
968 "dataset (%s) is not mounted. Can't check the grub "
969 "version from the grub capability file.\n"), bt->obe_zpool);
970 ret = BE_ERR_NO_MENU;
971 goto done;
972 }
973
974 (void) snprintf(zpool_cap_file, sizeof (zpool_cap_file), "%s%s",
975 pool_mntpnt, BE_CAP_FILE);
976
977 free(pool_mntpnt);
978 pool_mntpnt = NULL;
979
980 if ((cap_fp = fopen(cap_file, "r")) == NULL) {
981 err = errno;
982 be_print_err(gettext("be_do_installgrub: failed to open grub "
983 "capability file\n"));
984 ret = errno_to_be_err(err);
985 goto done;
986 }
987 if ((zpool_cap_fp = fopen(zpool_cap_file, "w")) == NULL) {
988 err = errno;
989 be_print_err(gettext("be_do_installgrub: failed to open new "
990 "grub capability file\n"));
991 ret = errno_to_be_err(err);
992 (void) fclose(cap_fp);
993 goto done;
994 }
995
996 while (fgets(line, BUFSIZ, cap_fp)) {
997 (void) fputs(line, zpool_cap_fp);
998 }
999
1000 (void) fclose(zpool_cap_fp);
1001 (void) fclose(cap_fp);
1002
1003 done:
1004 if (pool_mounted) {
1005 int iret = 0;
1006 iret = be_unmount_pool(zhp, ptmp_mntpnt, orig_mntpnt);
1007 if (ret == BE_SUCCESS)
1008 ret = iret;
1009 free(orig_mntpnt);
1010 free(ptmp_mntpnt);
1011 }
1012 ZFS_CLOSE(zhp);
1013 if (be_mounted)
1014 (void) _be_unmount(bt->obe_name, 0);
1015 zpool_close(zphp);
1016 free(tmp_mntpt);
1017 return (ret);
1018 }
1019
1020 /*
1021 * Function: be_promote_zone_ds
1022 * Description: This function finds the zones for the BE being activated
1023 * and the active zonepath dataset for each zone. Then each
1024 * active zonepath dataset is promoted.
1025 *
1026 * Parameters:
1027 * be_name - the name of the global zone BE that we need to
1028 * find the zones for.
1029 * be_root_ds - the root dataset for be_name.
1030 * Return:
1031 * BE_SUCCESS - Success
1032 * be_errno_t - Failure
1033 *
1034 * Scope:
1035 * Private
1036 */
1037 static int
1038 be_promote_zone_ds(char *be_name, char *be_root_ds)
1039 {
1040 char *zone_ds = NULL;
1041 char *temp_mntpt = NULL;
1042 char origin[MAXPATHLEN];
1043 char zoneroot_ds[MAXPATHLEN];
1044 zfs_handle_t *zhp = NULL;
1045 zfs_handle_t *z_zhp = NULL;
1046 zoneList_t zone_list = NULL;
1047 zoneBrandList_t *brands = NULL;
1048 boolean_t be_mounted = B_FALSE;
1049 int zone_index = 0;
1050 int err = BE_SUCCESS;
1051
1052 /*
1053 * Get the supported zone brands so we can pass that
1054 * to z_get_nonglobal_zone_list_by_brand. Currently
1055 * only the ipkg and labeled brand zones are supported
1056 *
1057 */
1058 if ((brands = be_get_supported_brandlist()) == NULL) {
1059 be_print_err(gettext("be_promote_zone_ds: no supported "
1060 "brands\n"));
1061 return (BE_SUCCESS);
1062 }
1063
1064 if ((zhp = zfs_open(g_zfs, be_root_ds,
1065 ZFS_TYPE_FILESYSTEM)) == NULL) {
1066 be_print_err(gettext("be_promote_zone_ds: Failed to open "
1067 "dataset (%s): %s\n"), be_root_ds,
1068 libzfs_error_description(g_zfs));
1069 err = zfs_err_to_be_err(g_zfs);
1070 z_free_brand_list(brands);
1071 return (err);
1072 }
1073
1074 if (!zfs_is_mounted(zhp, &temp_mntpt)) {
1075 if ((err = _be_mount(be_name, &temp_mntpt,
1076 BE_MOUNT_FLAG_NO_ZONES)) != BE_SUCCESS) {
1077 be_print_err(gettext("be_promote_zone_ds: failed to "
1078 "mount the BE for zones procesing.\n"));
1079 ZFS_CLOSE(zhp);
1080 z_free_brand_list(brands);
1081 return (err);
1082 }
1083 be_mounted = B_TRUE;
1084 }
1085
1086 /*
1087 * Set the zone root to the temp mount point for the BE we just mounted.
1088 */
1089 z_set_zone_root(temp_mntpt);
1090
1091 /*
1092 * Get all the zones based on the brands we're looking for. If no zones
1093 * are found that we're interested in unmount the BE and move on.
1094 */
1095 if ((zone_list = z_get_nonglobal_zone_list_by_brand(brands)) == NULL) {
1096 if (be_mounted)
1097 (void) _be_unmount(be_name, 0);
1098 ZFS_CLOSE(zhp);
1099 z_free_brand_list(brands);
1100 free(temp_mntpt);
1101 return (BE_SUCCESS);
1102 }
1103 for (zone_index = 0; z_zlist_get_zonename(zone_list, zone_index)
1104 != NULL; zone_index++) {
1105 char *zone_path = NULL;
1106
1107 /* Skip zones that aren't at least installed */
1108 if (z_zlist_get_current_state(zone_list, zone_index) <
1109 ZONE_STATE_INSTALLED)
1110 continue;
1111
1112 if (((zone_path =
1113 z_zlist_get_zonepath(zone_list, zone_index)) == NULL) ||
1114 ((zone_ds = be_get_ds_from_dir(zone_path)) == NULL) ||
1115 !be_zone_supported(zone_ds))
1116 continue;
1117
1118 if (be_find_active_zone_root(zhp, zone_ds,
1119 zoneroot_ds, sizeof (zoneroot_ds)) != 0) {
1120 be_print_err(gettext("be_promote_zone_ds: "
1121 "Zone does not have an active root "
1122 "dataset, skipping this zone.\n"));
1123 continue;
1124 }
1125
1126 if ((z_zhp = zfs_open(g_zfs, zoneroot_ds,
1127 ZFS_TYPE_FILESYSTEM)) == NULL) {
1128 be_print_err(gettext("be_promote_zone_ds: "
1129 "Failed to open dataset "
1130 "(%s): %s\n"), zoneroot_ds,
1131 libzfs_error_description(g_zfs));
1132 err = zfs_err_to_be_err(g_zfs);
1133 goto done;
1134 }
1135
1136 if (zfs_prop_get(z_zhp, ZFS_PROP_ORIGIN, origin,
1137 sizeof (origin), NULL, NULL, 0, B_FALSE) != 0) {
1138 ZFS_CLOSE(z_zhp);
1139 continue;
1140 }
1141
1142 /*
1143 * We don't need to close the zfs handle at this
1144 * point because the callback funtion
1145 * be_promote_ds_callback() will close it for us.
1146 */
1147 if (be_promote_ds_callback(z_zhp, NULL) != 0) {
1148 be_print_err(gettext("be_promote_zone_ds: "
1149 "failed to activate the "
1150 "datasets for %s: %s\n"),
1151 zoneroot_ds,
1152 libzfs_error_description(g_zfs));
1153 err = BE_ERR_PROMOTE;
1154 goto done;
1155 }
1156 }
1157 done:
1158 if (be_mounted)
1159 (void) _be_unmount(be_name, 0);
1160 ZFS_CLOSE(zhp);
1161 free(temp_mntpt);
1162 z_free_brand_list(brands);
1163 z_free_zone_list(zone_list);
1164 return (err);
1165 }
1166
1167 /*
1168 * Function: be_promote_ds_callback
1169 * Description: This function is used to promote the datasets for the BE
1170 * being activated as well as the datasets for the zones BE
1171 * being activated.
1172 *
1173 * Parameters:
1174 * zhp - the zfs handle for zone BE being activated.
1175 * data - not used.
1176 * Return:
1177 * 0 - Success
1178 * be_errno_t - Failure
1179 *
1180 * Scope:
1181 * Private
1182 */
1183 static int
1184 /* LINTED */
1185 be_promote_ds_callback(zfs_handle_t *zhp, void *data)
1186 {
1187 char origin[MAXPATHLEN];
1188 char *sub_dataset = NULL;
1189 int ret = 0;
1190
1191 if (zhp != NULL) {
1192 sub_dataset = strdup(zfs_get_name(zhp));
1193 if (sub_dataset == NULL) {
1194 ret = BE_ERR_NOMEM;
1195 goto done;
1196 }
1197 } else {
1198 be_print_err(gettext("be_promote_ds_callback: "
1199 "Invalid zfs handle passed into function\n"));
1200 ret = BE_ERR_INVAL;
1201 goto done;
1202 }
1203
1204 /*
1205 * This loop makes sure that we promote the dataset to the
1206 * top of the tree so that it is no longer a decendent of any
1207 * dataset. The ZFS close and then open is used to make sure that
1208 * the promotion is updated before we move on.
1209 */
1210 while (zfs_prop_get(zhp, ZFS_PROP_ORIGIN, origin,
1211 sizeof (origin), NULL, NULL, 0, B_FALSE) == 0) {
1212 if (zfs_promote(zhp) != 0) {
1213 if (libzfs_errno(g_zfs) != EZFS_EXISTS) {
1214 be_print_err(gettext("be_promote_ds_callback: "
1215 "promote of %s failed: %s\n"),
1216 zfs_get_name(zhp),
1217 libzfs_error_description(g_zfs));
1218 ret = zfs_err_to_be_err(g_zfs);
1219 goto done;
1220 } else {
1221 /*
1222 * If the call to zfs_promote returns the
1223 * error EZFS_EXISTS we've hit a snapshot name
1224 * collision. This means we're probably
1225 * attemping to promote a zone dataset above a
1226 * parent dataset that belongs to another zone
1227 * which this zone was cloned from.
1228 *
1229 * TODO: If this is a zone dataset at some
1230 * point we should skip this if the zone
1231 * paths for the dataset and the snapshot
1232 * don't match.
1233 */
1234 be_print_err(gettext("be_promote_ds_callback: "
1235 "promote of %s failed due to snapshot "
1236 "name collision: %s\n"), zfs_get_name(zhp),
1237 libzfs_error_description(g_zfs));
1238 ret = zfs_err_to_be_err(g_zfs);
1239 goto done;
1240 }
1241 }
1242 ZFS_CLOSE(zhp);
1243 if ((zhp = zfs_open(g_zfs, sub_dataset,
1244 ZFS_TYPE_FILESYSTEM)) == NULL) {
1245 be_print_err(gettext("be_promote_ds_callback: "
1246 "Failed to open dataset (%s): %s\n"), sub_dataset,
1247 libzfs_error_description(g_zfs));
1248 ret = zfs_err_to_be_err(g_zfs);
1249 goto done;
1250 }
1251 }
1252
1253 /* Iterate down this dataset's children and promote them */
1254 ret = zfs_iter_filesystems(zhp, be_promote_ds_callback, NULL);
1255
1256 done:
1257 free(sub_dataset);
1258 ZFS_CLOSE(zhp);
1259 return (ret);
1260 }