chiark / gitweb /
libudev: util_get_sys_(subsystem,driver}() -> util_get_sys_core_link_value()
[elogind.git] / libudev / libudev-util.c
1 /*
2  * libudev - interface to udev device information
3  *
4  * Copyright (C) 2008-2009 Kay Sievers <kay.sievers@vrfy.org>
5  *
6  * This library is free software; you can redistribute it and/or
7  * modify it under the terms of the GNU Lesser General Public
8  * License as published by the Free Software Foundation; either
9  * version 2.1 of the License, or (at your option) any later version.
10  */
11
12 #include <stdio.h>
13 #include <stdlib.h>
14 #include <stddef.h>
15 #include <unistd.h>
16 #include <errno.h>
17 #include <string.h>
18 #include <dirent.h>
19 #include <ctype.h>
20 #include <fcntl.h>
21 #include <time.h>
22 #include <sys/stat.h>
23
24 #include "libudev.h"
25 #include "libudev-private.h"
26
27 ssize_t util_get_sys_core_link_value(struct udev *udev, const char *slink, const char *syspath, char *value, size_t size)
28 {
29         char path[UTIL_PATH_SIZE];
30         char target[UTIL_PATH_SIZE];
31         ssize_t len;
32         const char *pos;
33
34         util_strscpyl(path, sizeof(path), syspath, "/", slink, NULL);
35         len = readlink(path, target, sizeof(target));
36         if (len <= 0 || len == (ssize_t)sizeof(target))
37                 return -1;
38         target[len] = '\0';
39         pos = strrchr(target, '/');
40         if (pos == NULL)
41                 return -1;
42         pos = &pos[1];
43         dbg(udev, "resolved link to: '%s'\n", pos);
44         return util_strscpy(value, size, pos);
45 }
46
47 int util_resolve_sys_link(struct udev *udev, char *syspath, size_t size)
48 {
49         char link_target[UTIL_PATH_SIZE];
50
51         ssize_t len;
52         int i;
53         int back;
54         char *base;
55
56         len = readlink(syspath, link_target, sizeof(link_target));
57         if (len <= 0 || len == (ssize_t)sizeof(link_target))
58                 return -1;
59         link_target[len] = '\0';
60         dbg(udev, "path link '%s' points to '%s'\n", syspath, link_target);
61
62         for (back = 0; strncmp(&link_target[back * 3], "../", 3) == 0; back++)
63                 ;
64         dbg(udev, "base '%s', tail '%s', back %i\n", syspath, &link_target[back * 3], back);
65         for (i = 0; i <= back; i++) {
66                 base = strrchr(syspath, '/');
67                 if (base == NULL)
68                         return -1;
69                 base[0] = '\0';
70         }
71         dbg(udev, "after moving back '%s'\n", syspath);
72         util_strscpyl(base, size - (base - syspath), "/", &link_target[back * 3], NULL);
73         return 0;
74 }
75
76 int util_log_priority(const char *priority)
77 {
78         char *endptr;
79         int prio;
80
81         prio = strtol(priority, &endptr, 10);
82         if (endptr[0] == '\0' || isspace(endptr[0]))
83                 return prio;
84         if (strncmp(priority, "err", 3) == 0)
85                 return LOG_ERR;
86         if (strncmp(priority, "info", 4) == 0)
87                 return LOG_INFO;
88         if (strncmp(priority, "debug", 5) == 0)
89                 return LOG_DEBUG;
90         return 0;
91 }
92
93 size_t util_path_encode(const char *src, char *dest, size_t size)
94 {
95         size_t i, j;
96
97         for (i = 0, j = 0; src[i] != '\0'; i++) {
98                 if (src[i] == '/') {
99                         if (j+4 >= size) {
100                                 j = 0;
101                                 break;
102                         }
103                         memcpy(&dest[j], "\\x2f", 4);
104                         j += 4;
105                 } else if (src[i] == '\\') {
106                         if (j+4 >= size) {
107                                 j = 0;
108                                 break;
109                         }
110                         memcpy(&dest[j], "\\x5c", 4);
111                         j += 4;
112                 } else {
113                         if (j+1 >= size) {
114                                 j = 0;
115                                 break;
116                         }
117                         dest[j] = src[i];
118                         j++;
119                 }
120         }
121         dest[j] = '\0';
122         return j;
123 }
124
125 size_t util_path_decode(char *s)
126 {
127         size_t i, j;
128
129         for (i = 0, j = 0; s[i] != '\0'; j++) {
130                 if (memcmp(&s[i], "\\x2f", 4) == 0) {
131                         s[j] = '/';
132                         i += 4;
133                 } else if (memcmp(&s[i], "\\x5c", 4) == 0) {
134                         s[j] = '\\';
135                         i += 4;
136                 } else {
137                         s[j] = s[i];
138                         i++;
139                 }
140         }
141         s[j] = '\0';
142         return j;
143 }
144
145 void util_remove_trailing_chars(char *path, char c)
146 {
147         size_t len;
148
149         if (path == NULL)
150                 return;
151         len = strlen(path);
152         while (len > 0 && path[len-1] == c)
153                 path[--len] = '\0';
154 }
155
156 /*
157  * Concatenates strings. In any case, terminates in _all_ cases with '\0'
158  * and moves the @dest pointer forward to the added '\0'. Returns the
159  * remaining size, and 0 if the string was truncated.
160  */
161 size_t util_strpcpy(char **dest, size_t size, const char *src)
162 {
163         size_t len;
164
165         len = strlen(src);
166         if (len >= size) {
167                 if (size > 1)
168                         *dest = mempcpy(*dest, src, size-1);
169                 size = 0;
170                 *dest[0] = '\0';
171         } else {
172                 if (len > 0) {
173                         *dest = mempcpy(*dest, src, len);
174                         size -= len;
175                 }
176                 *dest[0] = '\0';
177         }
178         return size;
179 }
180
181 /* concatenates list of strings, moves dest forward */
182 size_t util_strpcpyl(char **dest, size_t size, const char *src, ...)
183 {
184         va_list va;
185
186         va_start(va, src);
187         do {
188                 size = util_strpcpy(dest, size, src);
189                 src = va_arg(va, char *);
190         } while (src != NULL);
191         va_end(va);
192
193         return size;
194 }
195
196 /* copies string */
197 size_t util_strscpy(char *dest, size_t size, const char *src)
198 {
199         char *s;
200
201         s = dest;
202         return util_strpcpy(&s, size, src);
203 }
204
205 /* concatenates list of strings */
206 size_t util_strscpyl(char *dest, size_t size, const char *src, ...)
207 {
208         va_list va;
209         char *s;
210
211         va_start(va, src);
212         s = dest;
213         do {
214                 size = util_strpcpy(&s, size, src);
215                 src = va_arg(va, char *);
216         } while (src != NULL);
217         va_end(va);
218
219         return size;
220 }
221
222 /* count of characters used to encode one unicode char */
223 static int utf8_encoded_expected_len(const char *str)
224 {
225         unsigned char c = (unsigned char)str[0];
226
227         if (c < 0x80)
228                 return 1;
229         if ((c & 0xe0) == 0xc0)
230                 return 2;
231         if ((c & 0xf0) == 0xe0)
232                 return 3;
233         if ((c & 0xf8) == 0xf0)
234                 return 4;
235         if ((c & 0xfc) == 0xf8)
236                 return 5;
237         if ((c & 0xfe) == 0xfc)
238                 return 6;
239         return 0;
240 }
241
242 /* decode one unicode char */
243 static int utf8_encoded_to_unichar(const char *str)
244 {
245         int unichar;
246         int len;
247         int i;
248
249         len = utf8_encoded_expected_len(str);
250         switch (len) {
251         case 1:
252                 return (int)str[0];
253         case 2:
254                 unichar = str[0] & 0x1f;
255                 break;
256         case 3:
257                 unichar = (int)str[0] & 0x0f;
258                 break;
259         case 4:
260                 unichar = (int)str[0] & 0x07;
261                 break;
262         case 5:
263                 unichar = (int)str[0] & 0x03;
264                 break;
265         case 6:
266                 unichar = (int)str[0] & 0x01;
267                 break;
268         default:
269                 return -1;
270         }
271
272         for (i = 1; i < len; i++) {
273                 if (((int)str[i] & 0xc0) != 0x80)
274                         return -1;
275                 unichar <<= 6;
276                 unichar |= (int)str[i] & 0x3f;
277         }
278
279         return unichar;
280 }
281
282 /* expected size used to encode one unicode char */
283 static int utf8_unichar_to_encoded_len(int unichar)
284 {
285         if (unichar < 0x80)
286                 return 1;
287         if (unichar < 0x800)
288                 return 2;
289         if (unichar < 0x10000)
290                 return 3;
291         if (unichar < 0x200000)
292                 return 4;
293         if (unichar < 0x4000000)
294                 return 5;
295         return 6;
296 }
297
298 /* check if unicode char has a valid numeric range */
299 static int utf8_unichar_valid_range(int unichar)
300 {
301         if (unichar > 0x10ffff)
302                 return 0;
303         if ((unichar & 0xfffff800) == 0xd800)
304                 return 0;
305         if ((unichar > 0xfdcf) && (unichar < 0xfdf0))
306                 return 0;
307         if ((unichar & 0xffff) == 0xffff)
308                 return 0;
309         return 1;
310 }
311
312 /* validate one encoded unicode char and return its length */
313 static int utf8_encoded_valid_unichar(const char *str)
314 {
315         int len;
316         int unichar;
317         int i;
318
319         len = utf8_encoded_expected_len(str);
320         if (len == 0)
321                 return -1;
322
323         /* ascii is valid */
324         if (len == 1)
325                 return 1;
326
327         /* check if expected encoded chars are available */
328         for (i = 0; i < len; i++)
329                 if ((str[i] & 0x80) != 0x80)
330                         return -1;
331
332         unichar = utf8_encoded_to_unichar(str);
333
334         /* check if encoded length matches encoded value */
335         if (utf8_unichar_to_encoded_len(unichar) != len)
336                 return -1;
337
338         /* check if value has valid range */
339         if (!utf8_unichar_valid_range(unichar))
340                 return -1;
341
342         return len;
343 }
344
345 int udev_util_replace_whitespace(const char *str, char *to, size_t len)
346 {
347         size_t i, j;
348
349         /* strip trailing whitespace */
350         len = strnlen(str, len);
351         while (len && isspace(str[len-1]))
352                 len--;
353
354         /* strip leading whitespace */
355         i = 0;
356         while (isspace(str[i]) && (i < len))
357                 i++;
358
359         j = 0;
360         while (i < len) {
361                 /* substitute multiple whitespace with a single '_' */
362                 if (isspace(str[i])) {
363                         while (isspace(str[i]))
364                                 i++;
365                         to[j++] = '_';
366                 }
367                 to[j++] = str[i++];
368         }
369         to[j] = '\0';
370         return 0;
371 }
372
373 static int is_whitelisted(char c, const char *white)
374 {
375         if ((c >= '0' && c <= '9') ||
376             (c >= 'A' && c <= 'Z') ||
377             (c >= 'a' && c <= 'z') ||
378             strchr("#+-.:=@_", c) != NULL ||
379             (white != NULL && strchr(white, c) != NULL))
380                 return 1;
381         return 0;
382 }
383
384 /* allow chars in whitelist, plain ascii, hex-escaping and valid utf8 */
385 int udev_util_replace_chars(char *str, const char *white)
386 {
387         size_t i = 0;
388         int replaced = 0;
389
390         while (str[i] != '\0') {
391                 int len;
392
393                 if (is_whitelisted(str[i], white)) {
394                         i++;
395                         continue;
396                 }
397
398                 /* accept hex encoding */
399                 if (str[i] == '\\' && str[i+1] == 'x') {
400                         i += 2;
401                         continue;
402                 }
403
404                 /* accept valid utf8 */
405                 len = utf8_encoded_valid_unichar(&str[i]);
406                 if (len > 1) {
407                         i += len;
408                         continue;
409                 }
410
411                 /* if space is allowed, replace whitespace with ordinary space */
412                 if (isspace(str[i]) && white != NULL && strchr(white, ' ') != NULL) {
413                         str[i] = ' ';
414                         i++;
415                         replaced++;
416                         continue;
417                 }
418
419                 /* everything else is replaced with '_' */
420                 str[i] = '_';
421                 i++;
422                 replaced++;
423         }
424         return replaced;
425 }
426
427 /**
428  * util_encode_string:
429  * @str: input string to be encoded
430  * @str_enc: output string to store the encoded input string
431  * @len: maximum size of the output string, which may be
432  *       four times as long as the input string
433  *
434  * Encode all potentially unsafe characters of a string to the
435  * corresponding hex value prefixed by '\x'.
436  *
437  * Returns: 0 if the entire string was copied, non-zero otherwise.
438  **/
439 int udev_util_encode_string(const char *str, char *str_enc, size_t len)
440 {
441         size_t i, j;
442
443         if (str == NULL || str_enc == NULL)
444                 return -1;
445
446         for (i = 0, j = 0; str[i] != '\0'; i++) {
447                 int seqlen;
448
449                 seqlen = utf8_encoded_valid_unichar(&str[i]);
450                 if (seqlen > 1) {
451                         if (len-j < (size_t)seqlen)
452                                 goto err;
453                         memcpy(&str_enc[j], &str[i], seqlen);
454                         j += seqlen;
455                         i += (seqlen-1);
456                 } else if (str[i] == '\\' || !is_whitelisted(str[i], NULL)) {
457                         if (len-j < 4)
458                                 goto err;
459                         sprintf(&str_enc[j], "\\x%02x", (unsigned char) str[i]);
460                         j += 4;
461                 } else {
462                         if (len-j < 1)
463                                 goto err;
464                         str_enc[j] = str[i];
465                         j++;
466                 }
467         }
468         if (len-j < 1)
469                 goto err;
470         str_enc[j] = '\0';
471         return 0;
472 err:
473         return -1;
474 }
475
476 /*
477  * http://sites.google.com/site/murmurhash/
478  *
479  * All code is released to the public domain. For business purposes,
480  * Murmurhash is under the MIT license.
481  *
482  */
483 static unsigned int murmur_hash2(const char *key, int len, unsigned int seed)
484 {
485         /*
486          *  'm' and 'r' are mixing constants generated offline.
487          *  They're not really 'magic', they just happen to work well.
488          */
489         const unsigned int m = 0x5bd1e995;
490         const int r = 24;
491
492         /* initialize the hash to a 'random' value */
493         unsigned int h = seed ^ len;
494
495         /* mix 4 bytes at a time into the hash */
496         const unsigned char * data = (const unsigned char *)key;
497
498         while(len >= 4) {
499                 unsigned int k = *(unsigned int *)data;
500
501                 k *= m; 
502                 k ^= k >> r; 
503                 k *= m; 
504                 h *= m; 
505                 h ^= k;
506
507                 data += 4;
508                 len -= 4;
509         }
510
511         /* handle the last few bytes of the input array */
512         switch(len) {
513         case 3:
514                 h ^= data[2] << 16;
515         case 2:
516                 h ^= data[1] << 8;
517         case 1:
518                 h ^= data[0];
519                 h *= m;
520         };
521
522         /* do a few final mixes of the hash to ensure the last few bytes are well-incorporated */
523         h ^= h >> 13;
524         h *= m;
525         h ^= h >> 15;
526
527         return h;
528 }
529
530 unsigned int util_string_hash32(const char *str)
531 {
532         return murmur_hash2(str, strlen(str), 0);
533 }
534
535 /* get a bunch of bit numbers out of the hash, and set the bits in our bit field */
536 uint64_t util_string_bloom64(const char *str)
537 {
538         uint64_t bits = 0;
539         unsigned int hash = util_string_hash32(str);
540
541         bits |= 1LLU << (hash & 63);
542         bits |= 1LLU << ((hash >> 6) & 63);
543         bits |= 1LLU << ((hash >> 12) & 63);
544         bits |= 1LLU << ((hash >> 18) & 63);
545         return bits;
546 }
547
548 #define USEC_PER_SEC  1000000ULL
549 #define NSEC_PER_USEC 1000ULL
550 unsigned long long now_usec(void)
551 {
552         struct timespec ts;
553
554         if (clock_gettime(CLOCK_MONOTONIC, &ts) != 0)
555                 return 0;
556         return (unsigned long long) ts.tv_sec * USEC_PER_SEC +
557                (unsigned long long) ts.tv_nsec / NSEC_PER_USEC;
558 }