1 /***************************************************************************
4 * util.c - Various utilities
6 * Copyright (C) 2004 David Zeuthen, <david@fubar.dk>
8 * Licensed under the Academic Free License version 2.1
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.
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.
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
24 **************************************************************************/
45 #include <dbus/dbus.h>
46 #include <dbus/dbus-glib.h>
51 #include "hald_runner.h"
52 #include "hald_dbus.h"
53 #include "device_info.h"
57 /** Determine whether the given character is valid as the first character
60 #define VALID_INITIAL_NAME_CHARACTER(c) \
61 (((c) >= 'A' && (c) <= 'Z') || \
62 ((c) >= 'a' && (c) <= 'z') || \
65 /** Determine whether the given character is valid as a second or later
66 * character in a name.
68 #define VALID_NAME_CHARACTER(c) \
69 (((c) >= '0' && (c) <= '9') || \
70 ((c) >= 'A' && (c) <= 'Z') || \
71 ((c) >= 'a' && (c) <= 'z') || \
75 hal_util_remove_trailing_slash (gchar
*path
)
83 c
= strrchr (path
, '/');
85 HAL_WARNING (("Invalid path %s", path
));
94 /** Given a path, /foo/bar/bat/foobar, return the last element, e.g.
98 * @return Pointer into given string
101 hal_util_get_last_element (const gchar
*s
)
107 for (p
= s
+ len
- 1; p
> s
; --p
) {
115 /** Given a path, this functions finds the path representing the
116 * parent directory by truncation.
119 * @return Path for parent or NULL. Must be freed by caller
122 hal_util_get_parent_path (const gchar
*path
)
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';
134 parent_path
[i
] = '\0';
140 hal_util_get_normalized_path (const gchar
*path1
, const gchar
*path2
)
146 gchar buf
[HAL_PATH_MAX
];
148 len1
= strlen (path1
);
149 len2
= strlen (path2
);
154 while (p2
< path2
+ len2
&& strncmp (p2
, "../", 3) == 0) {
157 while (p1
>= path1
&& *(--p1
)!='/')
161 if ((p1
-path1
) < 0) {
162 HAL_ERROR (("Could not normalize '%s' and '%s', return 'NULL'", path1
, path2
));
166 strncpy (buf
, path1
, (p1
-path1
));
167 buf
[p1
-path1
] = '\0';
169 return g_strdup_printf ("%s/%s", buf
, p2
);
173 hal_util_get_int_from_file (const gchar
*directory
, const gchar
*file
, gint
*result
, gint base
)
177 gchar path
[HAL_PATH_MAX
];
183 g_snprintf (path
, sizeof (path
), "%s/%s", directory
, file
);
185 f
= fopen (path
, "rb");
187 HAL_ERROR (("Cannot open '%s'", path
));
191 if (fgets (buf
, sizeof (buf
), f
) == NULL
) {
192 HAL_ERROR (("Cannot read from '%s'", path
));
196 /* TODO: handle error condition */
197 *result
= strtol (buf
, NULL
, base
);
208 hal_util_set_int_from_file (HalDevice
*d
, const gchar
*key
, const gchar
*directory
, const gchar
*file
, gint base
)
215 if (hal_util_get_int_from_file (directory
, file
, &value
, base
))
216 ret
= hal_device_property_set_int (d
, key
, value
);
223 hal_util_get_uint64_from_file (const gchar
*directory
, const gchar
*file
, guint64
*result
, gint base
)
227 gchar path
[HAL_PATH_MAX
];
233 g_snprintf (path
, sizeof (path
), "%s/%s", directory
, file
);
235 f
= fopen (path
, "rb");
237 HAL_ERROR (("Cannot open '%s'", path
));
241 if (fgets (buf
, sizeof (buf
), f
) == NULL
) {
242 HAL_ERROR (("Cannot read from '%s'", path
));
246 /* TODO: handle error condition */
247 *result
= strtoll (buf
, NULL
, base
);
259 hal_util_set_uint64_from_file (HalDevice
*d
, const gchar
*key
, const gchar
*directory
, const gchar
*file
, gint base
)
266 if (hal_util_get_uint64_from_file (directory
, file
, &value
, base
))
267 ret
= hal_device_property_set_uint64 (d
, key
, value
);
273 hal_util_get_bcd2_from_file (const gchar
*directory
, const gchar
*file
, gint
*result
)
277 gchar path
[HAL_PATH_MAX
];
281 gboolean passed_white_space
;
290 g_snprintf (path
, sizeof (path
), "%s/%s", directory
, file
);
292 f
= fopen (path
, "rb");
294 HAL_ERROR (("Cannot open '%s'", path
));
298 if (fgets (buf
, sizeof (buf
), f
) == NULL
) {
299 HAL_ERROR (("Cannot read from '%s'", path
));
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
)
313 passed_white_space
= TRUE
;
316 digit
= (int) (c
- '0');
322 for (; i
< len
; i
++) {
323 if (g_ascii_isspace (buf
[i
]))
325 if (num_prec
== 2) /* Only care about two digits
330 digit
= (int) (c
- '0');
335 for (; num_prec
< 2; num_prec
++)
338 *result
= left
* 256 + (right
& 255);
349 hal_util_set_bcd2_from_file (HalDevice
*d
, const gchar
*key
, const gchar
*directory
, const gchar
*file
)
356 if (hal_util_get_bcd2_from_file (directory
, file
, &value
))
357 ret
= hal_device_property_set_int (d
, key
, value
);
363 hal_util_get_string_from_file (const gchar
*directory
, const gchar
*file
)
366 static gchar buf
[256];
367 gchar path
[HAL_PATH_MAX
];
375 g_snprintf (path
, sizeof (path
), "%s/%s", directory
, file
);
377 f
= fopen (path
, "rb");
379 HAL_ERROR (("Cannot open '%s'", path
));
384 if (fgets (buf
, sizeof (buf
), f
) == NULL
) {
385 HAL_ERROR (("Cannot read from '%s'", path
));
393 /* Clear remaining whitespace */
394 for (i
= len
- 2; i
>= 0; --i
) {
395 if (!g_ascii_isspace (buf
[i
]))
410 hal_util_set_string_from_file (HalDevice
*d
, const gchar
*key
, const gchar
*directory
, const gchar
*file
)
417 if ((buf
= hal_util_get_string_from_file (directory
, file
)) != NULL
)
418 ret
= hal_device_property_set_string (d
, key
, buf
);
424 hal_util_compute_udi (HalDeviceStore
*store
, gchar
*dst
, gsize dstsize
, const gchar
*format
, ...)
430 va_start (args
, format
);
431 g_vsnprintf (buf
, sizeof (buf
), format
, args
);
436 "abcdefghijklmnopqrstuvwxyz"
437 "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
440 g_strlcpy (dst
, buf
, dstsize
);
441 if (hal_device_store_find (store
, dst
) == NULL
)
445 g_snprintf (dst
, dstsize
, "%s_%d", buf
, i
);
446 if (hal_device_store_find (store
, dst
) == NULL
)
456 hal_util_path_ascend (gchar
*path
)
463 p
= strrchr (path
, '/');
471 static gboolean _grep_can_reuse
= FALSE
;
474 hal_util_grep_discard_existing_data (void)
476 _grep_can_reuse
= FALSE
;
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.
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.
497 hal_util_grep_file (const gchar
*directory
, const gchar
*file
, const gchar
*linestart
, gboolean reuse
)
499 static gchar buf
[2048];
500 static unsigned int bufsize
;
501 static gchar filename
[HAL_PATH_MAX
];
502 static gchar oldfilename
[HAL_PATH_MAX
];
509 /* TODO: use reuse and _grep_can_reuse parameters to avoid loading
510 * the file again and again
513 if (file
!= NULL
&& strlen (file
) > 0)
514 snprintf (filename
, sizeof (filename
), "%s/%s", directory
, file
);
516 strncpy (filename
, directory
, sizeof (filename
));
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));*/
524 f
= fopen (filename
, "r");
527 bufsize
= fread (buf
, sizeof (char), sizeof (buf
) - 1, f
);
531 /*HAL_INFO (("hal_util_grep_file: read %s of %d bytes", filename, bufsize));*/
535 _grep_can_reuse
= TRUE
;
536 strncpy (oldfilename
, filename
, sizeof(oldfilename
));
538 linestart_len
= strlen (linestart
);
543 unsigned int linelen
;
544 static char line
[256];
546 for (linelen
= 0; p
[linelen
] != '\n' && p
[linelen
] != '\0'; linelen
++)
549 if (linelen
< sizeof (line
)) {
551 strncpy (line
, p
, linelen
);
552 line
[linelen
] = '\0';
554 if (strncmp (line
, linestart
, linestart_len
) == 0) {
555 result
= line
+ linestart_len
;
562 } while (p
< buf
+ bufsize
);
569 hal_util_grep_string_elem_from_file (const gchar
*directory
, const gchar
*file
,
570 const gchar
*linestart
, guint elem
, gboolean reuse
)
574 static gchar buf
[256];
581 if (((line
= hal_util_grep_file (directory
, file
, linestart
, reuse
)) == NULL
) || (strlen (line
) == 0))
584 tokens
= g_strsplit_set (line
, " \t:", 0);
585 for (i
= 0, j
= 0; tokens
[i
] != NULL
; i
++) {
586 if (strlen (tokens
[i
]) == 0)
589 strncpy (buf
, tokens
[i
], sizeof (buf
));
604 hal_util_grep_int_elem_from_file (const gchar
*directory
, const gchar
*file
,
605 const gchar
*linestart
, guint elem
, guint base
, gboolean reuse
)
613 strvalue
= hal_util_grep_string_elem_from_file (directory
, file
, linestart
, elem
, reuse
);
614 if (strvalue
== NULL
)
617 value
= strtol (strvalue
, &endptr
, base
);
618 if (endptr
== strvalue
) {
627 /** Get a string value from a formatted text file and assign it to
628 * a property on a device object.
630 * Example: Given that the file /proc/acpi/battery/BAT0/info contains
633 * "design voltage: 10800 mV"
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.
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
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
)
659 if ((value
= hal_util_grep_string_elem_from_file (directory
, file
, linestart
, elem
, reuse
)) == NULL
)
662 res
= hal_device_property_set_string (d
, key
, value
);
667 /** Get an integer value from a formatted text file and assign it to
668 * a property on a device object.
670 * Example: Given that the file /proc/acpi/battery/BAT0/info contains
673 * "design voltage: 10800 mV"
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.
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
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
)
701 strvalue
= hal_util_grep_string_elem_from_file (directory
, file
, linestart
, elem
, reuse
);
702 if (strvalue
== NULL
)
705 value
= strtol (strvalue
, &endptr
, base
);
706 if (endptr
== strvalue
)
709 res
= hal_device_property_set_int (d
, key
, value
);
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
720 * Example: Given that the file /proc/acpi/battery/BAT0/info contains
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.
729 * If, instead, the line was
733 * the value assigned will be FALSE.
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
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
)
759 if (((line
= hal_util_grep_file (directory
, file
, linestart
, reuse
)) == NULL
) || (strlen (line
) == 0))
762 tokens
= g_strsplit_set (line
, " \t:", 0);
764 for (i
= 0, j
= 0; tokens
[i
] != NULL
; i
++) {
765 if (strlen (tokens
[i
]) == 0)
768 hal_device_property_set_bool (d
, key
, strcmp (tokens
[i
], expected
) == 0);
784 hal_util_dup_strv_from_g_slist (GSList
*strlist
)
791 len
= g_slist_length (strlist
);
792 strv
= g_new (char *, len
+ 1);
794 for (i
= strlist
, j
= 0; i
!= NULL
; i
= g_slist_next (i
), j
++) {
795 strv
[j
] = g_strdup ((const gchar
*) i
->data
);
802 /* -------------------------------------------------------------------------------------------------------------- */
810 HalCalloutsDone callback
;
816 static void callout_do_next (Callout
*c
);
819 callout_terminated (HalDevice
*d
, guint32 exit_type
,
820 gint return_code
, gchar
**error
,
821 gpointer data1
, gpointer data2
)
825 c
= (Callout
*) data1
;
830 callout_do_next (Callout
*c
)
833 /* Check if we're done */
834 if (c
->programs
[c
->next_program
] == NULL
) {
838 HalCalloutsDone callback
;
841 userdata1
= c
->userdata1
;
842 userdata2
= c
->userdata2
;
843 callback
= c
->callback
;
845 g_strfreev (c
->programs
);
846 g_strfreev (c
->extra_env
);
849 callback (d
, userdata1
, userdata2
);
852 hald_runner_run(c
->d
, c
->programs
[c
->next_program
], c
->extra_env
,
853 HAL_HELPER_TIMEOUT
, callout_terminated
,
860 hal_callout_device (HalDevice
*d
, HalCalloutsDone callback
, gpointer userdata1
, gpointer userdata2
,
861 GSList
*programs
, gchar
**extra_env
)
865 c
= g_new0 (Callout
, 1);
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
);
878 hal_util_callout_device_add (HalDevice
*d
, HalCalloutsDone callback
, gpointer userdata1
, gpointer userdata2
)
881 gchar
*extra_env
[2] = {"HALD_ACTION=add", NULL
};
883 if ((programs
= hal_device_property_get_strlist (d
, "info.callouts.add")) == NULL
) {
884 callback (d
, userdata1
, userdata2
);
888 HAL_INFO (("Add callouts for udi=%s", d
->udi
));
890 hal_callout_device (d
, callback
, userdata1
, userdata2
, programs
, extra_env
);
896 hal_util_callout_device_remove (HalDevice
*d
, HalCalloutsDone callback
, gpointer userdata1
, gpointer userdata2
)
899 gchar
*extra_env
[2] = {"HALD_ACTION=remove", NULL
};
901 if ((programs
= hal_device_property_get_strlist (d
, "info.callouts.remove")) == NULL
) {
902 callback (d
, userdata1
, userdata2
);
906 HAL_INFO (("Remove callouts for udi=%s", d
->udi
));
908 hal_callout_device (d
, callback
, userdata1
, userdata2
, programs
, extra_env
);
914 hal_util_callout_device_preprobe (HalDevice
*d
, HalCalloutsDone callback
, gpointer userdata1
, gpointer userdata2
)
917 gchar
*extra_env
[2] = {"HALD_ACTION=preprobe", NULL
};
919 if ((programs
= hal_device_property_get_strlist (d
, "info.callouts.preprobe")) == NULL
) {
920 callback (d
, userdata1
, userdata2
);
924 HAL_INFO (("Preprobe callouts for udi=%s", d
->udi
));
926 hal_callout_device (d
, callback
, userdata1
, userdata2
, programs
, extra_env
);
932 hal_util_strdup_valid_utf8 (const char *str
)
936 unsigned int count
= 0;
941 newstr
= g_strdup (str
);
943 while (!g_utf8_validate (newstr
, -1, (const char **) &endchar
)) {
948 if (strlen(newstr
) == count
)
955 hal_util_hexdump (const void *mem
, unsigned int size
)
960 const char *buf
= (const char *) mem
;
963 printf ("Dumping %d=0x%x bytes\n", size
, size
);
966 printf ("0x%04x: ", n
);
969 for (i
= 0; i
< 16; i
++) {
972 printf ("%02x ", buf
[j
]);
976 for ( ; i
< 16; i
++) {
983 for (i
= 0; i
< 16; i
++) {
986 printf ("%c", isprint(buf
[j
]) ? buf
[j
] : '.');
997 hal_util_is_mounted_by_hald (const char *mount_point
)
1008 hal_mtab_buf
= NULL
;
1011 /*HAL_DEBUG (("examining /media/.hal-mtab for %s", mount_point));*/
1013 hal_mtab
= fopen ("/media/.hal-mtab", "r");
1014 if (hal_mtab
== NULL
) {
1015 HAL_ERROR (("Cannot open /media/.hal-mtab"));
1018 if (fseek (hal_mtab
, 0L, SEEK_END
) != 0) {
1019 HAL_ERROR (("Cannot seek to end of /media/.hal-mtab"));
1022 hal_mtab_len
= ftell (hal_mtab
);
1023 if (hal_mtab_len
< 0) {
1024 HAL_ERROR (("Cannot determine size of /media/.hal-mtab"));
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"));
1038 /*HAL_DEBUG (("hal_mtab = '%s'\n", hal_mtab_buf));*/
1040 lines
= g_strsplit (hal_mtab_buf
, "\n", 0);
1041 g_free (hal_mtab_buf
);
1042 hal_mtab_buf
= NULL
;
1044 /* find the entry we're going to unmount */
1045 for (i
= 0; lines
[i
] != NULL
&& !found
; i
++) {
1046 char **line_elements
;
1048 /*HAL_DEBUG ((" line = '%s'", lines[i]));*/
1050 if ((lines
[i
])[0] == '#')
1053 line_elements
= g_strsplit (lines
[i
], "\t", 6);
1054 if (g_strv_length (line_elements
) == 6) {
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));
1065 if (strcmp (line_elements
[5], mount_point
) == 0) {
1067 /*HAL_INFO (("device at '%s' is indeed mounted by HAL's Mount()", mount_point));*/
1072 g_strfreev (line_elements
);
1078 if (hal_mtab
!= NULL
)
1080 if (hal_mtab_buf
!= NULL
)
1081 g_free (hal_mtab_buf
);
1087 hal_util_branch_claim (HalDeviceStore
*store
, HalDevice
*root
, dbus_bool_t claimed
,
1088 const char *service
, int uid
)
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
);
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");
1105 children
= hal_device_store_match_multiple_key_value_string (store
,
1106 "info.parent", root
->udi
);
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
);
1113 g_slist_free (children
);
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
1121 is_valid_interface_name(const char *name
) {
1124 const char *last_dot
;
1128 if (strlen(name
) == 0)
1131 end
= name
+ strlen(name
);
1133 if (*name
== '.') /* disallow starting with a . */
1135 else if (!VALID_INITIAL_NAME_CHARACTER (*name
))
1140 while (name
!= end
) {
1142 if ((name
+ 1) == end
)
1144 else if (!VALID_INITIAL_NAME_CHARACTER (*(name
+ 1)))
1147 name
++; /* we just validated the next char, so skip two */
1148 } else if (!VALID_NAME_CHARACTER (*name
))
1152 if (last_dot
== NULL
)