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