xref: /illumos-gate/usr/src/cmd/hal/hald/util.c (revision 1f5207b7604fb44407eb4342aff613f7c4508508)
1 /***************************************************************************
2  * CVSID: $Id$
3  *
4  * util.c - Various utilities
5  *
6  * Copyright (C) 2004 David Zeuthen, <david@fubar.dk>
7  *
8  * Licensed under the Academic Free License version 2.1
9  *
10  * This program is free software; you can redistribute it and/or modify
11  * it under the terms of the GNU General Public License as published by
12  * the Free Software Foundation; either version 2 of the License, or
13  * (at your option) any later version.
14  *
15  * This program is distributed in the hope that it will be useful,
16  * but WITHOUT ANY WARRANTY; without even the implied warranty of
17  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
18  * GNU General Public License for more details.
19  *
20  * You should have received a copy of the GNU General Public License
21  * along with this program; if not, write to the Free Software
22  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
23  *
24  **************************************************************************/
25 
26 #ifdef HAVE_CONFIG_H
27 #  include <config.h>
28 #endif
29 
30 #include <stdio.h>
31 #include <stdarg.h>
32 #include <string.h>
33 #include <errno.h>
34 #include <time.h>
35 #include <ctype.h>
36 #include <stdint.h>
37 #include <sys/stat.h>
38 #include <unistd.h>
39 #include <fcntl.h>
40 #include <signal.h>
41 #include <sys/wait.h>
42 #include <sys/file.h>
43 
44 #include <glib.h>
45 #include <dbus/dbus.h>
46 #include <dbus/dbus-glib.h>
47 
48 #include "osspec.h"
49 #include "logger.h"
50 #include "hald.h"
51 #include "hald_runner.h"
52 #include "hald_dbus.h"
53 #include "device_info.h"
54 
55 #include "util.h"
56 
57 /** Determine whether the given character is valid as the first character
58  *  in a name.
59  */
60 #define VALID_INITIAL_NAME_CHARACTER(c)          \
61 	(((c) >= 'A' && (c) <= 'Z') ||           \
62 	((c) >= 'a' && (c) <= 'z') ||            \
63 	((c) == '_'))
64 
65 /** Determine whether the given character is valid as a second or later
66  *  character in a name.
67  */
68 #define VALID_NAME_CHARACTER(c)                  \
69 	(((c) >= '0' && (c) <= '9') ||           \
70 	((c) >= 'A' && (c) <= 'Z') ||            \
71 	((c) >= 'a' && (c) <= 'z') ||            \
72 	((c) == '_'))
73 
74 gboolean
75 hal_util_remove_trailing_slash (gchar *path)
76 {
77 	gchar *c = NULL;
78 
79 	if (path == NULL) {
80 		return FALSE;
81 	}
82 
83 	c = strrchr (path, '/');
84 	if (c == NULL) {
85 		HAL_WARNING (("Invalid path %s", path));
86 		return 1;
87 	}
88 	if (*(c+1) == '\0')
89 		*c = '\0';
90 
91 	return TRUE;
92 }
93 
94 /** Given a path, /foo/bar/bat/foobar, return the last element, e.g.
95  *  foobar.
96  *
97  *  @param  path                Path
98  *  @return                     Pointer into given string
99  */
100 const gchar *
101 hal_util_get_last_element (const gchar *s)
102 {
103 	int len;
104 	const gchar *p;
105 
106 	len = strlen (s);
107 	for (p = s + len - 1; p > s; --p) {
108 		if ((*p) == '/')
109 			return p + 1;
110 	}
111 
112 	return s;
113 }
114 
115 /** Given a path, this functions finds the path representing the
116  *  parent directory by truncation.
117  *
118  *  @param  path                Path
119  *  @return                     Path for parent or NULL. Must be freed by caller
120  */
121 gchar *
122 hal_util_get_parent_path (const gchar *path)
123 {
124 	guint i;
125 	guint len;
126 	gchar *parent_path;
127 
128 	/* Find parent device by truncating our own path */
129 	parent_path = g_strndup (path, HAL_PATH_MAX);
130 	len = strlen (parent_path);
131 	for (i = len - 1; parent_path[i] != '/'; --i) {
132 		parent_path[i] = '\0';
133 	}
134 	parent_path[i] = '\0';
135 
136 	return parent_path;
137 }
138 
139 gchar *
140 hal_util_get_normalized_path (const gchar *path1, const gchar *path2)
141 {
142 	int len1;
143 	int len2;
144 	const gchar *p1;
145 	const gchar *p2;
146 	gchar buf[HAL_PATH_MAX];
147 
148 	len1 = strlen (path1);
149 	len2 = strlen (path2);
150 
151 	p1 = path1 + len1;
152 
153 	p2 = path2;
154 	while (p2 < path2 + len2 && strncmp (p2, "../", 3) == 0) {
155 		p2 += 3;
156 
157 		while (p1 >= path1 && *(--p1)!='/')
158 			;
159 	}
160 
161 	if ((p1-path1) < 0) {
162 		HAL_ERROR (("Could not normalize '%s' and '%s', return 'NULL'", path1, path2));
163 		return NULL;
164 	}
165 
166 	strncpy (buf, path1, (p1-path1));
167 	buf[p1-path1] = '\0';
168 
169 	return g_strdup_printf ("%s/%s", buf, p2);
170 }
171 
172 gboolean
173 hal_util_get_int_from_file (const gchar *directory, const gchar *file, gint *result, gint base)
174 {
175 	FILE *f;
176 	char buf[64];
177 	gchar path[HAL_PATH_MAX];
178 	gboolean ret;
179 
180 	f = NULL;
181 	ret = FALSE;
182 
183 	g_snprintf (path, sizeof (path), "%s/%s", directory, file);
184 
185 	f = fopen (path, "rb");
186 	if (f == NULL) {
187 		HAL_ERROR (("Cannot open '%s'", path));
188 		goto out;
189 	}
190 
191 	if (fgets (buf, sizeof (buf), f) == NULL) {
192 		HAL_ERROR (("Cannot read from '%s'", path));
193 		goto out;
194 	}
195 
196 	/* TODO: handle error condition */
197 	*result = strtol (buf, NULL, base);
198 	ret = TRUE;
199 
200 out:
201 	if (f != NULL)
202 		fclose (f);
203 
204 	return ret;
205 }
206 
207 gboolean
208 hal_util_set_int_from_file (HalDevice *d, const gchar *key, const gchar *directory, const gchar *file, gint base)
209 {
210 	gint value;
211 	gboolean ret;
212 
213 	ret = FALSE;
214 
215 	if (hal_util_get_int_from_file (directory, file, &value, base))
216 		ret = hal_device_property_set_int (d, key, value);
217 
218 	return ret;
219 }
220 
221 
222 gboolean
223 hal_util_get_uint64_from_file (const gchar *directory, const gchar *file, guint64 *result, gint base)
224 {
225 	FILE *f;
226 	char buf[64];
227 	gchar path[HAL_PATH_MAX];
228 	gboolean ret;
229 
230 	f = NULL;
231 	ret = FALSE;
232 
233 	g_snprintf (path, sizeof (path), "%s/%s", directory, file);
234 
235 	f = fopen (path, "rb");
236 	if (f == NULL) {
237 		HAL_ERROR (("Cannot open '%s'", path));
238 		goto out;
239 	}
240 
241 	if (fgets (buf, sizeof (buf), f) == NULL) {
242 		HAL_ERROR (("Cannot read from '%s'", path));
243 		goto out;
244 	}
245 
246 	/* TODO: handle error condition */
247 	*result = strtoll (buf, NULL, base);
248 
249 	ret = TRUE;
250 
251 out:
252 	if (f != NULL)
253 		fclose (f);
254 
255 	return ret;
256 }
257 
258 gboolean
259 hal_util_set_uint64_from_file (HalDevice *d, const gchar *key, const gchar *directory, const gchar *file, gint base)
260 {
261 	guint64 value;
262 	gboolean ret;
263 
264 	ret = FALSE;
265 
266 	if (hal_util_get_uint64_from_file (directory, file, &value, base))
267 		ret = hal_device_property_set_uint64 (d, key, value);
268 
269 	return ret;
270 }
271 
272 gboolean
273 hal_util_get_bcd2_from_file (const gchar *directory, const gchar *file, gint *result)
274 {
275 	FILE *f;
276 	char buf[64];
277 	gchar path[HAL_PATH_MAX];
278 	gboolean ret;
279 	gint digit;
280 	gint left, right;
281 	gboolean passed_white_space;
282 	gint num_prec;
283 	gsize len;
284 	gchar c;
285 	guint i;
286 
287 	f = NULL;
288 	ret = FALSE;
289 
290 	g_snprintf (path, sizeof (path), "%s/%s", directory, file);
291 
292 	f = fopen (path, "rb");
293 	if (f == NULL) {
294 		HAL_ERROR (("Cannot open '%s'", path));
295 		goto out;
296 	}
297 
298 	if (fgets (buf, sizeof (buf), f) == NULL) {
299 		HAL_ERROR (("Cannot read from '%s'", path));
300 		goto out;
301 	}
302 
303 	left = 0;
304 	len = strlen (buf);
305 	passed_white_space = FALSE;
306 	for (i = 0; i < len && buf[i] != '.'; i++) {
307 		if (g_ascii_isspace (buf[i])) {
308 			if (passed_white_space)
309 				break;
310 			else
311 				continue;
312 		}
313 		passed_white_space = TRUE;
314 		left *= 16;
315 		c = buf[i];
316 		digit = (int) (c - '0');
317 		left += digit;
318 	}
319 	i++;
320 	right = 0;
321 	num_prec = 0;
322 	for (; i < len; i++) {
323 		if (g_ascii_isspace (buf[i]))
324 			break;
325 		if (num_prec == 2)	        /* Only care about two digits
326 						 * of precision */
327 			break;
328 		right *= 16;
329 		c = buf[i];
330 		digit = (int) (c - '0');
331 		right += digit;
332 		num_prec++;
333 	}
334 
335 	for (; num_prec < 2; num_prec++)
336 		right *= 16;
337 
338 	*result = left * 256 + (right & 255);
339 	ret = TRUE;
340 
341 out:
342 	if (f != NULL)
343 		fclose (f);
344 
345 	return ret;
346 }
347 
348 gboolean
349 hal_util_set_bcd2_from_file (HalDevice *d, const gchar *key, const gchar *directory, const gchar *file)
350 {
351 	gint value;
352 	gboolean ret;
353 
354 	ret = FALSE;
355 
356 	if (hal_util_get_bcd2_from_file (directory, file, &value))
357 		ret = hal_device_property_set_int (d, key, value);
358 
359 	return ret;
360 }
361 
362 gchar *
363 hal_util_get_string_from_file (const gchar *directory, const gchar *file)
364 {
365 	FILE *f;
366 	static gchar buf[256];
367 	gchar path[HAL_PATH_MAX];
368 	gchar *result;
369 	gsize len;
370 	gint i;
371 
372 	f = NULL;
373 	result = NULL;
374 
375 	g_snprintf (path, sizeof (path), "%s/%s", directory, file);
376 
377 	f = fopen (path, "rb");
378 	if (f == NULL) {
379 		HAL_ERROR (("Cannot open '%s'", path));
380 		goto out;
381 	}
382 
383 	buf[0] = '\0';
384 	if (fgets (buf, sizeof (buf), f) == NULL) {
385 		HAL_ERROR (("Cannot read from '%s'", path));
386 		goto out;
387 	}
388 
389 	len = strlen (buf);
390 	if (len>0)
391 		buf[len-1] = '\0';
392 
393 	/* Clear remaining whitespace */
394 	for (i = len - 2; i >= 0; --i) {
395 		if (!g_ascii_isspace (buf[i]))
396 			break;
397 		buf[i] = '\0';
398 	}
399 
400 	result = buf;
401 
402 out:
403 	if (f != NULL)
404 		fclose (f);
405 
406 	return result;
407 }
408 
409 gboolean
410 hal_util_set_string_from_file (HalDevice *d, const gchar *key, const gchar *directory, const gchar *file)
411 {
412 	gchar *buf;
413 	gboolean ret;
414 
415 	ret = FALSE;
416 
417 	if ((buf = hal_util_get_string_from_file (directory, file)) != NULL)
418 		ret = hal_device_property_set_string (d, key, buf);
419 
420 	return ret;
421 }
422 
423 void
424 hal_util_compute_udi (HalDeviceStore *store, gchar *dst, gsize dstsize, const gchar *format, ...)
425 {
426 	guint i;
427 	va_list args;
428 	gchar buf[256];
429 
430 	va_start (args, format);
431 	g_vsnprintf (buf, sizeof (buf), format, args);
432 	va_end (args);
433 
434 	g_strcanon (buf,
435 		    "/_"
436 		    "abcdefghijklmnopqrstuvwxyz"
437 		    "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
438 		    "1234567890", '_');
439 
440 	g_strlcpy (dst, buf, dstsize);
441 	if (hal_device_store_find (store, dst) == NULL)
442 		goto out;
443 
444 	for (i = 0; ; i++) {
445 		g_snprintf (dst, dstsize, "%s_%d", buf, i);
446 		if (hal_device_store_find (store, dst) == NULL)
447 			goto out;
448 	}
449 
450 out:
451 	;
452 }
453 
454 
455 gboolean
456 hal_util_path_ascend (gchar *path)
457 {
458 	gchar *p;
459 
460 	if (path == NULL)
461 		return FALSE;
462 
463 	p = strrchr (path, '/');
464 	if (p == NULL)
465 		return FALSE;
466 
467 	*p = '\0';
468 	return TRUE;
469 }
470 
471 static gboolean _grep_can_reuse = FALSE;
472 
473 void
474 hal_util_grep_discard_existing_data (void)
475 {
476 	_grep_can_reuse = FALSE;
477 }
478 
479 /** Given a directory and filename, open the file and search for the
480  *  first line that starts with the given linestart string. Returns
481  *  the rest of the line as a string if found.
482  *
483  *  @param  directory           Directory, e.g. "/proc/acpi/battery/BAT0"
484  *  @param  file                File, e.g. "info"
485  *  @param  linestart           Start of line, e.g. "serial number"
486  *  @param  reuse               Whether we should reuse the file contents
487  *                              if the file is the same; can be cleared
488  *                              with hal_util_grep_discard_existing_data()
489  *  @return                     NULL if not found, otherwise the remainder
490  *                              of the line, e.g. ":           21805" if
491  *                              the file /proc/acpi/battery/BAT0 contains
492  *                              this line "serial number:           21805"
493  *                              The string is only valid until the next
494  *                              invocation of this function.
495  */
496 gchar *
497 hal_util_grep_file (const gchar *directory, const gchar *file, const gchar *linestart, gboolean reuse)
498 {
499 	static gchar buf[2048];
500 	static unsigned int bufsize;
501 	static gchar filename[HAL_PATH_MAX];
502 	static gchar oldfilename[HAL_PATH_MAX];
503 	gchar *result;
504 	gsize linestart_len;
505 	gchar *p;
506 
507 	result = NULL;
508 
509 	/* TODO: use reuse and _grep_can_reuse parameters to avoid loading
510 	 *       the file again and again
511 	 */
512 
513 	if (file != NULL && strlen (file) > 0)
514 		snprintf (filename, sizeof (filename), "%s/%s", directory, file);
515 	else
516 		strncpy (filename, directory, sizeof (filename));
517 
518 	if (_grep_can_reuse && reuse && strcmp (oldfilename, filename) == 0) {
519 		/* just reuse old file; e.g. bufsize, buf */
520 		/*HAL_INFO (("hal_util_grep_file: reusing buf for %s", filename));*/
521 	} else {
522 		FILE *f;
523 
524 		f = fopen (filename, "r");
525 		if (f == NULL)
526 			goto out;
527 		bufsize = fread (buf, sizeof (char), sizeof (buf) - 1, f);
528 		buf[bufsize] = '\0';
529 		fclose (f);
530 
531 		/*HAL_INFO (("hal_util_grep_file: read %s of %d bytes", filename, bufsize));*/
532 	}
533 
534 	/* book keeping */
535 	_grep_can_reuse = TRUE;
536 	strncpy (oldfilename, filename, sizeof(oldfilename));
537 
538 	linestart_len = strlen (linestart);
539 
540 	/* analyze buf */
541 	p = buf;
542 	do {
543 		unsigned int linelen;
544 		static char line[256];
545 
546 		for (linelen = 0; p[linelen] != '\n' && p[linelen] != '\0'; linelen++)
547 			;
548 
549 		if (linelen < sizeof (line)) {
550 
551 			strncpy (line, p, linelen);
552 			line[linelen] = '\0';
553 
554 			if (strncmp (line, linestart, linestart_len) == 0) {
555 				result = line + linestart_len;
556 				goto out;
557 			}
558 		}
559 
560 		p += linelen + 1;
561 
562 	} while (p < buf + bufsize);
563 
564 out:
565 	return result;
566 }
567 
568 gchar *
569 hal_util_grep_string_elem_from_file (const gchar *directory, const gchar *file,
570 				     const gchar *linestart, guint elem, gboolean reuse)
571 {
572 	gchar *line;
573 	gchar *res;
574 	static gchar buf[256];
575 	gchar **tokens;
576 	guint i, j;
577 
578 	res = NULL;
579 	tokens = NULL;
580 
581 	if (((line = hal_util_grep_file (directory, file, linestart, reuse)) == NULL) || (strlen (line) == 0))
582 		goto out;
583 
584 	tokens = g_strsplit_set (line, " \t:", 0);
585 	for (i = 0, j = 0; tokens[i] != NULL; i++) {
586 		if (strlen (tokens[i]) == 0)
587 			continue;
588 		if (j == elem) {
589 			strncpy (buf, tokens[i], sizeof (buf));
590 			res = buf;
591 			goto out;
592 		}
593 		j++;
594 	}
595 
596 out:
597 	if (tokens != NULL)
598 		g_strfreev (tokens);
599 
600 	return res;
601 }
602 
603 gint
604 hal_util_grep_int_elem_from_file (const gchar *directory, const gchar *file,
605 				  const gchar *linestart, guint elem, guint base, gboolean reuse)
606 {
607 	gchar *endptr;
608 	gchar *strvalue;
609 	int value;
610 
611 	value = G_MAXINT;
612 
613 	strvalue = hal_util_grep_string_elem_from_file (directory, file, linestart, elem, reuse);
614 	if (strvalue == NULL)
615 		goto out;
616 
617 	value = strtol (strvalue, &endptr, base);
618 	if (endptr == strvalue) {
619 		value = G_MAXINT;
620 		goto out;
621 	}
622 
623 out:
624 	return value;
625 }
626 
627 /** Get a string value from a formatted text file and assign it to
628  *  a property on a device object.
629  *
630  *  Example: Given that the file /proc/acpi/battery/BAT0/info contains
631  *  the line
632  *
633  *    "design voltage:          10800 mV"
634  *
635  *  then hal_util_set_string_elem_from_file (d, "battery.foo",
636  *  "/proc/acpi/battery/BAT0", "info", "design voltage", 1) will assign
637  *  the string "mV" to the property "battery.foo" on d.
638  *
639  *  @param  d                   Device object
640  *  @param  key                 Property name
641  *  @param  directory           Directory, e.g. "/proc/acpi/battery/BAT0"
642  *  @param  file                File, e.g. "info"
643  *  @param  linestart           Start of line, e.g. "design voltage"
644  *  @param  elem                Element number after linestart to extract
645  *                              excluding whitespace and ':' characters.
646  *  @return                     TRUE, if, and only if, the value could be
647  *                              extracted and the property was set
648  */
649 gboolean
650 hal_util_set_string_elem_from_file (HalDevice *d, const gchar *key,
651 				    const gchar *directory, const gchar *file,
652 				    const gchar *linestart, guint elem, gboolean reuse)
653 {
654 	gboolean res;
655 	gchar *value;
656 
657 	res = FALSE;
658 
659 	if ((value = hal_util_grep_string_elem_from_file (directory, file, linestart, elem, reuse)) == NULL)
660 		goto out;
661 
662 	res = hal_device_property_set_string (d, key, value);
663 out:
664 	return res;
665 }
666 
667 /** Get an integer value from a formatted text file and assign it to
668  *  a property on a device object.
669  *
670  *  Example: Given that the file /proc/acpi/battery/BAT0/info contains
671  *  the line
672  *
673  *    "design voltage:          10800 mV"
674  *
675  *  then hal_util_set_int_elem_from_file (d, "battery.foo",
676  *  "/proc/acpi/battery/BAT0", "info", "design voltage", 0) will assign
677  *  the integer 10800 to the property "battery.foo" on d.
678  *
679  *  @param  d                   Device object
680  *  @param  key                 Property name
681  *  @param  directory           Directory, e.g. "/proc/acpi/battery/BAT0"
682  *  @param  file                File, e.g. "info"
683  *  @param  linestart           Start of line, e.g. "design voltage"
684  *  @param  elem                Element number after linestart to extract
685  *                              excluding whitespace and ':' characters.
686  *  @return                     TRUE, if, and only if, the value could be
687  *                              extracted and the property was set
688  */
689 gboolean
690 hal_util_set_int_elem_from_file (HalDevice *d, const gchar *key,
691 				 const gchar *directory, const gchar *file,
692 				 const gchar *linestart, guint elem, guint base, gboolean reuse)
693 {
694 	gchar *endptr;
695 	gboolean res;
696 	gchar *strvalue;
697 	int value;
698 
699 	res = FALSE;
700 
701 	strvalue = hal_util_grep_string_elem_from_file (directory, file, linestart, elem, reuse);
702 	if (strvalue == NULL)
703 		goto out;
704 
705 	value = strtol (strvalue, &endptr, base);
706 	if (endptr == strvalue)
707 		goto out;
708 
709 	res = hal_device_property_set_int (d, key, value);
710 
711 out:
712 	return res;
713 
714 }
715 
716 /** Get a value from a formatted text file, test it against a given
717  *  value, and set a boolean property on a device object with the
718  *  test result.
719  *
720  *  Example: Given that the file /proc/acpi/battery/BAT0/info contains
721  *  the line
722  *
723  *    "present:                 yes"
724  *
725  *  then hal_util_set_bool_elem_from_file (d, "battery.baz",
726  *  "/proc/acpi/battery/BAT0", "info", "present", 0, "yes") will assign
727  *  the boolean TRUE to the property "battery.baz" on d.
728  *
729  *  If, instead, the line was
730  *
731  *    "present:                 no"
732  *
733  *  the value assigned will be FALSE.
734  *
735  *  @param  d                   Device object
736  *  @param  key                 Property name
737  *  @param  directory           Directory, e.g. "/proc/acpi/battery/BAT0"
738  *  @param  file                File, e.g. "info"
739  *  @param  linestart           Start of line, e.g. "design voltage"
740  *  @param  elem                Element number after linestart to extract
741  *                              excluding whitespace and ':' characters.
742  *  @param  expected            Value to test against
743  *  @return                     TRUE, if, and only if, the value could be
744  *                              extracted and the property was set
745  */
746 gboolean
747 hal_util_set_bool_elem_from_file (HalDevice *d, const gchar *key,
748 				  const gchar *directory, const gchar *file,
749 				  const gchar *linestart, guint elem, const gchar *expected, gboolean reuse)
750 {
751 	gchar *line;
752 	gboolean res;
753 	gchar **tokens;
754 	guint i, j;
755 
756 	res = FALSE;
757 	tokens = NULL;
758 
759 	if (((line = hal_util_grep_file (directory, file, linestart, reuse)) == NULL) || (strlen (line) == 0))
760 		goto out;
761 
762 	tokens = g_strsplit_set (line, " \t:", 0);
763 
764 	for (i = 0, j = 0; tokens[i] != NULL; i++) {
765 		if (strlen (tokens[i]) == 0)
766 			continue;
767 		if (j == elem) {
768 			hal_device_property_set_bool (d, key, strcmp (tokens[i], expected) == 0);
769 			res = TRUE;
770 			goto out;
771 		}
772 		j++;
773 	}
774 
775 
776 out:
777 	if (tokens != NULL)
778 		g_strfreev (tokens);
779 
780 	return res;
781 }
782 
783 gchar **
784 hal_util_dup_strv_from_g_slist (GSList *strlist)
785 {
786 	guint j;
787 	guint len;
788 	gchar **strv;
789 	GSList *i;
790 
791 	len = g_slist_length (strlist);
792 	strv = g_new (char *, len + 1);
793 
794 	for (i = strlist, j = 0; i != NULL; i = g_slist_next (i), j++) {
795 		strv[j] = g_strdup ((const gchar *) i->data);
796 	}
797 	strv[j] = NULL;
798 
799 	return strv;
800 }
801 
802 /* -------------------------------------------------------------------------------------------------------------- */
803 
804 typedef struct {
805 	HalDevice *d;
806 	gchar **programs;
807 	gchar **extra_env;
808 	guint next_program;
809 
810 	HalCalloutsDone callback;
811 	gpointer userdata1;
812 	gpointer userdata2;
813 
814 } Callout;
815 
816 static void callout_do_next (Callout *c);
817 
818 static void
819 callout_terminated (HalDevice *d, guint32 exit_type,
820                    gint return_code, gchar **error,
821                    gpointer data1, gpointer data2)
822 {
823 	Callout *c;
824 
825 	c = (Callout *) data1;
826 	callout_do_next (c);
827 }
828 
829 static void
830 callout_do_next (Callout *c)
831 {
832 
833 	/* Check if we're done */
834 	if (c->programs[c->next_program] == NULL) {
835 		HalDevice *d;
836 		gpointer userdata1;
837 		gpointer userdata2;
838 		HalCalloutsDone callback;
839 
840 		d = c->d;
841 		userdata1 = c->userdata1;
842 		userdata2 = c->userdata2;
843 		callback = c->callback;
844 
845 		g_strfreev (c->programs);
846 		g_strfreev (c->extra_env);
847 		g_free (c);
848 
849 		callback (d, userdata1, userdata2);
850 
851 	} else {
852     hald_runner_run(c->d, c->programs[c->next_program], c->extra_env,
853                     HAL_HELPER_TIMEOUT, callout_terminated,
854                     (gpointer)c, NULL);
855 		c->next_program++;
856 	}
857 }
858 
859 static void
860 hal_callout_device (HalDevice *d, HalCalloutsDone callback, gpointer userdata1, gpointer userdata2,
861 		    GSList *programs, gchar **extra_env)
862 {
863 	Callout *c;
864 
865 	c = g_new0 (Callout, 1);
866 	c->d = d;
867 	c->callback = callback;
868 	c->userdata1 = userdata1;
869 	c->userdata2 = userdata2;
870 	c->programs = hal_util_dup_strv_from_g_slist (programs);
871 	c->extra_env = g_strdupv (extra_env);
872 	c->next_program = 0;
873 
874 	callout_do_next (c);
875 }
876 
877 void
878 hal_util_callout_device_add (HalDevice *d, HalCalloutsDone callback, gpointer userdata1, gpointer userdata2)
879 {
880 	GSList *programs;
881 	gchar *extra_env[2] = {"HALD_ACTION=add", NULL};
882 
883 	if ((programs = hal_device_property_get_strlist (d, "info.callouts.add")) == NULL) {
884 		callback (d, userdata1, userdata2);
885 		goto out;
886 	}
887 
888 	HAL_INFO (("Add callouts for udi=%s", d->udi));
889 
890 	hal_callout_device (d, callback, userdata1, userdata2, programs, extra_env);
891 out:
892 	;
893 }
894 
895 void
896 hal_util_callout_device_remove (HalDevice *d, HalCalloutsDone callback, gpointer userdata1, gpointer userdata2)
897 {
898 	GSList *programs;
899 	gchar *extra_env[2] = {"HALD_ACTION=remove", NULL};
900 
901 	if ((programs = hal_device_property_get_strlist (d, "info.callouts.remove")) == NULL) {
902 		callback (d, userdata1, userdata2);
903 		goto out;
904 	}
905 
906 	HAL_INFO (("Remove callouts for udi=%s", d->udi));
907 
908 	hal_callout_device (d, callback, userdata1, userdata2, programs, extra_env);
909 out:
910 	;
911 }
912 
913 void
914 hal_util_callout_device_preprobe (HalDevice *d, HalCalloutsDone callback, gpointer userdata1, gpointer userdata2)
915 {
916 	GSList *programs;
917 	gchar *extra_env[2] = {"HALD_ACTION=preprobe", NULL};
918 
919 	if ((programs = hal_device_property_get_strlist (d, "info.callouts.preprobe")) == NULL) {
920 		callback (d, userdata1, userdata2);
921 		goto out;
922 	}
923 
924 	HAL_INFO (("Preprobe callouts for udi=%s", d->udi));
925 
926 	hal_callout_device (d, callback, userdata1, userdata2, programs, extra_env);
927 out:
928 	;
929 }
930 
931 gchar *
932 hal_util_strdup_valid_utf8 (const char *str)
933 {
934 	char *endchar;
935 	char *newstr;
936 	unsigned int count = 0;
937 
938 	if (str == NULL)
939 		return NULL;
940 
941 	newstr = g_strdup (str);
942 
943 	while (!g_utf8_validate (newstr, -1, (const char **) &endchar)) {
944 		*endchar = '?';
945 		count++;
946 	}
947 
948 	if (strlen(newstr) == count)
949 		return NULL;
950 	else
951 		return newstr;
952 }
953 
954 void
955 hal_util_hexdump (const void *mem, unsigned int size)
956 {
957 	unsigned int i;
958 	unsigned int j;
959 	unsigned int n;
960 	const char *buf = (const char *) mem;
961 
962 	n = 0;
963 	printf ("Dumping %d=0x%x bytes\n", size, size);
964 	while (n < size) {
965 
966 		printf ("0x%04x: ", n);
967 
968 		j = n;
969 		for (i = 0; i < 16; i++) {
970 			if (j >= size)
971 				break;
972 			printf ("%02x ", buf[j]);
973 			j++;
974 		}
975 
976 		for ( ; i < 16; i++) {
977 			printf ("   ");
978 		}
979 
980 		printf ("   ");
981 
982 		j = n;
983 		for (i = 0; i < 16; i++) {
984 			if (j >= size)
985 				break;
986 			printf ("%c", isprint(buf[j]) ? buf[j] : '.');
987 			j++;
988 		}
989 
990 		printf ("\n");
991 
992 		n += 16;
993 	}
994 }
995 
996 gboolean
997 hal_util_is_mounted_by_hald (const char *mount_point)
998 {
999 	int i;
1000 	FILE *hal_mtab;
1001 	int hal_mtab_len;
1002 	int num_read;
1003 	char *hal_mtab_buf;
1004 	char **lines;
1005 	gboolean found;
1006 
1007 	hal_mtab = NULL;
1008 	hal_mtab_buf = NULL;
1009 	found = FALSE;
1010 
1011 	/*HAL_DEBUG (("examining /media/.hal-mtab for %s", mount_point));*/
1012 
1013 	hal_mtab = fopen ("/media/.hal-mtab", "r");
1014 	if (hal_mtab == NULL) {
1015 		HAL_ERROR (("Cannot open /media/.hal-mtab"));
1016 		goto out;
1017 	}
1018 	if (fseek (hal_mtab, 0L, SEEK_END) != 0) {
1019 		HAL_ERROR (("Cannot seek to end of /media/.hal-mtab"));
1020 		goto out;
1021 	}
1022 	hal_mtab_len = ftell (hal_mtab);
1023 	if (hal_mtab_len < 0) {
1024 		HAL_ERROR (("Cannot determine size of /media/.hal-mtab"));
1025 		goto out;
1026 	}
1027 	rewind (hal_mtab);
1028 
1029 	hal_mtab_buf = g_new0 (char, hal_mtab_len + 1);
1030 	num_read = fread (hal_mtab_buf, 1, hal_mtab_len, hal_mtab);
1031 	if (num_read != hal_mtab_len) {
1032 		HAL_ERROR (("Cannot read from /media/.hal-mtab"));
1033 		goto out;
1034 	}
1035 	fclose (hal_mtab);
1036 	hal_mtab = NULL;
1037 
1038 	/*HAL_DEBUG (("hal_mtab = '%s'\n", hal_mtab_buf));*/
1039 
1040 	lines = g_strsplit (hal_mtab_buf, "\n", 0);
1041 	g_free (hal_mtab_buf);
1042 	hal_mtab_buf = NULL;
1043 
1044 	/* find the entry we're going to unmount */
1045 	for (i = 0; lines[i] != NULL && !found; i++) {
1046 		char **line_elements;
1047 
1048 		/*HAL_DEBUG ((" line = '%s'", lines[i]));*/
1049 
1050 		if ((lines[i])[0] == '#')
1051 			continue;
1052 
1053 		line_elements = g_strsplit (lines[i], "\t", 6);
1054 		if (g_strv_length (line_elements) == 6) {
1055 /*
1056 			HAL_DEBUG (("  devfile     = '%s'", line_elements[0]));
1057 			HAL_DEBUG (("  uid         = '%s'", line_elements[1]));
1058 			HAL_DEBUG (("  session id  = '%s'", line_elements[2]));
1059 			HAL_DEBUG (("  fs          = '%s'", line_elements[3]));
1060 			HAL_DEBUG (("  options     = '%s'", line_elements[4]));
1061 			HAL_DEBUG (("  mount_point = '%s'", line_elements[5]));
1062 			HAL_DEBUG (("  (comparing against '%s')", mount_point));
1063 */
1064 
1065 			if (strcmp (line_elements[5], mount_point) == 0) {
1066 				found = TRUE;
1067 				/*HAL_INFO (("device at '%s' is indeed mounted by HAL's Mount()", mount_point));*/
1068 			}
1069 
1070 		}
1071 
1072 		g_strfreev (line_elements);
1073 	}
1074 
1075 	g_strfreev (lines);
1076 
1077 out:
1078 	if (hal_mtab != NULL)
1079 		fclose (hal_mtab);
1080 	if (hal_mtab_buf != NULL)
1081 		g_free (hal_mtab_buf);
1082 
1083 	return found;
1084 }
1085 
1086 void
1087 hal_util_branch_claim (HalDeviceStore *store, HalDevice *root, dbus_bool_t claimed,
1088     const char *service, int uid)
1089 {
1090 	GSList *children;
1091 	GSList *i;
1092 	HalDevice *d;
1093 
1094 	if (claimed) {
1095 		hal_device_property_set_bool (root, "info.claimed", claimed);
1096 		hal_device_property_set_string (root, "info.claimed.service", service);
1097 		hal_device_property_set_int (root, "info.claimed.uid", uid);
1098 	} else {
1099 		hal_device_property_remove (root, "info.claimed");
1100 		hal_device_property_remove (root, "info.claimed.service");
1101 		hal_device_property_remove (root, "info.claimed.uid");
1102 	}
1103 
1104 
1105 	children = hal_device_store_match_multiple_key_value_string (store,
1106 	    "info.parent", root->udi);
1107 
1108 	for (i = children; i != NULL; i = g_slist_next (i)) {
1109 		d = HAL_DEVICE (i->data);
1110 		hal_util_branch_claim (store, d, claimed, service, uid);
1111 	}
1112 
1113 	g_slist_free (children);
1114 }
1115 
1116 /** Given an interface name, check if it is valid.
1117  *  @param  name	A given interface name
1118  *  @return		TRUE if name is valid, otherwise FALSE
1119  */
1120 gboolean
1121 is_valid_interface_name(const char *name) {
1122 
1123 	const char *end;
1124 	const char *last_dot;
1125 
1126 	last_dot = NULL;
1127 
1128 	if (strlen(name) == 0)
1129 		return FALSE;
1130 
1131 	end = name + strlen(name);
1132 
1133 	if (*name == '.')    /* disallow starting with a . */
1134 		return FALSE;
1135 	else if (!VALID_INITIAL_NAME_CHARACTER (*name))
1136 		return FALSE;
1137 	else
1138 		name++;
1139 
1140 	while (name != end) {
1141 		if (*name == '.') {
1142 			if ((name + 1) == end)
1143 				return FALSE;
1144 			else if (!VALID_INITIAL_NAME_CHARACTER (*(name + 1)))
1145 				return FALSE;
1146 			last_dot = name;
1147 			name++;    /* we just validated the next char, so skip two */
1148 		} else if (!VALID_NAME_CHARACTER (*name))
1149 			return FALSE;
1150 		name++;
1151 	}
1152 	if (last_dot == NULL)
1153 		return FALSE;
1154 
1155 	return TRUE;
1156 }
1157