chiark / gitweb /
libudev: device - lookup "subsystem" and "driver" only once
[elogind.git] / udev / lib / libudev-util.c
1 /*
2  * libudev - interface to udev device information
3  *
4  * Copyright (C) 2008 Kay Sievers <kay.sievers@vrfy.org>
5  *
6  * This program is free software: you can redistribute it and/or modify
7  * it under the terms of the GNU General Public License as published by
8  * the Free Software Foundation, either version 2 of the License, or
9  * (at your option) any later version.
10  *
11  * This program is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  * GNU General Public License for more details.
15  *
16  * You should have received a copy of the GNU General Public License
17  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
18  */
19
20 #include <stdio.h>
21 #include <stdlib.h>
22 #include <stddef.h>
23 #include <unistd.h>
24 #include <errno.h>
25 #include <string.h>
26 #include <dirent.h>
27 #include <ctype.h>
28 #include <fcntl.h>
29 #include <sys/stat.h>
30
31 #include "libudev.h"
32 #include "libudev-private.h"
33
34 static ssize_t get_sys_link(struct udev *udev, const char *slink, const char *syspath, char *value, size_t size)
35 {
36         char path[UTIL_PATH_SIZE];
37         ssize_t len;
38         const char *pos;
39
40         util_strlcpy(path, syspath, sizeof(path));
41         util_strlcat(path, "/", sizeof(path));
42         util_strlcat(path, slink, sizeof(path));
43         len = readlink(path, path, sizeof(path));
44         if (len < 0 || len >= (ssize_t) sizeof(path))
45                 return -1;
46         path[len] = '\0';
47         pos = strrchr(path, '/');
48         if (pos == NULL)
49                 return -1;
50         pos = &pos[1];
51         info(udev, "resolved link to: '%s'\n", pos);
52         return util_strlcpy(value, pos, size);
53 }
54
55 ssize_t util_get_sys_subsystem(struct udev *udev, const char *syspath, char *subsystem, size_t size)
56 {
57         return get_sys_link(udev, "subsystem", syspath, subsystem, size);
58 }
59
60 ssize_t util_get_sys_driver(struct udev *udev, const char *syspath, char *driver, size_t size)
61 {
62         return get_sys_link(udev, "driver", syspath, driver, size);
63 }
64
65 int util_resolve_sys_link(struct udev *udev, char *syspath, size_t size)
66 {
67         char link_target[UTIL_PATH_SIZE];
68
69         int len;
70         int i;
71         int back;
72
73         len = readlink(syspath, link_target, sizeof(link_target));
74         if (len <= 0)
75                 return -1;
76         link_target[len] = '\0';
77         dbg(udev, "path link '%s' points to '%s'\n", syspath, link_target);
78
79         for (back = 0; strncmp(&link_target[back * 3], "../", 3) == 0; back++)
80                 ;
81         dbg(udev, "base '%s', tail '%s', back %i\n", syspath, &link_target[back * 3], back);
82         for (i = 0; i <= back; i++) {
83                 char *pos = strrchr(syspath, '/');
84
85                 if (pos == NULL)
86                         return -1;
87                 pos[0] = '\0';
88         }
89         dbg(udev, "after moving back '%s'\n", syspath);
90         util_strlcat(syspath, "/", size);
91         util_strlcat(syspath, &link_target[back * 3], size);
92         return 0;
93 }
94
95 int util_log_priority(const char *priority)
96 {
97         char *endptr;
98         int prio;
99
100         prio = strtol(priority, &endptr, 10);
101         if (endptr[0] == '\0')
102                 return prio;
103         if (strncasecmp(priority, "err", 3) == 0)
104                 return LOG_ERR;
105         if (strcasecmp(priority, "info") == 0)
106                 return LOG_INFO;
107         if (strcasecmp(priority, "debug") == 0)
108                 return LOG_DEBUG;
109         return 0;
110 }
111
112 size_t util_path_encode(char *s, size_t len)
113 {
114         char t[(len * 3)+1];
115         size_t i, j;
116
117         t[0] = '\0';
118         for (i = 0, j = 0; s[i] != '\0'; i++) {
119                 if (s[i] == '/') {
120                         memcpy(&t[j], "\\x2f", 4);
121                         j += 4;
122                 } else if (s[i] == '\\') {
123                         memcpy(&t[j], "\\x5c", 4);
124                         j += 4;
125                 } else {
126                         t[j] = s[i];
127                         j++;
128                 }
129         }
130         t[j] = '\0';
131         strncpy(s, t, len);
132         return j;
133 }
134
135 size_t util_path_decode(char *s)
136 {
137         size_t i, j;
138
139         for (i = 0, j = 0; s[i] != '\0'; j++) {
140                 if (memcmp(&s[i], "\\x2f", 4) == 0) {
141                         s[j] = '/';
142                         i += 4;
143                 }else if (memcmp(&s[i], "\\x5c", 4) == 0) {
144                         s[j] = '\\';
145                         i += 4;
146                 } else {
147                         s[j] = s[i];
148                         i++;
149                 }
150         }
151         s[j] = '\0';
152         return j;
153 }
154
155 void util_remove_trailing_chars(char *path, char c)
156 {
157         size_t len;
158
159         if (path == NULL)
160                 return;
161         len = strlen(path);
162         while (len > 0 && path[len-1] == c)
163                 path[--len] = '\0';
164 }
165
166 size_t util_strlcpy(char *dst, const char *src, size_t size)
167 {
168         size_t bytes = 0;
169         char *q = dst;
170         const char *p = src;
171         char ch;
172
173         while ((ch = *p++)) {
174                 if (bytes+1 < size)
175                         *q++ = ch;
176                 bytes++;
177         }
178
179         /* If size == 0 there is no space for a final null... */
180         if (size)
181                 *q = '\0';
182         return bytes;
183 }
184
185 size_t util_strlcat(char *dst, const char *src, size_t size)
186 {
187         size_t bytes = 0;
188         char *q = dst;
189         const char *p = src;
190         char ch;
191
192         while (bytes < size && *q) {
193                 q++;
194                 bytes++;
195         }
196         if (bytes == size)
197                 return (bytes + strlen(src));
198
199         while ((ch = *p++)) {
200                 if (bytes+1 < size)
201                 *q++ = ch;
202                 bytes++;
203         }
204
205         *q = '\0';
206         return bytes;
207 }
208
209 /* count of characters used to encode one unicode char */
210 static int utf8_encoded_expected_len(const char *str)
211 {
212         unsigned char c = (unsigned char)str[0];
213
214         if (c < 0x80)
215                 return 1;
216         if ((c & 0xe0) == 0xc0)
217                 return 2;
218         if ((c & 0xf0) == 0xe0)
219                 return 3;
220         if ((c & 0xf8) == 0xf0)
221                 return 4;
222         if ((c & 0xfc) == 0xf8)
223                 return 5;
224         if ((c & 0xfe) == 0xfc)
225                 return 6;
226         return 0;
227 }
228
229 /* decode one unicode char */
230 static int utf8_encoded_to_unichar(const char *str)
231 {
232         int unichar;
233         int len;
234         int i;
235
236         len = utf8_encoded_expected_len(str);
237         switch (len) {
238         case 1:
239                 return (int)str[0];
240         case 2:
241                 unichar = str[0] & 0x1f;
242                 break;
243         case 3:
244                 unichar = (int)str[0] & 0x0f;
245                 break;
246         case 4:
247                 unichar = (int)str[0] & 0x07;
248                 break;
249         case 5:
250                 unichar = (int)str[0] & 0x03;
251                 break;
252         case 6:
253                 unichar = (int)str[0] & 0x01;
254                 break;
255         default:
256                 return -1;
257         }
258
259         for (i = 1; i < len; i++) {
260                 if (((int)str[i] & 0xc0) != 0x80)
261                         return -1;
262                 unichar <<= 6;
263                 unichar |= (int)str[i] & 0x3f;
264         }
265
266         return unichar;
267 }
268
269 /* expected size used to encode one unicode char */
270 static int utf8_unichar_to_encoded_len(int unichar)
271 {
272         if (unichar < 0x80)
273                 return 1;
274         if (unichar < 0x800)
275                 return 2;
276         if (unichar < 0x10000)
277                 return 3;
278         if (unichar < 0x200000)
279                 return 4;
280         if (unichar < 0x4000000)
281                 return 5;
282         return 6;
283 }
284
285 /* check if unicode char has a valid numeric range */
286 static int utf8_unichar_valid_range(int unichar)
287 {
288         if (unichar > 0x10ffff)
289                 return 0;
290         if ((unichar & 0xfffff800) == 0xd800)
291                 return 0;
292         if ((unichar > 0xfdcf) && (unichar < 0xfdf0))
293                 return 0;
294         if ((unichar & 0xffff) == 0xffff)
295                 return 0;
296         return 1;
297 }
298
299 /* validate one encoded unicode char and return its length */
300 static int utf8_encoded_valid_unichar(const char *str)
301 {
302         int len;
303         int unichar;
304         int i;
305
306         len = utf8_encoded_expected_len(str);
307         if (len == 0)
308                 return -1;
309
310         /* ascii is valid */
311         if (len == 1)
312                 return 1;
313
314         /* check if expected encoded chars are available */
315         for (i = 0; i < len; i++)
316                 if ((str[i] & 0x80) != 0x80)
317                         return -1;
318
319         unichar = utf8_encoded_to_unichar(str);
320
321         /* check if encoded length matches encoded value */
322         if (utf8_unichar_to_encoded_len(unichar) != len)
323                 return -1;
324
325         /* check if value has valid range */
326         if (!utf8_unichar_valid_range(unichar))
327                 return -1;
328
329         return len;
330 }
331
332 /* allow chars in whitelist, plain ascii, hex-escaping and valid utf8 */
333 int util_replace_chars(char *str, const char *white)
334 {
335         size_t i = 0;
336         int replaced = 0;
337
338         while (str[i] != '\0') {
339                 int len;
340
341                 /* accept whitelist */
342                 if (white != NULL && strchr(white, str[i]) != NULL) {
343                         i++;
344                         continue;
345                 }
346
347                 /* accept plain ascii char */
348                 if ((str[i] >= '0' && str[i] <= '9') ||
349                     (str[i] >= 'A' && str[i] <= 'Z') ||
350                     (str[i] >= 'a' && str[i] <= 'z')) {
351                         i++;
352                         continue;
353                 }
354
355                 /* accept hex encoding */
356                 if (str[i] == '\\' && str[i+1] == 'x') {
357                         i += 2;
358                         continue;
359                 }
360
361                 /* accept valid utf8 */
362                 len = utf8_encoded_valid_unichar(&str[i]);
363                 if (len > 1) {
364                         i += len;
365                         continue;
366                 }
367
368                 /* if space is allowed, replace whitespace with ordinary space */
369                 if (isspace(str[i]) && strchr(white, ' ') != NULL) {
370                         str[i] = ' ';
371                         i++;
372                         replaced++;
373                         continue;
374                 }
375
376                 /* everything else is replaced with '_' */
377                 str[i] = '_';
378                 i++;
379                 replaced++;
380         }
381
382         return replaced;
383 }