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