]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/libudev/libudev-util.c
Merge pull request #11827 from keszybz/pkgconfig-variables
[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 * Note that only 'len' characters are read from 'str'.
127 */
128 size_t util_replace_whitespace(const char *str, char *to, size_t len) {
129 bool is_space = false;
130 size_t i, j;
131
132 assert(str);
133 assert(to);
134
135 i = strspn(str, WHITESPACE);
136
137 for (j = 0; j < len && i < len && str[i] != '\0'; i++) {
138 if (isspace(str[i])) {
139 is_space = true;
140 continue;
141 }
142
143 if (is_space) {
144 if (j + 1 >= len)
145 break;
146
147 to[j++] = '_';
148 is_space = false;
149 }
150 to[j++] = str[i];
151 }
152
153 to[j] = '\0';
154 return j;
155 }
156
157 /* allow chars in whitelist, plain ascii, hex-escaping and valid utf8 */
158 size_t util_replace_chars(char *str, const char *white) {
159 size_t i = 0, replaced = 0;
160
161 assert(str);
162
163 while (str[i] != '\0') {
164 int len;
165
166 if (whitelisted_char_for_devnode(str[i], white)) {
167 i++;
168 continue;
169 }
170
171 /* accept hex encoding */
172 if (str[i] == '\\' && str[i+1] == 'x') {
173 i += 2;
174 continue;
175 }
176
177 /* accept valid utf8 */
178 len = utf8_encoded_valid_unichar(&str[i]);
179 if (len > 1) {
180 i += len;
181 continue;
182 }
183
184 /* if space is allowed, replace whitespace with ordinary space */
185 if (isspace(str[i]) && white && strchr(white, ' ')) {
186 str[i] = ' ';
187 i++;
188 replaced++;
189 continue;
190 }
191
192 /* everything else is replaced with '_' */
193 str[i] = '_';
194 i++;
195 replaced++;
196 }
197 return replaced;
198 }
199
200 /**
201 * udev_util_encode_string:
202 * @str: input string to be encoded
203 * @str_enc: output string to store the encoded input string
204 * @len: maximum size of the output string, which may be
205 * four times as long as the input string
206 *
207 * Encode all potentially unsafe characters of a string to the
208 * corresponding 2 char hex value prefixed by '\x'.
209 *
210 * Returns: 0 if the entire string was copied, non-zero otherwise.
211 **/
212 _public_ int udev_util_encode_string(const char *str, char *str_enc, size_t len) {
213 return encode_devnode_name(str, str_enc, len);
214 }