chiark / gitweb /
0e5bc37d4395cc2cd66e79e9aec67739c89bc494
[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 *subsystem, 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(subsystem, 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         struct stat statbuf;
68         char link_target[UTIL_PATH_SIZE];
69
70         int len;
71         int i;
72         int back;
73
74         if (lstat(syspath, &statbuf) < 0)
75                 return -1;
76         if (!S_ISLNK(statbuf.st_mode))
77                 return -1;
78         len = readlink(syspath, link_target, sizeof(link_target));
79         if (len <= 0)
80                 return -1;
81         link_target[len] = '\0';
82         dbg(udev, "path link '%s' points to '%s'\n", syspath, link_target);
83
84         for (back = 0; strncmp(&link_target[back * 3], "../", 3) == 0; back++)
85                 ;
86         dbg(udev, "base '%s', tail '%s', back %i\n", syspath, &link_target[back * 3], back);
87         for (i = 0; i <= back; i++) {
88                 char *pos = strrchr(syspath, '/');
89
90                 if (pos == NULL)
91                         return -1;
92                 pos[0] = '\0';
93         }
94         dbg(udev, "after moving back '%s'\n", syspath);
95         util_strlcat(syspath, "/", size);
96         util_strlcat(syspath, &link_target[back * 3], size);
97         return 0;
98 }
99
100 int util_log_priority(const char *priority)
101 {
102         char *endptr;
103         int prio;
104
105         prio = strtol(priority, &endptr, 10);
106         if (endptr[0] == '\0')
107                 return prio;
108         if (strncasecmp(priority, "err", 3) == 0)
109                 return LOG_ERR;
110         if (strcasecmp(priority, "info") == 0)
111                 return LOG_INFO;
112         if (strcasecmp(priority, "debug") == 0)
113                 return LOG_DEBUG;
114         return 0;
115 }
116
117 size_t util_path_encode(char *s, size_t len)
118 {
119         char t[(len * 3)+1];
120         size_t i, j;
121
122         t[0] = '\0';
123         for (i = 0, j = 0; s[i] != '\0'; i++) {
124                 if (s[i] == '/') {
125                         memcpy(&t[j], "\\x2f", 4);
126                         j += 4;
127                 } else if (s[i] == '\\') {
128                         memcpy(&t[j], "\\x5c", 4);
129                         j += 4;
130                 } else {
131                         t[j] = s[i];
132                         j++;
133                 }
134         }
135         t[j] = '\0';
136         strncpy(s, t, len);
137         return j;
138 }
139
140 size_t util_path_decode(char *s)
141 {
142         size_t i, j;
143
144         for (i = 0, j = 0; s[i] != '\0'; j++) {
145                 if (memcmp(&s[i], "\\x2f", 4) == 0) {
146                         s[j] = '/';
147                         i += 4;
148                 }else if (memcmp(&s[i], "\\x5c", 4) == 0) {
149                         s[j] = '\\';
150                         i += 4;
151                 } else {
152                         s[j] = s[i];
153                         i++;
154                 }
155         }
156         s[j] = '\0';
157         return j;
158 }
159
160 void util_remove_trailing_chars(char *path, char c)
161 {
162         size_t len;
163
164         if (path == NULL)
165                 return;
166         len = strlen(path);
167         while (len > 0 && path[len-1] == c)
168                 path[--len] = '\0';
169 }
170
171 size_t util_strlcpy(char *dst, const char *src, size_t size)
172 {
173         size_t bytes = 0;
174         char *q = dst;
175         const char *p = src;
176         char ch;
177
178         while ((ch = *p++)) {
179                 if (bytes+1 < size)
180                         *q++ = ch;
181                 bytes++;
182         }
183
184         /* If size == 0 there is no space for a final null... */
185         if (size)
186                 *q = '\0';
187         return bytes;
188 }
189
190 size_t util_strlcat(char *dst, const char *src, size_t size)
191 {
192         size_t bytes = 0;
193         char *q = dst;
194         const char *p = src;
195         char ch;
196
197         while (bytes < size && *q) {
198                 q++;
199                 bytes++;
200         }
201         if (bytes == size)
202                 return (bytes + strlen(src));
203
204         while ((ch = *p++)) {
205                 if (bytes+1 < size)
206                 *q++ = ch;
207                 bytes++;
208         }
209
210         *q = '\0';
211         return bytes;
212 }
213
214 /* count of characters used to encode one unicode char */
215 static int utf8_encoded_expected_len(const char *str)
216 {
217         unsigned char c = (unsigned char)str[0];
218
219         if (c < 0x80)
220                 return 1;
221         if ((c & 0xe0) == 0xc0)
222                 return 2;
223         if ((c & 0xf0) == 0xe0)
224                 return 3;
225         if ((c & 0xf8) == 0xf0)
226                 return 4;
227         if ((c & 0xfc) == 0xf8)
228                 return 5;
229         if ((c & 0xfe) == 0xfc)
230                 return 6;
231         return 0;
232 }
233
234 /* decode one unicode char */
235 static int utf8_encoded_to_unichar(const char *str)
236 {
237         int unichar;
238         int len;
239         int i;
240
241         len = utf8_encoded_expected_len(str);
242         switch (len) {
243         case 1:
244                 return (int)str[0];
245         case 2:
246                 unichar = str[0] & 0x1f;
247                 break;
248         case 3:
249                 unichar = (int)str[0] & 0x0f;
250                 break;
251         case 4:
252                 unichar = (int)str[0] & 0x07;
253                 break;
254         case 5:
255                 unichar = (int)str[0] & 0x03;
256                 break;
257         case 6:
258                 unichar = (int)str[0] & 0x01;
259                 break;
260         default:
261                 return -1;
262         }
263
264         for (i = 1; i < len; i++) {
265                 if (((int)str[i] & 0xc0) != 0x80)
266                         return -1;
267                 unichar <<= 6;
268                 unichar |= (int)str[i] & 0x3f;
269         }
270
271         return unichar;
272 }
273
274 /* expected size used to encode one unicode char */
275 static int utf8_unichar_to_encoded_len(int unichar)
276 {
277         if (unichar < 0x80)
278                 return 1;
279         if (unichar < 0x800)
280                 return 2;
281         if (unichar < 0x10000)
282                 return 3;
283         if (unichar < 0x200000)
284                 return 4;
285         if (unichar < 0x4000000)
286                 return 5;
287         return 6;
288 }
289
290 /* check if unicode char has a valid numeric range */
291 static int utf8_unichar_valid_range(int unichar)
292 {
293         if (unichar > 0x10ffff)
294                 return 0;
295         if ((unichar & 0xfffff800) == 0xd800)
296                 return 0;
297         if ((unichar > 0xfdcf) && (unichar < 0xfdf0))
298                 return 0;
299         if ((unichar & 0xffff) == 0xffff)
300                 return 0;
301         return 1;
302 }
303
304 /* validate one encoded unicode char and return its length */
305 static int utf8_encoded_valid_unichar(const char *str)
306 {
307         int len;
308         int unichar;
309         int i;
310
311         len = utf8_encoded_expected_len(str);
312         if (len == 0)
313                 return -1;
314
315         /* ascii is valid */
316         if (len == 1)
317                 return 1;
318
319         /* check if expected encoded chars are available */
320         for (i = 0; i < len; i++)
321                 if ((str[i] & 0x80) != 0x80)
322                         return -1;
323
324         unichar = utf8_encoded_to_unichar(str);
325
326         /* check if encoded length matches encoded value */
327         if (utf8_unichar_to_encoded_len(unichar) != len)
328                 return -1;
329
330         /* check if value has valid range */
331         if (!utf8_unichar_valid_range(unichar))
332                 return -1;
333
334         return len;
335 }
336
337 /* allow chars in whitelist, plain ascii, hex-escaping and valid utf8 */
338 int util_replace_chars(char *str, const char *white)
339 {
340         size_t i = 0;
341         int replaced = 0;
342
343         while (str[i] != '\0') {
344                 int len;
345
346                 /* accept whitelist */
347                 if (white != NULL && strchr(white, str[i]) != NULL) {
348                         i++;
349                         continue;
350                 }
351
352                 /* accept plain ascii char */
353                 if ((str[i] >= '0' && str[i] <= '9') ||
354                     (str[i] >= 'A' && str[i] <= 'Z') ||
355                     (str[i] >= 'a' && str[i] <= 'z')) {
356                         i++;
357                         continue;
358                 }
359
360                 /* accept hex encoding */
361                 if (str[i] == '\\' && str[i+1] == 'x') {
362                         i += 2;
363                         continue;
364                 }
365
366                 /* accept valid utf8 */
367                 len = utf8_encoded_valid_unichar(&str[i]);
368                 if (len > 1) {
369                         i += len;
370                         continue;
371                 }
372
373                 /* if space is allowed, replace whitespace with ordinary space */
374                 if (isspace(str[i]) && strchr(white, ' ') != NULL) {
375                         str[i] = ' ';
376                         i++;
377                         replaced++;
378                         continue;
379                 }
380
381                 /* everything else is replaced with '_' */
382                 str[i] = '_';
383                 i++;
384                 replaced++;
385         }
386
387         return replaced;
388 }