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 = keymap; 239 defmodel = model; 240 defxkblay = xkblay; 241 } 242 } else if (atoi(type) == kb_type) { 243 if (*type == '*') { 244 if (defkeymap == NULL) { 245 defkeymap = keymap; 246 defmodel = model; 247 defxkblay = xkblay; 248 } 249 } else if (atoi(layout) == kb_layout) { 250 found_keytable = 1; 251 break; 252 } 253 } 254 } 255 256 (void) fclose(fp); 257 258 if (!found_keytable) { 259 keymap = defkeymap; 260 model = defmodel; 261 xkblay = defxkblay; 262 } 263 264 if ((keymap != NULL) && (strcmp(keymap, "-") != 0)) { 265 *xkb_keymap = keymap; 266 } 267 if ((model != NULL) && (strcmp(model, "-") != 0)) { 268 *xkb_model = model; 269 } 270 if ((xkblay != NULL) && (strcmp(xkblay, "-") != 0)) { 271 *xkb_layout = xkblay; 272 } 273 274 return (0); 275 } 276 277 int 278 main(int argc, char *argv[]) 279 { 280 int ret = 1; 281 char *udi; 282 char *device_file; 283 LibHalContext *ctx = NULL; 284 LibHalChangeSet *cs = NULL; 285 DBusError error; 286 int kbd_type, kbd_layout; 287 char *xkbkeymap = NULL, *xkbmodel = NULL, *xkblayout = NULL; 288 289 if ((udi = getenv("UDI")) == NULL) { 290 goto out; 291 } 292 293 if ((device_file = getenv("HAL_PROP_INPUT_DEVICE")) == NULL) { 294 goto out; 295 } 296 297 drop_privileges(); 298 setup_logger(); 299 300 dbus_error_init(&error); 301 if ((ctx = libhal_ctx_init_direct(&error)) == NULL) { 302 goto out; 303 } 304 305 if ((cs = libhal_device_new_changeset(udi)) == NULL) { 306 HAL_DEBUG(("Cannot allocate changeset")); 307 goto out; 308 } 309 310 HAL_DEBUG(("Doing probe-xkb for %s (udi=%s)", device_file, udi)); 311 312 if (get_kbd_layout_type(device_file, &kbd_type, &kbd_layout)) { 313 goto out; 314 } 315 316 /* 317 * For some usb keyboard that is not self-identifying, get keyboard's 318 * layout and type from system default keyboard device--/dev/kbd. 319 */ 320 if ((kbd_layout == 0) && (strstr(device_file, "hid") != NULL)) { 321 if (get_kbd_layout_type(KBD_DEFAULT_DEVICE, 322 &kbd_type, &kbd_layout)) { 323 goto out; 324 } 325 } 326 327 if (sun_find_xkbnames(kbd_type, kbd_layout, 328 &xkbkeymap, &xkbmodel, &xkblayout)) { 329 goto out; 330 } 331 332 libhal_changeset_set_property_string(cs, 333 "input.x11_options.XkbModel", xkbmodel); 334 libhal_changeset_set_property_string(cs, 335 "input.x11_options.XkbLayout", xkblayout); 336 337 libhal_device_commit_changeset(ctx, cs, &error); 338 339 ret = 0; 340 341 out: 342 if (cs != NULL) { 343 libhal_device_free_changeset(cs); 344 } 345 346 if (ctx != NULL) { 347 libhal_ctx_shutdown(ctx, &error); 348 libhal_ctx_free(ctx); 349 if (dbus_error_is_set(&error)) { 350 dbus_error_free(&error); 351 } 352 } 353 return (ret); 354 } 355