1 /*************************************************************************** 2 * CVSID: $Id$ 3 * 4 * hal-device.c : add devices HAL 5 * 6 * Copyright (C) 2005 SuSE Linux Gmbh 7 * 8 * Authors: 9 * Steffen Winterfeldt <snwint@suse.de> 10 * 11 * Licensed under the Academic Free License version 2.1 12 * 13 * This program is free software; you can redistribute it and/or modify 14 * it under the terms of the GNU General Public License as published by 15 * the Free Software Foundation; either version 2 of the License, or 16 * (at your option) any later version. 17 * 18 * This program is distributed in the hope that it will be useful, 19 * but WITHOUT ANY WARRANTY; without even the implied warranty of 20 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 21 * GNU General Public License for more details. 22 * 23 * You should have received a copy of the GNU General Public License 24 * along with this program; if not, write to the Free Software 25 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA 26 * 27 */ 28 29 #define _GNU_SOURCE 30 31 #ifdef HAVE_CONFIG_H 32 # include <config.h> 33 #endif 34 35 #include <stdio.h> 36 #include <string.h> 37 #include <unistd.h> 38 #include <stdlib.h> 39 #include <ctype.h> 40 #include <inttypes.h> 41 #include <getopt.h> 42 43 #ifndef DBUS_API_SUBJECT_TO_CHANGE 44 #define DBUS_API_SUBJECT_TO_CHANGE 1 45 #endif 46 47 #include <dbus/dbus.h> 48 #include <libhal.h> 49 50 typedef struct { 51 char *udi; 52 char *real_udi; 53 } new_dev_t; 54 55 typedef struct lh_prop_s { 56 struct lh_prop_s *next; 57 LibHalPropertyType type; 58 char *key; 59 union { 60 char *str_value; 61 dbus_int32_t int_value; 62 dbus_uint64_t uint64_value; 63 double double_value; 64 dbus_bool_t bool_value; 65 char **strlist_value; 66 } v; 67 } lh_prop_t; 68 69 70 void help(void); 71 int dump_devices(LibHalContext *hal_ctx, char *arg); 72 int remove_udi(LibHalContext *hal_ctx, char *arg); 73 int add_udi(LibHalContext *hal_ctx, char *arg); 74 void process_property(LibHalContext *hal_ctx, char *buf, lh_prop_t **prop); 75 int add_properties(LibHalContext *hal_ctx, new_dev_t *nd, lh_prop_t *prop); 76 lh_prop_t *free_properties(lh_prop_t *prop); 77 static char *skip_space(char *s); 78 static char *skip_non_eq_or_space(char *s); 79 static char *skip_number(char *s); 80 static char *skip_nonquote(char *s); 81 82 83 new_dev_t new_dev; 84 85 struct { 86 unsigned remove:1; 87 unsigned add:1; 88 unsigned list:1; 89 char *udi; 90 } opt; 91 92 struct option options[] = { 93 { "remove", 1, NULL, 'r' }, 94 { "add", 1, NULL, 'a' }, 95 { "help", 0, NULL, 'h' }, 96 { 0, 0, 0, 0 } 97 }; 98 99 100 int main(int argc, char **argv) 101 { 102 DBusError error; 103 DBusConnection *conn; 104 LibHalContext *hal_ctx; 105 int i, err; 106 107 opterr = 0; 108 opt.list = 1; 109 110 while ((i = getopt_long(argc, argv, "a:hr:", options, NULL)) != -1) { 111 switch (i) { 112 case 'a': 113 opt.add = 1; 114 opt.list = 0; 115 opt.udi = optarg; 116 break; 117 case 'r': 118 opt.remove = 1; 119 opt.list = 0; 120 opt.udi = optarg; 121 break; 122 case 'h': 123 help(); 124 return 0; 125 default: 126 help(); 127 return 1; 128 } 129 } 130 131 dbus_error_init(&error); 132 133 if (!(conn = dbus_bus_get(DBUS_BUS_SYSTEM, &error))) { 134 fprintf(stderr, "error: dbus_bus_get: %s: %s\n", error.name, error.message); 135 LIBHAL_FREE_DBUS_ERROR (&error); 136 return 2; 137 } 138 139 /* fprintf(stderr, "connected to: %s\n", dbus_bus_get_unique_name(conn)); */ 140 if (!(hal_ctx = libhal_ctx_new())) return 3; 141 if (!libhal_ctx_set_dbus_connection(hal_ctx, conn)) return 4; 142 if (!libhal_ctx_init(hal_ctx, &error)) { 143 if (dbus_error_is_set(&error)) { 144 fprintf (stderr, "error: libhal_ctx_init: %s: %s\n", error.name, error.message); 145 LIBHAL_FREE_DBUS_ERROR (&error); 146 } 147 fprintf (stderr, "Could not initialise connection to hald.\n" 148 "Normally this means the HAL daemon (hald) is not running or not ready.\n"); 149 return 5; 150 } 151 152 err = 0; 153 if (opt.list) 154 err = dump_devices(hal_ctx, argv[optind]); 155 else if (opt.remove) 156 err = remove_udi(hal_ctx, opt.udi); 157 else if (opt.add) 158 err = add_udi(hal_ctx, opt.udi); 159 else 160 err = 6; 161 162 libhal_ctx_shutdown(hal_ctx, &error); 163 libhal_ctx_free(hal_ctx); 164 dbus_connection_close(conn); 165 dbus_connection_unref(conn); 166 dbus_error_free(&error); 167 168 return err; 169 } 170 171 172 void help() 173 { 174 fprintf(stderr, 175 "usage: hal-device [--help] [--add udi] [--remove udi] [udi]\n" 176 "Create, remove, or show HAL device. If no udi is given, shows all devices.\n" 177 "If udi doesn't start with a '/', '/org/freedesktop/Hal/devices/' is prepended.\n" 178 " -a, --add udi\t\tAdd new device.\n" 179 " \t\t\tReads property list in 'lshal' syntax from stdin.\n" 180 " -r, --remove udi\tRemove device.\n" 181 " -h, --help\t\tShow this text.\n" 182 ); 183 } 184 185 186 /* 187 * Dump all devices. 188 */ 189 int dump_devices(LibHalContext *hal_ctx, char *arg) 190 { 191 int i; 192 int num_devices; 193 char **device_names; 194 DBusError error; 195 char *udi = NULL; 196 197 if (arg) { 198 if (*arg == '/') { 199 udi = arg; 200 } else { 201 #ifdef HAVE_ASPRINTF 202 asprintf(&udi, "/org/freedesktop/Hal/devices/%s", arg); 203 #else 204 udi = calloc(1, sizeof ("/org/freedesktop/Hal/devices/%s") + strlen(arg)); 205 sprintf(udi, "/org/freedesktop/Hal/devices/%s", arg); 206 207 #endif 208 } 209 } 210 211 dbus_error_init(&error); 212 213 if (!udi) { 214 if (!(device_names = libhal_get_all_devices(hal_ctx, &num_devices, &error))) { 215 fprintf(stderr, "Empty HAL device list.\n"); 216 LIBHAL_FREE_DBUS_ERROR (&error); 217 return 31; 218 } 219 } else { 220 device_names = calloc(2, sizeof *device_names); 221 device_names[0] = strdup(udi); 222 num_devices = 1; 223 } 224 225 for(i = 0; i < num_devices; i++) { 226 LibHalPropertySet *props; 227 LibHalPropertySetIterator it; 228 int type; 229 230 if (!(props = libhal_device_get_all_properties(hal_ctx, device_names[i], &error))) { 231 fprintf(stderr, "%s: %s\n", error.name, error.message); 232 dbus_error_init(&error); 233 continue; 234 } 235 236 if (!udi) 237 printf("%d: ", i); 238 printf("udi = '%s'\n", device_names[i]); 239 240 for(libhal_psi_init(&it, props); libhal_psi_has_more(&it); libhal_psi_next(&it)) { 241 type = libhal_psi_get_type(&it); 242 switch (type) { 243 case LIBHAL_PROPERTY_TYPE_STRING: 244 printf(" %s = '%s' (string)\n", 245 libhal_psi_get_key(&it), 246 libhal_psi_get_string(&it) 247 ); 248 break; 249 case LIBHAL_PROPERTY_TYPE_INT32: 250 printf(" %s = %d (0x%x) (int)\n", 251 libhal_psi_get_key(&it), 252 libhal_psi_get_int(&it), 253 libhal_psi_get_int(&it) 254 ); 255 break; 256 case LIBHAL_PROPERTY_TYPE_UINT64: 257 printf(" %s = %lld (0x%llx) (uint64)\n", 258 libhal_psi_get_key(&it), 259 (long long) libhal_psi_get_uint64(&it), 260 (long long) libhal_psi_get_uint64(&it) 261 ); 262 break; 263 case LIBHAL_PROPERTY_TYPE_DOUBLE: 264 printf(" %s = %g (double)\n", 265 libhal_psi_get_key(&it), 266 libhal_psi_get_double(&it) 267 ); 268 break; 269 case LIBHAL_PROPERTY_TYPE_BOOLEAN: 270 printf(" %s = %s (bool)\n", 271 libhal_psi_get_key(&it), 272 libhal_psi_get_bool(&it) ? "true" : "false" 273 ); 274 break; 275 case LIBHAL_PROPERTY_TYPE_STRLIST: 276 { 277 char **strlist; 278 279 printf (" %s = { ", libhal_psi_get_key(&it)); 280 strlist = libhal_psi_get_strlist(&it); 281 while (*strlist) { 282 printf("'%s'%s", *strlist, strlist[1] ? ", " : ""); 283 strlist++; 284 } 285 printf(" } (string list)\n"); 286 } 287 break; 288 default: 289 printf("Unknown type %d = 0x%02x\n", type, type); 290 break; 291 } 292 } 293 294 libhal_free_property_set(props); 295 printf("\n"); 296 } 297 298 libhal_free_string_array(device_names); 299 dbus_error_free(&error); 300 301 return 0; 302 } 303 304 305 int remove_udi(LibHalContext *hal_ctx, char *arg) 306 { 307 DBusError error; 308 char *udi; 309 310 if (!arg) return 11; 311 312 if (*arg == '/') { 313 udi = arg; 314 } else { 315 #ifdef HAVE_ASPRINTF 316 asprintf(&udi, "/org/freedesktop/Hal/devices/%s", arg); 317 #else 318 udi = calloc(1, sizeof ("/org/freedesktop/Hal/devices/%s") + strlen(arg)); 319 sprintf(udi, "/org/freedesktop/Hal/devices/%s", arg); 320 #endif 321 322 } 323 324 dbus_error_init(&error); 325 326 if (opt.remove) { 327 if (!libhal_remove_device(hal_ctx, udi, &error)) { 328 fprintf(stderr, "%s: %s\n", error.name, error.message); 329 LIBHAL_FREE_DBUS_ERROR (&error); 330 return 12; 331 } 332 333 fprintf(stderr, "removed: %s\n", udi); 334 return 13; 335 } 336 337 return 0; 338 } 339 340 341 int add_udi(LibHalContext *hal_ctx, char *arg) 342 { 343 DBusError error; 344 dbus_bool_t dev_exists = FALSE; 345 char *udi = NULL, buf[1024]; 346 lh_prop_t *prop; 347 int err; 348 349 if (!arg) 350 return 21; 351 352 if (*arg == '/') { 353 udi = arg; 354 } else { 355 #ifdef HAVE_ASPRINTF 356 asprintf(&udi, "/org/freedesktop/Hal/devices/%s", arg); 357 #else 358 udi = calloc(1, sizeof ("/org/freedesktop/Hal/devices/%s") + strlen(arg)); 359 sprintf(udi, "/org/freedesktop/Hal/devices/%s", arg); 360 #endif 361 } 362 363 if (udi) 364 new_dev.udi = strdup(udi); 365 366 dbus_error_init(&error); 367 368 if (udi) 369 dev_exists = libhal_device_exists(hal_ctx, udi, &error); 370 371 if (dev_exists) { 372 new_dev.real_udi = strdup(new_dev.udi); 373 } else { 374 new_dev.real_udi = libhal_new_device(hal_ctx, &error); 375 376 if (!new_dev.real_udi) { 377 fprintf(stderr, "%s: %s\n", error.name, error.message); 378 LIBHAL_FREE_DBUS_ERROR (&error); 379 free(new_dev.real_udi); 380 381 return 22; 382 } 383 384 printf("tmp udi: %s\n", new_dev.real_udi); 385 } 386 387 prop = NULL; 388 389 while (fgets(buf, sizeof buf, stdin)) { 390 process_property(hal_ctx, buf, &prop); 391 } 392 393 err = add_properties(hal_ctx, &new_dev, prop); 394 395 prop = free_properties(prop); 396 397 if (!dev_exists) { 398 if (!libhal_device_commit_to_gdl(hal_ctx, new_dev.real_udi, new_dev.udi, &error)) { 399 fprintf(stderr, "%s: %s\n", error.name, error.message); 400 LIBHAL_FREE_DBUS_ERROR (&error); 401 free(new_dev.real_udi); 402 403 err = err ? err : 23; 404 } 405 } 406 407 printf("%s: %s\n", dev_exists ? "merged": "created", new_dev.udi); 408 409 return err; 410 } 411 412 413 char *skip_space(char *s) 414 { 415 while (isspace(*s)) s++; 416 417 return s; 418 } 419 420 421 char *skip_non_eq_or_space(char *s) 422 { 423 while (*s && *s != '=' && !isspace(*s)) 424 s++; 425 426 return s; 427 } 428 429 430 char *skip_number(char *s) 431 { 432 while (*s == '-' || *s == '+' || *s == '.' || isdigit(*s)) 433 s++; 434 435 return s; 436 } 437 438 439 char *skip_nonquote(char *s) 440 { 441 while (*s && *s != '\'') 442 s++; 443 444 return s; 445 } 446 447 448 void process_property(LibHalContext *hal_ctx, char *buf, lh_prop_t **prop) 449 { 450 char *s, *s1; 451 char *key, *s_val = NULL; 452 lh_prop_t *p; 453 unsigned len; 454 int remove = 0; 455 456 s = skip_space(buf); 457 458 if (*s == '-') { 459 remove = 1; 460 s = skip_space(s + 1); 461 } 462 463 if ((s1 = skip_number(s), s1 != s) && *s1 == ':') s = skip_space(s1 + 1); 464 465 s = skip_non_eq_or_space(key = s); 466 *s++ = 0; 467 if (!*key) 468 return; 469 470 if (*key == '#') 471 return; 472 473 if (*s == '=') 474 s++; 475 s = skip_space(s); 476 477 if (!*s) 478 remove = 1; 479 480 p = calloc(1, sizeof *p); 481 p->type = LIBHAL_PROPERTY_TYPE_INVALID; 482 p->key = strdup(key); 483 484 if (remove) { 485 p->next = *prop; 486 *prop = p; 487 return; 488 } 489 490 if (*s == '\'') { 491 s_val = s + 1; 492 s = strrchr(s_val, '\''); 493 *(s ? s : s_val) = 0; 494 p->type = LIBHAL_PROPERTY_TYPE_STRING; 495 p->v.str_value = strdup(s_val); 496 } else if (*s == '{') { 497 s_val = s + 1; 498 s1 = strrchr(s_val, '}'); 499 if (s1) *s1 = 0; 500 p->type = LIBHAL_PROPERTY_TYPE_STRLIST; 501 len = 0; 502 p->v.strlist_value = calloc(1, sizeof *p->v.strlist_value); 503 while (*s_val++ == '\'') { 504 s = skip_nonquote(s_val); 505 if (*s) *s++ = 0; 506 p->v.strlist_value = realloc(p->v.strlist_value, (len + 2) * sizeof *p->v.strlist_value); 507 p->v.strlist_value[len] = strdup(s_val); 508 p->v.strlist_value[++len] = NULL; 509 s_val = skip_nonquote(s); 510 } 511 } else if (!strncmp(s, "true", 4)) { 512 s += 4; 513 p->type = LIBHAL_PROPERTY_TYPE_BOOLEAN; 514 p->v.bool_value = TRUE; 515 } else if (!strncmp(s, "false", 5)) { 516 s += 5; 517 p->type = LIBHAL_PROPERTY_TYPE_BOOLEAN; 518 p->v.bool_value = FALSE; 519 } else if ((s1 = skip_number(s)) != s) { 520 if (strstr(s1, "(int)")) { 521 *s1++ = 0; 522 p->type = LIBHAL_PROPERTY_TYPE_INT32; 523 p->v.int_value = strtol(s, NULL, 10); 524 } else if (strstr(s1, "(uint64)")) { 525 *s1++ = 0; 526 p->type = LIBHAL_PROPERTY_TYPE_UINT64; 527 p->v.uint64_value = strtoull(s, NULL, 10); 528 } else if (strstr(s1, "(double)")) { 529 p->type = LIBHAL_PROPERTY_TYPE_DOUBLE; 530 p->v.double_value = strtod(s, NULL); 531 } 532 533 s = s1; 534 } 535 536 if (p->type == LIBHAL_PROPERTY_TYPE_INVALID) { 537 free(p->key); 538 free(p); 539 } else { 540 p->next = *prop; 541 *prop = p; 542 } 543 } 544 545 546 int add_properties(LibHalContext *hal_ctx, new_dev_t *nd, lh_prop_t *prop) 547 { 548 DBusError error; 549 lh_prop_t *p; 550 char *udi2 = NULL, *udi3 = NULL, **s; 551 LibHalPropertyType old_type; 552 553 dbus_error_init(&error); 554 555 for(p = prop; p; p = p->next) { 556 if (!strcmp(p->key, "udi") && p->type == LIBHAL_PROPERTY_TYPE_STRING) { 557 udi2 = p->v.str_value; 558 continue; 559 } 560 561 old_type = libhal_device_get_property_type(hal_ctx, nd->real_udi, p->key, &error); 562 dbus_error_init(&error); 563 564 if (old_type != LIBHAL_PROPERTY_TYPE_INVALID && 565 ( p->type != old_type || p->type == LIBHAL_PROPERTY_TYPE_STRLIST)) { 566 if (!libhal_device_remove_property(hal_ctx, nd->real_udi, p->key, &error)) { 567 fprintf(stderr, "%s: %s\n", error.name, error.message); 568 LIBHAL_FREE_DBUS_ERROR (&error); 569 return 41; 570 } 571 } 572 573 switch (p->type) { 574 case LIBHAL_PROPERTY_TYPE_INVALID: 575 break; 576 case LIBHAL_PROPERTY_TYPE_BOOLEAN: 577 if (!libhal_device_set_property_bool(hal_ctx, nd->real_udi, p->key, p->v.bool_value, &error)) { 578 fprintf(stderr, "%s: %s\n", error.name, error.message); 579 LIBHAL_FREE_DBUS_ERROR (&error); 580 return 42; 581 } 582 break; 583 case LIBHAL_PROPERTY_TYPE_INT32: 584 if (!libhal_device_set_property_int(hal_ctx, nd->real_udi, p->key, p->v.int_value, &error)) { 585 fprintf(stderr, "%s: %s\n", error.name, error.message); 586 LIBHAL_FREE_DBUS_ERROR (&error); 587 return 42; 588 } 589 break; 590 case LIBHAL_PROPERTY_TYPE_UINT64: 591 if (!libhal_device_set_property_uint64(hal_ctx, nd->real_udi, p->key, p->v.uint64_value, &error)) { 592 fprintf(stderr, "%s: %s\n", error.name, error.message); 593 LIBHAL_FREE_DBUS_ERROR (&error); 594 return 42; 595 } 596 break; 597 case LIBHAL_PROPERTY_TYPE_DOUBLE: 598 if (!libhal_device_set_property_double(hal_ctx, nd->real_udi, p->key, p->v.double_value, &error)) { 599 fprintf(stderr, "%s: %s\n", error.name, error.message); 600 LIBHAL_FREE_DBUS_ERROR (&error); 601 return 42; 602 } 603 break; 604 case LIBHAL_PROPERTY_TYPE_STRING: 605 if (!strcmp(p->key, "info.udi")) udi3 = p->v.str_value; 606 if (!libhal_device_set_property_string(hal_ctx, nd->real_udi, p->key, p->v.str_value, &error)) { 607 fprintf(stderr, "%s: %s\n", error.name, error.message); 608 LIBHAL_FREE_DBUS_ERROR (&error); 609 return 42; 610 } 611 break; 612 case LIBHAL_PROPERTY_TYPE_STRLIST: 613 for(s = p->v.strlist_value; *s; s++) { 614 if (!libhal_device_property_strlist_append(hal_ctx, nd->real_udi, p->key, *s, &error)) { 615 fprintf(stderr, "%s: %s\n", error.name, error.message); 616 LIBHAL_FREE_DBUS_ERROR (&error); 617 return 42; 618 } 619 } 620 break; 621 } 622 } 623 624 if (udi2) udi3 = NULL; 625 if (udi3) udi2 = udi3; 626 627 if (udi2 && !nd->udi) 628 nd->udi = strdup(udi2); 629 630 return 0; 631 } 632 633 634 lh_prop_t *free_properties(lh_prop_t *prop) 635 { 636 lh_prop_t *next; 637 char **s; 638 639 for(; prop; prop = next) { 640 next = prop->next; 641 642 free(prop->key); 643 if (prop->type == LIBHAL_PROPERTY_TYPE_STRING) free(prop->v.str_value); 644 if (prop->type == LIBHAL_PROPERTY_TYPE_STRLIST && prop->v.strlist_value) { 645 for(s = prop->v.strlist_value; *s; ) free(*s++); 646 free(prop->v.strlist_value); 647 } 648 free(prop); 649 } 650 651 return NULL; 652 } 653