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