1*9608SLin.Guo@Sun.COM /***************************************************************************
2*9608SLin.Guo@Sun.COM  *
3*9608SLin.Guo@Sun.COM  * probe-xkb.c : Probe for keyboard device information
4*9608SLin.Guo@Sun.COM  *
5*9608SLin.Guo@Sun.COM  * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
6*9608SLin.Guo@Sun.COM  * Use is subject to license terms.
7*9608SLin.Guo@Sun.COM  *
8*9608SLin.Guo@Sun.COM  * Licensed under the Academic Free License version 2.1
9*9608SLin.Guo@Sun.COM  *
10*9608SLin.Guo@Sun.COM  **************************************************************************/
11*9608SLin.Guo@Sun.COM 
12*9608SLin.Guo@Sun.COM #ifdef HAVE_CONFIG_H
13*9608SLin.Guo@Sun.COM #include <config.h>
14*9608SLin.Guo@Sun.COM #endif
15*9608SLin.Guo@Sun.COM 
16*9608SLin.Guo@Sun.COM #include <errno.h>
17*9608SLin.Guo@Sun.COM #include <string.h>
18*9608SLin.Guo@Sun.COM #include <strings.h>
19*9608SLin.Guo@Sun.COM #include <ctype.h>
20*9608SLin.Guo@Sun.COM #include <stdlib.h>
21*9608SLin.Guo@Sun.COM #include <stdio.h>
22*9608SLin.Guo@Sun.COM #include <sys/ioctl.h>
23*9608SLin.Guo@Sun.COM #include <sys/stropts.h>
24*9608SLin.Guo@Sun.COM #include <fcntl.h>
25*9608SLin.Guo@Sun.COM #include <unistd.h>
26*9608SLin.Guo@Sun.COM #include <priv.h>
27*9608SLin.Guo@Sun.COM 
28*9608SLin.Guo@Sun.COM #include <sys/kbd.h>
29*9608SLin.Guo@Sun.COM #include <sys/kbio.h>
30*9608SLin.Guo@Sun.COM 
31*9608SLin.Guo@Sun.COM #include <libhal.h>
32*9608SLin.Guo@Sun.COM #include <logger.h>
33*9608SLin.Guo@Sun.COM 
34*9608SLin.Guo@Sun.COM #define	MAXLINELEN		256
35*9608SLin.Guo@Sun.COM #define	COMMENTCHAR		'#'
36*9608SLin.Guo@Sun.COM #define	KBD_DEFAULT_DEVICE	"/dev/kbd"
37*9608SLin.Guo@Sun.COM #define	XKBTABLE_PATH		"/usr/X11/lib/X11/xkb/xkbtable.map"
38*9608SLin.Guo@Sun.COM 
39*9608SLin.Guo@Sun.COM static int		global_linenumber = 0;
40*9608SLin.Guo@Sun.COM static char		line[MAXLINELEN + 1];
41*9608SLin.Guo@Sun.COM 
42*9608SLin.Guo@Sun.COM static void
43*9608SLin.Guo@Sun.COM drop_privileges()
44*9608SLin.Guo@Sun.COM {
45*9608SLin.Guo@Sun.COM 	priv_set_t *pPrivSet = NULL;
46*9608SLin.Guo@Sun.COM 	priv_set_t *lPrivSet = NULL;
47*9608SLin.Guo@Sun.COM 
48*9608SLin.Guo@Sun.COM 	/*
49*9608SLin.Guo@Sun.COM 	 * Start with the 'basic' privilege set and then remove any
50*9608SLin.Guo@Sun.COM 	 * of the 'basic' privileges that will not be needed.
51*9608SLin.Guo@Sun.COM 	 */
52*9608SLin.Guo@Sun.COM 	if ((pPrivSet = priv_str_to_set("basic", ",", NULL)) == NULL) {
53*9608SLin.Guo@Sun.COM 		HAL_INFO(("Error in setting the priv"));
54*9608SLin.Guo@Sun.COM 		return;
55*9608SLin.Guo@Sun.COM 	}
56*9608SLin.Guo@Sun.COM 
57*9608SLin.Guo@Sun.COM 	/* Clear privileges we will not need from the 'basic' set */
58*9608SLin.Guo@Sun.COM 	(void) priv_delset(pPrivSet, PRIV_FILE_LINK_ANY);
59*9608SLin.Guo@Sun.COM 	(void) priv_delset(pPrivSet, PRIV_PROC_INFO);
60*9608SLin.Guo@Sun.COM 	(void) priv_delset(pPrivSet, PRIV_PROC_SESSION);
61*9608SLin.Guo@Sun.COM 	(void) priv_delset(pPrivSet, PRIV_PROC_EXEC);
62*9608SLin.Guo@Sun.COM 	(void) priv_delset(pPrivSet, PRIV_PROC_FORK);
63*9608SLin.Guo@Sun.COM 
64*9608SLin.Guo@Sun.COM 	(void) priv_addset(pPrivSet, PRIV_SYS_DEVICES);
65*9608SLin.Guo@Sun.COM 	(void) priv_addset(pPrivSet, PRIV_FILE_DAC_READ);
66*9608SLin.Guo@Sun.COM 
67*9608SLin.Guo@Sun.COM 	/* Set the permitted privilege set. */
68*9608SLin.Guo@Sun.COM 	if (setppriv(PRIV_SET, PRIV_PERMITTED, pPrivSet) != 0) {
69*9608SLin.Guo@Sun.COM 		return;
70*9608SLin.Guo@Sun.COM 	}
71*9608SLin.Guo@Sun.COM 
72*9608SLin.Guo@Sun.COM 	/* Clear the limit set. */
73*9608SLin.Guo@Sun.COM 	if ((lPrivSet = priv_allocset()) == NULL) {
74*9608SLin.Guo@Sun.COM 		return;
75*9608SLin.Guo@Sun.COM 	}
76*9608SLin.Guo@Sun.COM 
77*9608SLin.Guo@Sun.COM 	priv_emptyset(lPrivSet);
78*9608SLin.Guo@Sun.COM 
79*9608SLin.Guo@Sun.COM 	if (setppriv(PRIV_SET, PRIV_LIMIT, lPrivSet) != 0) {
80*9608SLin.Guo@Sun.COM 		return;
81*9608SLin.Guo@Sun.COM 	}
82*9608SLin.Guo@Sun.COM 
83*9608SLin.Guo@Sun.COM 	priv_freeset(lPrivSet);
84*9608SLin.Guo@Sun.COM 	priv_freeset(pPrivSet);
85*9608SLin.Guo@Sun.COM }
86*9608SLin.Guo@Sun.COM 
87*9608SLin.Guo@Sun.COM static int
88*9608SLin.Guo@Sun.COM get_kbd_layout_type(char *device_file, int *kbd_type, int *kbd_layout)
89*9608SLin.Guo@Sun.COM {
90*9608SLin.Guo@Sun.COM 	int ret = 1;
91*9608SLin.Guo@Sun.COM 	int fd = -1;
92*9608SLin.Guo@Sun.COM 
93*9608SLin.Guo@Sun.COM 	if ((fd = open(device_file, O_RDONLY | O_NONBLOCK)) < 0) {
94*9608SLin.Guo@Sun.COM 		HAL_DEBUG(("Cannot open %s: %s", device_file, strerror(errno)));
95*9608SLin.Guo@Sun.COM 		goto out;
96*9608SLin.Guo@Sun.COM 	}
97*9608SLin.Guo@Sun.COM 
98*9608SLin.Guo@Sun.COM 	/*
99*9608SLin.Guo@Sun.COM 	 * For usb keyboard devices, we need to first push "usbkbm" module upon
100*9608SLin.Guo@Sun.COM 	 * the stream.
101*9608SLin.Guo@Sun.COM 	 */
102*9608SLin.Guo@Sun.COM 	if (strstr(device_file, "hid") != NULL) {
103*9608SLin.Guo@Sun.COM 		if (ioctl(fd, I_FIND, "usbkbm") == 0) {
104*9608SLin.Guo@Sun.COM 			(void) ioctl(fd, I_PUSH, "usbkbm");
105*9608SLin.Guo@Sun.COM 			HAL_DEBUG(("usbkbm module has been pushed %s", strerror(errno)));
106*9608SLin.Guo@Sun.COM 		}
107*9608SLin.Guo@Sun.COM 	}
108*9608SLin.Guo@Sun.COM 
109*9608SLin.Guo@Sun.COM 	if (ioctl(fd, KIOCTYPE, kbd_type) < 0) {
110*9608SLin.Guo@Sun.COM 		HAL_DEBUG(("get keyboard type failed %s: %s",
111*9608SLin.Guo@Sun.COM 		    device_file, strerror(errno)));
112*9608SLin.Guo@Sun.COM 		goto out;
113*9608SLin.Guo@Sun.COM 	}
114*9608SLin.Guo@Sun.COM 	if (ioctl(fd, KIOCLAYOUT, kbd_layout) < 0) {
115*9608SLin.Guo@Sun.COM 		HAL_DEBUG(("get keyboard layout failed %s: %s",
116*9608SLin.Guo@Sun.COM 		    device_file, strerror(errno)));
117*9608SLin.Guo@Sun.COM 		goto out;
118*9608SLin.Guo@Sun.COM 	}
119*9608SLin.Guo@Sun.COM 
120*9608SLin.Guo@Sun.COM 	ret = 0;
121*9608SLin.Guo@Sun.COM 
122*9608SLin.Guo@Sun.COM out:	if (fd >= 0) {
123*9608SLin.Guo@Sun.COM 		close(fd);
124*9608SLin.Guo@Sun.COM 	}
125*9608SLin.Guo@Sun.COM 
126*9608SLin.Guo@Sun.COM 	return (ret);
127*9608SLin.Guo@Sun.COM }
128*9608SLin.Guo@Sun.COM 
129*9608SLin.Guo@Sun.COM /* Skips over the white space character in the string. */
130*9608SLin.Guo@Sun.COM static char *
131*9608SLin.Guo@Sun.COM skipwhite(char *ptr)
132*9608SLin.Guo@Sun.COM {
133*9608SLin.Guo@Sun.COM 	while ((*ptr == ' ') || (*ptr == '\t')) {
134*9608SLin.Guo@Sun.COM 		ptr++;
135*9608SLin.Guo@Sun.COM 	}
136*9608SLin.Guo@Sun.COM 
137*9608SLin.Guo@Sun.COM 	/* This should not occur. but .. */
138*9608SLin.Guo@Sun.COM 	if (*ptr == '\n') {
139*9608SLin.Guo@Sun.COM 		ptr = '\0';
140*9608SLin.Guo@Sun.COM 	}
141*9608SLin.Guo@Sun.COM 
142*9608SLin.Guo@Sun.COM 	return (ptr);
143*9608SLin.Guo@Sun.COM }
144*9608SLin.Guo@Sun.COM 
145*9608SLin.Guo@Sun.COM static char *
146*9608SLin.Guo@Sun.COM getaline(FILE *fp)
147*9608SLin.Guo@Sun.COM {
148*9608SLin.Guo@Sun.COM 	char    *ptr;
149*9608SLin.Guo@Sun.COM 	char    *tmp;
150*9608SLin.Guo@Sun.COM 	int	index;
151*9608SLin.Guo@Sun.COM 	int	c;
152*9608SLin.Guo@Sun.COM 
153*9608SLin.Guo@Sun.COM 	while (1) {
154*9608SLin.Guo@Sun.COM 		ptr = fgets(line, MAXLINELEN, fp);
155*9608SLin.Guo@Sun.COM 		if (!ptr) {
156*9608SLin.Guo@Sun.COM 			(void) fclose(fp);
157*9608SLin.Guo@Sun.COM 			return (NULL);
158*9608SLin.Guo@Sun.COM 		}
159*9608SLin.Guo@Sun.COM 
160*9608SLin.Guo@Sun.COM 		global_linenumber++;
161*9608SLin.Guo@Sun.COM 
162*9608SLin.Guo@Sun.COM 		/* Comment line */
163*9608SLin.Guo@Sun.COM 		if (ptr[0] == COMMENTCHAR) {
164*9608SLin.Guo@Sun.COM 			continue;
165*9608SLin.Guo@Sun.COM 		}
166*9608SLin.Guo@Sun.COM 
167*9608SLin.Guo@Sun.COM 		/* Blank line */
168*9608SLin.Guo@Sun.COM 		if (ptr[0] == '\n') {
169*9608SLin.Guo@Sun.COM 			continue;
170*9608SLin.Guo@Sun.COM 		}
171*9608SLin.Guo@Sun.COM 
172*9608SLin.Guo@Sun.COM 		if ((tmp = strchr(ptr, '#')) != NULL) {
173*9608SLin.Guo@Sun.COM 			*tmp = '\0';
174*9608SLin.Guo@Sun.COM 		}
175*9608SLin.Guo@Sun.COM 
176*9608SLin.Guo@Sun.COM 		if (ptr[strlen(ptr) - 1] == '\n') {
177*9608SLin.Guo@Sun.COM 			/* get rid of '\n' */
178*9608SLin.Guo@Sun.COM 			ptr[strlen(ptr) - 1] = '\0';
179*9608SLin.Guo@Sun.COM 		}
180*9608SLin.Guo@Sun.COM 
181*9608SLin.Guo@Sun.COM 		ptr = skipwhite(ptr);
182*9608SLin.Guo@Sun.COM 		if (*ptr) {
183*9608SLin.Guo@Sun.COM 			break;
184*9608SLin.Guo@Sun.COM 		}
185*9608SLin.Guo@Sun.COM 	}
186*9608SLin.Guo@Sun.COM 	return (ptr);
187*9608SLin.Guo@Sun.COM }
188*9608SLin.Guo@Sun.COM 
189*9608SLin.Guo@Sun.COM static int
190*9608SLin.Guo@Sun.COM sun_find_xkbnames(int kb_type, int kb_layout, char **xkb_keymap,
191*9608SLin.Guo@Sun.COM     char **xkb_model, char **xkb_layout)
192*9608SLin.Guo@Sun.COM {
193*9608SLin.Guo@Sun.COM 	const char  *type, *layout;
194*9608SLin.Guo@Sun.COM 	char	*keymap, *defkeymap = NULL;
195*9608SLin.Guo@Sun.COM 	char	*model, *defmodel = NULL;
196*9608SLin.Guo@Sun.COM 	char	*xkblay, *defxkblay = NULL;
197*9608SLin.Guo@Sun.COM 	FILE	*fp;
198*9608SLin.Guo@Sun.COM 	int	found_error = 0, found_keytable = 0;
199*9608SLin.Guo@Sun.COM 	int	ret = 1;
200*9608SLin.Guo@Sun.COM 
201*9608SLin.Guo@Sun.COM 	if ((fp = fopen(XKBTABLE_PATH, "r")) == NULL) {
202*9608SLin.Guo@Sun.COM 		return (ret);
203*9608SLin.Guo@Sun.COM 	}
204*9608SLin.Guo@Sun.COM 
205*9608SLin.Guo@Sun.COM 	global_linenumber = 0;
206*9608SLin.Guo@Sun.COM 	while (getaline(fp)) {
207*9608SLin.Guo@Sun.COM 		if ((type = strtok(line, " \t\n")) == NULL) {
208*9608SLin.Guo@Sun.COM 			found_error = 1;
209*9608SLin.Guo@Sun.COM 		}
210*9608SLin.Guo@Sun.COM 
211*9608SLin.Guo@Sun.COM 		if ((layout = strtok(NULL, " \t\n")) == NULL) {
212*9608SLin.Guo@Sun.COM 			found_error = 1;
213*9608SLin.Guo@Sun.COM 		}
214*9608SLin.Guo@Sun.COM 
215*9608SLin.Guo@Sun.COM 		if ((keymap = strtok(NULL, " \t\n")) == NULL) {
216*9608SLin.Guo@Sun.COM 			found_error = 1;
217*9608SLin.Guo@Sun.COM 		}
218*9608SLin.Guo@Sun.COM 
219*9608SLin.Guo@Sun.COM 		/* These two are optional entries */
220*9608SLin.Guo@Sun.COM 		model = strtok(NULL, " \t\n");
221*9608SLin.Guo@Sun.COM 		if ((model == NULL) || (*model == COMMENTCHAR)) {
222*9608SLin.Guo@Sun.COM 			model = xkblay = NULL;
223*9608SLin.Guo@Sun.COM 		} else {
224*9608SLin.Guo@Sun.COM 			xkblay = strtok(NULL, " \t\n");
225*9608SLin.Guo@Sun.COM 			if ((xkblay != NULL) && (*xkblay == COMMENTCHAR)) {
226*9608SLin.Guo@Sun.COM 			xkblay = NULL;
227*9608SLin.Guo@Sun.COM 			}
228*9608SLin.Guo@Sun.COM 		}
229*9608SLin.Guo@Sun.COM 
230*9608SLin.Guo@Sun.COM 		if (found_error) {
231*9608SLin.Guo@Sun.COM 			found_error = 0;
232*9608SLin.Guo@Sun.COM 			continue;
233*9608SLin.Guo@Sun.COM 		}
234*9608SLin.Guo@Sun.COM 
235*9608SLin.Guo@Sun.COM 		/* record default entry if/when found */
236*9608SLin.Guo@Sun.COM 		if (*type == '*') {
237*9608SLin.Guo@Sun.COM 			if (defkeymap == NULL) {
238*9608SLin.Guo@Sun.COM 				defkeymap = keymap;
239*9608SLin.Guo@Sun.COM 				defmodel = model;
240*9608SLin.Guo@Sun.COM 				defxkblay = xkblay;
241*9608SLin.Guo@Sun.COM 			}
242*9608SLin.Guo@Sun.COM 		} else if (atoi(type) == kb_type) {
243*9608SLin.Guo@Sun.COM 			if (*type == '*') {
244*9608SLin.Guo@Sun.COM 				if (defkeymap == NULL) {
245*9608SLin.Guo@Sun.COM 					defkeymap = keymap;
246*9608SLin.Guo@Sun.COM 					defmodel = model;
247*9608SLin.Guo@Sun.COM 					defxkblay = xkblay;
248*9608SLin.Guo@Sun.COM 				}
249*9608SLin.Guo@Sun.COM 			} else if (atoi(layout) == kb_layout) {
250*9608SLin.Guo@Sun.COM 				found_keytable = 1;
251*9608SLin.Guo@Sun.COM 				break;
252*9608SLin.Guo@Sun.COM 			}
253*9608SLin.Guo@Sun.COM 		}
254*9608SLin.Guo@Sun.COM 	}
255*9608SLin.Guo@Sun.COM 
256*9608SLin.Guo@Sun.COM 	(void) fclose(fp);
257*9608SLin.Guo@Sun.COM 
258*9608SLin.Guo@Sun.COM 	if (!found_keytable) {
259*9608SLin.Guo@Sun.COM 		keymap = defkeymap;
260*9608SLin.Guo@Sun.COM 		model = defmodel;
261*9608SLin.Guo@Sun.COM 		xkblay = defxkblay;
262*9608SLin.Guo@Sun.COM 	}
263*9608SLin.Guo@Sun.COM 
264*9608SLin.Guo@Sun.COM 	if ((keymap != NULL) && (strcmp(keymap, "-") != 0)) {
265*9608SLin.Guo@Sun.COM 		*xkb_keymap = keymap;
266*9608SLin.Guo@Sun.COM 	}
267*9608SLin.Guo@Sun.COM 	if ((model != NULL) && (strcmp(model, "-") != 0)) {
268*9608SLin.Guo@Sun.COM 		*xkb_model = model;
269*9608SLin.Guo@Sun.COM 	}
270*9608SLin.Guo@Sun.COM 	if ((xkblay != NULL) && (strcmp(xkblay, "-") != 0)) {
271*9608SLin.Guo@Sun.COM 		*xkb_layout = xkblay;
272*9608SLin.Guo@Sun.COM 	}
273*9608SLin.Guo@Sun.COM 
274*9608SLin.Guo@Sun.COM 	return (0);
275*9608SLin.Guo@Sun.COM }
276*9608SLin.Guo@Sun.COM 
277*9608SLin.Guo@Sun.COM int
278*9608SLin.Guo@Sun.COM main(int argc, char *argv[])
279*9608SLin.Guo@Sun.COM {
280*9608SLin.Guo@Sun.COM 	int ret = 1;
281*9608SLin.Guo@Sun.COM 	char *udi;
282*9608SLin.Guo@Sun.COM 	char *device_file;
283*9608SLin.Guo@Sun.COM 	LibHalContext *ctx = NULL;
284*9608SLin.Guo@Sun.COM 	LibHalChangeSet *cs = NULL;
285*9608SLin.Guo@Sun.COM 	DBusError error;
286*9608SLin.Guo@Sun.COM 	int kbd_type, kbd_layout;
287*9608SLin.Guo@Sun.COM 	char *xkbkeymap = NULL, *xkbmodel = NULL, *xkblayout = NULL;
288*9608SLin.Guo@Sun.COM 
289*9608SLin.Guo@Sun.COM 	if ((udi = getenv("UDI")) == NULL) {
290*9608SLin.Guo@Sun.COM 		goto out;
291*9608SLin.Guo@Sun.COM 	}
292*9608SLin.Guo@Sun.COM 
293*9608SLin.Guo@Sun.COM 	if ((device_file = getenv("HAL_PROP_INPUT_DEVICE")) == NULL) {
294*9608SLin.Guo@Sun.COM 		goto out;
295*9608SLin.Guo@Sun.COM 	}
296*9608SLin.Guo@Sun.COM 
297*9608SLin.Guo@Sun.COM 	drop_privileges();
298*9608SLin.Guo@Sun.COM 	setup_logger();
299*9608SLin.Guo@Sun.COM 
300*9608SLin.Guo@Sun.COM 	dbus_error_init(&error);
301*9608SLin.Guo@Sun.COM 	if ((ctx = libhal_ctx_init_direct(&error)) == NULL) {
302*9608SLin.Guo@Sun.COM 		goto out;
303*9608SLin.Guo@Sun.COM 	}
304*9608SLin.Guo@Sun.COM 
305*9608SLin.Guo@Sun.COM 	if ((cs = libhal_device_new_changeset(udi)) == NULL) {
306*9608SLin.Guo@Sun.COM 		HAL_DEBUG(("Cannot allocate changeset"));
307*9608SLin.Guo@Sun.COM 		goto out;
308*9608SLin.Guo@Sun.COM 	}
309*9608SLin.Guo@Sun.COM 
310*9608SLin.Guo@Sun.COM 	HAL_DEBUG(("Doing probe-xkb for %s (udi=%s)", device_file, udi));
311*9608SLin.Guo@Sun.COM 
312*9608SLin.Guo@Sun.COM 	if (get_kbd_layout_type(device_file, &kbd_type, &kbd_layout)) {
313*9608SLin.Guo@Sun.COM 		goto out;
314*9608SLin.Guo@Sun.COM 	}
315*9608SLin.Guo@Sun.COM 
316*9608SLin.Guo@Sun.COM 	/*
317*9608SLin.Guo@Sun.COM 	 * For some usb keyboard that is not self-identifying, get keyboard's
318*9608SLin.Guo@Sun.COM 	 * layout and type from system default keyboard device--/dev/kbd.
319*9608SLin.Guo@Sun.COM 	 */
320*9608SLin.Guo@Sun.COM 	if ((kbd_layout == 0) && (strstr(device_file, "hid") != NULL)) {
321*9608SLin.Guo@Sun.COM 		if (get_kbd_layout_type(KBD_DEFAULT_DEVICE,
322*9608SLin.Guo@Sun.COM 		    &kbd_type, &kbd_layout)) {
323*9608SLin.Guo@Sun.COM 			goto out;
324*9608SLin.Guo@Sun.COM 		}
325*9608SLin.Guo@Sun.COM 	}
326*9608SLin.Guo@Sun.COM 
327*9608SLin.Guo@Sun.COM 	if (sun_find_xkbnames(kbd_type, kbd_layout,
328*9608SLin.Guo@Sun.COM 	    &xkbkeymap, &xkbmodel, &xkblayout)) {
329*9608SLin.Guo@Sun.COM 		goto out;
330*9608SLin.Guo@Sun.COM 	}
331*9608SLin.Guo@Sun.COM 
332*9608SLin.Guo@Sun.COM 	libhal_changeset_set_property_string(cs,
333*9608SLin.Guo@Sun.COM 	    "input.x11_options.XkbModel", xkbmodel);
334*9608SLin.Guo@Sun.COM 	libhal_changeset_set_property_string(cs,
335*9608SLin.Guo@Sun.COM 	    "input.x11_options.XkbLayout", xkblayout);
336*9608SLin.Guo@Sun.COM 
337*9608SLin.Guo@Sun.COM 	libhal_device_commit_changeset(ctx, cs, &error);
338*9608SLin.Guo@Sun.COM 
339*9608SLin.Guo@Sun.COM 	ret = 0;
340*9608SLin.Guo@Sun.COM 
341*9608SLin.Guo@Sun.COM out:
342*9608SLin.Guo@Sun.COM 	if (cs != NULL) {
343*9608SLin.Guo@Sun.COM 		libhal_device_free_changeset(cs);
344*9608SLin.Guo@Sun.COM 	}
345*9608SLin.Guo@Sun.COM 
346*9608SLin.Guo@Sun.COM 	if (ctx != NULL) {
347*9608SLin.Guo@Sun.COM 		libhal_ctx_shutdown(ctx, &error);
348*9608SLin.Guo@Sun.COM 		libhal_ctx_free(ctx);
349*9608SLin.Guo@Sun.COM 		if (dbus_error_is_set(&error)) {
350*9608SLin.Guo@Sun.COM 			dbus_error_free(&error);
351*9608SLin.Guo@Sun.COM 		}
352*9608SLin.Guo@Sun.COM 	}
353*9608SLin.Guo@Sun.COM 	return (ret);
354*9608SLin.Guo@Sun.COM }
355