xref: /onnv-gate/usr/src/cmd/hal/probing/xkb/probe-xkb.c (revision 9809:decd140e1fad)
19608SLin.Guo@Sun.COM /***************************************************************************
29608SLin.Guo@Sun.COM  *
39608SLin.Guo@Sun.COM  * probe-xkb.c : Probe for keyboard device information
49608SLin.Guo@Sun.COM  *
59608SLin.Guo@Sun.COM  * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
69608SLin.Guo@Sun.COM  * Use is subject to license terms.
79608SLin.Guo@Sun.COM  *
89608SLin.Guo@Sun.COM  * Licensed under the Academic Free License version 2.1
99608SLin.Guo@Sun.COM  *
109608SLin.Guo@Sun.COM  **************************************************************************/
119608SLin.Guo@Sun.COM 
129608SLin.Guo@Sun.COM #ifdef HAVE_CONFIG_H
139608SLin.Guo@Sun.COM #include <config.h>
149608SLin.Guo@Sun.COM #endif
159608SLin.Guo@Sun.COM 
169608SLin.Guo@Sun.COM #include <errno.h>
179608SLin.Guo@Sun.COM #include <string.h>
189608SLin.Guo@Sun.COM #include <strings.h>
199608SLin.Guo@Sun.COM #include <ctype.h>
209608SLin.Guo@Sun.COM #include <stdlib.h>
219608SLin.Guo@Sun.COM #include <stdio.h>
229608SLin.Guo@Sun.COM #include <sys/ioctl.h>
239608SLin.Guo@Sun.COM #include <sys/stropts.h>
249608SLin.Guo@Sun.COM #include <fcntl.h>
259608SLin.Guo@Sun.COM #include <unistd.h>
269608SLin.Guo@Sun.COM #include <priv.h>
279608SLin.Guo@Sun.COM 
289608SLin.Guo@Sun.COM #include <sys/kbd.h>
299608SLin.Guo@Sun.COM #include <sys/kbio.h>
309608SLin.Guo@Sun.COM 
319608SLin.Guo@Sun.COM #include <libhal.h>
329608SLin.Guo@Sun.COM #include <logger.h>
339608SLin.Guo@Sun.COM 
349608SLin.Guo@Sun.COM #define	MAXLINELEN		256
359608SLin.Guo@Sun.COM #define	COMMENTCHAR		'#'
369608SLin.Guo@Sun.COM #define	KBD_DEFAULT_DEVICE	"/dev/kbd"
379608SLin.Guo@Sun.COM #define	XKBTABLE_PATH		"/usr/X11/lib/X11/xkb/xkbtable.map"
389608SLin.Guo@Sun.COM 
399608SLin.Guo@Sun.COM static int		global_linenumber = 0;
409608SLin.Guo@Sun.COM static char		line[MAXLINELEN + 1];
419608SLin.Guo@Sun.COM 
429608SLin.Guo@Sun.COM static void
drop_privileges()439608SLin.Guo@Sun.COM drop_privileges()
449608SLin.Guo@Sun.COM {
459608SLin.Guo@Sun.COM 	priv_set_t *pPrivSet = NULL;
469608SLin.Guo@Sun.COM 	priv_set_t *lPrivSet = NULL;
479608SLin.Guo@Sun.COM 
489608SLin.Guo@Sun.COM 	/*
499608SLin.Guo@Sun.COM 	 * Start with the 'basic' privilege set and then remove any
509608SLin.Guo@Sun.COM 	 * of the 'basic' privileges that will not be needed.
519608SLin.Guo@Sun.COM 	 */
529608SLin.Guo@Sun.COM 	if ((pPrivSet = priv_str_to_set("basic", ",", NULL)) == NULL) {
539608SLin.Guo@Sun.COM 		HAL_INFO(("Error in setting the priv"));
549608SLin.Guo@Sun.COM 		return;
559608SLin.Guo@Sun.COM 	}
569608SLin.Guo@Sun.COM 
579608SLin.Guo@Sun.COM 	/* Clear privileges we will not need from the 'basic' set */
589608SLin.Guo@Sun.COM 	(void) priv_delset(pPrivSet, PRIV_FILE_LINK_ANY);
599608SLin.Guo@Sun.COM 	(void) priv_delset(pPrivSet, PRIV_PROC_INFO);
609608SLin.Guo@Sun.COM 	(void) priv_delset(pPrivSet, PRIV_PROC_SESSION);
619608SLin.Guo@Sun.COM 	(void) priv_delset(pPrivSet, PRIV_PROC_EXEC);
629608SLin.Guo@Sun.COM 	(void) priv_delset(pPrivSet, PRIV_PROC_FORK);
639608SLin.Guo@Sun.COM 
649608SLin.Guo@Sun.COM 	(void) priv_addset(pPrivSet, PRIV_SYS_DEVICES);
659608SLin.Guo@Sun.COM 	(void) priv_addset(pPrivSet, PRIV_FILE_DAC_READ);
669608SLin.Guo@Sun.COM 
679608SLin.Guo@Sun.COM 	/* Set the permitted privilege set. */
689608SLin.Guo@Sun.COM 	if (setppriv(PRIV_SET, PRIV_PERMITTED, pPrivSet) != 0) {
699608SLin.Guo@Sun.COM 		return;
709608SLin.Guo@Sun.COM 	}
719608SLin.Guo@Sun.COM 
729608SLin.Guo@Sun.COM 	/* Clear the limit set. */
739608SLin.Guo@Sun.COM 	if ((lPrivSet = priv_allocset()) == NULL) {
749608SLin.Guo@Sun.COM 		return;
759608SLin.Guo@Sun.COM 	}
769608SLin.Guo@Sun.COM 
779608SLin.Guo@Sun.COM 	priv_emptyset(lPrivSet);
789608SLin.Guo@Sun.COM 
799608SLin.Guo@Sun.COM 	if (setppriv(PRIV_SET, PRIV_LIMIT, lPrivSet) != 0) {
809608SLin.Guo@Sun.COM 		return;
819608SLin.Guo@Sun.COM 	}
829608SLin.Guo@Sun.COM 
839608SLin.Guo@Sun.COM 	priv_freeset(lPrivSet);
849608SLin.Guo@Sun.COM 	priv_freeset(pPrivSet);
859608SLin.Guo@Sun.COM }
869608SLin.Guo@Sun.COM 
879608SLin.Guo@Sun.COM static int
get_kbd_layout_type(char * device_file,int * kbd_type,int * kbd_layout)889608SLin.Guo@Sun.COM get_kbd_layout_type(char *device_file, int *kbd_type, int *kbd_layout)
899608SLin.Guo@Sun.COM {
909608SLin.Guo@Sun.COM 	int ret = 1;
919608SLin.Guo@Sun.COM 	int fd = -1;
929608SLin.Guo@Sun.COM 
939608SLin.Guo@Sun.COM 	if ((fd = open(device_file, O_RDONLY | O_NONBLOCK)) < 0) {
949608SLin.Guo@Sun.COM 		HAL_DEBUG(("Cannot open %s: %s", device_file, strerror(errno)));
959608SLin.Guo@Sun.COM 		goto out;
969608SLin.Guo@Sun.COM 	}
979608SLin.Guo@Sun.COM 
989608SLin.Guo@Sun.COM 	/*
999608SLin.Guo@Sun.COM 	 * For usb keyboard devices, we need to first push "usbkbm" module upon
1009608SLin.Guo@Sun.COM 	 * the stream.
1019608SLin.Guo@Sun.COM 	 */
1029608SLin.Guo@Sun.COM 	if (strstr(device_file, "hid") != NULL) {
1039608SLin.Guo@Sun.COM 		if (ioctl(fd, I_FIND, "usbkbm") == 0) {
1049608SLin.Guo@Sun.COM 			(void) ioctl(fd, I_PUSH, "usbkbm");
1059608SLin.Guo@Sun.COM 			HAL_DEBUG(("usbkbm module has been pushed %s", strerror(errno)));
1069608SLin.Guo@Sun.COM 		}
1079608SLin.Guo@Sun.COM 	}
1089608SLin.Guo@Sun.COM 
1099608SLin.Guo@Sun.COM 	if (ioctl(fd, KIOCTYPE, kbd_type) < 0) {
1109608SLin.Guo@Sun.COM 		HAL_DEBUG(("get keyboard type failed %s: %s",
1119608SLin.Guo@Sun.COM 		    device_file, strerror(errno)));
1129608SLin.Guo@Sun.COM 		goto out;
1139608SLin.Guo@Sun.COM 	}
1149608SLin.Guo@Sun.COM 	if (ioctl(fd, KIOCLAYOUT, kbd_layout) < 0) {
1159608SLin.Guo@Sun.COM 		HAL_DEBUG(("get keyboard layout failed %s: %s",
1169608SLin.Guo@Sun.COM 		    device_file, strerror(errno)));
1179608SLin.Guo@Sun.COM 		goto out;
1189608SLin.Guo@Sun.COM 	}
1199608SLin.Guo@Sun.COM 
1209608SLin.Guo@Sun.COM 	ret = 0;
1219608SLin.Guo@Sun.COM 
1229608SLin.Guo@Sun.COM out:	if (fd >= 0) {
1239608SLin.Guo@Sun.COM 		close(fd);
1249608SLin.Guo@Sun.COM 	}
1259608SLin.Guo@Sun.COM 
1269608SLin.Guo@Sun.COM 	return (ret);
1279608SLin.Guo@Sun.COM }
1289608SLin.Guo@Sun.COM 
1299608SLin.Guo@Sun.COM /* Skips over the white space character in the string. */
1309608SLin.Guo@Sun.COM static char *
skipwhite(char * ptr)1319608SLin.Guo@Sun.COM skipwhite(char *ptr)
1329608SLin.Guo@Sun.COM {
1339608SLin.Guo@Sun.COM 	while ((*ptr == ' ') || (*ptr == '\t')) {
1349608SLin.Guo@Sun.COM 		ptr++;
1359608SLin.Guo@Sun.COM 	}
1369608SLin.Guo@Sun.COM 
1379608SLin.Guo@Sun.COM 	/* This should not occur. but .. */
1389608SLin.Guo@Sun.COM 	if (*ptr == '\n') {
1399608SLin.Guo@Sun.COM 		ptr = '\0';
1409608SLin.Guo@Sun.COM 	}
1419608SLin.Guo@Sun.COM 
1429608SLin.Guo@Sun.COM 	return (ptr);
1439608SLin.Guo@Sun.COM }
1449608SLin.Guo@Sun.COM 
1459608SLin.Guo@Sun.COM static char *
getaline(FILE * fp)1469608SLin.Guo@Sun.COM getaline(FILE *fp)
1479608SLin.Guo@Sun.COM {
1489608SLin.Guo@Sun.COM 	char    *ptr;
1499608SLin.Guo@Sun.COM 	char    *tmp;
1509608SLin.Guo@Sun.COM 	int	index;
1519608SLin.Guo@Sun.COM 	int	c;
1529608SLin.Guo@Sun.COM 
1539608SLin.Guo@Sun.COM 	while (1) {
1549608SLin.Guo@Sun.COM 		ptr = fgets(line, MAXLINELEN, fp);
1559608SLin.Guo@Sun.COM 		if (!ptr) {
1569608SLin.Guo@Sun.COM 			(void) fclose(fp);
1579608SLin.Guo@Sun.COM 			return (NULL);
1589608SLin.Guo@Sun.COM 		}
1599608SLin.Guo@Sun.COM 
1609608SLin.Guo@Sun.COM 		global_linenumber++;
1619608SLin.Guo@Sun.COM 
1629608SLin.Guo@Sun.COM 		/* Comment line */
1639608SLin.Guo@Sun.COM 		if (ptr[0] == COMMENTCHAR) {
1649608SLin.Guo@Sun.COM 			continue;
1659608SLin.Guo@Sun.COM 		}
1669608SLin.Guo@Sun.COM 
1679608SLin.Guo@Sun.COM 		/* Blank line */
1689608SLin.Guo@Sun.COM 		if (ptr[0] == '\n') {
1699608SLin.Guo@Sun.COM 			continue;
1709608SLin.Guo@Sun.COM 		}
1719608SLin.Guo@Sun.COM 
1729608SLin.Guo@Sun.COM 		if ((tmp = strchr(ptr, '#')) != NULL) {
1739608SLin.Guo@Sun.COM 			*tmp = '\0';
1749608SLin.Guo@Sun.COM 		}
1759608SLin.Guo@Sun.COM 
1769608SLin.Guo@Sun.COM 		if (ptr[strlen(ptr) - 1] == '\n') {
1779608SLin.Guo@Sun.COM 			/* get rid of '\n' */
1789608SLin.Guo@Sun.COM 			ptr[strlen(ptr) - 1] = '\0';
1799608SLin.Guo@Sun.COM 		}
1809608SLin.Guo@Sun.COM 
1819608SLin.Guo@Sun.COM 		ptr = skipwhite(ptr);
1829608SLin.Guo@Sun.COM 		if (*ptr) {
1839608SLin.Guo@Sun.COM 			break;
1849608SLin.Guo@Sun.COM 		}
1859608SLin.Guo@Sun.COM 	}
1869608SLin.Guo@Sun.COM 	return (ptr);
1879608SLin.Guo@Sun.COM }
1889608SLin.Guo@Sun.COM 
1899608SLin.Guo@Sun.COM static int
sun_find_xkbnames(int kb_type,int kb_layout,char ** xkb_keymap,char ** xkb_model,char ** xkb_layout)1909608SLin.Guo@Sun.COM sun_find_xkbnames(int kb_type, int kb_layout, char **xkb_keymap,
1919608SLin.Guo@Sun.COM     char **xkb_model, char **xkb_layout)
1929608SLin.Guo@Sun.COM {
1939608SLin.Guo@Sun.COM 	const char  *type, *layout;
1949608SLin.Guo@Sun.COM 	char	*keymap, *defkeymap = NULL;
1959608SLin.Guo@Sun.COM 	char	*model, *defmodel = NULL;
1969608SLin.Guo@Sun.COM 	char	*xkblay, *defxkblay = NULL;
1979608SLin.Guo@Sun.COM 	FILE	*fp;
1989608SLin.Guo@Sun.COM 	int	found_error = 0, found_keytable = 0;
1999608SLin.Guo@Sun.COM 	int	ret = 1;
2009608SLin.Guo@Sun.COM 
2019608SLin.Guo@Sun.COM 	if ((fp = fopen(XKBTABLE_PATH, "r")) == NULL) {
2029608SLin.Guo@Sun.COM 		return (ret);
2039608SLin.Guo@Sun.COM 	}
2049608SLin.Guo@Sun.COM 
2059608SLin.Guo@Sun.COM 	global_linenumber = 0;
2069608SLin.Guo@Sun.COM 	while (getaline(fp)) {
2079608SLin.Guo@Sun.COM 		if ((type = strtok(line, " \t\n")) == NULL) {
2089608SLin.Guo@Sun.COM 			found_error = 1;
2099608SLin.Guo@Sun.COM 		}
2109608SLin.Guo@Sun.COM 
2119608SLin.Guo@Sun.COM 		if ((layout = strtok(NULL, " \t\n")) == NULL) {
2129608SLin.Guo@Sun.COM 			found_error = 1;
2139608SLin.Guo@Sun.COM 		}
2149608SLin.Guo@Sun.COM 
2159608SLin.Guo@Sun.COM 		if ((keymap = strtok(NULL, " \t\n")) == NULL) {
2169608SLin.Guo@Sun.COM 			found_error = 1;
2179608SLin.Guo@Sun.COM 		}
2189608SLin.Guo@Sun.COM 
2199608SLin.Guo@Sun.COM 		/* These two are optional entries */
2209608SLin.Guo@Sun.COM 		model = strtok(NULL, " \t\n");
2219608SLin.Guo@Sun.COM 		if ((model == NULL) || (*model == COMMENTCHAR)) {
2229608SLin.Guo@Sun.COM 			model = xkblay = NULL;
2239608SLin.Guo@Sun.COM 		} else {
2249608SLin.Guo@Sun.COM 			xkblay = strtok(NULL, " \t\n");
2259608SLin.Guo@Sun.COM 			if ((xkblay != NULL) && (*xkblay == COMMENTCHAR)) {
2269608SLin.Guo@Sun.COM 			xkblay = NULL;
2279608SLin.Guo@Sun.COM 			}
2289608SLin.Guo@Sun.COM 		}
2299608SLin.Guo@Sun.COM 
2309608SLin.Guo@Sun.COM 		if (found_error) {
2319608SLin.Guo@Sun.COM 			found_error = 0;
2329608SLin.Guo@Sun.COM 			continue;
2339608SLin.Guo@Sun.COM 		}
2349608SLin.Guo@Sun.COM 
2359608SLin.Guo@Sun.COM 		/* record default entry if/when found */
2369608SLin.Guo@Sun.COM 		if (*type == '*') {
2379608SLin.Guo@Sun.COM 			if (defkeymap == NULL) {
238*9809SLin.Guo@Sun.COM 				defkeymap = strdup(keymap);
239*9809SLin.Guo@Sun.COM 				defmodel = strdup(model);
240*9809SLin.Guo@Sun.COM 				defxkblay = strdup(xkblay);
2419608SLin.Guo@Sun.COM 			}
2429608SLin.Guo@Sun.COM 		} else if (atoi(type) == kb_type) {
243*9809SLin.Guo@Sun.COM 			if (*layout == '*') {
244*9809SLin.Guo@Sun.COM 				defkeymap = strdup(keymap);
245*9809SLin.Guo@Sun.COM 				defmodel = strdup(model);
246*9809SLin.Guo@Sun.COM 				defxkblay = strdup(xkblay);
2479608SLin.Guo@Sun.COM 			} else if (atoi(layout) == kb_layout) {
2489608SLin.Guo@Sun.COM 				found_keytable = 1;
2499608SLin.Guo@Sun.COM 				break;
2509608SLin.Guo@Sun.COM 			}
2519608SLin.Guo@Sun.COM 		}
2529608SLin.Guo@Sun.COM 	}
2539608SLin.Guo@Sun.COM 
2549608SLin.Guo@Sun.COM 	(void) fclose(fp);
2559608SLin.Guo@Sun.COM 
2569608SLin.Guo@Sun.COM 	if (!found_keytable) {
2579608SLin.Guo@Sun.COM 		keymap = defkeymap;
2589608SLin.Guo@Sun.COM 		model = defmodel;
2599608SLin.Guo@Sun.COM 		xkblay = defxkblay;
2609608SLin.Guo@Sun.COM 	}
2619608SLin.Guo@Sun.COM 
2629608SLin.Guo@Sun.COM 	if ((keymap != NULL) && (strcmp(keymap, "-") != 0)) {
2639608SLin.Guo@Sun.COM 		*xkb_keymap = keymap;
2649608SLin.Guo@Sun.COM 	}
2659608SLin.Guo@Sun.COM 	if ((model != NULL) && (strcmp(model, "-") != 0)) {
2669608SLin.Guo@Sun.COM 		*xkb_model = model;
2679608SLin.Guo@Sun.COM 	}
2689608SLin.Guo@Sun.COM 	if ((xkblay != NULL) && (strcmp(xkblay, "-") != 0)) {
2699608SLin.Guo@Sun.COM 		*xkb_layout = xkblay;
2709608SLin.Guo@Sun.COM 	}
2719608SLin.Guo@Sun.COM 
2729608SLin.Guo@Sun.COM 	return (0);
2739608SLin.Guo@Sun.COM }
2749608SLin.Guo@Sun.COM 
2759608SLin.Guo@Sun.COM int
main(int argc,char * argv[])2769608SLin.Guo@Sun.COM main(int argc, char *argv[])
2779608SLin.Guo@Sun.COM {
2789608SLin.Guo@Sun.COM 	int ret = 1;
2799608SLin.Guo@Sun.COM 	char *udi;
2809608SLin.Guo@Sun.COM 	char *device_file;
2819608SLin.Guo@Sun.COM 	LibHalContext *ctx = NULL;
2829608SLin.Guo@Sun.COM 	LibHalChangeSet *cs = NULL;
2839608SLin.Guo@Sun.COM 	DBusError error;
2849608SLin.Guo@Sun.COM 	int kbd_type, kbd_layout;
2859608SLin.Guo@Sun.COM 	char *xkbkeymap = NULL, *xkbmodel = NULL, *xkblayout = NULL;
2869608SLin.Guo@Sun.COM 
2879608SLin.Guo@Sun.COM 	if ((udi = getenv("UDI")) == NULL) {
2889608SLin.Guo@Sun.COM 		goto out;
2899608SLin.Guo@Sun.COM 	}
2909608SLin.Guo@Sun.COM 
2919608SLin.Guo@Sun.COM 	if ((device_file = getenv("HAL_PROP_INPUT_DEVICE")) == NULL) {
2929608SLin.Guo@Sun.COM 		goto out;
2939608SLin.Guo@Sun.COM 	}
2949608SLin.Guo@Sun.COM 
2959608SLin.Guo@Sun.COM 	drop_privileges();
2969608SLin.Guo@Sun.COM 	setup_logger();
2979608SLin.Guo@Sun.COM 
2989608SLin.Guo@Sun.COM 	dbus_error_init(&error);
2999608SLin.Guo@Sun.COM 	if ((ctx = libhal_ctx_init_direct(&error)) == NULL) {
3009608SLin.Guo@Sun.COM 		goto out;
3019608SLin.Guo@Sun.COM 	}
3029608SLin.Guo@Sun.COM 
3039608SLin.Guo@Sun.COM 	if ((cs = libhal_device_new_changeset(udi)) == NULL) {
3049608SLin.Guo@Sun.COM 		HAL_DEBUG(("Cannot allocate changeset"));
3059608SLin.Guo@Sun.COM 		goto out;
3069608SLin.Guo@Sun.COM 	}
3079608SLin.Guo@Sun.COM 
3089608SLin.Guo@Sun.COM 	HAL_DEBUG(("Doing probe-xkb for %s (udi=%s)", device_file, udi));
3099608SLin.Guo@Sun.COM 
3109608SLin.Guo@Sun.COM 	if (get_kbd_layout_type(device_file, &kbd_type, &kbd_layout)) {
3119608SLin.Guo@Sun.COM 		goto out;
3129608SLin.Guo@Sun.COM 	}
3139608SLin.Guo@Sun.COM 
3149608SLin.Guo@Sun.COM 	/*
3159608SLin.Guo@Sun.COM 	 * For some usb keyboard that is not self-identifying, get keyboard's
3169608SLin.Guo@Sun.COM 	 * layout and type from system default keyboard device--/dev/kbd.
3179608SLin.Guo@Sun.COM 	 */
3189608SLin.Guo@Sun.COM 	if ((kbd_layout == 0) && (strstr(device_file, "hid") != NULL)) {
3199608SLin.Guo@Sun.COM 		if (get_kbd_layout_type(KBD_DEFAULT_DEVICE,
3209608SLin.Guo@Sun.COM 		    &kbd_type, &kbd_layout)) {
3219608SLin.Guo@Sun.COM 			goto out;
3229608SLin.Guo@Sun.COM 		}
3239608SLin.Guo@Sun.COM 	}
3249608SLin.Guo@Sun.COM 
3259608SLin.Guo@Sun.COM 	if (sun_find_xkbnames(kbd_type, kbd_layout,
3269608SLin.Guo@Sun.COM 	    &xkbkeymap, &xkbmodel, &xkblayout)) {
3279608SLin.Guo@Sun.COM 		goto out;
3289608SLin.Guo@Sun.COM 	}
3299608SLin.Guo@Sun.COM 
330*9809SLin.Guo@Sun.COM 	/*
331*9809SLin.Guo@Sun.COM 	 * If doesn't find matching entry in xkbtable.map, using default
332*9809SLin.Guo@Sun.COM 	 * values setting in 10-x11-input.fdi
333*9809SLin.Guo@Sun.COM 	 */
334*9809SLin.Guo@Sun.COM 	if ((xkbmodel != NULL) && (xkblayout != NULL)) {
335*9809SLin.Guo@Sun.COM 		libhal_changeset_set_property_string(cs,
336*9809SLin.Guo@Sun.COM 		    "input.x11_options.XkbModel", xkbmodel);
337*9809SLin.Guo@Sun.COM 		libhal_changeset_set_property_string(cs,
338*9809SLin.Guo@Sun.COM 		    "input.x11_options.XkbLayout", xkblayout);
3399608SLin.Guo@Sun.COM 
340*9809SLin.Guo@Sun.COM 		libhal_device_commit_changeset(ctx, cs, &error);
341*9809SLin.Guo@Sun.COM 	}
3429608SLin.Guo@Sun.COM 
3439608SLin.Guo@Sun.COM 	ret = 0;
3449608SLin.Guo@Sun.COM 
3459608SLin.Guo@Sun.COM out:
3469608SLin.Guo@Sun.COM 	if (cs != NULL) {
3479608SLin.Guo@Sun.COM 		libhal_device_free_changeset(cs);
3489608SLin.Guo@Sun.COM 	}
3499608SLin.Guo@Sun.COM 
3509608SLin.Guo@Sun.COM 	if (ctx != NULL) {
3519608SLin.Guo@Sun.COM 		libhal_ctx_shutdown(ctx, &error);
3529608SLin.Guo@Sun.COM 		libhal_ctx_free(ctx);
3539608SLin.Guo@Sun.COM 		if (dbus_error_is_set(&error)) {
3549608SLin.Guo@Sun.COM 			dbus_error_free(&error);
3559608SLin.Guo@Sun.COM 		}
3569608SLin.Guo@Sun.COM 	}
3579608SLin.Guo@Sun.COM 	return (ret);
3589608SLin.Guo@Sun.COM }
359