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 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 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 * 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 * 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 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 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