chiark / gitweb /
device-nodes: move device node specific code to own file
[elogind.git] / src / libudev / libudev-util.c
1 /***
2   This file is part of systemd.
3
4   Copyright 2008-2012 Kay Sievers <kay@vrfy.org>
5
6   systemd is free software; you can redistribute it and/or modify it
7   under the terms of the GNU Lesser General Public License as published by
8   the Free Software Foundation; either version 2.1 of the License, or
9   (at your option) any later version.
10
11   systemd is distributed in the hope that it will be useful, but
12   WITHOUT ANY WARRANTY; without even the implied warranty of
13   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14   Lesser General Public License for more details.
15
16   You should have received a copy of the GNU Lesser General Public License
17   along with systemd; If not, see <http://www.gnu.org/licenses/>.
18 ***/
19
20 #include <stdio.h>
21 #include <stdlib.h>
22 #include <stddef.h>
23 #include <unistd.h>
24 #include <errno.h>
25 #include <string.h>
26 #include <dirent.h>
27 #include <ctype.h>
28 #include <fcntl.h>
29 #include <time.h>
30 #include <pwd.h>
31 #include <grp.h>
32 #include <sys/stat.h>
33 #include <sys/param.h>
34
35 #include "device-nodes.h"
36 #include "libudev.h"
37 #include "libudev-private.h"
38 #include "utf8.h"
39
40 /**
41  * SECTION:libudev-util
42  * @short_description: utils
43  *
44  * Utilities useful when dealing with devices and device node names.
45  */
46
47 int util_delete_path(struct udev *udev, const char *path)
48 {
49         char p[UTIL_PATH_SIZE];
50         char *pos;
51         int err = 0;
52
53         if (path[0] == '/')
54                 while(path[1] == '/')
55                         path++;
56         strscpy(p, sizeof(p), path);
57         pos = strrchr(p, '/');
58         if (pos == p || pos == NULL)
59                 return 0;
60
61         for (;;) {
62                 *pos = '\0';
63                 pos = strrchr(p, '/');
64
65                 /* don't remove the last one */
66                 if ((pos == p) || (pos == NULL))
67                         break;
68
69                 err = rmdir(p);
70                 if (err < 0) {
71                         if (errno == ENOENT)
72                                 err = 0;
73                         break;
74                 }
75         }
76         return err;
77 }
78
79 uid_t util_lookup_user(struct udev *udev, const char *user)
80 {
81         char *endptr;
82         struct passwd pwbuf;
83         struct passwd *pw;
84         uid_t uid;
85         size_t buflen = sysconf(_SC_GETPW_R_SIZE_MAX);
86         char *buf = alloca(buflen);
87
88         if (streq(user, "root"))
89                 return 0;
90         uid = strtoul(user, &endptr, 10);
91         if (endptr[0] == '\0')
92                 return uid;
93
94         errno = getpwnam_r(user, &pwbuf, buf, buflen, &pw);
95         if (pw != NULL)
96                 return pw->pw_uid;
97         if (errno == 0 || errno == ENOENT || errno == ESRCH)
98                 udev_err(udev, "specified user '%s' unknown\n", user);
99         else
100                 udev_err(udev, "error resolving user '%s': %m\n", user);
101         return 0;
102 }
103
104 gid_t util_lookup_group(struct udev *udev, const char *group)
105 {
106         char *endptr;
107         struct group grbuf;
108         struct group *gr;
109         gid_t gid = 0;
110         size_t buflen = sysconf(_SC_GETPW_R_SIZE_MAX);
111         char *buf = NULL;
112
113         if (streq(group, "root"))
114                 return 0;
115         gid = strtoul(group, &endptr, 10);
116         if (endptr[0] == '\0')
117                 return gid;
118         gid = 0;
119         for (;;) {
120                 char *newbuf;
121
122                 newbuf = realloc(buf, buflen);
123                 if (!newbuf)
124                         break;
125                 buf = newbuf;
126                 errno = getgrnam_r(group, &grbuf, buf, buflen, &gr);
127                 if (gr != NULL) {
128                         gid = gr->gr_gid;
129                 } else if (errno == ERANGE) {
130                         buflen *= 2;
131                         continue;
132                 } else if (errno == 0 || errno == ENOENT || errno == ESRCH) {
133                         udev_err(udev, "specified group '%s' unknown\n", group);
134                 } else {
135                         udev_err(udev, "error resolving group '%s': %m\n", group);
136                 }
137                 break;
138         }
139         free(buf);
140         return gid;
141 }
142
143 /* handle "[<SUBSYSTEM>/<KERNEL>]<attribute>" format */
144 int util_resolve_subsys_kernel(struct udev *udev, const char *string,
145                                char *result, size_t maxsize, int read_value)
146 {
147         char temp[UTIL_PATH_SIZE];
148         char *subsys;
149         char *sysname;
150         struct udev_device *dev;
151         char *attr;
152
153         if (string[0] != '[')
154                 return -1;
155
156         strscpy(temp, sizeof(temp), string);
157
158         subsys = &temp[1];
159
160         sysname = strchr(subsys, '/');
161         if (sysname == NULL)
162                 return -1;
163         sysname[0] = '\0';
164         sysname = &sysname[1];
165
166         attr = strchr(sysname, ']');
167         if (attr == NULL)
168                 return -1;
169         attr[0] = '\0';
170         attr = &attr[1];
171         if (attr[0] == '/')
172                 attr = &attr[1];
173         if (attr[0] == '\0')
174                 attr = NULL;
175
176         if (read_value && attr == NULL)
177                 return -1;
178
179         dev = udev_device_new_from_subsystem_sysname(udev, subsys, sysname);
180         if (dev == NULL)
181                 return -1;
182
183         if (read_value) {
184                 const char *val;
185
186                 val = udev_device_get_sysattr_value(dev, attr);
187                 if (val != NULL)
188                         strscpy(result, maxsize, val);
189                 else
190                         result[0] = '\0';
191                 udev_dbg(udev, "value '[%s/%s]%s' is '%s'\n", subsys, sysname, attr, result);
192         } else {
193                 size_t l;
194                 char *s;
195
196                 s = result;
197                 l = strpcpyl(&s, maxsize, udev_device_get_syspath(dev), NULL);
198                 if (attr != NULL)
199                         strpcpyl(&s, l, "/", attr, NULL);
200                 udev_dbg(udev, "path '[%s/%s]%s' is '%s'\n", subsys, sysname, attr, result);
201         }
202         udev_device_unref(dev);
203         return 0;
204 }
205 ssize_t util_get_sys_core_link_value(struct udev *udev, const char *slink, const char *syspath, char *value, size_t size)
206 {
207         char path[UTIL_PATH_SIZE];
208         char target[UTIL_PATH_SIZE];
209         ssize_t len;
210         const char *pos;
211
212         strscpyl(path, sizeof(path), syspath, "/", slink, NULL);
213         len = readlink(path, target, sizeof(target));
214         if (len <= 0 || len == (ssize_t)sizeof(target))
215                 return -1;
216         target[len] = '\0';
217         pos = strrchr(target, '/');
218         if (pos == NULL)
219                 return -1;
220         pos = &pos[1];
221         return strscpy(value, size, pos);
222 }
223
224 int util_resolve_sys_link(struct udev *udev, char *syspath, size_t size)
225 {
226         char link_target[UTIL_PATH_SIZE];
227
228         ssize_t len;
229         int i;
230         int back;
231         char *base = NULL;
232
233         len = readlink(syspath, link_target, sizeof(link_target));
234         if (len <= 0 || len == (ssize_t)sizeof(link_target))
235                 return -1;
236         link_target[len] = '\0';
237
238         for (back = 0; startswith(&link_target[back * 3], "../"); back++)
239                 ;
240         for (i = 0; i <= back; i++) {
241                 base = strrchr(syspath, '/');
242                 if (base == NULL)
243                         return -EINVAL;
244                 base[0] = '\0';
245         }
246
247         strscpyl(base, size - (base - syspath), "/", &link_target[back * 3], NULL);
248         return 0;
249 }
250
251 int util_log_priority(const char *priority)
252 {
253         char *endptr;
254         int prio;
255
256         prio = strtol(priority, &endptr, 10);
257         if (endptr[0] == '\0' || isspace(endptr[0]))
258                 return prio;
259         if (startswith(priority, "err"))
260                 return LOG_ERR;
261         if (startswith(priority, "info"))
262                 return LOG_INFO;
263         if (startswith(priority, "debug"))
264                 return LOG_DEBUG;
265         return 0;
266 }
267
268 size_t util_path_encode(const char *src, char *dest, size_t size)
269 {
270         size_t i, j;
271
272         for (i = 0, j = 0; src[i] != '\0'; i++) {
273                 if (src[i] == '/') {
274                         if (j+4 >= size) {
275                                 j = 0;
276                                 break;
277                         }
278                         memcpy(&dest[j], "\\x2f", 4);
279                         j += 4;
280                 } else if (src[i] == '\\') {
281                         if (j+4 >= size) {
282                                 j = 0;
283                                 break;
284                         }
285                         memcpy(&dest[j], "\\x5c", 4);
286                         j += 4;
287                 } else {
288                         if (j+1 >= size) {
289                                 j = 0;
290                                 break;
291                         }
292                         dest[j] = src[i];
293                         j++;
294                 }
295         }
296         dest[j] = '\0';
297         return j;
298 }
299
300 void util_remove_trailing_chars(char *path, char c)
301 {
302         size_t len;
303
304         if (path == NULL)
305                 return;
306         len = strlen(path);
307         while (len > 0 && path[len-1] == c)
308                 path[--len] = '\0';
309 }
310
311 int util_replace_whitespace(const char *str, char *to, size_t len)
312 {
313         size_t i, j;
314
315         /* strip trailing whitespace */
316         len = strnlen(str, len);
317         while (len && isspace(str[len-1]))
318                 len--;
319
320         /* strip leading whitespace */
321         i = 0;
322         while (isspace(str[i]) && (i < len))
323                 i++;
324
325         j = 0;
326         while (i < len) {
327                 /* substitute multiple whitespace with a single '_' */
328                 if (isspace(str[i])) {
329                         while (isspace(str[i]))
330                                 i++;
331                         to[j++] = '_';
332                 }
333                 to[j++] = str[i++];
334         }
335         to[j] = '\0';
336         return 0;
337 }
338
339 /* allow chars in whitelist, plain ascii, hex-escaping and valid utf8 */
340 int util_replace_chars(char *str, const char *white)
341 {
342         size_t i = 0;
343         int replaced = 0;
344
345         while (str[i] != '\0') {
346                 int len;
347
348                 if (whitelisted_char_for_devnode(str[i], white)) {
349                         i++;
350                         continue;
351                 }
352
353                 /* accept hex encoding */
354                 if (str[i] == '\\' && str[i+1] == 'x') {
355                         i += 2;
356                         continue;
357                 }
358
359                 /* accept valid utf8 */
360                 len = utf8_encoded_valid_unichar(&str[i]);
361                 if (len > 1) {
362                         i += len;
363                         continue;
364                 }
365
366                 /* if space is allowed, replace whitespace with ordinary space */
367                 if (isspace(str[i]) && white != NULL && strchr(white, ' ') != NULL) {
368                         str[i] = ' ';
369                         i++;
370                         replaced++;
371                         continue;
372                 }
373
374                 /* everything else is replaced with '_' */
375                 str[i] = '_';
376                 i++;
377                 replaced++;
378         }
379         return replaced;
380 }
381
382 /**
383  * udev_util_encode_string:
384  * @str: input string to be encoded
385  * @str_enc: output string to store the encoded input string
386  * @len: maximum size of the output string, which may be
387  *       four times as long as the input string
388  *
389  * Encode all potentially unsafe characters of a string to the
390  * corresponding 2 char hex value prefixed by '\x'.
391  *
392  * Returns: 0 if the entire string was copied, non-zero otherwise.
393  **/
394 _public_ int udev_util_encode_string(const char *str, char *str_enc, size_t len)
395 {
396         return encode_devnode_name(str, str_enc, len);
397 }
398
399 /*
400  * http://sites.google.com/site/murmurhash/
401  *
402  * All code is released to the public domain. For business purposes,
403  * Murmurhash is under the MIT license.
404  *
405  */
406 static unsigned int murmur_hash2(const char *key, size_t len, unsigned int seed)
407 {
408         /*
409          *  'm' and 'r' are mixing constants generated offline.
410          *  They're not really 'magic', they just happen to work well.
411          */
412         const unsigned int m = 0x5bd1e995;
413         const int r = 24;
414
415         /* initialize the hash to a 'random' value */
416         unsigned int h = seed ^ len;
417
418         /* mix 4 bytes at a time into the hash */
419         const unsigned char * data = (const unsigned char *)key;
420
421         while(len >= sizeof(unsigned int)) {
422                 unsigned int k;
423
424                 memcpy(&k, data, sizeof(k));
425                 k *= m;
426                 k ^= k >> r;
427                 k *= m;
428                 h *= m;
429                 h ^= k;
430
431                 data += sizeof(k);
432                 len -= sizeof(k);
433         }
434
435         /* handle the last few bytes of the input array */
436         switch(len) {
437         case 3:
438                 h ^= data[2] << 16;
439         case 2:
440                 h ^= data[1] << 8;
441         case 1:
442                 h ^= data[0];
443                 h *= m;
444         };
445
446         /* do a few final mixes of the hash to ensure the last few bytes are well-incorporated */
447         h ^= h >> 13;
448         h *= m;
449         h ^= h >> 15;
450
451         return h;
452 }
453
454 unsigned int util_string_hash32(const char *str)
455 {
456         return murmur_hash2(str, strlen(str), 0);
457 }
458
459 /* get a bunch of bit numbers out of the hash, and set the bits in our bit field */
460 uint64_t util_string_bloom64(const char *str)
461 {
462         uint64_t bits = 0;
463         unsigned int hash = util_string_hash32(str);
464
465         bits |= 1LLU << (hash & 63);
466         bits |= 1LLU << ((hash >> 6) & 63);
467         bits |= 1LLU << ((hash >> 12) & 63);
468         bits |= 1LLU << ((hash >> 18) & 63);
469         return bits;
470 }
471
472 ssize_t print_kmsg(const char *fmt, ...)
473 {
474         int fd;
475         va_list ap;
476         char text[1024];
477         ssize_t len;
478         ssize_t ret;
479
480         fd = open("/dev/kmsg", O_WRONLY|O_NOCTTY|O_CLOEXEC);
481         if (fd < 0)
482                 return -errno;
483
484         len = snprintf(text, sizeof(text), "<30>systemd-udevd[%u]: ", getpid());
485
486         va_start(ap, fmt);
487         len += vsnprintf(text + len, sizeof(text) - len, fmt, ap);
488         va_end(ap);
489
490         ret = write(fd, text, len);
491         if (ret < 0)
492                 ret = -errno;
493         close(fd);
494         return ret;
495 }