mirror of
https://github.com/systemd/systemd.git
synced 2024-11-06 08:26:52 +03:00
e14bdd88e9
Messages send back by the udev daemon to the netlink socket are multiplexed by the kernel and delivered to multiple clients. The clients can upload a socket filter to let the kernel drop messages not belonging to a certain subsystem. This prevent needless wakeups and message processing for users who are only interested in a subset of available events. Recent kernels allow untrusted users to listen to the netlink messages. The messages send by the udev daemon are versioned, to prevent any custom software reading them without libudev. The message wire format may change with any udev version update.
529 lines
12 KiB
C
529 lines
12 KiB
C
/*
|
|
* libudev - interface to udev device information
|
|
*
|
|
* Copyright (C) 2008 Kay Sievers <kay.sievers@vrfy.org>
|
|
*
|
|
* This library is free software; you can redistribute it and/or
|
|
* modify it under the terms of the GNU Lesser General Public
|
|
* License as published by the Free Software Foundation; either
|
|
* version 2.1 of the License, or (at your option) any later version.
|
|
*/
|
|
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <stddef.h>
|
|
#include <unistd.h>
|
|
#include <errno.h>
|
|
#include <string.h>
|
|
#include <dirent.h>
|
|
#include <ctype.h>
|
|
#include <fcntl.h>
|
|
#include <sys/stat.h>
|
|
|
|
#include "libudev.h"
|
|
#include "libudev-private.h"
|
|
|
|
static ssize_t get_sys_link(struct udev *udev, const char *slink, const char *syspath, char *value, size_t size)
|
|
{
|
|
char path[UTIL_PATH_SIZE];
|
|
ssize_t len;
|
|
const char *pos;
|
|
|
|
util_strlcpy(path, syspath, sizeof(path));
|
|
util_strlcat(path, "/", sizeof(path));
|
|
util_strlcat(path, slink, sizeof(path));
|
|
len = readlink(path, path, sizeof(path));
|
|
if (len < 0 || len >= (ssize_t) sizeof(path))
|
|
return -1;
|
|
path[len] = '\0';
|
|
pos = strrchr(path, '/');
|
|
if (pos == NULL)
|
|
return -1;
|
|
pos = &pos[1];
|
|
dbg(udev, "resolved link to: '%s'\n", pos);
|
|
return util_strlcpy(value, pos, size);
|
|
}
|
|
|
|
ssize_t util_get_sys_subsystem(struct udev *udev, const char *syspath, char *subsystem, size_t size)
|
|
{
|
|
return get_sys_link(udev, "subsystem", syspath, subsystem, size);
|
|
}
|
|
|
|
ssize_t util_get_sys_driver(struct udev *udev, const char *syspath, char *driver, size_t size)
|
|
{
|
|
return get_sys_link(udev, "driver", syspath, driver, size);
|
|
}
|
|
|
|
int util_resolve_sys_link(struct udev *udev, char *syspath, size_t size)
|
|
{
|
|
char link_target[UTIL_PATH_SIZE];
|
|
|
|
int len;
|
|
int i;
|
|
int back;
|
|
|
|
len = readlink(syspath, link_target, sizeof(link_target));
|
|
if (len <= 0)
|
|
return -1;
|
|
link_target[len] = '\0';
|
|
dbg(udev, "path link '%s' points to '%s'\n", syspath, link_target);
|
|
|
|
for (back = 0; strncmp(&link_target[back * 3], "../", 3) == 0; back++)
|
|
;
|
|
dbg(udev, "base '%s', tail '%s', back %i\n", syspath, &link_target[back * 3], back);
|
|
for (i = 0; i <= back; i++) {
|
|
char *pos = strrchr(syspath, '/');
|
|
|
|
if (pos == NULL)
|
|
return -1;
|
|
pos[0] = '\0';
|
|
}
|
|
dbg(udev, "after moving back '%s'\n", syspath);
|
|
util_strlcat(syspath, "/", size);
|
|
util_strlcat(syspath, &link_target[back * 3], size);
|
|
return 0;
|
|
}
|
|
|
|
int util_log_priority(const char *priority)
|
|
{
|
|
char *endptr;
|
|
int prio;
|
|
|
|
prio = strtol(priority, &endptr, 10);
|
|
if (endptr[0] == '\0')
|
|
return prio;
|
|
if (strncasecmp(priority, "err", 3) == 0)
|
|
return LOG_ERR;
|
|
if (strcasecmp(priority, "info") == 0)
|
|
return LOG_INFO;
|
|
if (strcasecmp(priority, "debug") == 0)
|
|
return LOG_DEBUG;
|
|
return 0;
|
|
}
|
|
|
|
size_t util_path_encode(char *s, size_t size)
|
|
{
|
|
char t[(size * 4)+1];
|
|
size_t i, j;
|
|
|
|
for (i = 0, j = 0; s[i] != '\0' && i < size; i++) {
|
|
if (s[i] == '/') {
|
|
memcpy(&t[j], "\\x2f", 4);
|
|
j += 4;
|
|
} else if (s[i] == '\\') {
|
|
memcpy(&t[j], "\\x5c", 4);
|
|
j += 4;
|
|
} else {
|
|
t[j] = s[i];
|
|
j++;
|
|
}
|
|
}
|
|
if (i >= size)
|
|
return 0;
|
|
if (j >= size)
|
|
return 0;
|
|
memcpy(s, t, j);
|
|
s[j] = '\0';
|
|
return j;
|
|
}
|
|
|
|
size_t util_path_decode(char *s)
|
|
{
|
|
size_t i, j;
|
|
|
|
for (i = 0, j = 0; s[i] != '\0'; j++) {
|
|
if (memcmp(&s[i], "\\x2f", 4) == 0) {
|
|
s[j] = '/';
|
|
i += 4;
|
|
} else if (memcmp(&s[i], "\\x5c", 4) == 0) {
|
|
s[j] = '\\';
|
|
i += 4;
|
|
} else {
|
|
s[j] = s[i];
|
|
i++;
|
|
}
|
|
}
|
|
s[j] = '\0';
|
|
return j;
|
|
}
|
|
|
|
void util_remove_trailing_chars(char *path, char c)
|
|
{
|
|
size_t len;
|
|
|
|
if (path == NULL)
|
|
return;
|
|
len = strlen(path);
|
|
while (len > 0 && path[len-1] == c)
|
|
path[--len] = '\0';
|
|
}
|
|
|
|
size_t util_strlcpy(char *dst, const char *src, size_t size)
|
|
{
|
|
size_t bytes = 0;
|
|
char *q = dst;
|
|
const char *p = src;
|
|
char ch;
|
|
|
|
while ((ch = *p++)) {
|
|
if (bytes+1 < size)
|
|
*q++ = ch;
|
|
bytes++;
|
|
}
|
|
|
|
/* If size == 0 there is no space for a final null... */
|
|
if (size)
|
|
*q = '\0';
|
|
return bytes;
|
|
}
|
|
|
|
size_t util_strlcat(char *dst, const char *src, size_t size)
|
|
{
|
|
size_t bytes = 0;
|
|
char *q = dst;
|
|
const char *p = src;
|
|
char ch;
|
|
|
|
while (bytes < size && *q) {
|
|
q++;
|
|
bytes++;
|
|
}
|
|
if (bytes == size)
|
|
return (bytes + strlen(src));
|
|
|
|
while ((ch = *p++)) {
|
|
if (bytes+1 < size)
|
|
*q++ = ch;
|
|
bytes++;
|
|
}
|
|
|
|
*q = '\0';
|
|
return bytes;
|
|
}
|
|
|
|
/* count of characters used to encode one unicode char */
|
|
static int utf8_encoded_expected_len(const char *str)
|
|
{
|
|
unsigned char c = (unsigned char)str[0];
|
|
|
|
if (c < 0x80)
|
|
return 1;
|
|
if ((c & 0xe0) == 0xc0)
|
|
return 2;
|
|
if ((c & 0xf0) == 0xe0)
|
|
return 3;
|
|
if ((c & 0xf8) == 0xf0)
|
|
return 4;
|
|
if ((c & 0xfc) == 0xf8)
|
|
return 5;
|
|
if ((c & 0xfe) == 0xfc)
|
|
return 6;
|
|
return 0;
|
|
}
|
|
|
|
/* decode one unicode char */
|
|
static int utf8_encoded_to_unichar(const char *str)
|
|
{
|
|
int unichar;
|
|
int len;
|
|
int i;
|
|
|
|
len = utf8_encoded_expected_len(str);
|
|
switch (len) {
|
|
case 1:
|
|
return (int)str[0];
|
|
case 2:
|
|
unichar = str[0] & 0x1f;
|
|
break;
|
|
case 3:
|
|
unichar = (int)str[0] & 0x0f;
|
|
break;
|
|
case 4:
|
|
unichar = (int)str[0] & 0x07;
|
|
break;
|
|
case 5:
|
|
unichar = (int)str[0] & 0x03;
|
|
break;
|
|
case 6:
|
|
unichar = (int)str[0] & 0x01;
|
|
break;
|
|
default:
|
|
return -1;
|
|
}
|
|
|
|
for (i = 1; i < len; i++) {
|
|
if (((int)str[i] & 0xc0) != 0x80)
|
|
return -1;
|
|
unichar <<= 6;
|
|
unichar |= (int)str[i] & 0x3f;
|
|
}
|
|
|
|
return unichar;
|
|
}
|
|
|
|
/* expected size used to encode one unicode char */
|
|
static int utf8_unichar_to_encoded_len(int unichar)
|
|
{
|
|
if (unichar < 0x80)
|
|
return 1;
|
|
if (unichar < 0x800)
|
|
return 2;
|
|
if (unichar < 0x10000)
|
|
return 3;
|
|
if (unichar < 0x200000)
|
|
return 4;
|
|
if (unichar < 0x4000000)
|
|
return 5;
|
|
return 6;
|
|
}
|
|
|
|
/* check if unicode char has a valid numeric range */
|
|
static int utf8_unichar_valid_range(int unichar)
|
|
{
|
|
if (unichar > 0x10ffff)
|
|
return 0;
|
|
if ((unichar & 0xfffff800) == 0xd800)
|
|
return 0;
|
|
if ((unichar > 0xfdcf) && (unichar < 0xfdf0))
|
|
return 0;
|
|
if ((unichar & 0xffff) == 0xffff)
|
|
return 0;
|
|
return 1;
|
|
}
|
|
|
|
/* validate one encoded unicode char and return its length */
|
|
static int utf8_encoded_valid_unichar(const char *str)
|
|
{
|
|
int len;
|
|
int unichar;
|
|
int i;
|
|
|
|
len = utf8_encoded_expected_len(str);
|
|
if (len == 0)
|
|
return -1;
|
|
|
|
/* ascii is valid */
|
|
if (len == 1)
|
|
return 1;
|
|
|
|
/* check if expected encoded chars are available */
|
|
for (i = 0; i < len; i++)
|
|
if ((str[i] & 0x80) != 0x80)
|
|
return -1;
|
|
|
|
unichar = utf8_encoded_to_unichar(str);
|
|
|
|
/* check if encoded length matches encoded value */
|
|
if (utf8_unichar_to_encoded_len(unichar) != len)
|
|
return -1;
|
|
|
|
/* check if value has valid range */
|
|
if (!utf8_unichar_valid_range(unichar))
|
|
return -1;
|
|
|
|
return len;
|
|
}
|
|
|
|
int udev_util_replace_whitespace(const char *str, char *to, size_t len)
|
|
{
|
|
size_t i, j;
|
|
|
|
/* strip trailing whitespace */
|
|
len = strnlen(str, len);
|
|
while (len && isspace(str[len-1]))
|
|
len--;
|
|
|
|
/* strip leading whitespace */
|
|
i = 0;
|
|
while (isspace(str[i]) && (i < len))
|
|
i++;
|
|
|
|
j = 0;
|
|
while (i < len) {
|
|
/* substitute multiple whitespace with a single '_' */
|
|
if (isspace(str[i])) {
|
|
while (isspace(str[i]))
|
|
i++;
|
|
to[j++] = '_';
|
|
}
|
|
to[j++] = str[i++];
|
|
}
|
|
to[j] = '\0';
|
|
return 0;
|
|
}
|
|
|
|
static int is_whitelisted(char c, const char *white)
|
|
{
|
|
if ((c >= '0' && c <= '9') ||
|
|
(c >= 'A' && c <= 'Z') ||
|
|
(c >= 'a' && c <= 'z') ||
|
|
strchr("#+-.:=@_", c) != NULL ||
|
|
(white != NULL && strchr(white, c) != NULL))
|
|
return 1;
|
|
return 0;
|
|
}
|
|
|
|
/* allow chars in whitelist, plain ascii, hex-escaping and valid utf8 */
|
|
int udev_util_replace_chars(char *str, const char *white)
|
|
{
|
|
size_t i = 0;
|
|
int replaced = 0;
|
|
|
|
while (str[i] != '\0') {
|
|
int len;
|
|
|
|
if (is_whitelisted(str[i], white)) {
|
|
i++;
|
|
continue;
|
|
}
|
|
|
|
/* accept hex encoding */
|
|
if (str[i] == '\\' && str[i+1] == 'x') {
|
|
i += 2;
|
|
continue;
|
|
}
|
|
|
|
/* accept valid utf8 */
|
|
len = utf8_encoded_valid_unichar(&str[i]);
|
|
if (len > 1) {
|
|
i += len;
|
|
continue;
|
|
}
|
|
|
|
/* if space is allowed, replace whitespace with ordinary space */
|
|
if (isspace(str[i]) && white != NULL && strchr(white, ' ') != NULL) {
|
|
str[i] = ' ';
|
|
i++;
|
|
replaced++;
|
|
continue;
|
|
}
|
|
|
|
/* everything else is replaced with '_' */
|
|
str[i] = '_';
|
|
i++;
|
|
replaced++;
|
|
}
|
|
return replaced;
|
|
}
|
|
|
|
/**
|
|
* util_encode_string:
|
|
* @str: input string to be encoded
|
|
* @str_enc: output string to store the encoded input string
|
|
* @len: maximum size of the output string, which may be
|
|
* four times as long as the input string
|
|
*
|
|
* Encode all potentially unsafe characters of a string to the
|
|
* corresponding hex value prefixed by '\x'.
|
|
*
|
|
* Returns: 0 if the entire string was copied, non-zero otherwise.
|
|
**/
|
|
int udev_util_encode_string(const char *str, char *str_enc, size_t len)
|
|
{
|
|
size_t i, j;
|
|
|
|
if (str == NULL || str_enc == NULL || len == 0)
|
|
return -1;
|
|
|
|
str_enc[0] = '\0';
|
|
for (i = 0, j = 0; str[i] != '\0'; i++) {
|
|
int seqlen;
|
|
|
|
seqlen = utf8_encoded_valid_unichar(&str[i]);
|
|
if (seqlen > 1) {
|
|
memcpy(&str_enc[j], &str[i], seqlen);
|
|
j += seqlen;
|
|
i += (seqlen-1);
|
|
} else if (str[i] == '\\' || !is_whitelisted(str[i], NULL)) {
|
|
sprintf(&str_enc[j], "\\x%02x", (unsigned char) str[i]);
|
|
j += 4;
|
|
} else {
|
|
str_enc[j] = str[i];
|
|
j++;
|
|
}
|
|
if (j+3 >= len)
|
|
goto err;
|
|
}
|
|
str_enc[j] = '\0';
|
|
return 0;
|
|
err:
|
|
return -1;
|
|
}
|
|
|
|
void util_set_fd_cloexec(int fd)
|
|
{
|
|
int flags;
|
|
|
|
flags = fcntl(fd, F_GETFD);
|
|
if (flags < 0)
|
|
flags = FD_CLOEXEC;
|
|
else
|
|
flags |= FD_CLOEXEC;
|
|
fcntl(fd, F_SETFD, flags);
|
|
}
|
|
|
|
unsigned int util_crc32(const unsigned char *buf, size_t len)
|
|
{
|
|
unsigned int crc;
|
|
const unsigned char *end;
|
|
static const unsigned int crc32_table[256] = {
|
|
0x00000000, 0x77073096, 0xee0e612c, 0x990951ba, 0x076dc419,
|
|
0x706af48f, 0xe963a535, 0x9e6495a3, 0x0edb8832, 0x79dcb8a4,
|
|
0xe0d5e91e, 0x97d2d988, 0x09b64c2b, 0x7eb17cbd, 0xe7b82d07,
|
|
0x90bf1d91, 0x1db71064, 0x6ab020f2, 0xf3b97148, 0x84be41de,
|
|
0x1adad47d, 0x6ddde4eb, 0xf4d4b551, 0x83d385c7, 0x136c9856,
|
|
0x646ba8c0, 0xfd62f97a, 0x8a65c9ec, 0x14015c4f, 0x63066cd9,
|
|
0xfa0f3d63, 0x8d080df5, 0x3b6e20c8, 0x4c69105e, 0xd56041e4,
|
|
0xa2677172, 0x3c03e4d1, 0x4b04d447, 0xd20d85fd, 0xa50ab56b,
|
|
0x35b5a8fa, 0x42b2986c, 0xdbbbc9d6, 0xacbcf940, 0x32d86ce3,
|
|
0x45df5c75, 0xdcd60dcf, 0xabd13d59, 0x26d930ac, 0x51de003a,
|
|
0xc8d75180, 0xbfd06116, 0x21b4f4b5, 0x56b3c423, 0xcfba9599,
|
|
0xb8bda50f, 0x2802b89e, 0x5f058808, 0xc60cd9b2, 0xb10be924,
|
|
0x2f6f7c87, 0x58684c11, 0xc1611dab, 0xb6662d3d, 0x76dc4190,
|
|
0x01db7106, 0x98d220bc, 0xefd5102a, 0x71b18589, 0x06b6b51f,
|
|
0x9fbfe4a5, 0xe8b8d433, 0x7807c9a2, 0x0f00f934, 0x9609a88e,
|
|
0xe10e9818, 0x7f6a0dbb, 0x086d3d2d, 0x91646c97, 0xe6635c01,
|
|
0x6b6b51f4, 0x1c6c6162, 0x856530d8, 0xf262004e, 0x6c0695ed,
|
|
0x1b01a57b, 0x8208f4c1, 0xf50fc457, 0x65b0d9c6, 0x12b7e950,
|
|
0x8bbeb8ea, 0xfcb9887c, 0x62dd1ddf, 0x15da2d49, 0x8cd37cf3,
|
|
0xfbd44c65, 0x4db26158, 0x3ab551ce, 0xa3bc0074, 0xd4bb30e2,
|
|
0x4adfa541, 0x3dd895d7, 0xa4d1c46d, 0xd3d6f4fb, 0x4369e96a,
|
|
0x346ed9fc, 0xad678846, 0xda60b8d0, 0x44042d73, 0x33031de5,
|
|
0xaa0a4c5f, 0xdd0d7cc9, 0x5005713c, 0x270241aa, 0xbe0b1010,
|
|
0xc90c2086, 0x5768b525, 0x206f85b3, 0xb966d409, 0xce61e49f,
|
|
0x5edef90e, 0x29d9c998, 0xb0d09822, 0xc7d7a8b4, 0x59b33d17,
|
|
0x2eb40d81, 0xb7bd5c3b, 0xc0ba6cad, 0xedb88320, 0x9abfb3b6,
|
|
0x03b6e20c, 0x74b1d29a, 0xead54739, 0x9dd277af, 0x04db2615,
|
|
0x73dc1683, 0xe3630b12, 0x94643b84, 0x0d6d6a3e, 0x7a6a5aa8,
|
|
0xe40ecf0b, 0x9309ff9d, 0x0a00ae27, 0x7d079eb1, 0xf00f9344,
|
|
0x8708a3d2, 0x1e01f268, 0x6906c2fe, 0xf762575d, 0x806567cb,
|
|
0x196c3671, 0x6e6b06e7, 0xfed41b76, 0x89d32be0, 0x10da7a5a,
|
|
0x67dd4acc, 0xf9b9df6f, 0x8ebeeff9, 0x17b7be43, 0x60b08ed5,
|
|
0xd6d6a3e8, 0xa1d1937e, 0x38d8c2c4, 0x4fdff252, 0xd1bb67f1,
|
|
0xa6bc5767, 0x3fb506dd, 0x48b2364b, 0xd80d2bda, 0xaf0a1b4c,
|
|
0x36034af6, 0x41047a60, 0xdf60efc3, 0xa867df55, 0x316e8eef,
|
|
0x4669be79, 0xcb61b38c, 0xbc66831a, 0x256fd2a0, 0x5268e236,
|
|
0xcc0c7795, 0xbb0b4703, 0x220216b9, 0x5505262f, 0xc5ba3bbe,
|
|
0xb2bd0b28, 0x2bb45a92, 0x5cb36a04, 0xc2d7ffa7, 0xb5d0cf31,
|
|
0x2cd99e8b, 0x5bdeae1d, 0x9b64c2b0, 0xec63f226, 0x756aa39c,
|
|
0x026d930a, 0x9c0906a9, 0xeb0e363f, 0x72076785, 0x05005713,
|
|
0x95bf4a82, 0xe2b87a14, 0x7bb12bae, 0x0cb61b38, 0x92d28e9b,
|
|
0xe5d5be0d, 0x7cdcefb7, 0x0bdbdf21, 0x86d3d2d4, 0xf1d4e242,
|
|
0x68ddb3f8, 0x1fda836e, 0x81be16cd, 0xf6b9265b, 0x6fb077e1,
|
|
0x18b74777, 0x88085ae6, 0xff0f6a70, 0x66063bca, 0x11010b5c,
|
|
0x8f659eff, 0xf862ae69, 0x616bffd3, 0x166ccf45, 0xa00ae278,
|
|
0xd70dd2ee, 0x4e048354, 0x3903b3c2, 0xa7672661, 0xd06016f7,
|
|
0x4969474d, 0x3e6e77db, 0xaed16a4a, 0xd9d65adc, 0x40df0b66,
|
|
0x37d83bf0, 0xa9bcae53, 0xdebb9ec5, 0x47b2cf7f, 0x30b5ffe9,
|
|
0xbdbdf21c, 0xcabac28a, 0x53b39330, 0x24b4a3a6, 0xbad03605,
|
|
0xcdd70693, 0x54de5729, 0x23d967bf, 0xb3667a2e, 0xc4614ab8,
|
|
0x5d681b02, 0x2a6f2b94, 0xb40bbe37, 0xc30c8ea1, 0x5a05df1b,
|
|
0x2d02ef8d
|
|
};
|
|
|
|
crc = 0xffffffff;
|
|
for (end = buf + len; buf < end; ++buf)
|
|
crc = crc32_table[(crc ^ *buf) & 0xff] ^ (crc >> 8);
|
|
return ~crc;
|
|
}
|