summaryrefslogtreecommitdiff
path: root/src/shared/udev-util.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/shared/udev-util.c')
-rw-r--r--src/shared/udev-util.c173
1 files changed, 173 insertions, 0 deletions
diff --git a/src/shared/udev-util.c b/src/shared/udev-util.c
index 411b1f704b..df08e3a40e 100644
--- a/src/shared/udev-util.c
+++ b/src/shared/udev-util.c
@@ -1,12 +1,16 @@
/* SPDX-License-Identifier: LGPL-2.1-or-later */
+#include <ctype.h>
#include <errno.h>
+#include <sys/inotify.h>
#include <unistd.h>
#include "alloc-util.h"
+#include "device-nodes.h"
#include "device-util.h"
#include "env-file.h"
#include "escape.h"
+#include "fd-util.h"
#include "log.h"
#include "macro.h"
#include "parse-util.h"
@@ -14,6 +18,7 @@
#include "signal-util.h"
#include "string-table.h"
#include "string-util.h"
+#include "strxcpyx.h"
#include "udev-util.h"
#include "utf8.h"
@@ -383,3 +388,171 @@ int udev_rule_parse_value(char *str, char **ret_value, char **ret_endpos) {
*ret_endpos = i + 1;
return 0;
}
+
+size_t udev_replace_whitespace(const char *str, char *to, size_t len) {
+ bool is_space = false;
+ size_t i, j;
+
+ assert(str);
+ assert(to);
+
+ /* Copy from 'str' to 'to', while removing all leading and trailing whitespace, and replacing
+ * each run of consecutive whitespace with a single underscore. The chars from 'str' are copied
+ * up to the \0 at the end of the string, or at most 'len' chars. This appends \0 to 'to', at
+ * the end of the copied characters.
+ *
+ * If 'len' chars are copied into 'to', the final \0 is placed at len+1 (i.e. 'to[len] = \0'),
+ * so the 'to' buffer must have at least len+1 chars available.
+ *
+ * Note this may be called with 'str' == 'to', i.e. to replace whitespace in-place in a buffer.
+ * This function can handle that situation.
+ *
+ * Note that only 'len' characters are read from 'str'. */
+
+ i = strspn(str, WHITESPACE);
+
+ for (j = 0; j < len && i < len && str[i] != '\0'; i++) {
+ if (isspace(str[i])) {
+ is_space = true;
+ continue;
+ }
+
+ if (is_space) {
+ if (j + 1 >= len)
+ break;
+
+ to[j++] = '_';
+ is_space = false;
+ }
+ to[j++] = str[i];
+ }
+
+ to[j] = '\0';
+ return j;
+}
+
+size_t udev_replace_chars(char *str, const char *allow) {
+ size_t i = 0, replaced = 0;
+
+ assert(str);
+
+ /* allow chars in allow list, plain ascii, hex-escaping and valid utf8. */
+
+ while (str[i] != '\0') {
+ int len;
+
+ if (allow_listed_char_for_devnode(str[i], allow)) {
+ i++;
+ continue;
+ }
+
+ /* accept hex encoding */
+ if (str[i] == '\\' && str[i+1] == 'x') {
+ i += 2;
+ continue;
+ }
+
+ /* accept valid utf8 */
+ len = utf8_encoded_valid_unichar(str + i, (size_t) -1);
+ if (len > 1) {
+ i += len;
+ continue;
+ }
+
+ /* if space is allowed, replace whitespace with ordinary space */
+ if (isspace(str[i]) && allow && strchr(allow, ' ')) {
+ str[i] = ' ';
+ i++;
+ replaced++;
+ continue;
+ }
+
+ /* everything else is replaced with '_' */
+ str[i] = '_';
+ i++;
+ replaced++;
+ }
+ return replaced;
+}
+
+int udev_resolve_subsys_kernel(const char *string, char *result, size_t maxsize, bool read_value) {
+ _cleanup_(sd_device_unrefp) sd_device *dev = NULL;
+ _cleanup_free_ char *temp = NULL;
+ char *subsys, *sysname, *attr;
+ const char *val;
+ int r;
+
+ assert(string);
+ assert(result);
+
+ /* handle "[<SUBSYSTEM>/<KERNEL>]<attribute>" format */
+
+ if (string[0] != '[')
+ return -EINVAL;
+
+ temp = strdup(string);
+ if (!temp)
+ return -ENOMEM;
+
+ subsys = &temp[1];
+
+ sysname = strchr(subsys, '/');
+ if (!sysname)
+ return -EINVAL;
+ sysname[0] = '\0';
+ sysname = &sysname[1];
+
+ attr = strchr(sysname, ']');
+ if (!attr)
+ return -EINVAL;
+ attr[0] = '\0';
+ attr = &attr[1];
+ if (attr[0] == '/')
+ attr = &attr[1];
+ if (attr[0] == '\0')
+ attr = NULL;
+
+ if (read_value && !attr)
+ return -EINVAL;
+
+ r = sd_device_new_from_subsystem_sysname(&dev, subsys, sysname);
+ if (r < 0)
+ return r;
+
+ if (read_value) {
+ r = sd_device_get_sysattr_value(dev, attr, &val);
+ if (r < 0 && r != -ENOENT)
+ return r;
+ if (r == -ENOENT)
+ result[0] = '\0';
+ else
+ strscpy(result, maxsize, val);
+ log_debug("value '[%s/%s]%s' is '%s'", subsys, sysname, attr, result);
+ } else {
+ r = sd_device_get_syspath(dev, &val);
+ if (r < 0)
+ return r;
+
+ strscpyl(result, maxsize, val, attr ? "/" : NULL, attr ?: NULL, NULL);
+ log_debug("path '[%s/%s]%s' is '%s'", subsys, sysname, strempty(attr), result);
+ }
+ return 0;
+}
+
+int udev_queue_is_empty(void) {
+ return access("/run/udev/queue", F_OK) < 0 ?
+ (errno == ENOENT ? true : -errno) : false;
+}
+
+int udev_queue_init(void) {
+ _cleanup_close_ int fd = -1;
+
+ fd = inotify_init1(IN_CLOEXEC);
+ if (fd < 0)
+ return -errno;
+
+ if (inotify_add_watch(fd, "/run/udev" , IN_DELETE) < 0)
+ return -errno;
+
+ return TAKE_FD(fd);
+}