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_unref(conn); 165 dbus_error_free(&error); 166 167 return err; 168 } 169 170 171 void help() 172 { 173 fprintf(stderr, 174 "usage: hal-device [--help] [--add udi] [--remove udi] [udi]\n" 175 "Create, remove, or show HAL device. If no udi is given, shows all devices.\n" 176 "If udi doesn't start with a '/', '/org/freedesktop/Hal/devices/' is prepended.\n" 177 " -a, --add udi\t\tAdd new device.\n" 178 " \t\t\tReads property list in 'lshal' syntax from stdin.\n" 179 " -r, --remove udi\tRemove device.\n" 180 " -h, --help\t\tShow this text.\n" 181 ); 182 } 183 184 185 /* 186 * Dump all devices. 187 */ 188 int dump_devices(LibHalContext *hal_ctx, char *arg) 189 { 190 int i; 191 int num_devices; 192 char **device_names; 193 DBusError error; 194 char *udi = NULL; 195 196 if (arg) { 197 if (*arg == '/') { 198 udi = arg; 199 } else { 200 #ifdef HAVE_ASPRINTF 201 asprintf(&udi, "/org/freedesktop/Hal/devices/%s", arg); 202 #else 203 udi = calloc(1, sizeof ("/org/freedesktop/Hal/devices/%s") + strlen(arg)); 204 sprintf(udi, "/org/freedesktop/Hal/devices/%s", arg); 205 206 #endif 207 } 208 } 209 210 dbus_error_init(&error); 211 212 if (!udi) { 213 if (!(device_names = libhal_get_all_devices(hal_ctx, &num_devices, &error))) { 214 fprintf(stderr, "Empty HAL device list.\n"); 215 LIBHAL_FREE_DBUS_ERROR (&error); 216 return 31; 217 } 218 } else { 219 device_names = calloc(2, sizeof *device_names); 220 device_names[0] = strdup(udi); 221 num_devices = 1; 222 } 223 224 for(i = 0; i < num_devices; i++) { 225 LibHalPropertySet *props; 226 LibHalPropertySetIterator it; 227 int type; 228 229 if (!(props = libhal_device_get_all_properties(hal_ctx, device_names[i], &error))) { 230 fprintf(stderr, "%s: %s\n", error.name, error.message); 231 dbus_error_init(&error); 232 continue; 233 } 234 235 if (!udi) 236 printf("%d: ", i); 237 printf("udi = '%s'\n", device_names[i]); 238 239 for(libhal_psi_init(&it, props); libhal_psi_has_more(&it); libhal_psi_next(&it)) { 240 type = libhal_psi_get_type(&it); 241 switch (type) { 242 case LIBHAL_PROPERTY_TYPE_STRING: 243 printf(" %s = '%s' (string)\n", 244 libhal_psi_get_key(&it), 245 libhal_psi_get_string(&it) 246 ); 247 break; 248 case LIBHAL_PROPERTY_TYPE_INT32: 249 printf(" %s = %d (0x%x) (int)\n", 250 libhal_psi_get_key(&it), 251 libhal_psi_get_int(&it), 252 libhal_psi_get_int(&it) 253 ); 254 break; 255 case LIBHAL_PROPERTY_TYPE_UINT64: 256 printf(" %s = %lld (0x%llx) (uint64)\n", 257 libhal_psi_get_key(&it), 258 (long long) libhal_psi_get_uint64(&it), 259 (long long) libhal_psi_get_uint64(&it) 260 ); 261 break; 262 case LIBHAL_PROPERTY_TYPE_DOUBLE: 263 printf(" %s = %g (double)\n", 264 libhal_psi_get_key(&it), 265 libhal_psi_get_double(&it) 266 ); 267 break; 268 case LIBHAL_PROPERTY_TYPE_BOOLEAN: 269 printf(" %s = %s (bool)\n", 270 libhal_psi_get_key(&it), 271 libhal_psi_get_bool(&it) ? "true" : "false" 272 ); 273 break; 274 case LIBHAL_PROPERTY_TYPE_STRLIST: 275 { 276 char **strlist; 277 278 printf (" %s = { ", libhal_psi_get_key(&it)); 279 strlist = libhal_psi_get_strlist(&it); 280 while (*strlist) { 281 printf("'%s'%s", *strlist, strlist[1] ? ", " : ""); 282 strlist++; 283 } 284 printf(" } (string list)\n"); 285 } 286 break; 287 default: 288 printf("Unknown type %d = 0x%02x\n", type, type); 289 break; 290 } 291 } 292 293 libhal_free_property_set(props); 294 printf("\n"); 295 } 296 297 libhal_free_string_array(device_names); 298 dbus_error_free(&error); 299 300 return 0; 301 } 302 303 304 int remove_udi(LibHalContext *hal_ctx, char *arg) 305 { 306 DBusError error; 307 char *udi; 308 309 if (!arg) return 11; 310 311 if (*arg == '/') { 312 udi = arg; 313 } else { 314 #ifdef HAVE_ASPRINTF 315 asprintf(&udi, "/org/freedesktop/Hal/devices/%s", arg); 316 #else 317 udi = calloc(1, sizeof ("/org/freedesktop/Hal/devices/%s") + strlen(arg)); 318 sprintf(udi, "/org/freedesktop/Hal/devices/%s", arg); 319 #endif 320 321 } 322 323 dbus_error_init(&error); 324 325 if (opt.remove) { 326 if (!libhal_remove_device(hal_ctx, udi, &error)) { 327 fprintf(stderr, "%s: %s\n", error.name, error.message); 328 LIBHAL_FREE_DBUS_ERROR (&error); 329 return 12; 330 } 331 332 fprintf(stderr, "removed: %s\n", udi); 333 return 13; 334 } 335 336 return 0; 337 } 338 339 340 int add_udi(LibHalContext *hal_ctx, char *arg) 341 { 342 DBusError error; 343 dbus_bool_t dev_exists = FALSE; 344 char *udi = NULL, buf[1024]; 345 lh_prop_t *prop; 346 int err; 347 348 if (!arg) 349 return 21; 350 351 if (*arg == '/') { 352 udi = arg; 353 } else { 354 #ifdef HAVE_ASPRINTF 355 asprintf(&udi, "/org/freedesktop/Hal/devices/%s", arg); 356 #else 357 udi = calloc(1, sizeof ("/org/freedesktop/Hal/devices/%s") + strlen(arg)); 358 sprintf(udi, "/org/freedesktop/Hal/devices/%s", arg); 359 #endif 360 } 361 362 if (udi) 363 new_dev.udi = strdup(udi); 364 365 dbus_error_init(&error); 366 367 if (udi) 368 dev_exists = libhal_device_exists(hal_ctx, udi, &error); 369 370 if (dev_exists) { 371 new_dev.real_udi = strdup(new_dev.udi); 372 } else { 373 new_dev.real_udi = libhal_new_device(hal_ctx, &error); 374 375 if (!new_dev.real_udi) { 376 fprintf(stderr, "%s: %s\n", error.name, error.message); 377 LIBHAL_FREE_DBUS_ERROR (&error); 378 free(new_dev.real_udi); 379 380 return 22; 381 } 382 383 printf("tmp udi: %s\n", new_dev.real_udi); 384 } 385 386 prop = NULL; 387 388 while (fgets(buf, sizeof buf, stdin)) { 389 process_property(hal_ctx, buf, &prop); 390 } 391 392 err = add_properties(hal_ctx, &new_dev, prop); 393 394 prop = free_properties(prop); 395 396 if (!dev_exists) { 397 if (!libhal_device_commit_to_gdl(hal_ctx, new_dev.real_udi, new_dev.udi, &error)) { 398 fprintf(stderr, "%s: %s\n", error.name, error.message); 399 LIBHAL_FREE_DBUS_ERROR (&error); 400 free(new_dev.real_udi); 401 402 err = err ? err : 23; 403 } 404 } 405 406 printf("%s: %s\n", dev_exists ? "merged": "created", new_dev.udi); 407 408 return err; 409 } 410 411 412 char *skip_space(char *s) 413 { 414 while (isspace(*s)) s++; 415 416 return s; 417 } 418 419 420 char *skip_non_eq_or_space(char *s) 421 { 422 while (*s && *s != '=' && !isspace(*s)) 423 s++; 424 425 return s; 426 } 427 428 429 char *skip_number(char *s) 430 { 431 while (*s == '-' || *s == '+' || *s == '.' || isdigit(*s)) 432 s++; 433 434 return s; 435 } 436 437 438 char *skip_nonquote(char *s) 439 { 440 while (*s && *s != '\'') 441 s++; 442 443 return s; 444 } 445 446 447 void process_property(LibHalContext *hal_ctx, char *buf, lh_prop_t **prop) 448 { 449 char *s, *s1; 450 char *key, *s_val = NULL; 451 lh_prop_t *p; 452 unsigned len; 453 int remove = 0; 454 455 s = skip_space(buf); 456 457 if (*s == '-') { 458 remove = 1; 459 s = skip_space(s + 1); 460 } 461 462 if ((s1 = skip_number(s), s1 != s) && *s1 == ':') s = skip_space(s1 + 1); 463 464 s = skip_non_eq_or_space(key = s); 465 *s++ = 0; 466 if (!*key) 467 return; 468 469 if (*key == '#') 470 return; 471 472 if (*s == '=') 473 s++; 474 s = skip_space(s); 475 476 if (!*s) 477 remove = 1; 478 479 p = calloc(1, sizeof *p); 480 p->type = LIBHAL_PROPERTY_TYPE_INVALID; 481 p->key = strdup(key); 482 483 if (remove) { 484 p->next = *prop; 485 *prop = p; 486 return; 487 } 488 489 if (*s == '\'') { 490 s_val = s + 1; 491 s = strrchr(s_val, '\''); 492 *(s ? s : s_val) = 0; 493 p->type = LIBHAL_PROPERTY_TYPE_STRING; 494 p->v.str_value = strdup(s_val); 495 } else if (*s == '{') { 496 s_val = s + 1; 497 s1 = strrchr(s_val, '}'); 498 if (s1) *s1 = 0; 499 p->type = LIBHAL_PROPERTY_TYPE_STRLIST; 500 len = 0; 501 p->v.strlist_value = calloc(1, sizeof *p->v.strlist_value); 502 while (*s_val++ == '\'') { 503 s = skip_nonquote(s_val); 504 if (*s) *s++ = 0; 505 p->v.strlist_value = realloc(p->v.strlist_value, (len + 2) * sizeof *p->v.strlist_value); 506 p->v.strlist_value[len] = strdup(s_val); 507 p->v.strlist_value[++len] = NULL; 508 s_val = skip_nonquote(s); 509 } 510 } else if (!strncmp(s, "true", 4)) { 511 s += 4; 512 p->type = LIBHAL_PROPERTY_TYPE_BOOLEAN; 513 p->v.bool_value = TRUE; 514 } else if (!strncmp(s, "false", 5)) { 515 s += 5; 516 p->type = LIBHAL_PROPERTY_TYPE_BOOLEAN; 517 p->v.bool_value = FALSE; 518 } else if ((s1 = skip_number(s)) != s) { 519 if (strstr(s1, "(int)")) { 520 *s1++ = 0; 521 p->type = LIBHAL_PROPERTY_TYPE_INT32; 522 p->v.int_value = strtol(s, NULL, 10); 523 } else if (strstr(s1, "(uint64)")) { 524 *s1++ = 0; 525 p->type = LIBHAL_PROPERTY_TYPE_UINT64; 526 p->v.uint64_value = strtoull(s, NULL, 10); 527 } else if (strstr(s1, "(double)")) { 528 p->type = LIBHAL_PROPERTY_TYPE_DOUBLE; 529 p->v.double_value = strtod(s, NULL); 530 } 531 532 s = s1; 533 } 534 535 if (p->type == LIBHAL_PROPERTY_TYPE_INVALID) { 536 free(p->key); 537 free(p); 538 } else { 539 p->next = *prop; 540 *prop = p; 541 } 542 } 543 544 545 int add_properties(LibHalContext *hal_ctx, new_dev_t *nd, lh_prop_t *prop) 546 { 547 DBusError error; 548 lh_prop_t *p; 549 char *udi2 = NULL, *udi3 = NULL, **s; 550 LibHalPropertyType old_type; 551 552 dbus_error_init(&error); 553 554 for(p = prop; p; p = p->next) { 555 if (!strcmp(p->key, "udi") && p->type == LIBHAL_PROPERTY_TYPE_STRING) { 556 udi2 = p->v.str_value; 557 continue; 558 } 559 560 old_type = libhal_device_get_property_type(hal_ctx, nd->real_udi, p->key, &error); 561 dbus_error_init(&error); 562 563 if (old_type != LIBHAL_PROPERTY_TYPE_INVALID && 564 ( p->type != old_type || p->type == LIBHAL_PROPERTY_TYPE_STRLIST)) { 565 if (!libhal_device_remove_property(hal_ctx, nd->real_udi, p->key, &error)) { 566 fprintf(stderr, "%s: %s\n", error.name, error.message); 567 LIBHAL_FREE_DBUS_ERROR (&error); 568 return 41; 569 } 570 } 571 572 switch (p->type) { 573 case LIBHAL_PROPERTY_TYPE_BOOLEAN: 574 if (!libhal_device_set_property_bool(hal_ctx, nd->real_udi, p->key, p->v.bool_value, &error)) { 575 fprintf(stderr, "%s: %s\n", error.name, error.message); 576 LIBHAL_FREE_DBUS_ERROR (&error); 577 return 42; 578 } 579 break; 580 case LIBHAL_PROPERTY_TYPE_INT32: 581 if (!libhal_device_set_property_int(hal_ctx, nd->real_udi, p->key, p->v.int_value, &error)) { 582 fprintf(stderr, "%s: %s\n", error.name, error.message); 583 LIBHAL_FREE_DBUS_ERROR (&error); 584 return 42; 585 } 586 break; 587 case LIBHAL_PROPERTY_TYPE_UINT64: 588 if (!libhal_device_set_property_uint64(hal_ctx, nd->real_udi, p->key, p->v.uint64_value, &error)) { 589 fprintf(stderr, "%s: %s\n", error.name, error.message); 590 LIBHAL_FREE_DBUS_ERROR (&error); 591 return 42; 592 } 593 break; 594 case LIBHAL_PROPERTY_TYPE_DOUBLE: 595 if (!libhal_device_set_property_double(hal_ctx, nd->real_udi, p->key, p->v.double_value, &error)) { 596 fprintf(stderr, "%s: %s\n", error.name, error.message); 597 LIBHAL_FREE_DBUS_ERROR (&error); 598 return 42; 599 } 600 break; 601 case LIBHAL_PROPERTY_TYPE_STRING: 602 if (!strcmp(p->key, "info.udi")) udi3 = p->v.str_value; 603 if (!libhal_device_set_property_string(hal_ctx, nd->real_udi, p->key, p->v.str_value, &error)) { 604 fprintf(stderr, "%s: %s\n", error.name, error.message); 605 LIBHAL_FREE_DBUS_ERROR (&error); 606 return 42; 607 } 608 break; 609 case LIBHAL_PROPERTY_TYPE_STRLIST: 610 for(s = p->v.strlist_value; *s; s++) { 611 if (!libhal_device_property_strlist_append(hal_ctx, nd->real_udi, p->key, *s, &error)) { 612 fprintf(stderr, "%s: %s\n", error.name, error.message); 613 LIBHAL_FREE_DBUS_ERROR (&error); 614 return 42; 615 } 616 } 617 break; 618 default: 619 break; 620 } 621 } 622 623 if (udi2) udi3 = NULL; 624 if (udi3) udi2 = udi3; 625 626 if (udi2 && !nd->udi) 627 nd->udi = strdup(udi2); 628 629 return 0; 630 } 631 632 633 lh_prop_t *free_properties(lh_prop_t *prop) 634 { 635 lh_prop_t *next; 636 char **s; 637 638 for(; prop; prop = next) { 639 next = prop->next; 640 641 free(prop->key); 642 if (prop->type == LIBHAL_PROPERTY_TYPE_STRING) free(prop->v.str_value); 643 if (prop->type == LIBHAL_PROPERTY_TYPE_STRLIST && prop->v.strlist_value) { 644 for(s = prop->v.strlist_value; *s; ) free(*s++); 645 free(prop->v.strlist_value); 646 } 647 free(prop); 648 } 649 650 return NULL; 651 } 652