]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/libudev/libudev-util.c
Merge pull request #10866 from yuwata/libudev-util-cleanups
[thirdparty/systemd.git] / src / libudev / libudev-util.c
1 /* SPDX-License-Identifier: LGPL-2.1+ */
2
3 #include <ctype.h>
4 #include <errno.h>
5
6 #include "sd-device.h"
7
8 #include "device-nodes.h"
9 #include "libudev-util.h"
10 #include "string-util.h"
11 #include "strxcpyx.h"
12 #include "utf8.h"
13
14 /**
15 * SECTION:libudev-util
16 * @short_description: utils
17 *
18 * Utilities useful when dealing with devices and device node names.
19 */
20
21 /* handle "[<SUBSYSTEM>/<KERNEL>]<attribute>" format */
22 int util_resolve_subsys_kernel(const char *string, char *result, size_t maxsize, bool read_value) {
23 char temp[UTIL_PATH_SIZE], *subsys, *sysname, *attr;
24 _cleanup_(sd_device_unrefp) sd_device *dev = NULL;
25 const char *val;
26 int r;
27
28 if (string[0] != '[')
29 return -EINVAL;
30
31 strscpy(temp, sizeof(temp), string);
32
33 subsys = &temp[1];
34
35 sysname = strchr(subsys, '/');
36 if (!sysname)
37 return -EINVAL;
38 sysname[0] = '\0';
39 sysname = &sysname[1];
40
41 attr = strchr(sysname, ']');
42 if (!attr)
43 return -EINVAL;
44 attr[0] = '\0';
45 attr = &attr[1];
46 if (attr[0] == '/')
47 attr = &attr[1];
48 if (attr[0] == '\0')
49 attr = NULL;
50
51 if (read_value && !attr)
52 return -EINVAL;
53
54 r = sd_device_new_from_subsystem_sysname(&dev, subsys, sysname);
55 if (r < 0)
56 return r;
57
58 if (read_value) {
59 r = sd_device_get_sysattr_value(dev, attr, &val);
60 if (r < 0 && r != -ENOENT)
61 return r;
62 if (r == -ENOENT)
63 result[0] = '\0';
64 else
65 strscpy(result, maxsize, val);
66 log_debug("value '[%s/%s]%s' is '%s'", subsys, sysname, attr, result);
67 } else {
68 r = sd_device_get_syspath(dev, &val);
69 if (r < 0)
70 return r;
71
72 strscpyl(result, maxsize, val, attr ? "/" : NULL, attr ?: NULL, NULL);
73 log_debug("path '[%s/%s]%s' is '%s'", subsys, sysname, strempty(attr), result);
74 }
75 return 0;
76 }
77
78 size_t util_path_encode(const char *src, char *dest, size_t size) {
79 size_t i, j;
80
81 assert(src);
82 assert(dest);
83
84 for (i = 0, j = 0; src[i] != '\0'; i++) {
85 if (src[i] == '/') {
86 if (j+4 >= size) {
87 j = 0;
88 break;
89 }
90 memcpy(&dest[j], "\\x2f", 4);
91 j += 4;
92 } else if (src[i] == '\\') {
93 if (j+4 >= size) {
94 j = 0;
95 break;
96 }
97 memcpy(&dest[j], "\\x5c", 4);
98 j += 4;
99 } else {
100 if (j+1 >= size) {
101 j = 0;
102 break;
103 }
104 dest[j] = src[i];
105 j++;
106 }
107 }
108 dest[j] = '\0';
109 return j;
110 }
111
112 /*
113 * Copy from 'str' to 'to', while removing all leading and trailing whitespace,
114 * and replacing each run of consecutive whitespace with a single underscore.
115 * The chars from 'str' are copied up to the \0 at the end of the string, or
116 * at most 'len' chars. This appends \0 to 'to', at the end of the copied
117 * characters.
118 *
119 * If 'len' chars are copied into 'to', the final \0 is placed at len+1
120 * (i.e. 'to[len] = \0'), so the 'to' buffer must have at least len+1
121 * chars available.
122 *
123 * Note this may be called with 'str' == 'to', i.e. to replace whitespace
124 * in-place in a buffer. This function can handle that situation.
125 */
126 size_t util_replace_whitespace(const char *str, char *to, size_t len) {
127 bool is_space = false;
128 const char *p = str;
129 size_t j;
130
131 assert(str);
132 assert(to);
133
134 p += strspn(p, WHITESPACE);
135
136 for (j = 0; j < len && *p != '\0'; p++) {
137 if (isspace(*p)) {
138 is_space = true;
139 continue;
140 }
141
142 if (is_space) {
143 if (j + 1 >= len)
144 break;
145
146 to[j++] = '_';
147 is_space = false;
148 }
149 to[j++] = *p;
150 }
151
152 to[j] = '\0';
153 return j;
154 }
155
156 /* allow chars in whitelist, plain ascii, hex-escaping and valid utf8 */
157 size_t util_replace_chars(char *str, const char *white) {
158 size_t i = 0, replaced = 0;
159
160 assert(str);
161
162 while (str[i] != '\0') {
163 int len;
164
165 if (whitelisted_char_for_devnode(str[i], white)) {
166 i++;
167 continue;
168 }
169
170 /* accept hex encoding */
171 if (str[i] == '\\' && str[i+1] == 'x') {
172 i += 2;
173 continue;
174 }
175
176 /* accept valid utf8 */
177 len = utf8_encoded_valid_unichar(&str[i]);
178 if (len > 1) {
179 i += len;
180 continue;
181 }
182
183 /* if space is allowed, replace whitespace with ordinary space */
184 if (isspace(str[i]) && white && strchr(white, ' ')) {
185 str[i] = ' ';
186 i++;
187 replaced++;
188 continue;
189 }
190
191 /* everything else is replaced with '_' */
192 str[i] = '_';
193 i++;
194 replaced++;
195 }
196 return replaced;
197 }
198
199 /**
200 * udev_util_encode_string:
201 * @str: input string to be encoded
202 * @str_enc: output string to store the encoded input string
203 * @len: maximum size of the output string, which may be
204 * four times as long as the input string
205 *
206 * Encode all potentially unsafe characters of a string to the
207 * corresponding 2 char hex value prefixed by '\x'.
208 *
209 * Returns: 0 if the entire string was copied, non-zero otherwise.
210 **/
211 _public_ int udev_util_encode_string(const char *str, char *str_enc, size_t len) {
212 return encode_devnode_name(str, str_enc, len);
213 }