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  * Copyright 2012 OmniTI Computer Consulting, Inc.  All rights reserved.
25  * Copyright 2021 OmniOS Community Edition (OmniOSce) Association.
26  */
27 
28 #include <Python.h>
29 #include <sys/varargs.h>
30 #include <stdio.h>
31 #include <libnvpair.h>
32 
33 #include <libbe.h>
34 #include <libbe_priv.h>
35 
36 enum {
37 	BE_PY_SUCCESS = 0,
38 	BE_PY_ERR_APPEND = 6000,
39 	BE_PY_ERR_DICT,
40 	BE_PY_ERR_LIST,
41 	BE_PY_ERR_NVLIST,
42 	BE_PY_ERR_PARSETUPLE,
43 	BE_PY_ERR_PRINT_ERR,
44 	BE_PY_ERR_VAR_CONV,
45 } bePyErr;
46 
47 /*
48  * public libbe functions
49  */
50 
51 PyObject *beCreateSnapshot(PyObject *, PyObject *);
52 PyObject *beCopy(PyObject *, PyObject *);
53 PyObject *beList(PyObject *, PyObject *, PyObject *);
54 PyObject *beActivate(PyObject *, PyObject *, PyObject *);
55 PyObject *beDestroy(PyObject *, PyObject *);
56 PyObject *beDestroySnapshot(PyObject *, PyObject *);
57 PyObject *beRename(PyObject *, PyObject *);
58 PyObject *beMount(PyObject *, PyObject *);
59 PyObject *beUnmount(PyObject *, PyObject *);
60 PyObject *bePrintErrors(PyObject *, PyObject *);
61 PyObject *beGetErrDesc(PyObject *, PyObject *);
62 char *beMapLibbePyErrorToString(int);
63 
64 static boolean_t convertBEInfoToDictionary(be_node_list_t *be,
65     PyObject **listDict);
66 static boolean_t convertDatasetInfoToDictionary(be_dataset_list_t *ds,
67     PyObject **listDict);
68 static boolean_t convertSnapshotInfoToDictionary(be_snapshot_list_t *ss,
69     PyObject **listDict);
70 static boolean_t convertPyArgsToNvlist(nvlist_t **nvList, int numArgs, ...);
71 
72 
73 /* ~~~~~~~~~~~~~~~ */
74 /* Public Funtions */
75 /* ~~~~~~~~~~~~~~~ */
76 
77 /*
78  * Function:    beCreateSnapshot
79  * Description: Convert Python args to nvlist pairs and
80  *              call libbe:be_create_snapshot to create a
81  *              snapshot of all the datasets within a BE
82  * Parameters:
83  *   args -          pointer to a python object containing:
84  *        beName -   The name of the BE to create a snapshot of
85  *        snapName - The name of the snapshot to create (optional)
86  *
87  *        The following public attribute values. defined by libbe.h,
88  *        are used by this function:
89  *
90  * Returns a pointer to a python object and an optional snapshot name:
91  *      0, [snapName] - Success
92  *      1, [snapName] - Failure
93  * Scope:
94  *      Public
95  */
96 PyObject *
beCreateSnapshot(PyObject * self,PyObject * args)97 beCreateSnapshot(PyObject *self, PyObject *args)
98 {
99 	char	*beName = NULL;
100 	char	*snapName = NULL;
101 	int	ret = BE_PY_SUCCESS;
102 	nvlist_t	*beAttrs = NULL;
103 	PyObject	*retVals = NULL;
104 
105 	if (!PyArg_ParseTuple(args, "z|z", &beName, &snapName)) {
106 		return (Py_BuildValue("[is]", BE_PY_ERR_PARSETUPLE, NULL));
107 	}
108 
109 	if (!convertPyArgsToNvlist(&beAttrs, 4,
110 	    BE_ATTR_ORIG_BE_NAME, beName,
111 	    BE_ATTR_SNAP_NAME, snapName)) {
112 		nvlist_free(beAttrs);
113 		return (Py_BuildValue("[is]", BE_PY_ERR_NVLIST, NULL));
114 	}
115 
116 	if (beAttrs == NULL) {
117 		return (Py_BuildValue("i", BE_PY_ERR_NVLIST));
118 	}
119 
120 	if ((ret = be_create_snapshot(beAttrs)) != 0) {
121 		nvlist_free(beAttrs);
122 		return (Py_BuildValue("[is]", ret, NULL));
123 	}
124 	if (snapName == NULL) {
125 		if (nvlist_lookup_pairs(beAttrs, NV_FLAG_NOENTOK,
126 		    BE_ATTR_SNAP_NAME, DATA_TYPE_STRING, &snapName,
127 		    NULL) != 0) {
128 			nvlist_free(beAttrs);
129 			return (Py_BuildValue("[is]",
130 			    BE_PY_ERR_NVLIST, NULL));
131 		}
132 		retVals = Py_BuildValue("[is]", ret, snapName);
133 		nvlist_free(beAttrs);
134 		return (retVals);
135 	}
136 	nvlist_free(beAttrs);
137 
138 	return (Py_BuildValue("[is]", ret, NULL));
139 }
140 
141 /*
142  * Function:    beCopy
143  * Description: Convert Python args to nvlist pairs and call libbe:be_copy
144  *              to create a Boot Environment
145  * Parameters:
146  *   args -     pointer to a python object containing:
147  *     trgtBeName - The name of the BE to create
148  *     srcBeName - The name of the BE used to create trgtBeName (optional)
149  *     rpool - The pool to create the new BE in (optional)
150  *     srcSnapName - The snapshot name (optional)
151  *     beNameProperties - The properties to use when creating
152  *                        the BE (optional)
153  *
154  * Returns a pointer to a python object. That Python object will consist of
155  * the return code and optional attributes, trgtBeName and snapshotName
156  *      BE_SUCCESS, [trgtBeName], [trgtSnapName] - Success
157  *      1, [trgtBeName], [trgtSnapName] - Failure
158  * Scope:
159  *      Public
160  */
161 PyObject *
beCopy(PyObject * self,PyObject * args)162 beCopy(PyObject *self, PyObject *args)
163 {
164 	char	*trgtBeName = NULL;
165 	char	*srcBeName = NULL;
166 	char	*srcSnapName = NULL;
167 	char	*trgtSnapName = NULL;
168 	char	*rpool = NULL;
169 	char	*beDescription = NULL;
170 	Py_ssize_t	pos = 0;
171 	int		ret = BE_PY_SUCCESS;
172 	nvlist_t	*beAttrs = NULL;
173 	nvlist_t	*beProps = NULL;
174 	PyObject	*beNameProperties = NULL;
175 	PyObject	*pkey = NULL;
176 	PyObject	*pvalue = NULL;
177 	PyObject	*retVals = NULL;
178 
179 	if (!PyArg_ParseTuple(args, "|zzzzOz", &trgtBeName, &srcBeName,
180 	    &srcSnapName, &rpool, &beNameProperties, &beDescription)) {
181 		return (Py_BuildValue("[iss]", BE_PY_ERR_PARSETUPLE,
182 		    NULL, NULL));
183 	}
184 
185 	if (!convertPyArgsToNvlist(&beAttrs, 10,
186 	    BE_ATTR_NEW_BE_NAME, trgtBeName,
187 	    BE_ATTR_ORIG_BE_NAME, srcBeName,
188 	    BE_ATTR_SNAP_NAME, srcSnapName,
189 	    BE_ATTR_NEW_BE_POOL, rpool,
190 	    BE_ATTR_NEW_BE_DESC, beDescription)) {
191 		nvlist_free(beAttrs);
192 		return (Py_BuildValue("[iss]", BE_PY_ERR_NVLIST, NULL, NULL));
193 	}
194 
195 	if (beNameProperties != NULL) {
196 		if (nvlist_alloc(&beProps, NV_UNIQUE_NAME, 0) != 0) {
197 			(void) printf("nvlist_alloc failed.\n");
198 			nvlist_free(beAttrs);
199 			return (Py_BuildValue("[iss]", BE_PY_ERR_NVLIST,
200 			    NULL, NULL));
201 		}
202 		while (PyDict_Next(beNameProperties, &pos, &pkey, &pvalue)) {
203 #if PY_MAJOR_VERSION >= 3
204 			if (!convertPyArgsToNvlist(&beProps, 2,
205 			    PyUnicode_AsUTF8(pkey),
206 			    PyUnicode_AsUTF8(pvalue))) {
207 #else
208 			if (!convertPyArgsToNvlist(&beProps, 2,
209 			    PyString_AsString(pkey),
210 			    PyString_AsString(pvalue))) {
211 #endif
212 				nvlist_free(beProps);
213 				nvlist_free(beAttrs);
214 				return (Py_BuildValue("[iss]", BE_PY_ERR_NVLIST,
215 				    NULL, NULL));
216 			}
217 		}
218 	}
219 
220 	if (beProps != NULL && beAttrs != NULL &&
221 	    nvlist_add_nvlist(beAttrs, BE_ATTR_ZFS_PROPERTIES,
222 	    beProps) != 0) {
223 		nvlist_free(beProps);
224 		nvlist_free(beAttrs);
225 		return (Py_BuildValue("[iss]", BE_PY_ERR_NVLIST,
226 		    NULL, NULL));
227 	}
228 
229 	nvlist_free(beProps);
230 
231 	if (trgtBeName == NULL) {
232 		/*
233 		 * Caller wants to get back the BE_ATTR_NEW_BE_NAME and
234 		 * BE_ATTR_SNAP_NAME
235 		 */
236 		if ((ret = be_copy(beAttrs)) != BE_SUCCESS) {
237 			nvlist_free(beAttrs);
238 			return (Py_BuildValue("[iss]", ret, NULL, NULL));
239 		}
240 
241 		/*
242 		 * When no trgtBeName is passed to be_copy, be_copy
243 		 * returns an auto generated beName and snapshot name.
244 		 */
245 		if (nvlist_lookup_string(beAttrs, BE_ATTR_NEW_BE_NAME,
246 		    &trgtBeName) != 0) {
247 			nvlist_free(beAttrs);
248 			return (Py_BuildValue("[iss]", BE_PY_ERR_NVLIST,
249 			    NULL, NULL));
250 		}
251 		if (nvlist_lookup_string(beAttrs, BE_ATTR_SNAP_NAME,
252 		    &trgtSnapName) != 0) {
253 			nvlist_free(beAttrs);
254 			return (Py_BuildValue("[iss]", BE_PY_ERR_NVLIST,
255 			    NULL, NULL));
256 		}
257 
258 		retVals = Py_BuildValue("[iss]", BE_PY_SUCCESS,
259 		    trgtBeName, trgtSnapName);
260 		nvlist_free(beAttrs);
261 		return (retVals);
262 
263 	} else {
264 		ret = be_copy(beAttrs);
265 		nvlist_free(beAttrs);
266 		return (Py_BuildValue("[iss]", ret, NULL, NULL));
267 	}
268 }
269 
270 /*
271  * Function:    beList
272  * Description: Convert Python args to nvlist pairs and call libbe:be_list
273  *              to gather information about Boot Environments
274  * Parameters:
275  *   args -     pointer to a python object containing:
276  *     bename  - The name of the BE to list (optional)
277  *     nosnaps - boolean indicating whether to exclude snapshots (optional)
278  *
279  * Returns a pointer to a python object. That Python object will consist of
280  * the return code and a list of Dicts or NULL.
281  *      BE_PY_SUCCESS, listOfDicts - Success
282  *      bePyErr or be_errno_t, NULL - Failure
283  * Scope:
284  *      Public
285  */
286 PyObject *
287 beList(PyObject *self, PyObject *args, PyObject *keywds)
288 {
289 	char	*beName = NULL;
290 	int	noSnaps = 0;
291 	int	ret = BE_PY_SUCCESS;
292 	be_node_list_t *list = NULL;
293 	be_node_list_t *be = NULL;
294 	PyObject *dict = NULL;
295 	PyObject *listOfDicts = NULL;
296 	uint64_t listopts = BE_LIST_SNAPSHOTS;
297 
298 	static char *kwlist[] = {"bename", "nosnaps", NULL};
299 
300 	if ((listOfDicts = PyList_New(0)) == NULL) {
301 		ret = BE_PY_ERR_DICT;
302 		listOfDicts = Py_None;
303 		goto done;
304 	}
305 
306 	if (!PyArg_ParseTupleAndKeywords(args, keywds, "|zi",
307 	    kwlist, &beName, &noSnaps)) {
308 		ret = BE_PY_ERR_PARSETUPLE;
309 		goto done;
310 	}
311 
312 	if (noSnaps)
313 		listopts &= ~BE_LIST_SNAPSHOTS;
314 
315 	if ((ret = be_list(beName, &list, listopts)) != BE_SUCCESS) {
316 		goto done;
317 	}
318 
319 	for (be = list; be != NULL; be = be->be_next_node) {
320 		be_dataset_list_t *ds = be->be_node_datasets;
321 		be_snapshot_list_t *ss = be->be_node_snapshots;
322 
323 		if ((dict = PyDict_New()) == NULL) {
324 			ret = BE_PY_ERR_DICT;
325 			goto done;
326 		}
327 
328 		if (!convertBEInfoToDictionary(be, &dict)) {
329 			/* LINTED */
330 			Py_DECREF(dict);
331 			ret = BE_PY_ERR_VAR_CONV;
332 			goto done;
333 		}
334 
335 		if (PyList_Append(listOfDicts, dict) != 0) {
336 			/* LINTED */
337 			Py_DECREF(dict);
338 			ret = BE_PY_ERR_APPEND;
339 			goto done;
340 		}
341 
342 		/* LINTED */
343 		Py_DECREF(dict);
344 
345 		while (ds != NULL) {
346 			if ((dict = PyDict_New()) == NULL) {
347 				ret = BE_PY_ERR_DICT;
348 				goto done;
349 			}
350 
351 			if (!convertDatasetInfoToDictionary(ds, &dict)) {
352 				/* LINTED */
353 				Py_DECREF(dict);
354 				ret = BE_PY_ERR_VAR_CONV;
355 				goto done;
356 			}
357 
358 			if (PyList_Append(listOfDicts, dict) != 0) {
359 				/* LINTED */
360 				Py_DECREF(dict);
361 				ret = BE_PY_ERR_APPEND;
362 				goto done;
363 			}
364 
365 			ds = ds->be_next_dataset;
366 
367 			/* LINTED */
368 			Py_DECREF(dict);
369 		}
370 
371 
372 		while (ss != NULL) {
373 			if ((dict = PyDict_New()) == NULL) {
374 				/* LINTED */
375 				Py_DECREF(dict);
376 				ret = BE_PY_ERR_DICT;
377 				goto done;
378 			}
379 
380 			if (!convertSnapshotInfoToDictionary(ss, &dict)) {
381 				/* LINTED */
382 				Py_DECREF(dict);
383 				ret = BE_PY_ERR_VAR_CONV;
384 				goto done;
385 			}
386 
387 			if (PyList_Append(listOfDicts, dict) != 0) {
388 				/* LINTED */
389 				Py_DECREF(dict);
390 				ret = BE_PY_ERR_APPEND;
391 				goto done;
392 			}
393 
394 			ss = ss->be_next_snapshot;
395 
396 			/* LINTED */
397 			Py_DECREF(dict);
398 		}
399 	}
400 
401 done:
402 	if (list != NULL)
403 		be_free_list(list);
404 	return (Py_BuildValue("[iO]", ret, listOfDicts));
405 }
406 
407 /*
408  * Function:    beActivate
409  * Description: Convert Python args to nvlist pairs and call libbe:be_activate
410  *              to activate a Boot Environment
411  * Parameters:
412  *   args -     pointer to a python object containing:
413  *     bename    - The name of the BE to activate
414  *     temporary - If True, perform a temporary BE activation
415  *                 If False, remove a temporary BE activation
416  *                 If not present, do nothing in regard to temporary activation
417  *
418  * Returns a pointer to a python object:
419  *      BE_SUCCESS - Success
420  *      bePyErr or be_errno_t - Failure
421  * Scope:
422  *      Public
423  */
424 PyObject *
425 beActivate(PyObject *self, PyObject *args, PyObject *keywds)
426 {
427 	char		*beName = NULL;
428 	int		ret = BE_PY_SUCCESS;
429 	nvlist_t	*beAttrs = NULL;
430 	int		temp = -1;
431 
432 	static char *kwlist[] = {"bename", "temporary", NULL};
433 
434 	if (!PyArg_ParseTupleAndKeywords(args, keywds, "z|i",
435 	    kwlist, &beName, &temp)) {
436 		ret = BE_PY_ERR_PARSETUPLE;
437 		goto done;
438 	}
439 
440 	if (!convertPyArgsToNvlist(&beAttrs, 2, BE_ATTR_ORIG_BE_NAME, beName) ||
441 	    beAttrs == NULL) {
442 		ret = BE_PY_ERR_NVLIST;
443 		goto done;
444 	}
445 
446 	if (temp != -1 && nvlist_add_boolean_value(beAttrs,
447 	    BE_ATTR_ACTIVE_NEXTBOOT, temp == 0 ? B_FALSE : B_TRUE) != 0) {
448 		ret = BE_PY_ERR_NVLIST;
449 		goto done;
450 	}
451 
452 	ret = be_activate(beAttrs);
453 done:
454 	nvlist_free(beAttrs);
455 	return (Py_BuildValue("i", ret));
456 }
457 
458 /*
459  * Function:    beDestroy
460  * Description: Convert Python args to nvlist pairs and call libbe:be_destroy
461  *              to destroy a Boot Environment
462  * Parameters:
463  *   args -     pointer to a python object containing:
464  *     beName - The name of the BE to destroy
465  *
466  * Returns a pointer to a python object:
467  *      BE_SUCCESS - Success
468  *      bePyErr or be_errno_t - Failure
469  * Scope:
470  *      Public
471  */
472 PyObject *
473 beDestroy(PyObject *self, PyObject *args)
474 {
475 	char		*beName = NULL;
476 	int		destroy_snaps = 0;
477 	int		force_unmount = 0;
478 	int		destroy_flags = 0;
479 	int		ret = BE_PY_SUCCESS;
480 	nvlist_t	*beAttrs = NULL;
481 
482 	if (!PyArg_ParseTuple(args, "z|ii", &beName, &destroy_snaps,
483 	    &force_unmount)) {
484 		return (Py_BuildValue("i", BE_PY_ERR_PARSETUPLE));
485 	}
486 
487 	if (destroy_snaps == 1)
488 		destroy_flags |= BE_DESTROY_FLAG_SNAPSHOTS;
489 
490 	if (force_unmount == 1)
491 		destroy_flags |= BE_DESTROY_FLAG_FORCE_UNMOUNT;
492 
493 	if (!convertPyArgsToNvlist(&beAttrs, 2, BE_ATTR_ORIG_BE_NAME, beName)) {
494 		nvlist_free(beAttrs);
495 		return (Py_BuildValue("i", BE_PY_ERR_NVLIST));
496 	}
497 
498 	if (nvlist_add_uint16(beAttrs, BE_ATTR_DESTROY_FLAGS, destroy_flags)
499 	    != 0) {
500 		(void) printf("nvlist_add_uint16 failed for "
501 		    "BE_ATTR_DESTROY_FLAGS (%d).\n", destroy_flags);
502 		nvlist_free(beAttrs);
503 		return (Py_BuildValue("i", BE_PY_ERR_NVLIST));
504 	}
505 
506 	if (beAttrs == NULL) {
507 		return (Py_BuildValue("i", BE_PY_ERR_NVLIST));
508 	}
509 
510 	ret = be_destroy(beAttrs);
511 	nvlist_free(beAttrs);
512 	return (Py_BuildValue("i", ret));
513 }
514 
515 /*
516  * Function:    beDestroySnapshot
517  * Description: Convert Python args to nvlist pairs and call libbe:be_destroy
518  *              to destroy a snapshot of a Boot Environment
519  * Parameters:
520  *   args -     pointer to a python object containing:
521  *     beName - The name of the BE to destroy
522  *     snapName - The name of the snapshot to destroy
523  *
524  * Returns a pointer to a python object:
525  *      BE_SUCCESS - Success
526  *      bePyErr or be_errno_t - Failure
527  * Scope:
528  *      Public
529  */
530 PyObject *
531 beDestroySnapshot(PyObject *self, PyObject *args)
532 {
533 	char		*beName = NULL;
534 	char		*snapName = NULL;
535 	int		ret = BE_PY_SUCCESS;
536 	nvlist_t	*beAttrs = NULL;
537 
538 	if (!PyArg_ParseTuple(args, "zz", &beName, &snapName)) {
539 		return (Py_BuildValue("i", BE_PY_ERR_PARSETUPLE));
540 	}
541 
542 	if (!convertPyArgsToNvlist(&beAttrs, 4,
543 	    BE_ATTR_ORIG_BE_NAME, beName,
544 	    BE_ATTR_SNAP_NAME, snapName)) {
545 		nvlist_free(beAttrs);
546 		return (Py_BuildValue("i", BE_PY_ERR_NVLIST));
547 	}
548 
549 	if (beAttrs == NULL) {
550 		return (Py_BuildValue("i", BE_PY_ERR_NVLIST));
551 	}
552 
553 	ret = be_destroy_snapshot(beAttrs);
554 	nvlist_free(beAttrs);
555 	return (Py_BuildValue("i", ret));
556 }
557 
558 /*
559  * Function:    beRename
560  * Description: Convert Python args to nvlist pairs and call libbe:be_rename
561  *              to rename a Boot Environment
562  * Parameters:
563  *   args -     pointer to a python object containing:
564  *     oldBeName - The name of the old Boot Environment
565  *     newBeName - The name of the new Boot Environment
566  *
567  * Returns a pointer to a python object:
568  *      BE_SUCCESS - Success
569  *      bePyErr or be_errno_t - Failure
570  * Scope:
571  *      Public
572  */
573 PyObject *
574 beRename(PyObject *self, PyObject *args)
575 {
576 	char		*oldBeName = NULL;
577 	char		*newBeName = NULL;
578 	int		ret = BE_PY_SUCCESS;
579 	nvlist_t	*beAttrs = NULL;
580 
581 	if (!PyArg_ParseTuple(args, "zz", &oldBeName, &newBeName)) {
582 		return (Py_BuildValue("i", BE_PY_ERR_PARSETUPLE));
583 	}
584 
585 	if (!convertPyArgsToNvlist(&beAttrs, 4,
586 	    BE_ATTR_ORIG_BE_NAME, oldBeName,
587 	    BE_ATTR_NEW_BE_NAME, newBeName)) {
588 		nvlist_free(beAttrs);
589 		return (Py_BuildValue("i", BE_PY_ERR_NVLIST));
590 	}
591 
592 	if (beAttrs == NULL) {
593 		return (Py_BuildValue("i", BE_PY_ERR_NVLIST));
594 	}
595 
596 	ret = be_rename(beAttrs);
597 	nvlist_free(beAttrs);
598 	return (Py_BuildValue("i", ret));
599 }
600 
601 /*
602  * Function:    beMount
603  * Description: Convert Python args to nvlist pairs and call libbe:be_mount
604  *              to mount a Boot Environment
605  * Parameters:
606  *   args -     pointer to a python object containing:
607  *     beName - The name of the Boot Environment to mount
608  *     mountpoint - The path of the mountpoint to mount the
609  *                  Boot Environment on (optional)
610  *
611  * Returns a pointer to a python object:
612  *      BE_SUCCESS - Success
613  *      bePyErr or be_errno_t - Failure
614  * Scope:
615  *      Public
616  */
617 PyObject *
618 beMount(PyObject *self, PyObject *args)
619 {
620 	char		*beName = NULL;
621 	char		*mountpoint = NULL;
622 	int		ret = BE_PY_SUCCESS;
623 	nvlist_t	*beAttrs = NULL;
624 
625 	if (!PyArg_ParseTuple(args, "zz", &beName, &mountpoint)) {
626 		return (Py_BuildValue("i", BE_PY_ERR_PARSETUPLE));
627 	}
628 
629 	if (!convertPyArgsToNvlist(&beAttrs, 4,
630 	    BE_ATTR_ORIG_BE_NAME, beName,
631 	    BE_ATTR_MOUNTPOINT, mountpoint)) {
632 		nvlist_free(beAttrs);
633 		return (Py_BuildValue("i", BE_PY_ERR_NVLIST));
634 	}
635 
636 	if (beAttrs == NULL) {
637 		return (Py_BuildValue("i", BE_PY_ERR_NVLIST));
638 	}
639 
640 	ret = be_mount(beAttrs);
641 	nvlist_free(beAttrs);
642 	return (Py_BuildValue("i", ret));
643 }
644 
645 /*
646  * Function:    beUnmount
647  * Description: Convert Python args to nvlist pairs and call libbe:be_unmount
648  *              to unmount a Boot Environment
649  * Parameters:
650  *   args -     pointer to a python object containing:
651  *     beName - The name of the Boot Environment to unmount
652  *
653  * Returns a pointer to a python object:
654  *      BE_SUCCESS - Success
655  *      bePyErr or be_errno_t - Failure
656  * Scope:
657  *      Public
658  */
659 PyObject *
660 beUnmount(PyObject *self, PyObject *args)
661 {
662 	char		*beName = NULL;
663 	int		force_unmount = 0;
664 	int		unmount_flags = 0;
665 	int		ret = BE_PY_SUCCESS;
666 	nvlist_t	*beAttrs = NULL;
667 
668 	if (!PyArg_ParseTuple(args, "z|i", &beName, &force_unmount)) {
669 		return (Py_BuildValue("i", BE_PY_ERR_PARSETUPLE));
670 	}
671 
672 	if (force_unmount == 1)
673 		unmount_flags |= BE_UNMOUNT_FLAG_FORCE;
674 
675 	if (!convertPyArgsToNvlist(&beAttrs, 2,
676 	    BE_ATTR_ORIG_BE_NAME, beName)) {
677 		nvlist_free(beAttrs);
678 		return (Py_BuildValue("i", BE_PY_ERR_NVLIST));
679 	}
680 
681 	if (nvlist_add_uint16(beAttrs, BE_ATTR_UNMOUNT_FLAGS, unmount_flags)
682 	    != 0) {
683 		(void) printf("nvlist_add_uint16 failed for "
684 		    "BE_ATTR_UNMOUNT_FLAGS (%d).\n", unmount_flags);
685 		nvlist_free(beAttrs);
686 		return (Py_BuildValue("i", BE_PY_ERR_NVLIST));
687 	}
688 
689 	if (beAttrs == NULL) {
690 		return (Py_BuildValue("i", BE_PY_ERR_NVLIST));
691 	}
692 
693 	ret = be_unmount(beAttrs);
694 	nvlist_free(beAttrs);
695 	return (Py_BuildValue("i", ret));
696 }
697 
698 /*
699  * Function:    beRollback
700  * Description: Convert Python args to nvlist pairs and call libbe:be_rollback
701  *              to rollback a Boot Environment to a previously taken
702  *               snapshot.
703  * Parameters:
704  *   args -     pointer to a python object containing:
705  *     beName - The name of the Boot Environment to unmount
706  *
707  * Returns a pointer to a python object:
708  *      BE_SUCCESS - Success
709  *      bePyErr or be_errno_t - Failure
710  * Scope:
711  *      Public
712  */
713 PyObject *
714 beRollback(PyObject *self, PyObject *args)
715 {
716 	char		*beName = NULL;
717 	char		*snapName = NULL;
718 	int		ret = BE_PY_SUCCESS;
719 	nvlist_t	*beAttrs = NULL;
720 
721 	if (!PyArg_ParseTuple(args, "zz", &beName, &snapName)) {
722 		return (Py_BuildValue("i", BE_PY_ERR_PARSETUPLE));
723 	}
724 
725 	if (!convertPyArgsToNvlist(&beAttrs, 4,
726 	    BE_ATTR_ORIG_BE_NAME, beName,
727 	    BE_ATTR_SNAP_NAME, snapName)) {
728 		nvlist_free(beAttrs);
729 		return (Py_BuildValue("i", BE_PY_ERR_NVLIST));
730 	}
731 
732 	if (beAttrs == NULL) {
733 		return (Py_BuildValue("i", BE_PY_ERR_NVLIST));
734 	}
735 
736 	ret = be_rollback(beAttrs);
737 	nvlist_free(beAttrs);
738 	return (Py_BuildValue("i", ret));
739 }
740 
741 /*
742  * Function:    bePrintErrors
743  * Description: Convert Python args to boolean and call libbe_print_errors to
744  *			turn on/off error output for the library.
745  * Parameter:
746  *   args -     pointer to a python object containing:
747  *		print_errors - Boolean that turns library error
748  *			       printing on or off.
749  * Parameters:
750  *   args -     pointer to a python object containing:
751  *     0 - do not print errors - Python boolean "False"
752  *     1 - print errors - Python boolean "True"
753  *
754  * Returns 1 on missing or invalid argument, 0 otherwise
755  * Scope:
756  *      Public
757  */
758 PyObject *
759 bePrintErrors(PyObject *self, PyObject *args)
760 {
761 	int		print_errors;
762 
763 	if (!PyArg_ParseTuple(args, "i", &print_errors) ||
764 	    (print_errors != 1 && print_errors != 0))
765 		return (Py_BuildValue("i", BE_PY_ERR_PRINT_ERR));
766 	libbe_print_errors(print_errors == 1);
767 	return (Py_BuildValue("i", BE_PY_SUCCESS));
768 }
769 
770 /*
771  * Function:    beGetErrDesc
772  * Description: Convert Python args to an int and call be_err_to_str to
773  *			map an error code to an error string.
774  * Parameter:
775  *   args -     pointer to a python object containing:
776  *		errCode - value to map to an error string.
777  *
778  * Returns: error string or NULL
779  * Scope:
780  *      Public
781  */
782 PyObject *
783 beGetErrDesc(PyObject *self, PyObject *args)
784 {
785 	int	errCode = 0;
786 	char	*beErrStr = NULL;
787 
788 	if (!PyArg_ParseTuple(args, "i", &errCode)) {
789 		return (Py_BuildValue("s", NULL));
790 	}
791 
792 	/*
793 	 * First check libbe_py errors. If NULL is returned check error codes
794 	 * in libbe.
795 	 */
796 
797 	if ((beErrStr = beMapLibbePyErrorToString(errCode)) == NULL) {
798 		beErrStr = be_err_to_str(errCode);
799 	}
800 
801 	return (Py_BuildValue("s", beErrStr));
802 }
803 
804 /*
805  * Function:    beVerifyBEName
806  * Description: Call be_valid_be_name() to verify the BE name.
807  * Parameter:
808  *   args -     pointer to a python object containing:
809  *		string - value to map to a string.
810  *
811  * Returns:  0 for success or 1 for failure
812  * Scope:
813  *      Public
814  */
815 PyObject *
816 beVerifyBEName(PyObject *self, PyObject *args)
817 {
818 	char	*string = NULL;
819 
820 	if (!PyArg_ParseTuple(args, "s", &string)) {
821 		return (Py_BuildValue("i", 1));
822 	}
823 
824 	if (be_valid_be_name(string)) {
825 		return (Py_BuildValue("i", 0));
826 	} else {
827 		return (Py_BuildValue("i", 1));
828 	}
829 }
830 
831 /* ~~~~~~~~~~~~~~~~~ */
832 /* Private Functions */
833 /* ~~~~~~~~~~~~~~~~~ */
834 
835 static boolean_t
836 convertBEInfoToDictionary(be_node_list_t *be, PyObject **listDict)
837 {
838 	if (be->be_node_name != NULL) {
839 		if (PyDict_SetItemString(*listDict, BE_ATTR_ORIG_BE_NAME,
840 		    PyUnicode_FromString(be->be_node_name)) != 0) {
841 			return (B_FALSE);
842 		}
843 	}
844 
845 	if (be->be_rpool != NULL) {
846 		if (PyDict_SetItemString(*listDict, BE_ATTR_ORIG_BE_POOL,
847 		    PyUnicode_FromString(be->be_rpool)) != 0) {
848 			return (B_FALSE);
849 		}
850 	}
851 
852 	if (be->be_mntpt != NULL) {
853 		if (PyDict_SetItemString(*listDict, BE_ATTR_MOUNTPOINT,
854 		    PyUnicode_FromString(be->be_mntpt)) != 0) {
855 			return (B_FALSE);
856 		}
857 	}
858 
859 	if (PyDict_SetItemString(*listDict, BE_ATTR_MOUNTED,
860 	    (be->be_mounted ? Py_True : Py_False)) != 0) {
861 		return (B_FALSE);
862 	}
863 
864 	if (PyDict_SetItemString(*listDict, BE_ATTR_ACTIVE,
865 	    (be->be_active ? Py_True : Py_False)) != 0) {
866 		return (B_FALSE);
867 	}
868 
869 	if (PyDict_SetItemString(*listDict, BE_ATTR_ACTIVE_ON_BOOT,
870 	    (be->be_active_on_boot ? Py_True : Py_False)) != 0) {
871 		return (B_FALSE);
872 	}
873 
874 	if (PyDict_SetItemString(*listDict, BE_ATTR_ACTIVE_NEXTBOOT,
875 	    (be->be_active_next ? Py_True : Py_False)) != 0) {
876 		return (B_FALSE);
877 	}
878 
879 	if (PyDict_SetItemString(*listDict, BE_ATTR_GLOBAL_ACTIVE,
880 	    (be->be_global_active ? Py_True : Py_False)) != 0) {
881 		return (B_FALSE);
882 	}
883 
884 	if (be->be_space_used != 0) {
885 		if (PyDict_SetItemString(*listDict, BE_ATTR_SPACE,
886 		    PyLong_FromUnsignedLongLong(be->be_space_used)) != 0) {
887 			return (B_FALSE);
888 		}
889 	}
890 
891 	if (be->be_root_ds != NULL) {
892 		if (PyDict_SetItemString(*listDict, BE_ATTR_ROOT_DS,
893 		    PyUnicode_FromString(be->be_root_ds)) != 0) {
894 			return (B_FALSE);
895 		}
896 	}
897 
898 	if (be->be_node_creation != 0) {
899 		if (PyDict_SetItemString(*listDict, BE_ATTR_DATE,
900 		    PyLong_FromLong(be->be_node_creation)) != 0) {
901 			return (B_FALSE);
902 		}
903 	}
904 
905 	if (be->be_policy_type != NULL) {
906 		if (PyDict_SetItemString(*listDict, BE_ATTR_POLICY,
907 		    PyUnicode_FromString(be->be_policy_type)) != 0) {
908 			return (B_FALSE);
909 		}
910 	}
911 
912 	if (be->be_uuid_str != NULL) {
913 		if (PyDict_SetItemString(*listDict, BE_ATTR_UUID_STR,
914 		    PyUnicode_FromString(be->be_uuid_str)) != 0) {
915 			return (B_FALSE);
916 		}
917 	}
918 
919 	return (B_TRUE);
920 }
921 
922 static boolean_t
923 convertDatasetInfoToDictionary(be_dataset_list_t *ds, PyObject **listDict)
924 {
925 	if (ds->be_dataset_name != NULL) {
926 		if (PyDict_SetItemString(*listDict, BE_ATTR_DATASET,
927 		    PyUnicode_FromString(ds->be_dataset_name)) != 0) {
928 			return (B_FALSE);
929 		}
930 	}
931 
932 	if (PyDict_SetItemString(*listDict, BE_ATTR_STATUS,
933 	    (ds->be_ds_mounted ? Py_True : Py_False)) != 0) {
934 			return (B_FALSE);
935 	}
936 
937 	if (ds->be_ds_mntpt != NULL) {
938 		if (PyDict_SetItemString(*listDict, BE_ATTR_MOUNTPOINT,
939 		    PyUnicode_FromString(ds->be_ds_mntpt)) != 0) {
940 			return (B_FALSE);
941 		}
942 	}
943 
944 	if (PyDict_SetItemString(*listDict, BE_ATTR_MOUNTED,
945 	    (ds->be_ds_mounted ? Py_True : Py_False)) != 0) {
946 		return (B_FALSE);
947 	}
948 
949 	if (ds->be_ds_space_used != 0) {
950 		if (PyDict_SetItemString(*listDict, BE_ATTR_SPACE,
951 		    PyLong_FromUnsignedLongLong(ds->be_ds_space_used))
952 		    != 0) {
953 			return (B_FALSE);
954 		}
955 	}
956 
957 	if (ds->be_dataset_name != 0) {
958 		if (PyDict_SetItemString(*listDict, BE_ATTR_DATASET,
959 		    PyUnicode_FromString(ds->be_dataset_name)) != 0) {
960 			return (B_FALSE);
961 		}
962 	}
963 
964 	if (ds->be_ds_plcy_type != NULL) {
965 		if (PyDict_SetItemString(*listDict, BE_ATTR_POLICY,
966 		    PyUnicode_FromString(ds->be_ds_plcy_type)) != 0) {
967 			return (B_FALSE);
968 		}
969 	}
970 
971 	if (ds->be_ds_creation != 0) {
972 		if (PyDict_SetItemString(*listDict, BE_ATTR_DATE,
973 		    PyLong_FromLong(ds->be_ds_creation)) != 0) {
974 			return (B_FALSE);
975 		}
976 	}
977 
978 	return (B_TRUE);
979 }
980 
981 static boolean_t
982 convertSnapshotInfoToDictionary(be_snapshot_list_t *ss, PyObject **listDict)
983 {
984 	if (ss->be_snapshot_name != NULL) {
985 		if (PyDict_SetItemString(*listDict, BE_ATTR_SNAP_NAME,
986 		    PyUnicode_FromString(ss->be_snapshot_name)) != 0) {
987 			return (B_FALSE);
988 		}
989 	}
990 
991 	if (ss->be_snapshot_creation != 0) {
992 		if (PyDict_SetItemString(*listDict, BE_ATTR_DATE,
993 		    PyLong_FromLong(ss->be_snapshot_creation)) != 0) {
994 			return (B_FALSE);
995 		}
996 	}
997 
998 	if (ss->be_snapshot_type != NULL) {
999 		if (PyDict_SetItemString(*listDict, BE_ATTR_POLICY,
1000 		    PyUnicode_FromString(ss->be_snapshot_type)) != 0) {
1001 			return (B_FALSE);
1002 		}
1003 	}
1004 
1005 	if (ss->be_snapshot_space_used != 0) {
1006 		if (PyDict_SetItemString(*listDict, BE_ATTR_SPACE,
1007 		    PyLong_FromUnsignedLongLong(ss->be_snapshot_space_used))
1008 		    != 0) {
1009 			return (B_FALSE);
1010 		}
1011 	}
1012 
1013 	return (B_TRUE);
1014 }
1015 
1016 /*
1017  * Convert string arguments to nvlist attributes
1018  */
1019 
1020 static boolean_t
1021 convertPyArgsToNvlist(nvlist_t **nvList, int numArgs, ...)
1022 {
1023 	char *pt, *pt2;
1024 	va_list ap;
1025 	int i;
1026 
1027 	if (*nvList == NULL) {
1028 		if (nvlist_alloc(nvList, NV_UNIQUE_NAME, 0) != 0) {
1029 			(void) printf("nvlist_alloc failed.\n");
1030 			return (B_FALSE);
1031 		}
1032 	}
1033 
1034 	va_start(ap, numArgs);
1035 
1036 	for (i = 0; i < numArgs; i += 2) {
1037 		if ((pt = va_arg(ap, char *)) == NULL ||
1038 		    (pt2 = va_arg(ap, char *)) == NULL) {
1039 			continue;
1040 		}
1041 		if (nvlist_add_string(*nvList, pt, pt2) != 0) {
1042 			(void) printf("nvlist_add_string failed for %s (%s).\n",
1043 			    pt, pt2);
1044 			nvlist_free(*nvList);
1045 			*nvList = NULL;
1046 			return (B_FALSE);
1047 		}
1048 	}
1049 
1050 	va_end(ap);
1051 
1052 	return (B_TRUE);
1053 }
1054 
1055 /*
1056  * Function:    beMapLibbePyErrorToString
1057  * Description: Convert Python args to an int and map an error code to an
1058  *			error string.
1059  * Parameter:
1060  *		errCode - value to map to an error string.
1061  *
1062  * Returns error string or NULL
1063  * Scope:
1064  *      Public
1065  */
1066 
1067 char *
1068 beMapLibbePyErrorToString(int errCode)
1069 {
1070 	switch (errCode) {
1071 	case BE_PY_ERR_APPEND:
1072 		return ("Unable to append a dictionary to a list "
1073 		    "of dictionaries.");
1074 	case BE_PY_ERR_DICT:
1075 		return ("Creation of a Python dictionary failed.");
1076 	case BE_PY_ERR_LIST:
1077 		return ("beList() failed.");
1078 	case BE_PY_ERR_NVLIST:
1079 		return ("An nvlist operation failed.");
1080 	case BE_PY_ERR_PARSETUPLE:
1081 		return ("PyArg_ParseTuple() failed to convert variable to C.");
1082 	case BE_PY_ERR_PRINT_ERR:
1083 		return ("bePrintErrors() failed.");
1084 	case BE_PY_ERR_VAR_CONV:
1085 		return ("Unable to add variables to a Python dictionary.");
1086 	default:
1087 		return (NULL);
1088 	}
1089 }
1090 
1091 /* Private python initialization structure */
1092 
1093 static struct PyMethodDef libbeMethods[] = {
1094 	{"beCopy", beCopy, METH_VARARGS, "Create/Copy a BE."},
1095 	{"beCreateSnapshot", beCreateSnapshot, METH_VARARGS,
1096 	    "Create a snapshot."},
1097 	{"beDestroy", beDestroy, METH_VARARGS, "Destroy a BE."},
1098 	{"beDestroySnapshot", beDestroySnapshot, METH_VARARGS,
1099 	    "Destroy a snapshot."},
1100 	{"beMount", beMount, METH_VARARGS, "Mount a BE."},
1101 	{"beUnmount", beUnmount, METH_VARARGS, "Unmount a BE."},
1102 	{"beList", (PyCFunction)(uintptr_t)beList, METH_VARARGS | METH_KEYWORDS,
1103 	    "List BE info."},
1104 	{"beRename", beRename, METH_VARARGS, "Rename a BE."},
1105 	{"beActivate", (PyCFunction)(uintptr_t)beActivate,
1106 	    METH_VARARGS | METH_KEYWORDS, "Activate a BE."},
1107 	{"beRollback", beRollback, METH_VARARGS, "Rollback a BE."},
1108 	{"bePrintErrors", bePrintErrors, METH_VARARGS,
1109 	    "Enable/disable error printing."},
1110 	{"beGetErrDesc", beGetErrDesc, METH_VARARGS,
1111 	    "Map Error codes to strings."},
1112 	{"beVerifyBEName", beVerifyBEName, METH_VARARGS,
1113 	    "Verify BE name."},
1114 	{NULL, NULL, 0, NULL}
1115 };
1116 
1117 #if PY_MAJOR_VERSION >= 3
1118 static struct PyModuleDef libbe_module = {
1119 	PyModuleDef_HEAD_INIT,
1120 	"libbe_py",
1121 	NULL,
1122 	-1,
1123 	libbeMethods
1124 };
1125 #endif
1126 
1127 static PyObject *
1128 moduleinit()
1129 {
1130 	/* PyMODINIT_FUNC; */
1131 #if PY_MAJOR_VERSION >= 3
1132 	return (PyModule_Create(&libbe_module));
1133 #else
1134 	/*
1135 	 * Python2 module initialisation functions are void and may not return
1136 	 * a value. However, they will set an exception if appropriate.
1137 	 */
1138 	(void) Py_InitModule("libbe_py", libbeMethods);
1139 	return (NULL);
1140 #endif
1141 }
1142 
1143 #if PY_MAJOR_VERSION >= 3
1144 PyMODINIT_FUNC
1145 PyInit_libbe_py(void)
1146 {
1147 	return (moduleinit());
1148 }
1149 #else
1150 PyMODINIT_FUNC
1151 initlibbe_py(void)
1152 {
1153 	(void) moduleinit();
1154 }
1155 #endif
1156