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
main(int argc,char ** argv)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
help()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 */
dump_devices(LibHalContext * hal_ctx,char * arg)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
remove_udi(LibHalContext * hal_ctx,char * arg)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
add_udi(LibHalContext * hal_ctx,char * arg)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
skip_space(char * s)412 char *skip_space(char *s)
413 {
414 while (isspace(*s)) s++;
415
416 return s;
417 }
418
419
skip_non_eq_or_space(char * s)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
skip_number(char * s)429 char *skip_number(char *s)
430 {
431 while (*s == '-' || *s == '+' || *s == '.' || isdigit(*s))
432 s++;
433
434 return s;
435 }
436
437
skip_nonquote(char * s)438 char *skip_nonquote(char *s)
439 {
440 while (*s && *s != '\'')
441 s++;
442
443 return s;
444 }
445
446
process_property(LibHalContext * hal_ctx,char * buf,lh_prop_t ** prop)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
add_properties(LibHalContext * hal_ctx,new_dev_t * nd,lh_prop_t * prop)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_INVALID:
574 break;
575 case LIBHAL_PROPERTY_TYPE_BOOLEAN:
576 if (!libhal_device_set_property_bool(hal_ctx, nd->real_udi, p->key, p->v.bool_value, &error)) {
577 fprintf(stderr, "%s: %s\n", error.name, error.message);
578 LIBHAL_FREE_DBUS_ERROR (&error);
579 return 42;
580 }
581 break;
582 case LIBHAL_PROPERTY_TYPE_INT32:
583 if (!libhal_device_set_property_int(hal_ctx, nd->real_udi, p->key, p->v.int_value, &error)) {
584 fprintf(stderr, "%s: %s\n", error.name, error.message);
585 LIBHAL_FREE_DBUS_ERROR (&error);
586 return 42;
587 }
588 break;
589 case LIBHAL_PROPERTY_TYPE_UINT64:
590 if (!libhal_device_set_property_uint64(hal_ctx, nd->real_udi, p->key, p->v.uint64_value, &error)) {
591 fprintf(stderr, "%s: %s\n", error.name, error.message);
592 LIBHAL_FREE_DBUS_ERROR (&error);
593 return 42;
594 }
595 break;
596 case LIBHAL_PROPERTY_TYPE_DOUBLE:
597 if (!libhal_device_set_property_double(hal_ctx, nd->real_udi, p->key, p->v.double_value, &error)) {
598 fprintf(stderr, "%s: %s\n", error.name, error.message);
599 LIBHAL_FREE_DBUS_ERROR (&error);
600 return 42;
601 }
602 break;
603 case LIBHAL_PROPERTY_TYPE_STRING:
604 if (!strcmp(p->key, "info.udi")) udi3 = p->v.str_value;
605 if (!libhal_device_set_property_string(hal_ctx, nd->real_udi, p->key, p->v.str_value, &error)) {
606 fprintf(stderr, "%s: %s\n", error.name, error.message);
607 LIBHAL_FREE_DBUS_ERROR (&error);
608 return 42;
609 }
610 break;
611 case LIBHAL_PROPERTY_TYPE_STRLIST:
612 for(s = p->v.strlist_value; *s; s++) {
613 if (!libhal_device_property_strlist_append(hal_ctx, nd->real_udi, p->key, *s, &error)) {
614 fprintf(stderr, "%s: %s\n", error.name, error.message);
615 LIBHAL_FREE_DBUS_ERROR (&error);
616 return 42;
617 }
618 }
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
free_properties(lh_prop_t * prop)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