2930 beadm should sort by creation date
[illumos-gate.git] / usr / src / lib / libbe / common / be_list.c
blob8d355e865ae43ffc6389cd1ba0d509cfe7eaeaee
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.
28 * Copyright 2015 Toomas Soome <tsoome@me.com>
31 #include <assert.h>
32 #include <libintl.h>
33 #include <libnvpair.h>
34 #include <libzfs.h>
35 #include <stdio.h>
36 #include <stdlib.h>
37 #include <string.h>
38 #include <strings.h>
39 #include <sys/types.h>
40 #include <sys/stat.h>
41 #include <unistd.h>
42 #include <errno.h>
44 #include <libbe.h>
45 #include <libbe_priv.h>
48 * Callback data used for zfs_iter calls.
50 typedef struct list_callback_data {
51 char *zpool_name;
52 char *be_name;
53 be_node_list_t *be_nodes_head;
54 be_node_list_t *be_nodes;
55 char current_be[MAXPATHLEN];
56 } list_callback_data_t;
59 * Private function prototypes
61 static int be_add_children_callback(zfs_handle_t *zhp, void *data);
62 static int be_get_list_callback(zpool_handle_t *, void *);
63 static int be_get_node_data(zfs_handle_t *, be_node_list_t *, char *,
64 const char *, char *, char *);
65 static int be_get_zone_node_data(be_node_list_t *, char *);
66 static int be_get_ds_data(zfs_handle_t *, char *, be_dataset_list_t *,
67 be_node_list_t *);
68 static int be_get_ss_data(zfs_handle_t *, char *, be_snapshot_list_t *,
69 be_node_list_t *);
70 static void be_sort_list(be_node_list_t **,
71 int (*)(const void *, const void *));
72 static int be_qsort_compare_BEs_name(const void *, const void *);
73 static int be_qsort_compare_BEs_name_rev(const void *, const void *);
74 static int be_qsort_compare_BEs_date(const void *, const void *);
75 static int be_qsort_compare_BEs_date_rev(const void *, const void *);
76 static int be_qsort_compare_BEs_space(const void *, const void *);
77 static int be_qsort_compare_BEs_space_rev(const void *, const void *);
78 static int be_qsort_compare_snapshots(const void *x, const void *y);
79 static int be_qsort_compare_datasets(const void *x, const void *y);
80 static void *be_list_alloc(int *, size_t);
83 * Private data.
85 static char be_container_ds[MAXPATHLEN];
86 static boolean_t zone_be = B_FALSE;
88 /* ******************************************************************** */
89 /* Public Functions */
90 /* ******************************************************************** */
93 * Function: be_list
94 * Description: Calls _be_list which finds all the BEs on the system and
95 * returns the datasets and snapshots belonging to each BE.
96 * Also data, such as dataset and snapshot properties,
97 * for each BE and their snapshots and datasets is
98 * returned. The data returned is as described in the
99 * be_dataset_list_t, be_snapshot_list_t and be_node_list_t
100 * structures.
101 * Parameters:
102 * be_name - The name of the BE to look up.
103 * If NULL a list of all BEs will be returned.
104 * be_nodes - A reference pointer to the list of BEs. The list
105 * structure will be allocated by _be_list and must
106 * be freed by a call to be_free_list. If there are no
107 * BEs found on the system this reference will be
108 * set to NULL.
109 * Return:
110 * BE_SUCCESS - Success
111 * be_errno_t - Failure
112 * Scope:
113 * Public
116 be_list(char *be_name, be_node_list_t **be_nodes)
118 int ret = BE_SUCCESS;
120 /* Initialize libzfs handle */
121 if (!be_zfs_init())
122 return (BE_ERR_INIT);
124 /* Validate be_name if its not NULL */
125 if (be_name != NULL) {
126 if (!be_valid_be_name(be_name)) {
127 be_print_err(gettext("be_list: "
128 "invalid BE name %s\n"), be_name);
129 return (BE_ERR_INVAL);
133 ret = _be_list(be_name, be_nodes);
135 be_zfs_fini();
137 return (ret);
141 * Function: be_sort
142 * Description: Sort BE node list
143 * Parameters:
144 * pointer to address of list head
145 * sort order type
146 * Returns:
147 * nothing
148 * Side effect:
149 * node list sorted by name
150 * Scope:
151 * Public
153 void
154 be_sort(be_node_list_t **be_nodes, int order)
156 int (*compar)(const void *, const void *) = be_qsort_compare_BEs_date;
158 if (be_nodes == NULL)
159 return;
161 switch (order) {
162 case BE_SORT_UNSPECIFIED:
163 case BE_SORT_DATE:
164 compar = be_qsort_compare_BEs_date;
165 break;
166 case BE_SORT_DATE_REV:
167 compar = be_qsort_compare_BEs_date_rev;
168 break;
169 case BE_SORT_NAME:
170 compar = be_qsort_compare_BEs_name;
171 break;
172 case BE_SORT_NAME_REV:
173 compar = be_qsort_compare_BEs_name_rev;
174 break;
175 case BE_SORT_SPACE:
176 compar = be_qsort_compare_BEs_space;
177 break;
178 case BE_SORT_SPACE_REV:
179 compar = be_qsort_compare_BEs_space_rev;
180 break;
181 default:
182 be_print_err(gettext("be_sort: invalid sort order %d\n"),
183 order);
184 return;
187 be_sort_list(be_nodes, compar);
190 /* ******************************************************************** */
191 /* Semi-Private Functions */
192 /* ******************************************************************** */
195 * Function: _be_list
196 * Description: This does the actual work described in be_list.
197 * Parameters:
198 * be_name - The name of the BE to look up.
199 * If NULL a list of all BEs will be returned.
200 * be_nodes - A reference pointer to the list of BEs. The list
201 * structure will be allocated here and must
202 * be freed by a call to be_free_list. If there are no
203 * BEs found on the system this reference will be
204 * set to NULL.
205 * Return:
206 * BE_SUCCESS - Success
207 * be_errno_t - Failure
208 * Scope:
209 * Semi-private (library wide use only)
212 _be_list(char *be_name, be_node_list_t **be_nodes)
214 list_callback_data_t cb = { 0 };
215 be_transaction_data_t bt = { 0 };
216 int ret = BE_SUCCESS;
217 zpool_handle_t *zphp;
218 char *rpool = NULL;
219 struct be_defaults be_defaults;
221 if (be_nodes == NULL)
222 return (BE_ERR_INVAL);
224 be_get_defaults(&be_defaults);
226 if (be_find_current_be(&bt) != BE_SUCCESS) {
228 * We were unable to find a currently booted BE which
229 * probably means that we're not booted in a BE envoronment.
230 * None of the BE's will be marked as the active BE.
232 (void) strcpy(cb.current_be, "-");
233 } else {
234 (void) strncpy(cb.current_be, bt.obe_name,
235 sizeof (cb.current_be));
236 rpool = bt.obe_zpool;
240 * If be_name is NULL we'll look for all BE's on the system.
241 * If not then we will only return data for the specified BE.
243 if (be_name != NULL)
244 cb.be_name = strdup(be_name);
246 if (be_defaults.be_deflt_rpool_container && rpool != NULL) {
247 if ((zphp = zpool_open(g_zfs, rpool)) == NULL) {
248 be_print_err(gettext("be_list: failed to "
249 "open rpool (%s): %s\n"), rpool,
250 libzfs_error_description(g_zfs));
251 free(cb.be_name);
252 return (zfs_err_to_be_err(g_zfs));
255 ret = be_get_list_callback(zphp, &cb);
256 } else {
257 if ((zpool_iter(g_zfs, be_get_list_callback, &cb)) != 0) {
258 if (cb.be_nodes_head != NULL) {
259 be_free_list(cb.be_nodes_head);
260 cb.be_nodes_head = NULL;
261 cb.be_nodes = NULL;
263 ret = BE_ERR_BE_NOENT;
267 if (cb.be_nodes_head == NULL) {
268 if (be_name != NULL)
269 be_print_err(gettext("be_list: BE (%s) does not "
270 "exist\n"), be_name);
271 else
272 be_print_err(gettext("be_list: No BE's found\n"));
273 ret = BE_ERR_BE_NOENT;
276 *be_nodes = cb.be_nodes_head;
278 free(cb.be_name);
280 be_sort(be_nodes, BE_SORT_DATE);
282 return (ret);
286 * Function: be_free_list
287 * Description: Frees up all the data allocated for the list of BEs,
288 * datasets and snapshots returned by be_list.
289 * Parameters:
290 * be_node - be_nodes_t structure returned from call to be_list.
291 * Returns:
292 * none
293 * Scope:
294 * Semi-private (library wide use only)
296 void
297 be_free_list(be_node_list_t *be_nodes)
299 be_node_list_t *temp_node = NULL;
300 be_node_list_t *list = be_nodes;
302 while (list != NULL) {
303 be_dataset_list_t *datasets = list->be_node_datasets;
304 be_snapshot_list_t *snapshots = list->be_node_snapshots;
306 while (datasets != NULL) {
307 be_dataset_list_t *temp_ds = datasets;
308 datasets = datasets->be_next_dataset;
309 free(temp_ds->be_dataset_name);
310 free(temp_ds->be_ds_mntpt);
311 free(temp_ds->be_ds_plcy_type);
312 free(temp_ds);
315 while (snapshots != NULL) {
316 be_snapshot_list_t *temp_ss = snapshots;
317 snapshots = snapshots->be_next_snapshot;
318 free(temp_ss->be_snapshot_name);
319 free(temp_ss->be_snapshot_type);
320 free(temp_ss);
323 temp_node = list;
324 list = list->be_next_node;
325 free(temp_node->be_node_name);
326 free(temp_node->be_root_ds);
327 free(temp_node->be_rpool);
328 free(temp_node->be_mntpt);
329 free(temp_node->be_policy_type);
330 free(temp_node->be_uuid_str);
331 free(temp_node);
336 * Function: be_get_zone_be_list
337 * Description: Finds all the BEs for this zone on the system.
338 * Parameters:
339 * zone_be_name - The name of the BE to look up.
340 * zone_be_container_ds - The dataset for the zone.
341 * zbe_nodes - A reference pointer to the list of BEs. The list
342 * structure will be allocated here and must
343 * be freed by a call to be_free_list. If there are no
344 * BEs found on the system this reference will be
345 * set to NULL.
346 * Return:
347 * BE_SUCCESS - Success
348 * be_errno_t - Failure
349 * Scope:
350 * Semi-private (library wide use only)
353 be_get_zone_be_list(
354 /* LINTED */
355 char *zone_be_name,
356 char *zone_be_container_ds,
357 be_node_list_t **zbe_nodes)
359 zfs_handle_t *zhp = NULL;
360 list_callback_data_t cb = { 0 };
361 int ret = BE_SUCCESS;
363 if (zbe_nodes == NULL)
364 return (BE_ERR_INVAL);
366 if (!zfs_dataset_exists(g_zfs, zone_be_container_ds,
367 ZFS_TYPE_FILESYSTEM)) {
368 return (BE_ERR_BE_NOENT);
371 zone_be = B_TRUE;
373 if ((zhp = zfs_open(g_zfs, zone_be_container_ds,
374 ZFS_TYPE_FILESYSTEM)) == NULL) {
375 be_print_err(gettext("be_get_zone_be_list: failed to open "
376 "the zone BE dataset %s: %s\n"), zone_be_container_ds,
377 libzfs_error_description(g_zfs));
378 ret = zfs_err_to_be_err(g_zfs);
379 goto cleanup;
382 (void) strcpy(be_container_ds, zone_be_container_ds);
384 if (cb.be_nodes_head == NULL) {
385 if ((cb.be_nodes_head = be_list_alloc(&ret,
386 sizeof (be_node_list_t))) == NULL) {
387 ZFS_CLOSE(zhp);
388 goto cleanup;
390 cb.be_nodes = cb.be_nodes_head;
392 if (ret == 0)
393 ret = zfs_iter_filesystems(zhp, be_add_children_callback, &cb);
394 ZFS_CLOSE(zhp);
396 *zbe_nodes = cb.be_nodes_head;
398 cleanup:
399 zone_be = B_FALSE;
401 return (ret);
404 /* ******************************************************************** */
405 /* Private Functions */
406 /* ******************************************************************** */
409 * Function: be_get_list_callback
410 * Description: Callback function used by zfs_iter to look through all
411 * the pools on the system looking for BEs. If a BE name was
412 * specified only that BE's information will be collected and
413 * returned.
414 * Parameters:
415 * zlp - handle to the first zfs dataset. (provided by the
416 * zfs_iter_* call)
417 * data - pointer to the callback data and where we'll pass
418 * the BE information back.
419 * Returns:
420 * 0 - Success
421 * be_errno_t - Failure
422 * Scope:
423 * Private
425 static int
426 be_get_list_callback(zpool_handle_t *zlp, void *data)
428 list_callback_data_t *cb = (list_callback_data_t *)data;
429 char be_ds[MAXPATHLEN];
430 char *open_ds = NULL;
431 char *rpool = NULL;
432 zfs_handle_t *zhp = NULL;
433 int ret = 0;
435 cb->zpool_name = rpool = (char *)zpool_get_name(zlp);
438 * Generate string for the BE container dataset
440 be_make_container_ds(rpool, be_container_ds,
441 sizeof (be_container_ds));
444 * If a BE name was specified we use it's root dataset in place of
445 * the container dataset. This is because we only want to collect
446 * the information for the specified BE.
448 if (cb->be_name != NULL) {
449 if (!be_valid_be_name(cb->be_name))
450 return (BE_ERR_INVAL);
452 * Generate string for the BE root dataset
454 be_make_root_ds(rpool, cb->be_name, be_ds, sizeof (be_ds));
455 open_ds = be_ds;
456 } else {
457 open_ds = be_container_ds;
461 * Check if the dataset exists
463 if (!zfs_dataset_exists(g_zfs, open_ds,
464 ZFS_TYPE_FILESYSTEM)) {
466 * The specified dataset does not exist in this pool or
467 * there are no valid BE's in this pool. Try the next zpool.
469 zpool_close(zlp);
470 return (0);
473 if ((zhp = zfs_open(g_zfs, open_ds, ZFS_TYPE_FILESYSTEM)) == NULL) {
474 be_print_err(gettext("be_get_list_callback: failed to open "
475 "the BE dataset %s: %s\n"), open_ds,
476 libzfs_error_description(g_zfs));
477 ret = zfs_err_to_be_err(g_zfs);
478 zpool_close(zlp);
479 return (ret);
483 * If a BE name was specified we iterate through the datasets
484 * and snapshots for this BE only. Otherwise we will iterate
485 * through the next level of datasets to find all the BE's
486 * within the pool
488 if (cb->be_name != NULL) {
489 if (cb->be_nodes_head == NULL) {
490 if ((cb->be_nodes_head = be_list_alloc(&ret,
491 sizeof (be_node_list_t))) == NULL) {
492 ZFS_CLOSE(zhp);
493 zpool_close(zlp);
494 return (ret);
496 cb->be_nodes = cb->be_nodes_head;
499 if ((ret = be_get_node_data(zhp, cb->be_nodes, cb->be_name,
500 rpool, cb->current_be, be_ds)) != BE_SUCCESS) {
501 ZFS_CLOSE(zhp);
502 zpool_close(zlp);
503 return (ret);
505 ret = zfs_iter_snapshots(zhp, be_add_children_callback, cb);
508 if (ret == 0)
509 ret = zfs_iter_filesystems(zhp, be_add_children_callback, cb);
510 ZFS_CLOSE(zhp);
512 zpool_close(zlp);
513 return (ret);
517 * Function: be_add_children_callback
518 * Description: Callback function used by zfs_iter to look through all
519 * the datasets and snapshots for each BE and add them to
520 * the lists of information to be passed back.
521 * Parameters:
522 * zhp - handle to the first zfs dataset. (provided by the
523 * zfs_iter_* call)
524 * data - pointer to the callback data and where we'll pass
525 * the BE information back.
526 * Returns:
527 * 0 - Success
528 * be_errno_t - Failure
529 * Scope:
530 * Private
532 static int
533 be_add_children_callback(zfs_handle_t *zhp, void *data)
535 list_callback_data_t *cb = (list_callback_data_t *)data;
536 char *str = NULL, *ds_path = NULL;
537 int ret = 0;
538 struct be_defaults be_defaults;
540 be_get_defaults(&be_defaults);
542 ds_path = str = strdup(zfs_get_name(zhp));
545 * get past the end of the container dataset plus the trailing "/"
547 str = str + (strlen(be_container_ds) + 1);
548 if (be_defaults.be_deflt_rpool_container) {
549 /* just skip if invalid */
550 if (!be_valid_be_name(str))
551 return (BE_SUCCESS);
554 if (cb->be_nodes_head == NULL) {
555 if ((cb->be_nodes_head = be_list_alloc(&ret,
556 sizeof (be_node_list_t))) == NULL) {
557 ZFS_CLOSE(zhp);
558 return (ret);
560 cb->be_nodes = cb->be_nodes_head;
563 if (zfs_get_type(zhp) == ZFS_TYPE_SNAPSHOT && !zone_be) {
564 be_snapshot_list_t *snapshots = NULL;
565 if (cb->be_nodes->be_node_snapshots == NULL) {
566 if ((cb->be_nodes->be_node_snapshots =
567 be_list_alloc(&ret, sizeof (be_snapshot_list_t)))
568 == NULL || ret != BE_SUCCESS) {
569 ZFS_CLOSE(zhp);
570 return (ret);
572 cb->be_nodes->be_node_snapshots->be_next_snapshot =
573 NULL;
574 snapshots = cb->be_nodes->be_node_snapshots;
575 } else {
576 for (snapshots = cb->be_nodes->be_node_snapshots;
577 snapshots != NULL;
578 snapshots = snapshots->be_next_snapshot) {
579 if (snapshots->be_next_snapshot != NULL)
580 continue;
582 * We're at the end of the list add the
583 * new snapshot.
585 if ((snapshots->be_next_snapshot =
586 be_list_alloc(&ret,
587 sizeof (be_snapshot_list_t))) == NULL ||
588 ret != BE_SUCCESS) {
589 ZFS_CLOSE(zhp);
590 return (ret);
592 snapshots = snapshots->be_next_snapshot;
593 snapshots->be_next_snapshot = NULL;
594 break;
597 if ((ret = be_get_ss_data(zhp, str, snapshots,
598 cb->be_nodes)) != BE_SUCCESS) {
599 ZFS_CLOSE(zhp);
600 return (ret);
602 } else if (strchr(str, '/') == NULL) {
603 if (cb->be_nodes->be_node_name != NULL) {
604 if ((cb->be_nodes->be_next_node =
605 be_list_alloc(&ret, sizeof (be_node_list_t))) ==
606 NULL || ret != BE_SUCCESS) {
607 ZFS_CLOSE(zhp);
608 return (ret);
610 cb->be_nodes = cb->be_nodes->be_next_node;
611 cb->be_nodes->be_next_node = NULL;
615 * If this is a zone root dataset then we only need
616 * the name of the zone BE at this point. We grab that
617 * and return.
619 if (zone_be) {
620 ret = be_get_zone_node_data(cb->be_nodes, str);
621 ZFS_CLOSE(zhp);
622 return (ret);
625 if ((ret = be_get_node_data(zhp, cb->be_nodes, str,
626 cb->zpool_name, cb->current_be, ds_path)) != BE_SUCCESS) {
627 ZFS_CLOSE(zhp);
628 return (ret);
630 } else if (strchr(str, '/') != NULL && !zone_be) {
631 be_dataset_list_t *datasets = NULL;
632 if (cb->be_nodes->be_node_datasets == NULL) {
633 if ((cb->be_nodes->be_node_datasets =
634 be_list_alloc(&ret, sizeof (be_dataset_list_t)))
635 == NULL || ret != BE_SUCCESS) {
636 ZFS_CLOSE(zhp);
637 return (ret);
639 cb->be_nodes->be_node_datasets->be_next_dataset = NULL;
640 datasets = cb->be_nodes->be_node_datasets;
641 } else {
642 for (datasets = cb->be_nodes->be_node_datasets;
643 datasets != NULL;
644 datasets = datasets->be_next_dataset) {
645 if (datasets->be_next_dataset != NULL)
646 continue;
648 * We're at the end of the list add
649 * the new dataset.
651 if ((datasets->be_next_dataset =
652 be_list_alloc(&ret,
653 sizeof (be_dataset_list_t)))
654 == NULL || ret != BE_SUCCESS) {
655 ZFS_CLOSE(zhp);
656 return (ret);
658 datasets = datasets->be_next_dataset;
659 datasets->be_next_dataset = NULL;
660 break;
664 if ((ret = be_get_ds_data(zhp, str,
665 datasets, cb->be_nodes)) != BE_SUCCESS) {
666 ZFS_CLOSE(zhp);
667 return (ret);
670 ret = zfs_iter_children(zhp, be_add_children_callback, cb);
671 if (ret != 0) {
672 be_print_err(gettext("be_add_children_callback: "
673 "encountered error: %s\n"),
674 libzfs_error_description(g_zfs));
675 ret = zfs_err_to_be_err(g_zfs);
677 ZFS_CLOSE(zhp);
678 return (ret);
682 * Function: be_sort_list
683 * Description: Sort BE node list
684 * Parameters:
685 * pointer to address of list head
686 * compare function
687 * Returns:
688 * nothing
689 * Side effect:
690 * node list sorted by name
691 * Scope:
692 * Private
694 static void
695 be_sort_list(be_node_list_t **pstart, int (*compar)(const void *, const void *))
697 size_t ibe, nbe;
698 be_node_list_t *p = NULL;
699 be_node_list_t **ptrlist = NULL;
701 if (pstart == NULL)
702 return;
703 /* build array of linked list BE struct pointers */
704 for (p = *pstart, nbe = 0; p != NULL; nbe++, p = p->be_next_node) {
705 ptrlist = realloc(ptrlist,
706 sizeof (be_node_list_t *) * (nbe + 2));
707 ptrlist[nbe] = p;
709 if (nbe == 0)
710 return;
711 /* in-place list quicksort using qsort(3C) */
712 if (nbe > 1) /* no sort if less than 2 BEs */
713 qsort(ptrlist, nbe, sizeof (be_node_list_t *), compar);
715 ptrlist[nbe] = NULL; /* add linked list terminator */
716 *pstart = ptrlist[0]; /* set new linked list header */
717 /* for each BE in list */
718 for (ibe = 0; ibe < nbe; ibe++) {
719 size_t k, ns; /* subordinate index, count */
721 /* rewrite list pointer chain, including terminator */
722 ptrlist[ibe]->be_next_node = ptrlist[ibe + 1];
723 /* sort subordinate snapshots */
724 if (ptrlist[ibe]->be_node_num_snapshots > 1) {
725 const size_t nmax = ptrlist[ibe]->be_node_num_snapshots;
726 be_snapshot_list_t ** const slist =
727 malloc(sizeof (be_snapshot_list_t *) * (nmax + 1));
728 be_snapshot_list_t *p;
730 if (slist == NULL)
731 continue;
732 /* build array of linked list snapshot struct ptrs */
733 for (ns = 0, p = ptrlist[ibe]->be_node_snapshots;
734 ns < nmax && p != NULL;
735 ns++, p = p->be_next_snapshot) {
736 slist[ns] = p;
738 if (ns < 2)
739 goto end_snapshot;
740 slist[ns] = NULL; /* add terminator */
741 /* in-place list quicksort using qsort(3C) */
742 qsort(slist, ns, sizeof (be_snapshot_list_t *),
743 be_qsort_compare_snapshots);
744 /* rewrite list pointer chain, including terminator */
745 ptrlist[ibe]->be_node_snapshots = slist[0];
746 for (k = 0; k < ns; k++)
747 slist[k]->be_next_snapshot = slist[k + 1];
748 end_snapshot:
749 free(slist);
751 /* sort subordinate datasets */
752 if (ptrlist[ibe]->be_node_num_datasets > 1) {
753 const size_t nmax = ptrlist[ibe]->be_node_num_datasets;
754 be_dataset_list_t ** const slist =
755 malloc(sizeof (be_dataset_list_t *) * (nmax + 1));
756 be_dataset_list_t *p;
758 if (slist == NULL)
759 continue;
760 /* build array of linked list dataset struct ptrs */
761 for (ns = 0, p = ptrlist[ibe]->be_node_datasets;
762 ns < nmax && p != NULL;
763 ns++, p = p->be_next_dataset) {
764 slist[ns] = p;
766 if (ns < 2) /* subordinate datasets < 2 - no sort */
767 goto end_dataset;
768 slist[ns] = NULL; /* add terminator */
769 /* in-place list quicksort using qsort(3C) */
770 qsort(slist, ns, sizeof (be_dataset_list_t *),
771 be_qsort_compare_datasets);
772 /* rewrite list pointer chain, including terminator */
773 ptrlist[ibe]->be_node_datasets = slist[0];
774 for (k = 0; k < ns; k++)
775 slist[k]->be_next_dataset = slist[k + 1];
776 end_dataset:
777 free(slist);
780 free:
781 free(ptrlist);
785 * Function: be_qsort_compare_BEs_date
786 * Description: compare BE creation times for qsort(3C)
787 * will sort BE list from oldest to most recent
788 * Parameters:
789 * x,y - BEs with names to compare
790 * Returns:
791 * positive if x>y, negative if y>x, 0 if equal
792 * Scope:
793 * Private
795 static int
796 be_qsort_compare_BEs_date(const void *x, const void *y)
798 be_node_list_t *p = *(be_node_list_t **)x;
799 be_node_list_t *q = *(be_node_list_t **)y;
801 assert(p != NULL);
802 assert(q != NULL);
804 if (p->be_node_creation > q->be_node_creation)
805 return (1);
806 if (p->be_node_creation < q->be_node_creation)
807 return (-1);
808 return (0);
812 * Function: be_qsort_compare_BEs_date_rev
813 * Description: compare BE creation times for qsort(3C)
814 * will sort BE list from recent to oldest
815 * Parameters:
816 * x,y - BEs with names to compare
817 * Returns:
818 * positive if y>x, negative if x>y, 0 if equal
819 * Scope:
820 * Private
822 static int
823 be_qsort_compare_BEs_date_rev(const void *x, const void *y)
825 return (be_qsort_compare_BEs_date(y, x));
829 * Function: be_qsort_compare_BEs_name
830 * Description: lexical compare of BE names for qsort(3C)
831 * Parameters:
832 * x,y - BEs with names to compare
833 * Returns:
834 * positive if x>y, negative if y>x, 0 if equal
835 * Scope:
836 * Private
838 static int
839 be_qsort_compare_BEs_name(const void *x, const void *y)
841 be_node_list_t *p = *(be_node_list_t **)x;
842 be_node_list_t *q = *(be_node_list_t **)y;
844 assert(p != NULL);
845 assert(p->be_node_name != NULL);
846 assert(q != NULL);
847 assert(q->be_node_name != NULL);
849 return (strcmp(p->be_node_name, q->be_node_name));
853 * Function: be_qsort_compare_BEs_name_rev
854 * Description: reverse lexical compare of BE names for qsort(3C)
855 * Parameters:
856 * x,y - BEs with names to compare
857 * Returns:
858 * positive if y>x, negative if x>y, 0 if equal
859 * Scope:
860 * Private
862 static int
863 be_qsort_compare_BEs_name_rev(const void *x, const void *y)
865 return (be_qsort_compare_BEs_name(y, x));
869 * Function: be_qsort_compare_BEs_space
870 * Description: compare BE sizes for qsort(3C)
871 * will sort BE list in growing order
872 * Parameters:
873 * x,y - BEs with names to compare
874 * Returns:
875 * positive if x>y, negative if y>x, 0 if equal
876 * Scope:
877 * Private
879 static int
880 be_qsort_compare_BEs_space(const void *x, const void *y)
882 be_node_list_t *p = *(be_node_list_t **)x;
883 be_node_list_t *q = *(be_node_list_t **)y;
885 assert(p != NULL);
886 assert(q != NULL);
888 if (p->be_space_used > q->be_space_used)
889 return (1);
890 if (p->be_space_used < q->be_space_used)
891 return (-1);
892 return (0);
896 * Function: be_qsort_compare_BEs_space_rev
897 * Description: compare BE sizes for qsort(3C)
898 * will sort BE list in shrinking
899 * Parameters:
900 * x,y - BEs with names to compare
901 * Returns:
902 * positive if y>x, negative if x>y, 0 if equal
903 * Scope:
904 * Private
906 static int
907 be_qsort_compare_BEs_space_rev(const void *x, const void *y)
909 return (be_qsort_compare_BEs_space(y, x));
913 * Function: be_qsort_compare_snapshots
914 * Description: lexical compare of BE names for qsort(3C)
915 * Parameters:
916 * x,y - BE snapshots with names to compare
917 * Returns:
918 * positive if y>x, negative if x>y, 0 if equal
919 * Scope:
920 * Private
922 static int
923 be_qsort_compare_snapshots(const void *x, const void *y)
925 be_snapshot_list_t *p = *(be_snapshot_list_t **)x;
926 be_snapshot_list_t *q = *(be_snapshot_list_t **)y;
928 if (p == NULL || p->be_snapshot_name == NULL)
929 return (1);
930 if (q == NULL || q->be_snapshot_name == NULL)
931 return (-1);
932 return (strcmp(p->be_snapshot_name, q->be_snapshot_name));
936 * Function: be_qsort_compare_datasets
937 * Description: lexical compare of dataset names for qsort(3C)
938 * Parameters:
939 * x,y - BE snapshots with names to compare
940 * Returns:
941 * positive if y>x, negative if x>y, 0 if equal
942 * Scope:
943 * Private
945 static int
946 be_qsort_compare_datasets(const void *x, const void *y)
948 be_dataset_list_t *p = *(be_dataset_list_t **)x;
949 be_dataset_list_t *q = *(be_dataset_list_t **)y;
951 if (p == NULL || p->be_dataset_name == NULL)
952 return (1);
953 if (q == NULL || q->be_dataset_name == NULL)
954 return (-1);
955 return (strcmp(p->be_dataset_name, q->be_dataset_name));
959 * Function: be_get_node_data
960 * Description: Helper function used to collect all the information to fill
961 * in the be_node_list structure to be returned by be_list.
962 * Parameters:
963 * zhp - Handle to the root dataset for the BE whose information
964 * we're collecting.
965 * be_node - a pointer to the node structure we're filling in.
966 * be_name - The BE name of the node whose information we're
967 * collecting.
968 * current_be - the name of the currently active BE.
969 * be_ds - The dataset name for the BE.
971 * Returns:
972 * BE_SUCCESS - Success
973 * be_errno_t - Failure
974 * Scope:
975 * Private
977 static int
978 be_get_node_data(
979 zfs_handle_t *zhp,
980 be_node_list_t *be_node,
981 char *be_name,
982 const char *rpool,
983 char *current_be,
984 char *be_ds)
986 char prop_buf[MAXPATHLEN];
987 nvlist_t *userprops = NULL;
988 nvlist_t *propval = NULL;
989 nvlist_t *zone_propval = NULL;
990 char *prop_str = NULL;
991 char *zone_prop_str = NULL;
992 char *grub_default_bootfs = NULL;
993 zpool_handle_t *zphp = NULL;
994 int err = 0;
996 if (be_node == NULL || be_name == NULL || current_be == NULL ||
997 be_ds == NULL) {
998 be_print_err(gettext("be_get_node_data: invalid arguments, "
999 "can not be NULL\n"));
1000 return (BE_ERR_INVAL);
1003 errno = 0;
1005 be_node->be_root_ds = strdup(be_ds);
1006 if ((err = errno) != 0 || be_node->be_root_ds == NULL) {
1007 be_print_err(gettext("be_get_node_data: failed to "
1008 "copy root dataset name\n"));
1009 return (errno_to_be_err(err));
1012 be_node->be_node_name = strdup(be_name);
1013 if ((err = errno) != 0 || be_node->be_node_name == NULL) {
1014 be_print_err(gettext("be_get_node_data: failed to "
1015 "copy BE name\n"));
1016 return (errno_to_be_err(err));
1018 if (strncmp(be_name, current_be, MAXPATHLEN) == 0)
1019 be_node->be_active = B_TRUE;
1020 else
1021 be_node->be_active = B_FALSE;
1023 be_node->be_rpool = strdup(rpool);
1024 if (be_node->be_rpool == NULL || (err = errno) != 0) {
1025 be_print_err(gettext("be_get_node_data: failed to "
1026 "copy root pool name\n"));
1027 return (errno_to_be_err(err));
1030 be_node->be_space_used = zfs_prop_get_int(zhp, ZFS_PROP_USED);
1032 if (getzoneid() == GLOBAL_ZONEID) {
1033 if ((zphp = zpool_open(g_zfs, rpool)) == NULL) {
1034 be_print_err(gettext("be_get_node_data: failed to open "
1035 "pool (%s): %s\n"), rpool,
1036 libzfs_error_description(g_zfs));
1037 return (zfs_err_to_be_err(g_zfs));
1040 (void) zpool_get_prop(zphp, ZPOOL_PROP_BOOTFS, prop_buf,
1041 ZFS_MAXPROPLEN, NULL, B_FALSE);
1042 if (be_has_grub() && (be_default_grub_bootfs(rpool,
1043 &grub_default_bootfs) == BE_SUCCESS) &&
1044 grub_default_bootfs != NULL)
1045 if (strcmp(grub_default_bootfs, be_ds) == 0)
1046 be_node->be_active_on_boot = B_TRUE;
1047 else
1048 be_node->be_active_on_boot = B_FALSE;
1049 else if (prop_buf != NULL && strcmp(prop_buf, be_ds) == 0)
1050 be_node->be_active_on_boot = B_TRUE;
1051 else
1052 be_node->be_active_on_boot = B_FALSE;
1054 be_node->be_global_active = B_TRUE;
1056 free(grub_default_bootfs);
1057 zpool_close(zphp);
1058 } else {
1059 if (be_zone_compare_uuids(be_node->be_root_ds))
1060 be_node->be_global_active = B_TRUE;
1061 else
1062 be_node->be_global_active = B_FALSE;
1066 * If the dataset is mounted use the mount point
1067 * returned from the zfs_is_mounted call. If the
1068 * dataset is not mounted then pull the mount
1069 * point information out of the zfs properties.
1071 be_node->be_mounted = zfs_is_mounted(zhp,
1072 &(be_node->be_mntpt));
1073 if (!be_node->be_mounted) {
1074 if (zfs_prop_get(zhp, ZFS_PROP_MOUNTPOINT, prop_buf,
1075 ZFS_MAXPROPLEN, NULL, NULL, 0, B_FALSE) == 0)
1076 be_node->be_mntpt = strdup(prop_buf);
1077 else
1078 return (zfs_err_to_be_err(g_zfs));
1081 be_node->be_node_creation = (time_t)zfs_prop_get_int(zhp,
1082 ZFS_PROP_CREATION);
1084 /* Get all user properties used for libbe */
1085 if ((userprops = zfs_get_user_props(zhp)) == NULL) {
1086 be_node->be_policy_type = strdup(be_default_policy());
1087 } else {
1088 if (getzoneid() != GLOBAL_ZONEID) {
1089 if (nvlist_lookup_nvlist(userprops,
1090 BE_ZONE_ACTIVE_PROPERTY, &zone_propval) != 0 ||
1091 zone_propval == NULL) {
1092 be_node->be_active_on_boot = B_FALSE;
1093 } else {
1094 verify(nvlist_lookup_string(zone_propval,
1095 ZPROP_VALUE, &zone_prop_str) == 0);
1096 if (strcmp(zone_prop_str, "on") == 0) {
1097 be_node->be_active_on_boot = B_TRUE;
1098 } else {
1099 be_node->be_active_on_boot = B_FALSE;
1104 if (nvlist_lookup_nvlist(userprops, BE_POLICY_PROPERTY,
1105 &propval) != 0 || propval == NULL) {
1106 be_node->be_policy_type =
1107 strdup(be_default_policy());
1108 } else {
1109 verify(nvlist_lookup_string(propval, ZPROP_VALUE,
1110 &prop_str) == 0);
1111 if (prop_str == NULL || strcmp(prop_str, "-") == 0 ||
1112 strcmp(prop_str, "") == 0)
1113 be_node->be_policy_type =
1114 strdup(be_default_policy());
1115 else
1116 be_node->be_policy_type = strdup(prop_str);
1118 if (getzoneid() != GLOBAL_ZONEID) {
1119 if (nvlist_lookup_nvlist(userprops,
1120 BE_ZONE_PARENTBE_PROPERTY, &propval) != 0 &&
1121 nvlist_lookup_string(propval, ZPROP_VALUE,
1122 &prop_str) == 0) {
1123 be_node->be_uuid_str = strdup(prop_str);
1125 } else {
1126 if (nvlist_lookup_nvlist(userprops, BE_UUID_PROPERTY,
1127 &propval) == 0 && nvlist_lookup_string(propval,
1128 ZPROP_VALUE, &prop_str) == 0) {
1129 be_node->be_uuid_str = strdup(prop_str);
1135 * Increment the dataset counter to include the root dataset
1136 * of the BE.
1138 be_node->be_node_num_datasets++;
1140 return (BE_SUCCESS);
1144 * Function: be_get_ds_data
1145 * Description: Helper function used by be_add_children_callback to collect
1146 * the dataset related information that will be returned by
1147 * be_list.
1148 * Parameters:
1149 * zhp - Handle to the zfs dataset whose information we're
1150 * collecting.
1151 * name - The name of the dataset we're processing.
1152 * dataset - A pointer to the be_dataset_list structure
1153 * we're filling in.
1154 * node - The node structure that this dataset belongs to.
1155 * Return:
1156 * BE_SUCCESS - Success
1157 * be_errno_t - Failure
1158 * Scope:
1159 * Private
1161 static int
1162 be_get_ds_data(
1163 zfs_handle_t *zfshp,
1164 char *name,
1165 be_dataset_list_t *dataset,
1166 be_node_list_t *node)
1168 char prop_buf[ZFS_MAXPROPLEN];
1169 nvlist_t *propval = NULL;
1170 nvlist_t *userprops = NULL;
1171 char *prop_str = NULL;
1172 int err = 0;
1174 if (zfshp == NULL || name == NULL || dataset == NULL || node == NULL) {
1175 be_print_err(gettext("be_get_ds_data: invalid arguments, "
1176 "can not be NULL\n"));
1177 return (BE_ERR_INVAL);
1180 errno = 0;
1182 dataset->be_dataset_name = strdup(name);
1183 if ((err = errno) != 0) {
1184 be_print_err(gettext("be_get_ds_data: failed to copy "
1185 "dataset name\n"));
1186 return (errno_to_be_err(err));
1189 dataset->be_ds_space_used = zfs_prop_get_int(zfshp, ZFS_PROP_USED);
1192 * If the dataset is mounted use the mount point
1193 * returned from the zfs_is_mounted call. If the
1194 * dataset is not mounted then pull the mount
1195 * point information out of the zfs properties.
1197 if (!(dataset->be_ds_mounted = zfs_is_mounted(zfshp,
1198 &(dataset->be_ds_mntpt)))) {
1199 if (zfs_prop_get(zfshp, ZFS_PROP_MOUNTPOINT,
1200 prop_buf, ZFS_MAXPROPLEN, NULL, NULL, 0,
1201 B_FALSE) == 0)
1202 dataset->be_ds_mntpt = strdup(prop_buf);
1203 else
1204 return (zfs_err_to_be_err(g_zfs));
1206 dataset->be_ds_creation =
1207 (time_t)zfs_prop_get_int(zfshp, ZFS_PROP_CREATION);
1210 * Get the user property used for the libbe
1211 * cleaup policy
1213 if ((userprops = zfs_get_user_props(zfshp)) == NULL) {
1214 dataset->be_ds_plcy_type =
1215 strdup(node->be_policy_type);
1216 } else {
1217 if (nvlist_lookup_nvlist(userprops,
1218 BE_POLICY_PROPERTY, &propval) != 0 ||
1219 propval == NULL) {
1220 dataset->be_ds_plcy_type =
1221 strdup(node->be_policy_type);
1222 } else {
1223 verify(nvlist_lookup_string(propval,
1224 ZPROP_VALUE, &prop_str) == 0);
1225 if (prop_str == NULL ||
1226 strcmp(prop_str, "-") == 0 ||
1227 strcmp(prop_str, "") == 0)
1228 dataset->be_ds_plcy_type
1229 = strdup(node->be_policy_type);
1230 else
1231 dataset->be_ds_plcy_type = strdup(prop_str);
1235 node->be_node_num_datasets++;
1236 return (BE_SUCCESS);
1240 * Function: be_get_ss_data
1241 * Description: Helper function used by be_add_children_callback to collect
1242 * the dataset related information that will be returned by
1243 * be_list.
1244 * Parameters:
1245 * zhp - Handle to the zfs snapshot whose information we're
1246 * collecting.
1247 * name - The name of the snapshot we're processing.
1248 * shapshot - A pointer to the be_snapshot_list structure
1249 * we're filling in.
1250 * node - The node structure that this snapshot belongs to.
1251 * Returns:
1252 * BE_SUCCESS - Success
1253 * be_errno_t - Failure
1254 * Scope:
1255 * Private
1257 static int
1258 be_get_ss_data(
1259 zfs_handle_t *zfshp,
1260 char *name,
1261 be_snapshot_list_t *snapshot,
1262 be_node_list_t *node)
1264 nvlist_t *propval = NULL;
1265 nvlist_t *userprops = NULL;
1266 char *prop_str = NULL;
1267 int err = 0;
1269 if (zfshp == NULL || name == NULL || snapshot == NULL || node == NULL) {
1270 be_print_err(gettext("be_get_ss_data: invalid arguments, "
1271 "can not be NULL\n"));
1272 return (BE_ERR_INVAL);
1275 errno = 0;
1277 snapshot->be_snapshot_name = strdup(name);
1278 if ((err = errno) != 0) {
1279 be_print_err(gettext("be_get_ss_data: failed to copy name\n"));
1280 return (errno_to_be_err(err));
1283 snapshot->be_snapshot_creation = (time_t)zfs_prop_get_int(zfshp,
1284 ZFS_PROP_CREATION);
1287 * Try to get this snapshot's cleanup policy from its
1288 * user properties first. If not there, use default
1289 * cleanup policy.
1291 if ((userprops = zfs_get_user_props(zfshp)) != NULL &&
1292 nvlist_lookup_nvlist(userprops, BE_POLICY_PROPERTY,
1293 &propval) == 0 && nvlist_lookup_string(propval,
1294 ZPROP_VALUE, &prop_str) == 0) {
1295 snapshot->be_snapshot_type =
1296 strdup(prop_str);
1297 } else {
1298 snapshot->be_snapshot_type =
1299 strdup(be_default_policy());
1302 snapshot->be_snapshot_space_used = zfs_prop_get_int(zfshp,
1303 ZFS_PROP_USED);
1305 node->be_node_num_snapshots++;
1306 return (BE_SUCCESS);
1310 * Function: be_list_alloc
1311 * Description: Helper function used to allocate memory for the various
1312 * sructures that make up a BE node.
1313 * Parameters:
1314 * err - Used to return any errors encountered.
1315 * BE_SUCCESS - Success
1316 * BE_ERR_NOMEM - Allocation failure
1317 * size - The size of memory to allocate.
1318 * Returns:
1319 * Success - A pointer to the allocated memory
1320 * Failure - NULL
1321 * Scope:
1322 * Private
1324 static void*
1325 be_list_alloc(int *err, size_t size)
1327 void *bep = NULL;
1329 bep = calloc(1, size);
1330 if (bep == NULL) {
1331 be_print_err(gettext("be_list_alloc: memory "
1332 "allocation failed\n"));
1333 *err = BE_ERR_NOMEM;
1335 *err = BE_SUCCESS;
1336 return (bep);
1340 * Function: be_get_zone_node_data
1341 * Description: Helper function used to collect all the information to
1342 * fill in the be_node_list structure to be returned by
1343 * be_get_zone_list.
1344 * Parameters:
1345 * be_node - a pointer to the node structure we're filling in.
1346 * be_name - The BE name of the node whose information we're
1347 * Returns:
1348 * BE_SUCCESS - Success
1349 * be_errno_t - Failure
1350 * Scope:
1351 * Private
1353 * NOTE: This function currently only collects the zone BE name but when
1354 * support for beadm/libbe in a zone is provided it will need to fill
1355 * in the rest of the information needed for a zone BE.
1357 static int
1358 be_get_zone_node_data(be_node_list_t *be_node, char *be_name)
1360 if ((be_node->be_node_name = strdup(be_name)) != NULL)
1361 return (BE_SUCCESS);
1362 return (BE_ERR_NOMEM);