xref: /illumos-gate/usr/src/cmd/hal/tools/hal-device.c (revision 60a3f738d56f92ae8b80e4b62a2331c6e1f2311f)
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