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