]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/libudev-util.c
tabs are as useful as a hole in the head
[thirdparty/systemd.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 }