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