]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/libudev/libudev-util.c
udev: use startswith() and streq()
[thirdparty/systemd.git] / src / libudev / libudev-util.c
CommitLineData
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 32ssize_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 51int 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
79int 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 96size_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
128size_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
148void 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 */
164size_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 */
185size_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 */
200size_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 */
209size_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 */
226static 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 */
246static 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 */
286static 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 */
302static 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 */
316static 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 348int 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
376static 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 388int 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 475err:
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 */
486static 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 533unsigned 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 */
539uint64_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
553unsigned 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 559unsigned 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}