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