hal-device.c revision 2912:85ea316d9c18
1285101Semaste/***************************************************************************
2285101Semaste * CVSID: $Id$
3285101Semaste *
4285101Semaste * hal-device.c : add devices HAL
5285101Semaste *
6285101Semaste * Copyright (C) 2005 SuSE Linux Gmbh
7285101Semaste *
8285101Semaste * Authors:
9285101Semaste *	Steffen Winterfeldt <snwint@suse.de>
10285101Semaste *
11285101Semaste * Licensed under the Academic Free License version 2.1
12285101Semaste *
13285101Semaste * This program is free software; you can redistribute it and/or modify
14285101Semaste * it under the terms of the GNU General Public License as published by
15285101Semaste * the Free Software Foundation; either version 2 of the License, or
16285101Semaste * (at your option) any later version.
17285101Semaste *
18285101Semaste * This program is distributed in the hope that it will be useful,
19285101Semaste * but WITHOUT ANY WARRANTY; without even the implied warranty of
20285101Semaste * 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
50typedef struct {
51	char *udi;
52	char *real_udi;
53} new_dev_t;
54
55typedef 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
70void help(void);
71int dump_devices(LibHalContext *hal_ctx, char *arg);
72int remove_udi(LibHalContext *hal_ctx, char *arg);
73int add_udi(LibHalContext *hal_ctx, char *arg);
74void process_property(LibHalContext *hal_ctx, char *buf, lh_prop_t **prop);
75int add_properties(LibHalContext *hal_ctx, new_dev_t *nd, lh_prop_t *prop);
76lh_prop_t *free_properties(lh_prop_t *prop);
77static char *skip_space(char *s);
78static char *skip_non_eq_or_space(char *s);
79static char *skip_number(char *s);
80static char *skip_nonquote(char *s);
81
82
83new_dev_t new_dev;
84
85struct {
86	unsigned remove:1;
87	unsigned add:1;
88	unsigned list:1;
89	char *udi;
90} opt;
91
92struct 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
100int 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
172void 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 */
189int 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
305int 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
341int 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
413char *skip_space(char *s)
414{
415	while (isspace(*s)) s++;
416
417	return s;
418}
419
420
421char *skip_non_eq_or_space(char *s)
422{
423	while (*s && *s != '=' && !isspace(*s))
424		s++;
425
426	return s;
427}
428
429
430char *skip_number(char *s)
431{
432	while (*s == '-' || *s == '+' || *s == '.' || isdigit(*s))
433		s++;
434
435	return s;
436}
437
438
439char *skip_nonquote(char *s)
440{
441	while (*s && *s != '\'')
442		s++;
443
444	return s;
445}
446
447
448void 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
546int 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
634lh_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