diff options
author | Kay Sievers <kay.sievers@vrfy.org> | 2012-01-05 22:41:45 +0100 |
---|---|---|
committer | Kay Sievers <kay.sievers@vrfy.org> | 2012-01-06 05:07:10 +0100 |
commit | ad29a9f14fa8b1932c0e418bfcf1c10ce6a35a33 (patch) | |
tree | 9212c3d9d937234c25d19b993664b8ed164869a5 /udev | |
parent | 9fbe27d9d6dc6e4530ce904d35c74326e415e34e (diff) |
merge udev/, libudev/, systemd/ files in src/; move extras/ to src/
Diffstat (limited to 'udev')
31 files changed, 0 insertions, 13017 deletions
diff --git a/udev/.gitignore b/udev/.gitignore deleted file mode 100644 index 73f746cb7d..0000000000 --- a/udev/.gitignore +++ /dev/null @@ -1,6 +0,0 @@ -udevd -udevadm -test-udev -*.[78] -*.html -udev.pc diff --git a/udev/sd-daemon.c b/udev/sd-daemon.c deleted file mode 100644 index e68b70875c..0000000000 --- a/udev/sd-daemon.c +++ /dev/null @@ -1,526 +0,0 @@ -/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/ - -/*** - Copyright 2010 Lennart Poettering - - Permission is hereby granted, free of charge, to any person - obtaining a copy of this software and associated documentation files - (the "Software"), to deal in the Software without restriction, - including without limitation the rights to use, copy, modify, merge, - publish, distribute, sublicense, and/or sell copies of the Software, - and to permit persons to whom the Software is furnished to do so, - subject to the following conditions: - - The above copyright notice and this permission notice shall be - included in all copies or substantial portions of the Software. - - THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, - EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF - MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND - NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS - BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN - ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN - CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - SOFTWARE. -***/ - -#ifndef _GNU_SOURCE -#define _GNU_SOURCE -#endif - -#include <sys/types.h> -#include <sys/stat.h> -#include <sys/socket.h> -#include <sys/un.h> -#include <sys/fcntl.h> -#include <netinet/in.h> -#include <stdlib.h> -#include <errno.h> -#include <unistd.h> -#include <string.h> -#include <stdarg.h> -#include <stdio.h> -#include <stddef.h> -#include <limits.h> - -#if defined(__linux__) -#include <mqueue.h> -#endif - -#include "sd-daemon.h" - -#if (__GNUC__ >= 4) -#ifdef SD_EXPORT_SYMBOLS -/* Export symbols */ -#define _sd_export_ __attribute__ ((visibility("default"))) -#else -/* Don't export the symbols */ -#define _sd_export_ __attribute__ ((visibility("hidden"))) -#endif -#else -#define _sd_export_ -#endif - -_sd_export_ int sd_listen_fds(int unset_environment) { - -#if defined(DISABLE_SYSTEMD) || !defined(__linux__) - return 0; -#else - int r, fd; - const char *e; - char *p = NULL; - unsigned long l; - - if (!(e = getenv("LISTEN_PID"))) { - r = 0; - goto finish; - } - - errno = 0; - l = strtoul(e, &p, 10); - - if (errno != 0) { - r = -errno; - goto finish; - } - - if (!p || *p || l <= 0) { - r = -EINVAL; - goto finish; - } - - /* Is this for us? */ - if (getpid() != (pid_t) l) { - r = 0; - goto finish; - } - - if (!(e = getenv("LISTEN_FDS"))) { - r = 0; - goto finish; - } - - errno = 0; - l = strtoul(e, &p, 10); - - if (errno != 0) { - r = -errno; - goto finish; - } - - if (!p || *p) { - r = -EINVAL; - goto finish; - } - - for (fd = SD_LISTEN_FDS_START; fd < SD_LISTEN_FDS_START + (int) l; fd ++) { - int flags; - - if ((flags = fcntl(fd, F_GETFD)) < 0) { - r = -errno; - goto finish; - } - - if (flags & FD_CLOEXEC) - continue; - - if (fcntl(fd, F_SETFD, flags | FD_CLOEXEC) < 0) { - r = -errno; - goto finish; - } - } - - r = (int) l; - -finish: - if (unset_environment) { - unsetenv("LISTEN_PID"); - unsetenv("LISTEN_FDS"); - } - - return r; -#endif -} - -_sd_export_ int sd_is_fifo(int fd, const char *path) { - struct stat st_fd; - - if (fd < 0) - return -EINVAL; - - memset(&st_fd, 0, sizeof(st_fd)); - if (fstat(fd, &st_fd) < 0) - return -errno; - - if (!S_ISFIFO(st_fd.st_mode)) - return 0; - - if (path) { - struct stat st_path; - - memset(&st_path, 0, sizeof(st_path)); - if (stat(path, &st_path) < 0) { - - if (errno == ENOENT || errno == ENOTDIR) - return 0; - - return -errno; - } - - return - st_path.st_dev == st_fd.st_dev && - st_path.st_ino == st_fd.st_ino; - } - - return 1; -} - -_sd_export_ int sd_is_special(int fd, const char *path) { - struct stat st_fd; - - if (fd < 0) - return -EINVAL; - - if (fstat(fd, &st_fd) < 0) - return -errno; - - if (!S_ISREG(st_fd.st_mode) && !S_ISCHR(st_fd.st_mode)) - return 0; - - if (path) { - struct stat st_path; - - if (stat(path, &st_path) < 0) { - - if (errno == ENOENT || errno == ENOTDIR) - return 0; - - return -errno; - } - - if (S_ISREG(st_fd.st_mode) && S_ISREG(st_path.st_mode)) - return - st_path.st_dev == st_fd.st_dev && - st_path.st_ino == st_fd.st_ino; - else if (S_ISCHR(st_fd.st_mode) && S_ISCHR(st_path.st_mode)) - return st_path.st_rdev == st_fd.st_rdev; - else - return 0; - } - - return 1; -} - -static int sd_is_socket_internal(int fd, int type, int listening) { - struct stat st_fd; - - if (fd < 0 || type < 0) - return -EINVAL; - - if (fstat(fd, &st_fd) < 0) - return -errno; - - if (!S_ISSOCK(st_fd.st_mode)) - return 0; - - if (type != 0) { - int other_type = 0; - socklen_t l = sizeof(other_type); - - if (getsockopt(fd, SOL_SOCKET, SO_TYPE, &other_type, &l) < 0) - return -errno; - - if (l != sizeof(other_type)) - return -EINVAL; - - if (other_type != type) - return 0; - } - - if (listening >= 0) { - int accepting = 0; - socklen_t l = sizeof(accepting); - - if (getsockopt(fd, SOL_SOCKET, SO_ACCEPTCONN, &accepting, &l) < 0) - return -errno; - - if (l != sizeof(accepting)) - return -EINVAL; - - if (!accepting != !listening) - return 0; - } - - return 1; -} - -union sockaddr_union { - struct sockaddr sa; - struct sockaddr_in in4; - struct sockaddr_in6 in6; - struct sockaddr_un un; - struct sockaddr_storage storage; -}; - -_sd_export_ int sd_is_socket(int fd, int family, int type, int listening) { - int r; - - if (family < 0) - return -EINVAL; - - if ((r = sd_is_socket_internal(fd, type, listening)) <= 0) - return r; - - if (family > 0) { - union sockaddr_union sockaddr; - socklen_t l; - - memset(&sockaddr, 0, sizeof(sockaddr)); - l = sizeof(sockaddr); - - if (getsockname(fd, &sockaddr.sa, &l) < 0) - return -errno; - - if (l < sizeof(sa_family_t)) - return -EINVAL; - - return sockaddr.sa.sa_family == family; - } - - return 1; -} - -_sd_export_ int sd_is_socket_inet(int fd, int family, int type, int listening, uint16_t port) { - union sockaddr_union sockaddr; - socklen_t l; - int r; - - if (family != 0 && family != AF_INET && family != AF_INET6) - return -EINVAL; - - if ((r = sd_is_socket_internal(fd, type, listening)) <= 0) - return r; - - memset(&sockaddr, 0, sizeof(sockaddr)); - l = sizeof(sockaddr); - - if (getsockname(fd, &sockaddr.sa, &l) < 0) - return -errno; - - if (l < sizeof(sa_family_t)) - return -EINVAL; - - if (sockaddr.sa.sa_family != AF_INET && - sockaddr.sa.sa_family != AF_INET6) - return 0; - - if (family > 0) - if (sockaddr.sa.sa_family != family) - return 0; - - if (port > 0) { - if (sockaddr.sa.sa_family == AF_INET) { - if (l < sizeof(struct sockaddr_in)) - return -EINVAL; - - return htons(port) == sockaddr.in4.sin_port; - } else { - if (l < sizeof(struct sockaddr_in6)) - return -EINVAL; - - return htons(port) == sockaddr.in6.sin6_port; - } - } - - return 1; -} - -_sd_export_ int sd_is_socket_unix(int fd, int type, int listening, const char *path, size_t length) { - union sockaddr_union sockaddr; - socklen_t l; - int r; - - if ((r = sd_is_socket_internal(fd, type, listening)) <= 0) - return r; - - memset(&sockaddr, 0, sizeof(sockaddr)); - l = sizeof(sockaddr); - - if (getsockname(fd, &sockaddr.sa, &l) < 0) - return -errno; - - if (l < sizeof(sa_family_t)) - return -EINVAL; - - if (sockaddr.sa.sa_family != AF_UNIX) - return 0; - - if (path) { - if (length <= 0) - length = strlen(path); - - if (length <= 0) - /* Unnamed socket */ - return l == offsetof(struct sockaddr_un, sun_path); - - if (path[0]) - /* Normal path socket */ - return - (l >= offsetof(struct sockaddr_un, sun_path) + length + 1) && - memcmp(path, sockaddr.un.sun_path, length+1) == 0; - else - /* Abstract namespace socket */ - return - (l == offsetof(struct sockaddr_un, sun_path) + length) && - memcmp(path, sockaddr.un.sun_path, length) == 0; - } - - return 1; -} - -_sd_export_ int sd_is_mq(int fd, const char *path) { -#if !defined(__linux__) - return 0; -#else - struct mq_attr attr; - - if (fd < 0) - return -EINVAL; - - if (mq_getattr(fd, &attr) < 0) - return -errno; - - if (path) { - char fpath[PATH_MAX]; - struct stat a, b; - - if (path[0] != '/') - return -EINVAL; - - if (fstat(fd, &a) < 0) - return -errno; - - strncpy(stpcpy(fpath, "/dev/mqueue"), path, sizeof(fpath) - 12); - fpath[sizeof(fpath)-1] = 0; - - if (stat(fpath, &b) < 0) - return -errno; - - if (a.st_dev != b.st_dev || - a.st_ino != b.st_ino) - return 0; - } - - return 1; -#endif -} - -_sd_export_ int sd_notify(int unset_environment, const char *state) { -#if defined(DISABLE_SYSTEMD) || !defined(__linux__) || !defined(SOCK_CLOEXEC) - return 0; -#else - int fd = -1, r; - struct msghdr msghdr; - struct iovec iovec; - union sockaddr_union sockaddr; - const char *e; - - if (!state) { - r = -EINVAL; - goto finish; - } - - if (!(e = getenv("NOTIFY_SOCKET"))) - return 0; - - /* Must be an abstract socket, or an absolute path */ - if ((e[0] != '@' && e[0] != '/') || e[1] == 0) { - r = -EINVAL; - goto finish; - } - - if ((fd = socket(AF_UNIX, SOCK_DGRAM|SOCK_CLOEXEC, 0)) < 0) { - r = -errno; - goto finish; - } - - memset(&sockaddr, 0, sizeof(sockaddr)); - sockaddr.sa.sa_family = AF_UNIX; - strncpy(sockaddr.un.sun_path, e, sizeof(sockaddr.un.sun_path)); - - if (sockaddr.un.sun_path[0] == '@') - sockaddr.un.sun_path[0] = 0; - - memset(&iovec, 0, sizeof(iovec)); - iovec.iov_base = (char*) state; - iovec.iov_len = strlen(state); - - memset(&msghdr, 0, sizeof(msghdr)); - msghdr.msg_name = &sockaddr; - msghdr.msg_namelen = offsetof(struct sockaddr_un, sun_path) + strlen(e); - - if (msghdr.msg_namelen > sizeof(struct sockaddr_un)) - msghdr.msg_namelen = sizeof(struct sockaddr_un); - - msghdr.msg_iov = &iovec; - msghdr.msg_iovlen = 1; - - if (sendmsg(fd, &msghdr, MSG_NOSIGNAL) < 0) { - r = -errno; - goto finish; - } - - r = 1; - -finish: - if (unset_environment) - unsetenv("NOTIFY_SOCKET"); - - if (fd >= 0) - close(fd); - - return r; -#endif -} - -_sd_export_ int sd_notifyf(int unset_environment, const char *format, ...) { -#if defined(DISABLE_SYSTEMD) || !defined(__linux__) - return 0; -#else - va_list ap; - char *p = NULL; - int r; - - va_start(ap, format); - r = vasprintf(&p, format, ap); - va_end(ap); - - if (r < 0 || !p) - return -ENOMEM; - - r = sd_notify(unset_environment, p); - free(p); - - return r; -#endif -} - -_sd_export_ int sd_booted(void) { -#if defined(DISABLE_SYSTEMD) || !defined(__linux__) - return 0; -#else - - struct stat a, b; - - /* We simply test whether the systemd cgroup hierarchy is - * mounted */ - - if (lstat("/sys/fs/cgroup", &a) < 0) - return 0; - - if (lstat("/sys/fs/cgroup/systemd", &b) < 0) - return 0; - - return a.st_dev != b.st_dev; -#endif -} diff --git a/udev/sd-daemon.h b/udev/sd-daemon.h deleted file mode 100644 index 46dc7fd7e5..0000000000 --- a/udev/sd-daemon.h +++ /dev/null @@ -1,277 +0,0 @@ -/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/ - -#ifndef foosddaemonhfoo -#define foosddaemonhfoo - -/*** - Copyright 2010 Lennart Poettering - - Permission is hereby granted, free of charge, to any person - obtaining a copy of this software and associated documentation files - (the "Software"), to deal in the Software without restriction, - including without limitation the rights to use, copy, modify, merge, - publish, distribute, sublicense, and/or sell copies of the Software, - and to permit persons to whom the Software is furnished to do so, - subject to the following conditions: - - The above copyright notice and this permission notice shall be - included in all copies or substantial portions of the Software. - - THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, - EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF - MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND - NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS - BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN - ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN - CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - SOFTWARE. -***/ - -#include <sys/types.h> -#include <inttypes.h> - -#ifdef __cplusplus -extern "C" { -#endif - -/* - Reference implementation of a few systemd related interfaces for - writing daemons. These interfaces are trivial to implement. To - simplify porting we provide this reference implementation. - Applications are welcome to reimplement the algorithms described - here if they do not want to include these two source files. - - The following functionality is provided: - - - Support for logging with log levels on stderr - - File descriptor passing for socket-based activation - - Daemon startup and status notification - - Detection of systemd boots - - You may compile this with -DDISABLE_SYSTEMD to disable systemd - support. This makes all those calls NOPs that are directly related to - systemd (i.e. only sd_is_xxx() will stay useful). - - Since this is drop-in code we don't want any of our symbols to be - exported in any case. Hence we declare hidden visibility for all of - them. - - You may find an up-to-date version of these source files online: - - http://cgit.freedesktop.org/systemd/plain/src/sd-daemon.h - http://cgit.freedesktop.org/systemd/plain/src/sd-daemon.c - - This should compile on non-Linux systems, too, but with the - exception of the sd_is_xxx() calls all functions will become NOPs. - - See sd-daemon(7) for more information. -*/ - -#ifndef _sd_printf_attr_ -#if __GNUC__ >= 4 -#define _sd_printf_attr_(a,b) __attribute__ ((format (printf, a, b))) -#else -#define _sd_printf_attr_(a,b) -#endif -#endif - -/* - Log levels for usage on stderr: - - fprintf(stderr, SD_NOTICE "Hello World!\n"); - - This is similar to printk() usage in the kernel. -*/ -#define SD_EMERG "<0>" /* system is unusable */ -#define SD_ALERT "<1>" /* action must be taken immediately */ -#define SD_CRIT "<2>" /* critical conditions */ -#define SD_ERR "<3>" /* error conditions */ -#define SD_WARNING "<4>" /* warning conditions */ -#define SD_NOTICE "<5>" /* normal but significant condition */ -#define SD_INFO "<6>" /* informational */ -#define SD_DEBUG "<7>" /* debug-level messages */ - -/* The first passed file descriptor is fd 3 */ -#define SD_LISTEN_FDS_START 3 - -/* - Returns how many file descriptors have been passed, or a negative - errno code on failure. Optionally, removes the $LISTEN_FDS and - $LISTEN_PID file descriptors from the environment (recommended, but - problematic in threaded environments). If r is the return value of - this function you'll find the file descriptors passed as fds - SD_LISTEN_FDS_START to SD_LISTEN_FDS_START+r-1. Returns a negative - errno style error code on failure. This function call ensures that - the FD_CLOEXEC flag is set for the passed file descriptors, to make - sure they are not passed on to child processes. If FD_CLOEXEC shall - not be set, the caller needs to unset it after this call for all file - descriptors that are used. - - See sd_listen_fds(3) for more information. -*/ -int sd_listen_fds(int unset_environment); - -/* - Helper call for identifying a passed file descriptor. Returns 1 if - the file descriptor is a FIFO in the file system stored under the - specified path, 0 otherwise. If path is NULL a path name check will - not be done and the call only verifies if the file descriptor - refers to a FIFO. Returns a negative errno style error code on - failure. - - See sd_is_fifo(3) for more information. -*/ -int sd_is_fifo(int fd, const char *path); - -/* - Helper call for identifying a passed file descriptor. Returns 1 if - the file descriptor is a special character device on the file - system stored under the specified path, 0 otherwise. - If path is NULL a path name check will not be done and the call - only verifies if the file descriptor refers to a special character. - Returns a negative errno style error code on failure. - - See sd_is_special(3) for more information. -*/ -int sd_is_special(int fd, const char *path); - -/* - Helper call for identifying a passed file descriptor. Returns 1 if - the file descriptor is a socket of the specified family (AF_INET, - ...) and type (SOCK_DGRAM, SOCK_STREAM, ...), 0 otherwise. If - family is 0 a socket family check will not be done. If type is 0 a - socket type check will not be done and the call only verifies if - the file descriptor refers to a socket. If listening is > 0 it is - verified that the socket is in listening mode. (i.e. listen() has - been called) If listening is == 0 it is verified that the socket is - not in listening mode. If listening is < 0 no listening mode check - is done. Returns a negative errno style error code on failure. - - See sd_is_socket(3) for more information. -*/ -int sd_is_socket(int fd, int family, int type, int listening); - -/* - Helper call for identifying a passed file descriptor. Returns 1 if - the file descriptor is an Internet socket, of the specified family - (either AF_INET or AF_INET6) and the specified type (SOCK_DGRAM, - SOCK_STREAM, ...), 0 otherwise. If version is 0 a protocol version - check is not done. If type is 0 a socket type check will not be - done. If port is 0 a socket port check will not be done. The - listening flag is used the same way as in sd_is_socket(). Returns a - negative errno style error code on failure. - - See sd_is_socket_inet(3) for more information. -*/ -int sd_is_socket_inet(int fd, int family, int type, int listening, uint16_t port); - -/* - Helper call for identifying a passed file descriptor. Returns 1 if - the file descriptor is an AF_UNIX socket of the specified type - (SOCK_DGRAM, SOCK_STREAM, ...) and path, 0 otherwise. If type is 0 - a socket type check will not be done. If path is NULL a socket path - check will not be done. For normal AF_UNIX sockets set length to - 0. For abstract namespace sockets set length to the length of the - socket name (including the initial 0 byte), and pass the full - socket path in path (including the initial 0 byte). The listening - flag is used the same way as in sd_is_socket(). Returns a negative - errno style error code on failure. - - See sd_is_socket_unix(3) for more information. -*/ -int sd_is_socket_unix(int fd, int type, int listening, const char *path, size_t length); - -/* - Helper call for identifying a passed file descriptor. Returns 1 if - the file descriptor is a POSIX Message Queue of the specified name, - 0 otherwise. If path is NULL a message queue name check is not - done. Returns a negative errno style error code on failure. -*/ -int sd_is_mq(int fd, const char *path); - -/* - Informs systemd about changed daemon state. This takes a number of - newline separated environment-style variable assignments in a - string. The following variables are known: - - READY=1 Tells systemd that daemon startup is finished (only - relevant for services of Type=notify). The passed - argument is a boolean "1" or "0". Since there is - little value in signaling non-readiness the only - value daemons should send is "READY=1". - - STATUS=... Passes a single-line status string back to systemd - that describes the daemon state. This is free-from - and can be used for various purposes: general state - feedback, fsck-like programs could pass completion - percentages and failing programs could pass a human - readable error message. Example: "STATUS=Completed - 66% of file system check..." - - ERRNO=... If a daemon fails, the errno-style error code, - formatted as string. Example: "ERRNO=2" for ENOENT. - - BUSERROR=... If a daemon fails, the D-Bus error-style error - code. Example: "BUSERROR=org.freedesktop.DBus.Error.TimedOut" - - MAINPID=... The main pid of a daemon, in case systemd did not - fork off the process itself. Example: "MAINPID=4711" - - Daemons can choose to send additional variables. However, it is - recommended to prefix variable names not listed above with X_. - - Returns a negative errno-style error code on failure. Returns > 0 - if systemd could be notified, 0 if it couldn't possibly because - systemd is not running. - - Example: When a daemon finished starting up, it could issue this - call to notify systemd about it: - - sd_notify(0, "READY=1"); - - See sd_notifyf() for more complete examples. - - See sd_notify(3) for more information. -*/ -int sd_notify(int unset_environment, const char *state); - -/* - Similar to sd_notify() but takes a format string. - - Example 1: A daemon could send the following after initialization: - - sd_notifyf(0, "READY=1\n" - "STATUS=Processing requests...\n" - "MAINPID=%lu", - (unsigned long) getpid()); - - Example 2: A daemon could send the following shortly before - exiting, on failure: - - sd_notifyf(0, "STATUS=Failed to start up: %s\n" - "ERRNO=%i", - strerror(errno), - errno); - - See sd_notifyf(3) for more information. -*/ -int sd_notifyf(int unset_environment, const char *format, ...) _sd_printf_attr_(2,3); - -/* - Returns > 0 if the system was booted with systemd. Returns < 0 on - error. Returns 0 if the system was not booted with systemd. Note - that all of the functions above handle non-systemd boots just - fine. You should NOT protect them with a call to this function. Also - note that this function checks whether the system, not the user - session is controlled by systemd. However the functions above work - for both user and system services. - - See sd_booted(3) for more information. -*/ -int sd_booted(void); - -#ifdef __cplusplus -} -#endif - -#endif diff --git a/udev/test-udev.c b/udev/test-udev.c deleted file mode 100644 index 8d5baf7f54..0000000000 --- a/udev/test-udev.c +++ /dev/null @@ -1,121 +0,0 @@ -/* - * Copyright (C) 2003-2004 Greg Kroah-Hartman <greg@kroah.com> - * Copyright (C) 2004-2008 Kay Sievers <kay.sievers@vrfy.org> - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see <http://www.gnu.org/licenses/>. - */ - -#include <stdio.h> -#include <stddef.h> -#include <stdlib.h> -#include <string.h> -#include <fcntl.h> -#include <ctype.h> -#include <errno.h> -#include <unistd.h> -#include <syslog.h> -#include <grp.h> -#include <sys/signalfd.h> - -#include "udev.h" - -void udev_main_log(struct udev *udev, int priority, - const char *file, int line, const char *fn, - const char *format, va_list args) {} - -int main(int argc, char *argv[]) -{ - struct udev *udev; - struct udev_event *event = NULL; - struct udev_device *dev = NULL; - struct udev_rules *rules = NULL; - char syspath[UTIL_PATH_SIZE]; - const char *devpath; - const char *action; - sigset_t mask, sigmask_orig; - int err = -EINVAL; - - udev = udev_new(); - if (udev == NULL) - exit(1); - info(udev, "version %s\n", VERSION); - udev_selinux_init(udev); - - sigprocmask(SIG_SETMASK, NULL, &sigmask_orig); - - action = argv[1]; - if (action == NULL) { - err(udev, "action missing\n"); - goto out; - } - - devpath = argv[2]; - if (devpath == NULL) { - err(udev, "devpath missing\n"); - goto out; - } - - rules = udev_rules_new(udev, 1); - - util_strscpyl(syspath, sizeof(syspath), udev_get_sys_path(udev), devpath, NULL); - dev = udev_device_new_from_syspath(udev, syspath); - if (dev == NULL) { - info(udev, "unknown device '%s'\n", devpath); - goto out; - } - - udev_device_set_action(dev, action); - event = udev_event_new(dev); - - sigfillset(&mask); - sigprocmask(SIG_SETMASK, &mask, &sigmask_orig); - event->fd_signal = signalfd(-1, &mask, SFD_NONBLOCK|SFD_CLOEXEC); - if (event->fd_signal < 0) { - fprintf(stderr, "error creating signalfd\n"); - goto out; - } - - /* do what devtmpfs usually provides us */ - if (udev_device_get_devnode(dev) != NULL) { - mode_t mode; - - if (strcmp(udev_device_get_subsystem(dev), "block") == 0) - mode |= S_IFBLK; - else - mode |= S_IFCHR; - - if (strcmp(action, "remove") != 0) { - util_create_path(udev, udev_device_get_devnode(dev)); - mknod(udev_device_get_devnode(dev), mode, udev_device_get_devnum(dev)); - } else { - unlink(udev_device_get_devnode(dev)); - util_delete_path(udev, udev_device_get_devnode(dev)); - } - } - - err = udev_event_execute_rules(event, rules, &sigmask_orig); - if (err == 0) - udev_event_execute_run(event, NULL); -out: - if (event != NULL && event->fd_signal >= 0) - close(event->fd_signal); - udev_event_unref(event); - udev_device_unref(dev); - udev_rules_unref(rules); - udev_selinux_exit(udev); - udev_unref(udev); - if (err != 0) - return 1; - return 0; -} diff --git a/udev/udev-builtin-blkid.c b/udev/udev-builtin-blkid.c deleted file mode 100644 index 0260c440e2..0000000000 --- a/udev/udev-builtin-blkid.c +++ /dev/null @@ -1,207 +0,0 @@ -/* - * probe disks for filesystems and partitions - * - * Copyright (C) 2011 Kay Sievers <kay.sievers@vrfy.org> - * Copyright (C) 2011 Karel Zak <kzak@redhat.com> - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see <http://www.gnu.org/licenses/>. - */ - -#include <stdio.h> -#include <stdlib.h> -#include <stdarg.h> -#include <unistd.h> -#include <string.h> -#include <errno.h> -#include <fcntl.h> -#include <getopt.h> -#include <sys/stat.h> -#include <blkid/blkid.h> - -#include "udev.h" - -static void print_property(struct udev_device *dev, bool test, const char *name, const char *value) -{ - char s[265]; - - s[0] = '\0'; - - if (!strcmp(name, "TYPE")) { - udev_builtin_add_property(dev, test, "ID_FS_TYPE", value); - - } else if (!strcmp(name, "USAGE")) { - udev_builtin_add_property(dev, test, "ID_FS_USAGE", value); - - } else if (!strcmp(name, "VERSION")) { - udev_builtin_add_property(dev, test, "ID_FS_VERSION", value); - - } else if (!strcmp(name, "UUID")) { - blkid_safe_string(value, s, sizeof(s)); - udev_builtin_add_property(dev, test, "ID_FS_UUID", s); - blkid_encode_string(value, s, sizeof(s)); - udev_builtin_add_property(dev, test, "ID_FS_UUID_ENC", s); - - } else if (!strcmp(name, "UUID_SUB")) { - blkid_safe_string(value, s, sizeof(s)); - udev_builtin_add_property(dev, test, "ID_FS_UUID_SUB", s); - blkid_encode_string(value, s, sizeof(s)); - udev_builtin_add_property(dev, test, "ID_FS_UUID_SUB_ENC", s); - - } else if (!strcmp(name, "LABEL")) { - blkid_safe_string(value, s, sizeof(s)); - udev_builtin_add_property(dev, test, "ID_FS_LABEL", s); - blkid_encode_string(value, s, sizeof(s)); - udev_builtin_add_property(dev, test, "ID_FS_LABEL_ENC", s); - - } else if (!strcmp(name, "PTTYPE")) { - udev_builtin_add_property(dev, test, "ID_PART_TABLE_TYPE", value); - - } else if (!strcmp(name, "PART_ENTRY_NAME")) { - blkid_encode_string(value, s, sizeof(s)); - udev_builtin_add_property(dev, test, "PART_ENTRY_NAME", s); - - } else if (!strcmp(name, "PART_ENTRY_TYPE")) { - blkid_encode_string(value, s, sizeof(s)); - udev_builtin_add_property(dev, test, "PART_ENTRY_TYPE", s); - - } else if (!strncmp(name, "PART_ENTRY_", 11)) { - util_strscpyl(s, sizeof(s), "ID_", name, NULL); - udev_builtin_add_property(dev, test, name, value); - } -} - -static int probe_superblocks(blkid_probe pr) -{ - struct stat st; - int rc; - - if (fstat(blkid_probe_get_fd(pr), &st)) - return -1; - - blkid_probe_enable_partitions(pr, 1); - - if (!S_ISCHR(st.st_mode) && blkid_probe_get_size(pr) <= 1024 * 1440 && - blkid_probe_is_wholedisk(pr)) { - /* - * check if the small disk is partitioned, if yes then - * don't probe for filesystems. - */ - blkid_probe_enable_superblocks(pr, 0); - - rc = blkid_do_fullprobe(pr); - if (rc < 0) - return rc; /* -1 = error, 1 = nothing, 0 = succes */ - - if (blkid_probe_lookup_value(pr, "PTTYPE", NULL, NULL) == 0) - return 0; /* partition table detected */ - } - - blkid_probe_set_partitions_flags(pr, BLKID_PARTS_ENTRY_DETAILS); - blkid_probe_enable_superblocks(pr, 1); - - return blkid_do_safeprobe(pr); -} - -static int builtin_blkid(struct udev_device *dev, int argc, char *argv[], bool test) -{ - struct udev *udev = udev_device_get_udev(dev); - int64_t offset = 0; - bool noraid = false; - int fd = -1; - blkid_probe pr; - const char *data; - const char *name; - int nvals; - int i; - size_t len; - int err = 0; - - static const struct option options[] = { - { "offset", optional_argument, NULL, 'o' }, - { "noraid", no_argument, NULL, 'R' }, - {} - }; - - for (;;) { - int option; - - option = getopt_long(argc, argv, "oR", options, NULL); - if (option == -1) - break; - - switch (option) { - case 'o': - offset = strtoull(optarg, NULL, 0); - break; - case 'R': - noraid = true; - break; - } - } - - pr = blkid_new_probe(); - if (!pr) { - err = -ENOMEM; - return EXIT_FAILURE; - } - - blkid_probe_set_superblocks_flags(pr, - BLKID_SUBLKS_LABEL | BLKID_SUBLKS_UUID | - BLKID_SUBLKS_TYPE | BLKID_SUBLKS_SECTYPE | - BLKID_SUBLKS_USAGE | BLKID_SUBLKS_VERSION); - - if (noraid) - blkid_probe_filter_superblocks_usage(pr, BLKID_FLTR_NOTIN, BLKID_USAGE_RAID); - - fd = open(udev_device_get_devnode(dev), O_RDONLY|O_CLOEXEC); - if (fd < 0) { - fprintf(stderr, "error: %s: %m\n", udev_device_get_devnode(dev)); - goto out; - } - - err = blkid_probe_set_device(pr, fd, offset, 0); - if (err < 0) - goto out; - - info(udev, "probe %s %sraid offset=%llu\n", - udev_device_get_devnode(dev), - noraid ? "no" : "", (unsigned long long) offset); - - err = probe_superblocks(pr); - if (err < 0) - goto out; - - nvals = blkid_probe_numof_values(pr); - for (i = 0; i < nvals; i++) { - if (blkid_probe_get_value(pr, i, &name, &data, &len)) - continue; - len = strnlen((char *) data, len); - print_property(dev, test, name, (char *) data); - } - - blkid_free_probe(pr); -out: - if (fd > 0) - close(fd); - if (err < 0) - return EXIT_FAILURE; - return EXIT_SUCCESS; -} - -const struct udev_builtin udev_builtin_blkid = { - .name = "blkid", - .cmd = builtin_blkid, - .help = "filesystem and partition probing", - .run_once = true, -}; diff --git a/udev/udev-builtin-firmware.c b/udev/udev-builtin-firmware.c deleted file mode 100644 index d7921a2693..0000000000 --- a/udev/udev-builtin-firmware.c +++ /dev/null @@ -1,168 +0,0 @@ -/* - * firmware - Kernel firmware loader - * - * Copyright (C) 2009 Piter Punk <piterpunk@slackware.com> - * Copyright (C) 2009-2011 Kay Sievers <kay.sievers@vrfy.org> - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License as - * published by the Free Software Foundation; either version 2 of the - * License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * General Public License for more details:* - */ - -#include <unistd.h> -#include <stdlib.h> -#include <string.h> -#include <stdio.h> -#include <getopt.h> -#include <errno.h> -#include <stdbool.h> -#include <sys/utsname.h> -#include <sys/stat.h> - -#include "udev.h" - -static bool set_loading(struct udev *udev, char *loadpath, const char *state) -{ - FILE *ldfile; - - ldfile = fopen(loadpath, "we"); - if (ldfile == NULL) { - err(udev, "error: can not open '%s'\n", loadpath); - return false; - }; - fprintf(ldfile, "%s\n", state); - fclose(ldfile); - return true; -} - -static bool copy_firmware(struct udev *udev, const char *source, const char *target, size_t size) -{ - char *buf; - FILE *fsource = NULL, *ftarget = NULL; - bool ret = false; - - buf = malloc(size); - if (buf == NULL) { - err(udev,"No memory available to load firmware file"); - return false; - } - - info(udev, "writing '%s' (%zi) to '%s'\n", source, size, target); - - fsource = fopen(source, "re"); - if (fsource == NULL) - goto exit; - ftarget = fopen(target, "we"); - if (ftarget == NULL) - goto exit; - if (fread(buf, size, 1, fsource) != 1) - goto exit; - if (fwrite(buf, size, 1, ftarget) == 1) - ret = true; -exit: - if (ftarget != NULL) - fclose(ftarget); - if (fsource != NULL) - fclose(fsource); - free(buf); - return ret; -} - -static int builtin_firmware(struct udev_device *dev, int argc, char *argv[], bool test) -{ - struct udev *udev = udev_device_get_udev(dev); - static const char *searchpath[] = { FIRMWARE_PATH }; - char fwencpath[UTIL_PATH_SIZE]; - char misspath[UTIL_PATH_SIZE]; - char loadpath[UTIL_PATH_SIZE]; - char datapath[UTIL_PATH_SIZE]; - char fwpath[UTIL_PATH_SIZE]; - const char *firmware; - FILE *fwfile; - struct utsname kernel; - struct stat statbuf; - unsigned int i; - int rc = EXIT_SUCCESS; - - firmware = udev_device_get_property_value(dev, "FIRMWARE"); - if (firmware == NULL) { - err(udev, "firmware parameter missing\n\n"); - rc = EXIT_FAILURE; - goto exit; - } - - /* lookup firmware file */ - uname(&kernel); - for (i = 0; i < ARRAY_SIZE(searchpath); i++) { - util_strscpyl(fwpath, sizeof(fwpath), searchpath[i], kernel.release, "/", firmware, NULL); - dbg(udev, "trying %s\n", fwpath); - fwfile = fopen(fwpath, "re"); - if (fwfile != NULL) - break; - - util_strscpyl(fwpath, sizeof(fwpath), searchpath[i], firmware, NULL); - dbg(udev, "trying %s\n", fwpath); - fwfile = fopen(fwpath, "re"); - if (fwfile != NULL) - break; - } - - util_path_encode(firmware, fwencpath, sizeof(fwencpath)); - util_strscpyl(misspath, sizeof(misspath), udev_get_run_path(udev), "/firmware-missing/", fwencpath, NULL); - util_strscpyl(loadpath, sizeof(loadpath), udev_device_get_syspath(dev), "/loading", NULL); - - if (fwfile == NULL) { - int err; - - /* This link indicates the missing firmware file and the associated device */ - info(udev, "did not find firmware file '%s'\n", firmware); - do { - err = util_create_path(udev, misspath); - if (err != 0 && err != -ENOENT) - break; - err = symlink(udev_device_get_devpath(dev), misspath); - if (err != 0) - err = -errno; - } while (err == -ENOENT); - rc = EXIT_FAILURE; - set_loading(udev, loadpath, "-1"); - goto exit; - } - - if (stat(fwpath, &statbuf) < 0 || statbuf.st_size == 0) { - rc = EXIT_FAILURE; - goto exit; - } - if (unlink(misspath) == 0) - util_delete_path(udev, misspath); - - if (!set_loading(udev, loadpath, "1")) - goto exit; - - util_strscpyl(datapath, sizeof(datapath), udev_device_get_syspath(dev), "/data", NULL); - if (!copy_firmware(udev, fwpath, datapath, statbuf.st_size)) { - err(udev, "error sending firmware '%s' to device\n", firmware); - set_loading(udev, loadpath, "-1"); - rc = EXIT_FAILURE; - goto exit; - }; - - set_loading(udev, loadpath, "0"); -exit: - if (fwfile) - fclose(fwfile); - return rc; -} - -const struct udev_builtin udev_builtin_firmware = { - .name = "firmware", - .cmd = builtin_firmware, - .help = "kernel firmware loader", - .run_once = true, -}; diff --git a/udev/udev-builtin-hwdb.c b/udev/udev-builtin-hwdb.c deleted file mode 100644 index b6af4b6fcf..0000000000 --- a/udev/udev-builtin-hwdb.c +++ /dev/null @@ -1,247 +0,0 @@ -/* - * usb-db, pci-db - lookup vendor/product database - * - * Copyright (C) 2009 Lennart Poettering <lennart@poettering.net> - * Copyright (C) 2011 Kay Sievers <kay.sievers@vrfy.org> - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see <http://www.gnu.org/licenses/>. - */ - -#include <stdio.h> -#include <errno.h> -#include <string.h> -#include <inttypes.h> -#include <ctype.h> -#include <stdlib.h> - -#include "udev.h" - -static int get_id_attr( - struct udev_device *parent, - const char *name, - uint16_t *value) { - - const char *t; - unsigned u; - - if (!(t = udev_device_get_sysattr_value(parent, name))) { - fprintf(stderr, "%s lacks %s.\n", udev_device_get_syspath(parent), name); - return -1; - } - - if (!strncmp(t, "0x", 2)) - t += 2; - - if (sscanf(t, "%04x", &u) != 1 || u > 0xFFFFU) { - fprintf(stderr, "Failed to parse %s on %s.\n", name, udev_device_get_syspath(parent)); - return -1; - } - - *value = (uint16_t) u; - return 0; -} - -static int get_vid_pid( - struct udev_device *parent, - const char *vendor_attr, - const char *product_attr, - uint16_t *vid, - uint16_t *pid) { - - if (get_id_attr(parent, vendor_attr, vid) < 0) - return -1; - else if (*vid <= 0) { - fprintf(stderr, "Invalid vendor id.\n"); - return -1; - } - - if (get_id_attr(parent, product_attr, pid) < 0) - return -1; - - return 0; -} - -static void rstrip(char *n) { - size_t i; - - for (i = strlen(n); i > 0 && isspace(n[i-1]); i--) - n[i-1] = 0; -} - -#define HEXCHARS "0123456789abcdefABCDEF" -#define WHITESPACE " \t\n\r" -static int lookup_vid_pid(const char *database, - uint16_t vid, uint16_t pid, - char **vendor, char **product) -{ - - FILE *f; - int ret = -1; - int found_vendor = 0; - char *line = NULL; - - *vendor = *product = NULL; - - if (!(f = fopen(database, "rme"))) { - fprintf(stderr, "Failed to open database file '%s': %s\n", database, strerror(errno)); - return -1; - } - - for (;;) { - size_t n; - - if (getline(&line, &n, f) < 0) - break; - - rstrip(line); - - if (line[0] == '#' || line[0] == 0) - continue; - - if (strspn(line, HEXCHARS) == 4) { - unsigned u; - - if (found_vendor) - break; - - if (sscanf(line, "%04x", &u) == 1 && u == vid) { - char *t; - - t = line+4; - t += strspn(t, WHITESPACE); - - if (!(*vendor = strdup(t))) { - fprintf(stderr, "Out of memory.\n"); - goto finish; - } - - found_vendor = 1; - } - - continue; - } - - if (found_vendor && line[0] == '\t' && strspn(line+1, HEXCHARS) == 4) { - unsigned u; - - if (sscanf(line+1, "%04x", &u) == 1 && u == pid) { - char *t; - - t = line+5; - t += strspn(t, WHITESPACE); - - if (!(*product = strdup(t))) { - fprintf(stderr, "Out of memory.\n"); - goto finish; - } - - break; - } - } - } - - ret = 0; - -finish: - free(line); - fclose(f); - - if (ret < 0) { - free(*product); - free(*vendor); - - *product = *vendor = NULL; - } - - return ret; -} - -static struct udev_device *find_device(struct udev_device *dev, const char *subsys, const char *devtype) -{ - const char *str; - - str = udev_device_get_subsystem(dev); - if (str == NULL) - goto try_parent; - if (strcmp(str, subsys) != 0) - goto try_parent; - - if (devtype != NULL) { - str = udev_device_get_devtype(dev); - if (str == NULL) - goto try_parent; - if (strcmp(str, devtype) != 0) - goto try_parent; - } - return dev; -try_parent: - return udev_device_get_parent_with_subsystem_devtype(dev, subsys, devtype); -} - - -static int builtin_db(struct udev_device *dev, bool test, - const char *database, - const char *vendor_attr, const char *product_attr, - const char *subsys, const char *devtype) -{ - struct udev_device *parent; - uint16_t vid = 0, pid = 0; - char *vendor = NULL, *product = NULL; - - parent = find_device(dev, subsys, devtype); - if (!parent) { - fprintf(stderr, "Failed to find device.\n"); - goto finish; - } - - if (get_vid_pid(parent, vendor_attr, product_attr, &vid, &pid) < 0) - goto finish; - - if (lookup_vid_pid(database, vid, pid, &vendor, &product) < 0) - goto finish; - - if (vendor) - udev_builtin_add_property(dev, test, "ID_VENDOR_FROM_DATABASE", vendor); - if (product) - udev_builtin_add_property(dev, test, "ID_MODEL_FROM_DATABASE", product); - -finish: - free(vendor); - free(product); - return 0; -} - -static int builtin_usb_db(struct udev_device *dev, int argc, char *argv[], bool test) -{ - return builtin_db(dev, test, USB_DATABASE, "idVendor", "idProduct", "usb", "usb_device"); -} - -static int builtin_pci_db(struct udev_device *dev, int argc, char *argv[], bool test) -{ - return builtin_db(dev, test, PCI_DATABASE, "vendor", "device", "pci", NULL); -} - -const struct udev_builtin udev_builtin_usb_db = { - .name = "usb-db", - .cmd = builtin_usb_db, - .help = "USB vendor/product database", - .run_once = true, -}; - -const struct udev_builtin udev_builtin_pci_db = { - .name = "pci-db", - .cmd = builtin_pci_db, - .help = "PCI vendor/product database", - .run_once = true, -}; diff --git a/udev/udev-builtin-input_id.c b/udev/udev-builtin-input_id.c deleted file mode 100644 index 4ef060d899..0000000000 --- a/udev/udev-builtin-input_id.c +++ /dev/null @@ -1,218 +0,0 @@ -/* - * compose persistent device path - * - * Copyright (C) 2009 Martin Pitt <martin.pitt@ubuntu.com> - * Portions Copyright (C) 2004 David Zeuthen, <david@fubar.dk> - * Copyright (C) 2011 Kay Sievers <kay.sievers@vrfy.org> - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see <http://www.gnu.org/licenses/>. - */ - -#include <stdio.h> -#include <stdlib.h> -#include <stdarg.h> -#include <unistd.h> -#include <string.h> -#include <errno.h> -#include <linux/limits.h> -#include <linux/input.h> - -#include "udev.h" - -/* we must use this kernel-compatible implementation */ -#define BITS_PER_LONG (sizeof(unsigned long) * 8) -#define NBITS(x) ((((x)-1)/BITS_PER_LONG)+1) -#define OFF(x) ((x)%BITS_PER_LONG) -#define BIT(x) (1UL<<OFF(x)) -#define LONG(x) ((x)/BITS_PER_LONG) -#define test_bit(bit, array) ((array[LONG(bit)] >> OFF(bit)) & 1) - -/* - * Read a capability attribute and return bitmask. - * @param dev udev_device - * @param attr sysfs attribute name (e. g. "capabilities/key") - * @param bitmask: Output array which has a sizeof of bitmask_size - */ -static void get_cap_mask(struct udev_device *dev, - struct udev_device *pdev, const char* attr, - unsigned long *bitmask, size_t bitmask_size, - bool test) -{ - struct udev *udev = udev_device_get_udev(dev); - char text[4096]; - unsigned i; - char* word; - unsigned long val; - - snprintf(text, sizeof(text), "%s", udev_device_get_sysattr_value(pdev, attr)); - info(udev, "%s raw kernel attribute: %s\n", attr, text); - - memset (bitmask, 0, bitmask_size); - i = 0; - while ((word = strrchr(text, ' ')) != NULL) { - val = strtoul (word+1, NULL, 16); - if (i < bitmask_size/sizeof(unsigned long)) - bitmask[i] = val; - else - info(udev, "ignoring %s block %lX which is larger than maximum size\n", attr, val); - *word = '\0'; - ++i; - } - val = strtoul (text, NULL, 16); - if (i < bitmask_size / sizeof(unsigned long)) - bitmask[i] = val; - else - info(udev, "ignoring %s block %lX which is larger than maximum size\n", attr, val); - - if (test) { - /* printf pattern with the right unsigned long number of hex chars */ - snprintf(text, sizeof(text), " bit %%4u: %%0%zilX\n", 2 * sizeof(unsigned long)); - info(udev, "%s decoded bit map:\n", attr); - val = bitmask_size / sizeof (unsigned long); - /* skip over leading zeros */ - while (bitmask[val-1] == 0 && val > 0) - --val; - for (i = 0; i < val; ++i) - info(udev, text, i * BITS_PER_LONG, bitmask[i]); - } -} - -/* pointer devices */ -static void test_pointers (struct udev_device *dev, - const unsigned long* bitmask_ev, - const unsigned long* bitmask_abs, - const unsigned long* bitmask_key, - const unsigned long* bitmask_rel, - bool test) -{ - int is_mouse = 0; - int is_touchpad = 0; - - if (!test_bit (EV_KEY, bitmask_ev)) { - if (test_bit (EV_ABS, bitmask_ev) && - test_bit (ABS_X, bitmask_abs) && - test_bit (ABS_Y, bitmask_abs) && - test_bit (ABS_Z, bitmask_abs)) - udev_builtin_add_property(dev, test, "ID_INPUT_ACCELEROMETER", "1"); - return; - } - - if (test_bit (EV_ABS, bitmask_ev) && - test_bit (ABS_X, bitmask_abs) && test_bit (ABS_Y, bitmask_abs)) { - if (test_bit (BTN_STYLUS, bitmask_key) || test_bit (BTN_TOOL_PEN, bitmask_key)) - udev_builtin_add_property(dev, test, "ID_INPUT_TABLET", "1"); - else if (test_bit (BTN_TOOL_FINGER, bitmask_key) && !test_bit (BTN_TOOL_PEN, bitmask_key)) - is_touchpad = 1; - else if (test_bit (BTN_TRIGGER, bitmask_key) || - test_bit (BTN_A, bitmask_key) || - test_bit (BTN_1, bitmask_key)) - udev_builtin_add_property(dev, test, "ID_INPUT_JOYSTICK", "1"); - else if (test_bit (BTN_MOUSE, bitmask_key)) - /* This path is taken by VMware's USB mouse, which has - * absolute axes, but no touch/pressure button. */ - is_mouse = 1; - else if (test_bit (BTN_TOUCH, bitmask_key)) - udev_builtin_add_property(dev, test, "ID_INPUT_TOUCHSCREEN", "1"); - } - - if (test_bit (EV_REL, bitmask_ev) && - test_bit (REL_X, bitmask_rel) && test_bit (REL_Y, bitmask_rel) && - test_bit (BTN_MOUSE, bitmask_key)) - is_mouse = 1; - - if (is_mouse) - udev_builtin_add_property(dev, test, "ID_INPUT_MOUSE", "1"); - if (is_touchpad) - udev_builtin_add_property(dev, test, "ID_INPUT_TOUCHPAD", "1"); -} - -/* key like devices */ -static void test_key (struct udev_device *dev, - const unsigned long* bitmask_ev, - const unsigned long* bitmask_key, - bool test) -{ - struct udev *udev = udev_device_get_udev(dev); - unsigned i; - unsigned long found; - unsigned long mask; - - /* do we have any KEY_* capability? */ - if (!test_bit (EV_KEY, bitmask_ev)) { - info(udev, "test_key: no EV_KEY capability\n"); - return; - } - - /* only consider KEY_* here, not BTN_* */ - found = 0; - for (i = 0; i < BTN_MISC/BITS_PER_LONG; ++i) { - found |= bitmask_key[i]; - info(udev, "test_key: checking bit block %lu for any keys; found=%i\n", i*BITS_PER_LONG, found > 0); - } - /* If there are no keys in the lower block, check the higher block */ - if (!found) { - for (i = KEY_OK; i < BTN_TRIGGER_HAPPY; ++i) { - if (test_bit (i, bitmask_key)) { - info(udev, "test_key: Found key %x in high block\n", i); - found = 1; - break; - } - } - } - - if (found > 0) - udev_builtin_add_property(dev, test, "ID_INPUT_KEY", "1"); - - /* the first 32 bits are ESC, numbers, and Q to D; if we have all of - * those, consider it a full keyboard; do not test KEY_RESERVED, though */ - mask = 0xFFFFFFFE; - if ((bitmask_key[0] & mask) == mask) - udev_builtin_add_property(dev, test, "ID_INPUT_KEYBOARD", "1"); -} - -static int builtin_input_id(struct udev_device *dev, int argc, char *argv[], bool test) -{ - struct udev_device *pdev; - unsigned long bitmask_ev[NBITS(EV_MAX)]; - unsigned long bitmask_abs[NBITS(ABS_MAX)]; - unsigned long bitmask_key[NBITS(KEY_MAX)]; - unsigned long bitmask_rel[NBITS(REL_MAX)]; - - /* walk up the parental chain until we find the real input device; the - * argument is very likely a subdevice of this, like eventN */ - pdev = dev; - while (pdev != NULL && udev_device_get_sysattr_value(pdev, "capabilities/ev") == NULL) - pdev = udev_device_get_parent_with_subsystem_devtype(pdev, "input", NULL); - - /* not an "input" class device */ - if (pdev == NULL) - return EXIT_SUCCESS; - - /* Use this as a flag that input devices were detected, so that this - * program doesn't need to be called more than once per device */ - udev_builtin_add_property(dev, test, "ID_INPUT", "1"); - get_cap_mask(dev, pdev, "capabilities/ev", bitmask_ev, sizeof(bitmask_ev), test); - get_cap_mask(dev, pdev, "capabilities/abs", bitmask_abs, sizeof(bitmask_abs), test); - get_cap_mask(dev, pdev, "capabilities/rel", bitmask_rel, sizeof(bitmask_rel), test); - get_cap_mask(dev, pdev, "capabilities/key", bitmask_key, sizeof(bitmask_key), test); - test_pointers(dev, bitmask_ev, bitmask_abs, bitmask_key, bitmask_rel, test); - test_key(dev, bitmask_ev, bitmask_key, test); - return EXIT_SUCCESS; -} - -const struct udev_builtin udev_builtin_input_id = { - .name = "input_id", - .cmd = builtin_input_id, - .help = "input device properties", -}; diff --git a/udev/udev-builtin-kmod.c b/udev/udev-builtin-kmod.c deleted file mode 100644 index 6719432c08..0000000000 --- a/udev/udev-builtin-kmod.c +++ /dev/null @@ -1,146 +0,0 @@ -/* - * load kernel modules - * - * Copyright (C) 2011 Kay Sievers <kay.sievers@vrfy.org> - * Copyright (C) 2011 ProFUSION embedded systems - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see <http://www.gnu.org/licenses/>. - */ - -#include <stdio.h> -#include <stdlib.h> -#include <stdarg.h> -#include <unistd.h> -#include <string.h> -#include <errno.h> -#include <fcntl.h> -#include <sys/stat.h> -#include <sys/wait.h> -#include <libkmod.h> - -#include "udev.h" - -static struct kmod_ctx *ctx; - -static int load_module(struct udev *udev, const char *alias) -{ - struct kmod_list *list = NULL; - struct kmod_list *listb = NULL; - struct kmod_list *l; - int err; - - err = kmod_module_new_from_lookup(ctx, alias, &list); - if (err < 0) - return err; - - err = kmod_module_get_filtered_blacklist(ctx, list, &listb); - if (err < 0) - return err; - - if (list == NULL) - info(udev, "no module matches '%s'\n", alias); - - kmod_list_foreach(l, listb) { - struct kmod_module *mod = kmod_module_get_module(l); - - err = kmod_module_probe_insert_module(mod, 0, NULL, NULL, NULL); - if (err >=0 ) - info(udev, "inserted '%s'\n", kmod_module_get_name(mod)); - else - info(udev, "failed to insert '%s'\n", kmod_module_get_name(mod)); - - kmod_module_unref(mod); - } - - kmod_module_unref_list(list); - kmod_module_unref_list(listb); - return err; -} - -static void udev_kmod_log(void *data, int priority, const char *file, int line, - const char *fn, const char *format, va_list args) -{ - udev_main_log(data, priority, file, line, fn, format, args); -} - -/* needs to re-instantiate the context after a reload */ -static int builtin_kmod(struct udev_device *dev, int argc, char *argv[], bool test) -{ - struct udev *udev = udev_device_get_udev(dev); - int i; - - if (!ctx) { - ctx = kmod_new(NULL, NULL); - if (!ctx) - return -ENOMEM; - - info(udev, "load module index\n"); - kmod_set_log_fn(ctx, udev_kmod_log, udev); - kmod_load_resources(ctx); - } - - if (argc < 3 || strcmp(argv[1], "load")) { - err(udev, "expect: %s load <module>\n", argv[0]); - return EXIT_FAILURE; - } - - for (i = 2; argv[i]; i++) { - info(udev, "execute '%s' '%s'\n", argv[1], argv[i]); - load_module(udev, argv[i]); - } - - return EXIT_SUCCESS; -} - -/* called at udev startup */ -static int builtin_kmod_init(struct udev *udev) -{ - if (ctx) - return 0; - - ctx = kmod_new(NULL, NULL); - if (!ctx) - return -ENOMEM; - - info(udev, "load module index\n"); - kmod_set_log_fn(ctx, udev_kmod_log, udev); - kmod_load_resources(ctx); - return 0; -} - -/* called on udev shutdown and reload request */ -static void builtin_kmod_exit(struct udev *udev) -{ - info(udev, "unload module index\n"); - ctx = kmod_unref(ctx); -} - -/* called every couple of seconds during event activity; 'true' if config has changed */ -static bool builtin_kmod_validate(struct udev *udev) -{ - info(udev, "validate module index\n"); - if (kmod_validate_resources(ctx) != KMOD_RESOURCES_OK) - return true; - return false; -} - -const struct udev_builtin udev_builtin_kmod = { - .name = "kmod", - .cmd = builtin_kmod, - .init = builtin_kmod_init, - .exit = builtin_kmod_exit, - .validate = builtin_kmod_validate, - .help = "kernel module loader", - .run_once = false, -}; diff --git a/udev/udev-builtin-path_id.c b/udev/udev-builtin-path_id.c deleted file mode 100644 index 18af12f29a..0000000000 --- a/udev/udev-builtin-path_id.c +++ /dev/null @@ -1,487 +0,0 @@ -/* - * compose persistent device path - * - * Copyright (C) 2009-2011 Kay Sievers <kay.sievers@vrfy.org> - * - * Logic based on Hannes Reinecke's shell script. - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see <http://www.gnu.org/licenses/>. - */ - -#include <stdio.h> -#include <stdlib.h> -#include <stdarg.h> -#include <unistd.h> -#include <string.h> -#include <ctype.h> -#include <fcntl.h> -#include <errno.h> -#include <dirent.h> -#include <getopt.h> - -#include "udev.h" - -static int path_prepend(char **path, const char *fmt, ...) -{ - va_list va; - char *pre; - int err = 0; - - va_start(va, fmt); - err = vasprintf(&pre, fmt, va); - va_end(va); - if (err < 0) - goto out; - - if (*path != NULL) { - char *new; - - err = asprintf(&new, "%s-%s", pre, *path); - free(pre); - if (err < 0) - goto out; - free(*path); - *path = new; - } else { - *path = pre; - } -out: - return err; -} - -/* -** Linux only supports 32 bit luns. -** See drivers/scsi/scsi_scan.c::scsilun_to_int() for more details. -*/ -static int format_lun_number(struct udev_device *dev, char **path) -{ - unsigned long lun = strtoul(udev_device_get_sysnum(dev), NULL, 10); - - /* address method 0, peripheral device addressing with bus id of zero */ - if (lun < 256) - return path_prepend(path, "lun-%d", lun); - /* handle all other lun addressing methods by using a variant of the original lun format */ - return path_prepend(path, "lun-0x%04x%04x00000000", (lun & 0xffff), (lun >> 16) & 0xffff); -} - -static struct udev_device *skip_subsystem(struct udev_device *dev, const char *subsys) -{ - struct udev_device *parent = dev; - - while (parent != NULL) { - const char *subsystem; - - subsystem = udev_device_get_subsystem(parent); - if (subsystem == NULL || strcmp(subsystem, subsys) != 0) - break; - dev = parent; - parent = udev_device_get_parent(parent); - } - return dev; -} - -static struct udev_device *handle_scsi_fibre_channel(struct udev_device *parent, char **path) -{ - struct udev *udev = udev_device_get_udev(parent); - struct udev_device *targetdev; - struct udev_device *fcdev = NULL; - const char *port; - char *lun = NULL;; - - targetdev = udev_device_get_parent_with_subsystem_devtype(parent, "scsi", "scsi_target"); - if (targetdev == NULL) - return NULL; - - fcdev = udev_device_new_from_subsystem_sysname(udev, "fc_transport", udev_device_get_sysname(targetdev)); - if (fcdev == NULL) - return NULL; - port = udev_device_get_sysattr_value(fcdev, "port_name"); - if (port == NULL) { - parent = NULL; - goto out; - } - - format_lun_number(parent, &lun); - path_prepend(path, "fc-%s-%s", port, lun); - if (lun) - free(lun); -out: - udev_device_unref(fcdev); - return parent; -} - -static struct udev_device *handle_scsi_sas(struct udev_device *parent, char **path) -{ - struct udev *udev = udev_device_get_udev(parent); - struct udev_device *targetdev; - struct udev_device *target_parent; - struct udev_device *sasdev; - const char *sas_address; - char *lun = NULL; - - targetdev = udev_device_get_parent_with_subsystem_devtype(parent, "scsi", "scsi_target"); - if (targetdev == NULL) - return NULL; - - target_parent = udev_device_get_parent(targetdev); - if (target_parent == NULL) - return NULL; - - sasdev = udev_device_new_from_subsystem_sysname(udev, "sas_device", - udev_device_get_sysname(target_parent)); - if (sasdev == NULL) - return NULL; - - sas_address = udev_device_get_sysattr_value(sasdev, "sas_address"); - if (sas_address == NULL) { - parent = NULL; - goto out; - } - - format_lun_number(parent, &lun); - path_prepend(path, "sas-%s-%s", sas_address, lun); - if (lun) - free(lun); -out: - udev_device_unref(sasdev); - return parent; -} - -static struct udev_device *handle_scsi_iscsi(struct udev_device *parent, char **path) -{ - struct udev *udev = udev_device_get_udev(parent); - struct udev_device *transportdev; - struct udev_device *sessiondev = NULL; - const char *target; - char *connname; - struct udev_device *conndev = NULL; - const char *addr; - const char *port; - char *lun = NULL; - - /* find iscsi session */ - transportdev = parent; - for (;;) { - transportdev = udev_device_get_parent(transportdev); - if (transportdev == NULL) - return NULL; - if (strncmp(udev_device_get_sysname(transportdev), "session", 7) == 0) - break; - } - - /* find iscsi session device */ - sessiondev = udev_device_new_from_subsystem_sysname(udev, "iscsi_session", udev_device_get_sysname(transportdev)); - if (sessiondev == NULL) - return NULL; - target = udev_device_get_sysattr_value(sessiondev, "targetname"); - if (target == NULL) { - parent = NULL; - goto out; - } - - if (asprintf(&connname, "connection%s:0", udev_device_get_sysnum(transportdev)) < 0) { - parent = NULL; - goto out; - } - conndev = udev_device_new_from_subsystem_sysname(udev, "iscsi_connection", connname); - free(connname); - if (conndev == NULL) { - parent = NULL; - goto out; - } - addr = udev_device_get_sysattr_value(conndev, "persistent_address"); - port = udev_device_get_sysattr_value(conndev, "persistent_port"); - if (addr == NULL || port == NULL) { - parent = NULL; - goto out; - } - - format_lun_number(parent, &lun); - path_prepend(path, "ip-%s:%s-iscsi-%s-%s", addr, port, target, lun); - if (lun) - free(lun); -out: - udev_device_unref(sessiondev); - udev_device_unref(conndev); - return parent; -} - -static struct udev_device *handle_scsi_default(struct udev_device *parent, char **path) -{ - struct udev_device *hostdev; - int host, bus, target, lun; - const char *name; - char *base; - char *pos; - DIR *dir; - struct dirent *dent; - int basenum; - - hostdev = udev_device_get_parent_with_subsystem_devtype(parent, "scsi", "scsi_host"); - if (hostdev == NULL) - return NULL; - - name = udev_device_get_sysname(parent); - if (sscanf(name, "%d:%d:%d:%d", &host, &bus, &target, &lun) != 4) - return NULL; - - /* rebase host offset to get the local relative number */ - basenum = -1; - base = strdup(udev_device_get_syspath(hostdev)); - if (base == NULL) - return NULL; - pos = strrchr(base, '/'); - if (pos == NULL) { - parent = NULL; - goto out; - } - pos[0] = '\0'; - dir = opendir(base); - if (dir == NULL) { - parent = NULL; - goto out; - } - for (dent = readdir(dir); dent != NULL; dent = readdir(dir)) { - char *rest; - int i; - - if (dent->d_name[0] == '.') - continue; - if (dent->d_type != DT_DIR && dent->d_type != DT_LNK) - continue; - if (strncmp(dent->d_name, "host", 4) != 0) - continue; - i = strtoul(&dent->d_name[4], &rest, 10); - if (rest[0] != '\0') - continue; - if (basenum == -1 || i < basenum) - basenum = i; - } - closedir(dir); - if (basenum == -1) { - parent = NULL; - goto out; - } - host -= basenum; - - path_prepend(path, "scsi-%u:%u:%u:%u", host, bus, target, lun); -out: - free(base); - return hostdev; -} - -static struct udev_device *handle_scsi(struct udev_device *parent, char **path) -{ - const char *devtype; - const char *name; - const char *id; - - devtype = udev_device_get_devtype(parent); - if (devtype == NULL || strcmp(devtype, "scsi_device") != 0) - return parent; - - /* firewire */ - id = udev_device_get_sysattr_value(parent, "ieee1394_id"); - if (id != NULL) { - parent = skip_subsystem(parent, "scsi"); - path_prepend(path, "ieee1394-0x%s", id); - goto out; - } - - /* lousy scsi sysfs does not have a "subsystem" for the transport */ - name = udev_device_get_syspath(parent); - - if (strstr(name, "/rport-") != NULL) { - parent = handle_scsi_fibre_channel(parent, path); - goto out; - } - - if (strstr(name, "/end_device-") != NULL) { - parent = handle_scsi_sas(parent, path); - goto out; - } - - if (strstr(name, "/session") != NULL) { - parent = handle_scsi_iscsi(parent, path); - goto out; - } - - parent = handle_scsi_default(parent, path); -out: - return parent; -} - -static void handle_scsi_tape(struct udev_device *dev, char **path) -{ - const char *name; - - /* must be the last device in the syspath */ - if (*path != NULL) - return; - - name = udev_device_get_sysname(dev); - if (strncmp(name, "nst", 3) == 0 && strchr("lma", name[3]) != NULL) - path_prepend(path, "nst%c", name[3]); - else if (strncmp(name, "st", 2) == 0 && strchr("lma", name[2]) != NULL) - path_prepend(path, "st%c", name[2]); -} - -static struct udev_device *handle_usb(struct udev_device *parent, char **path) -{ - const char *devtype; - const char *str; - const char *port; - - devtype = udev_device_get_devtype(parent); - if (devtype == NULL) - return parent; - if (strcmp(devtype, "usb_interface") != 0 && strcmp(devtype, "usb_device") != 0) - return parent; - - str = udev_device_get_sysname(parent); - port = strchr(str, '-'); - if (port == NULL) - return parent; - port++; - - parent = skip_subsystem(parent, "usb"); - path_prepend(path, "usb-0:%s", port); - return parent; -} - -static struct udev_device *handle_cciss(struct udev_device *parent, char **path) -{ - return NULL; -} - -static struct udev_device *handle_ccw(struct udev_device *parent, struct udev_device *dev, char **path) -{ - struct udev_device *scsi_dev; - - scsi_dev = udev_device_get_parent_with_subsystem_devtype(dev, "scsi", "scsi_device"); - if (scsi_dev != NULL) { - const char *wwpn; - const char *lun; - const char *hba_id; - - hba_id = udev_device_get_sysattr_value(scsi_dev, "hba_id"); - wwpn = udev_device_get_sysattr_value(scsi_dev, "wwpn"); - lun = udev_device_get_sysattr_value(scsi_dev, "fcp_lun"); - if (hba_id != NULL && lun != NULL && wwpn != NULL) { - path_prepend(path, "ccw-%s-zfcp-%s:%s", hba_id, wwpn, lun); - goto out; - } - } - - path_prepend(path, "ccw-%s", udev_device_get_sysname(parent)); -out: - parent = skip_subsystem(parent, "ccw"); - return parent; -} - -static int builtin_path_id(struct udev_device *dev, int argc, char *argv[], bool test) -{ - struct udev_device *parent; - char *path = NULL; - - /* S390 ccw bus */ - parent = udev_device_get_parent_with_subsystem_devtype(dev, "ccw", NULL); - if (parent != NULL) { - handle_ccw(parent, dev, &path); - goto out; - } - - /* walk up the chain of devices and compose path */ - parent = dev; - while (parent != NULL) { - const char *subsys; - - subsys = udev_device_get_subsystem(parent); - if (subsys == NULL) { - ; - } else if (strcmp(subsys, "scsi_tape") == 0) { - handle_scsi_tape(parent, &path); - } else if (strcmp(subsys, "scsi") == 0) { - parent = handle_scsi(parent, &path); - } else if (strcmp(subsys, "cciss") == 0) { - handle_cciss(parent, &path); - } else if (strcmp(subsys, "usb") == 0) { - parent = handle_usb(parent, &path); - } else if (strcmp(subsys, "serio") == 0) { - path_prepend(&path, "serio-%s", udev_device_get_sysnum(parent)); - parent = skip_subsystem(parent, "serio"); - } else if (strcmp(subsys, "pci") == 0) { - path_prepend(&path, "pci-%s", udev_device_get_sysname(parent)); - parent = skip_subsystem(parent, "pci"); - } else if (strcmp(subsys, "platform") == 0) { - path_prepend(&path, "platform-%s", udev_device_get_sysname(parent)); - parent = skip_subsystem(parent, "platform"); - } else if (strcmp(subsys, "acpi") == 0) { - path_prepend(&path, "acpi-%s", udev_device_get_sysname(parent)); - parent = skip_subsystem(parent, "acpi"); - } else if (strcmp(subsys, "xen") == 0) { - path_prepend(&path, "xen-%s", udev_device_get_sysname(parent)); - parent = skip_subsystem(parent, "xen"); - } else if (strcmp(subsys, "virtio") == 0) { - path_prepend(&path, "virtio-pci-%s", udev_device_get_sysname(parent)); - parent = skip_subsystem(parent, "virtio"); - } - - parent = udev_device_get_parent(parent); - } -out: - if (path != NULL) { - char tag[UTIL_NAME_SIZE]; - size_t i; - const char *p; - - /* compose valid udev tag name */ - for (p = path, i = 0; *p; p++) { - if ((*p >= '0' && *p <= '9') || - (*p >= 'A' && *p <= 'Z') || - (*p >= 'a' && *p <= 'z') || - *p == '-') { - tag[i++] = *p; - continue; - } - - /* skip all leading '_' */ - if (i == 0) - continue; - - /* avoid second '_' */ - if (tag[i-1] == '_') - continue; - - tag[i++] = '_'; - } - /* strip trailing '_' */ - while (i > 0 && tag[i-1] == '_') - i--; - tag[i] = '\0'; - - udev_builtin_add_property(dev, test, "ID_PATH", path); - udev_builtin_add_property(dev, test, "ID_PATH_TAG", tag); - free(path); - return EXIT_SUCCESS; - } - return EXIT_FAILURE; -} - -const struct udev_builtin udev_builtin_path_id = { - .name = "path_id", - .cmd = builtin_path_id, - .help = "compose persistent device path", - .run_once = true, -}; diff --git a/udev/udev-builtin-usb_id.c b/udev/udev-builtin-usb_id.c deleted file mode 100644 index 21c3c03d8a..0000000000 --- a/udev/udev-builtin-usb_id.c +++ /dev/null @@ -1,482 +0,0 @@ -/* - * USB device properties and persistent device path - * - * Copyright (c) 2005 SUSE Linux Products GmbH, Germany - * Author: Hannes Reinecke <hare@suse.de> - * - * Copyright (C) 2005-2011 Kay Sievers <kay.sievers@vrfy.org> - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see <http://www.gnu.org/licenses/>. - */ - -#include <stdio.h> -#include <stdlib.h> -#include <stdarg.h> -#include <unistd.h> -#include <string.h> -#include <ctype.h> -#include <fcntl.h> -#include <errno.h> - -#include "udev.h" - -static void set_usb_iftype(char *to, int if_class_num, size_t len) -{ - char *type = "generic"; - - switch (if_class_num) { - case 1: - type = "audio"; - break; - case 2: /* CDC-Control */ - break; - case 3: - type = "hid"; - break; - case 5: /* Physical */ - break; - case 6: - type = "media"; - break; - case 7: - type = "printer"; - break; - case 8: - type = "storage"; - break; - case 9: - type = "hub"; - break; - case 0x0a: /* CDC-Data */ - break; - case 0x0b: /* Chip/Smart Card */ - break; - case 0x0d: /* Content Security */ - break; - case 0x0e: - type = "video"; - break; - case 0xdc: /* Diagnostic Device */ - break; - case 0xe0: /* Wireless Controller */ - break; - case 0xfe: /* Application-specific */ - break; - case 0xff: /* Vendor-specific */ - break; - default: - break; - } - strncpy(to, type, len); - to[len-1] = '\0'; -} - -static int set_usb_mass_storage_ifsubtype(char *to, const char *from, size_t len) -{ - int type_num = 0; - char *eptr; - char *type = "generic"; - - type_num = strtoul(from, &eptr, 0); - if (eptr != from) { - switch (type_num) { - case 2: - type = "atapi"; - break; - case 3: - type = "tape"; - break; - case 4: /* UFI */ - case 5: /* SFF-8070i */ - type = "floppy"; - break; - case 1: /* RBC devices */ - type = "rbc"; - break; - case 6: /* Transparent SPC-2 devices */ - type = "scsi"; - break; - default: - break; - } - } - util_strscpy(to, len, type); - return type_num; -} - -static void set_scsi_type(char *to, const char *from, size_t len) -{ - int type_num; - char *eptr; - char *type = "generic"; - - type_num = strtoul(from, &eptr, 0); - if (eptr != from) { - switch (type_num) { - case 0: - case 0xe: - type = "disk"; - break; - case 1: - type = "tape"; - break; - case 4: - case 7: - case 0xf: - type = "optical"; - break; - case 5: - type = "cd"; - break; - default: - break; - } - } - util_strscpy(to, len, type); -} - -#define USB_DT_DEVICE 0x01 -#define USB_DT_INTERFACE 0x04 - -static int dev_if_packed_info(struct udev_device *dev, char *ifs_str, size_t len) -{ - char *filename = NULL; - int fd; - ssize_t size; - unsigned char buf[18 + 65535]; - unsigned int pos, strpos; - struct usb_interface_descriptor { - u_int8_t bLength; - u_int8_t bDescriptorType; - u_int8_t bInterfaceNumber; - u_int8_t bAlternateSetting; - u_int8_t bNumEndpoints; - u_int8_t bInterfaceClass; - u_int8_t bInterfaceSubClass; - u_int8_t bInterfaceProtocol; - u_int8_t iInterface; - } __attribute__((packed)); - int err = 0; - - if (asprintf(&filename, "%s/descriptors", udev_device_get_syspath(dev)) < 0) { - err = -1; - goto out; - } - fd = open(filename, O_RDONLY|O_CLOEXEC); - if (fd < 0) { - fprintf(stderr, "error opening USB device 'descriptors' file\n"); - err = -1; - goto out; - } - size = read(fd, buf, sizeof(buf)); - close(fd); - if (size < 18 || size == sizeof(buf)) { - err = -1; - goto out; - } - - pos = 0; - strpos = 0; - ifs_str[0] = '\0'; - while (pos < sizeof(buf) && strpos+7 < len-2) { - struct usb_interface_descriptor *desc; - char if_str[8]; - - desc = (struct usb_interface_descriptor *) &buf[pos]; - if (desc->bLength < 3) - break; - pos += desc->bLength; - - if (desc->bDescriptorType != USB_DT_INTERFACE) - continue; - - if (snprintf(if_str, 8, ":%02x%02x%02x", - desc->bInterfaceClass, - desc->bInterfaceSubClass, - desc->bInterfaceProtocol) != 7) - continue; - - if (strstr(ifs_str, if_str) != NULL) - continue; - - memcpy(&ifs_str[strpos], if_str, 8), - strpos += 7; - } - if (strpos > 0) { - ifs_str[strpos++] = ':'; - ifs_str[strpos++] = '\0'; - } -out: - free(filename); - return err; -} - -/* - * A unique USB identification is generated like this: - * - * 1.) Get the USB device type from InterfaceClass and InterfaceSubClass - * 2.) If the device type is 'Mass-Storage/SPC-2' or 'Mass-Storage/RBC' - * use the SCSI vendor and model as USB-Vendor and USB-model. - * 3.) Otherwise use the USB manufacturer and product as - * USB-Vendor and USB-model. Any non-printable characters - * in those strings will be skipped; a slash '/' will be converted - * into a full stop '.'. - * 4.) If that fails, too, we will use idVendor and idProduct - * as USB-Vendor and USB-model. - * 5.) The USB identification is the USB-vendor and USB-model - * string concatenated with an underscore '_'. - * 6.) If the device supplies a serial number, this number - * is concatenated with the identification with an underscore '_'. - */ -static int builtin_usb_id(struct udev_device *dev, int argc, char *argv[], bool test) -{ - char vendor_str[64]; - char vendor_str_enc[256]; - const char *vendor_id; - char model_str[64]; - char model_str_enc[256]; - const char *product_id; - char serial_str[UTIL_NAME_SIZE]; - char packed_if_str[UTIL_NAME_SIZE]; - char revision_str[64]; - char type_str[64]; - char instance_str[64]; - const char *ifnum = NULL; - const char *driver = NULL; - char serial[256]; - - struct udev *udev = udev_device_get_udev(dev); - struct udev_device *dev_interface = NULL; - struct udev_device *dev_usb = NULL; - const char *if_class, *if_subclass; - int if_class_num; - int protocol = 0; - size_t l; - char *s; - - vendor_str[0] = '\0'; - model_str[0] = '\0'; - serial_str[0] = '\0'; - packed_if_str[0] = '\0'; - revision_str[0] = '\0'; - type_str[0] = '\0'; - instance_str[0] = '\0'; - - dbg(udev, "syspath %s\n", udev_device_get_syspath(dev)); - - /* shortcut, if we are called directly for a "usb_device" type */ - if (udev_device_get_devtype(dev) != NULL && strcmp(udev_device_get_devtype(dev), "usb_device") == 0) { - dev_if_packed_info(dev, packed_if_str, sizeof(packed_if_str)); - dev_usb = dev; - goto fallback; - } - - /* usb interface directory */ - dev_interface = udev_device_get_parent_with_subsystem_devtype(dev, "usb", "usb_interface"); - if (dev_interface == NULL) { - info(udev, "unable to access usb_interface device of '%s'\n", - udev_device_get_syspath(dev)); - return EXIT_FAILURE; - } - - ifnum = udev_device_get_sysattr_value(dev_interface, "bInterfaceNumber"); - driver = udev_device_get_sysattr_value(dev_interface, "driver"); - - if_class = udev_device_get_sysattr_value(dev_interface, "bInterfaceClass"); - if (!if_class) { - info(udev, "%s: cannot get bInterfaceClass attribute\n", - udev_device_get_sysname(dev)); - return EXIT_FAILURE; - } - - if_class_num = strtoul(if_class, NULL, 16); - if (if_class_num == 8) { - /* mass storage */ - if_subclass = udev_device_get_sysattr_value(dev_interface, "bInterfaceSubClass"); - if (if_subclass != NULL) - protocol = set_usb_mass_storage_ifsubtype(type_str, if_subclass, sizeof(type_str)-1); - } else { - set_usb_iftype(type_str, if_class_num, sizeof(type_str)-1); - } - - info(udev, "%s: if_class %d protocol %d\n", - udev_device_get_syspath(dev_interface), if_class_num, protocol); - - /* usb device directory */ - dev_usb = udev_device_get_parent_with_subsystem_devtype(dev_interface, "usb", "usb_device"); - if (!dev_usb) { - info(udev, "unable to find parent 'usb' device of '%s'\n", - udev_device_get_syspath(dev)); - return EXIT_FAILURE; - } - - /* all interfaces of the device in a single string */ - dev_if_packed_info(dev_usb, packed_if_str, sizeof(packed_if_str)); - - /* mass storage : SCSI or ATAPI */ - if ((protocol == 6 || protocol == 2)) { - struct udev_device *dev_scsi; - const char *scsi_model, *scsi_vendor, *scsi_type, *scsi_rev; - int host, bus, target, lun; - - /* get scsi device */ - dev_scsi = udev_device_get_parent_with_subsystem_devtype(dev, "scsi", "scsi_device"); - if (dev_scsi == NULL) { - info(udev, "unable to find parent 'scsi' device of '%s'\n", - udev_device_get_syspath(dev)); - goto fallback; - } - if (sscanf(udev_device_get_sysname(dev_scsi), "%d:%d:%d:%d", &host, &bus, &target, &lun) != 4) { - info(udev, "invalid scsi device '%s'\n", udev_device_get_sysname(dev_scsi)); - goto fallback; - } - - /* Generic SPC-2 device */ - scsi_vendor = udev_device_get_sysattr_value(dev_scsi, "vendor"); - if (!scsi_vendor) { - info(udev, "%s: cannot get SCSI vendor attribute\n", - udev_device_get_sysname(dev_scsi)); - goto fallback; - } - udev_util_encode_string(scsi_vendor, vendor_str_enc, sizeof(vendor_str_enc)); - util_replace_whitespace(scsi_vendor, vendor_str, sizeof(vendor_str)-1); - util_replace_chars(vendor_str, NULL); - - scsi_model = udev_device_get_sysattr_value(dev_scsi, "model"); - if (!scsi_model) { - info(udev, "%s: cannot get SCSI model attribute\n", - udev_device_get_sysname(dev_scsi)); - goto fallback; - } - udev_util_encode_string(scsi_model, model_str_enc, sizeof(model_str_enc)); - util_replace_whitespace(scsi_model, model_str, sizeof(model_str)-1); - util_replace_chars(model_str, NULL); - - scsi_type = udev_device_get_sysattr_value(dev_scsi, "type"); - if (!scsi_type) { - info(udev, "%s: cannot get SCSI type attribute\n", - udev_device_get_sysname(dev_scsi)); - goto fallback; - } - set_scsi_type(type_str, scsi_type, sizeof(type_str)-1); - - scsi_rev = udev_device_get_sysattr_value(dev_scsi, "rev"); - if (!scsi_rev) { - info(udev, "%s: cannot get SCSI revision attribute\n", - udev_device_get_sysname(dev_scsi)); - goto fallback; - } - util_replace_whitespace(scsi_rev, revision_str, sizeof(revision_str)-1); - util_replace_chars(revision_str, NULL); - - /* - * some broken devices have the same identifiers - * for all luns, export the target:lun number - */ - sprintf(instance_str, "%d:%d", target, lun); - } - -fallback: - vendor_id = udev_device_get_sysattr_value(dev_usb, "idVendor"); - product_id = udev_device_get_sysattr_value(dev_usb, "idProduct"); - - /* fallback to USB vendor & device */ - if (vendor_str[0] == '\0') { - const char *usb_vendor = NULL; - - usb_vendor = udev_device_get_sysattr_value(dev_usb, "manufacturer"); - if (!usb_vendor) - usb_vendor = vendor_id; - if (!usb_vendor) { - info(udev, "No USB vendor information available\n"); - return EXIT_FAILURE; - } - udev_util_encode_string(usb_vendor, vendor_str_enc, sizeof(vendor_str_enc)); - util_replace_whitespace(usb_vendor, vendor_str, sizeof(vendor_str)-1); - util_replace_chars(vendor_str, NULL); - } - - if (model_str[0] == '\0') { - const char *usb_model = NULL; - - usb_model = udev_device_get_sysattr_value(dev_usb, "product"); - if (!usb_model) - usb_model = product_id; - if (!usb_model) { - dbg(udev, "No USB model information available\n"); - return EXIT_FAILURE; - } - udev_util_encode_string(usb_model, model_str_enc, sizeof(model_str_enc)); - util_replace_whitespace(usb_model, model_str, sizeof(model_str)-1); - util_replace_chars(model_str, NULL); - } - - if (revision_str[0] == '\0') { - const char *usb_rev; - - usb_rev = udev_device_get_sysattr_value(dev_usb, "bcdDevice"); - if (usb_rev) { - util_replace_whitespace(usb_rev, revision_str, sizeof(revision_str)-1); - util_replace_chars(revision_str, NULL); - } - } - - if (serial_str[0] == '\0') { - const char *usb_serial; - - usb_serial = udev_device_get_sysattr_value(dev_usb, "serial"); - if (usb_serial) { - util_replace_whitespace(usb_serial, serial_str, sizeof(serial_str)-1); - util_replace_chars(serial_str, NULL); - } - } - - s = serial; - l = util_strpcpyl(&s, sizeof(serial), vendor_str, "_", model_str, NULL); - if (serial_str[0] != '\0') - l = util_strpcpyl(&s, l, "_", serial_str, NULL); - - if (instance_str[0] != '\0') - util_strpcpyl(&s, l, "-", instance_str, NULL); - - udev_builtin_add_property(dev, test, "ID_VENDOR", vendor_str); - udev_builtin_add_property(dev, test, "ID_VENDOR_ENC", vendor_str_enc); - udev_builtin_add_property(dev, test, "ID_VENDOR_ID", vendor_id); - udev_builtin_add_property(dev, test, "ID_MODEL", model_str); - udev_builtin_add_property(dev, test, "ID_MODEL_ENC", model_str_enc); - udev_builtin_add_property(dev, test, "ID_MODEL_ID", product_id); - udev_builtin_add_property(dev, test, "ID_REVISION", revision_str); - udev_builtin_add_property(dev, test, "ID_SERIAL", serial); - if (serial_str[0] != '\0') - udev_builtin_add_property(dev, test, "ID_SERIAL_SHORT", serial_str); - if (type_str[0] != '\0') - udev_builtin_add_property(dev, test, "ID_TYPE", type_str); - if (instance_str[0] != '\0') - udev_builtin_add_property(dev, test, "ID_INSTANCE", instance_str); - udev_builtin_add_property(dev, test, "ID_BUS", "usb"); - if (packed_if_str[0] != '\0') - udev_builtin_add_property(dev, test, "ID_USB_INTERFACES", packed_if_str); - if (ifnum != NULL) - udev_builtin_add_property(dev, test, "ID_USB_INTERFACE_NUM", ifnum); - if (driver != NULL) - udev_builtin_add_property(dev, test, "ID_USB_DRIVER", driver); - return EXIT_SUCCESS; -} - -const struct udev_builtin udev_builtin_usb_id = { - .name = "usb_id", - .cmd = builtin_usb_id, - .help = "usb device properties", - .run_once = true, -}; diff --git a/udev/udev-builtin.c b/udev/udev-builtin.c deleted file mode 100644 index 8beac8a678..0000000000 --- a/udev/udev-builtin.c +++ /dev/null @@ -1,134 +0,0 @@ -/* - * Copyright (C) 2007-2009 Kay Sievers <kay.sievers@vrfy.org> - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see <http://www.gnu.org/licenses/>. - */ - -#include <unistd.h> -#include <stdio.h> -#include <stdlib.h> -#include <stddef.h> -#include <string.h> -#include <errno.h> -#include <getopt.h> - -#include "udev.h" - -static const struct udev_builtin *builtins[] = { - [UDEV_BUILTIN_BLKID] = &udev_builtin_blkid, - [UDEV_BUILTIN_FIRMWARE] = &udev_builtin_firmware, - [UDEV_BUILTIN_INPUT_ID] = &udev_builtin_input_id, - [UDEV_BUILTIN_KMOD] = &udev_builtin_kmod, - [UDEV_BUILTIN_PATH_ID] = &udev_builtin_path_id, - [UDEV_BUILTIN_PCI_DB] = &udev_builtin_pci_db, - [UDEV_BUILTIN_USB_DB] = &udev_builtin_usb_db, - [UDEV_BUILTIN_USB_ID] = &udev_builtin_usb_id, -}; - -int udev_builtin_init(struct udev *udev) -{ - unsigned int i; - int err; - - for (i = 0; i < ARRAY_SIZE(builtins); i++) { - if (builtins[i]->init) { - err = builtins[i]->init(udev); - if (err < 0) - break; - } - } - return err; -} - -void udev_builtin_exit(struct udev *udev) -{ - unsigned int i; - - for (i = 0; i < ARRAY_SIZE(builtins); i++) - if (builtins[i]->exit) - builtins[i]->exit(udev); -} - -bool udev_builtin_validate(struct udev *udev) -{ - unsigned int i; - bool change = false; - - for (i = 0; i < ARRAY_SIZE(builtins); i++) - if (builtins[i]->validate) - if (builtins[i]->validate(udev)) - change = true; - return change; -} - -void udev_builtin_list(struct udev *udev) -{ - unsigned int i; - - for (i = 0; i < ARRAY_SIZE(builtins); i++) - fprintf(stderr, " %-12s %s\n", builtins[i]->name, builtins[i]->help); -} - -const char *udev_builtin_name(enum udev_builtin_cmd cmd) -{ - return builtins[cmd]->name; -} - -bool udev_builtin_run_once(enum udev_builtin_cmd cmd) -{ - return builtins[cmd]->run_once; -} - -enum udev_builtin_cmd udev_builtin_lookup(const char *command) -{ - char name[UTIL_PATH_SIZE]; - enum udev_builtin_cmd i; - char *pos; - - util_strscpy(name, sizeof(name), command); - pos = strchr(name, ' '); - if (pos) - pos[0] = '\0'; - for (i = 0; i < ARRAY_SIZE(builtins); i++) - if (strcmp(builtins[i]->name, name) == 0) - return i; - return UDEV_BUILTIN_MAX; -} - -int udev_builtin_run(struct udev_device *dev, enum udev_builtin_cmd cmd, const char *command, bool test) -{ - char arg[UTIL_PATH_SIZE]; - int argc; - char *argv[128]; - - optind = 0; - util_strscpy(arg, sizeof(arg), command); - udev_build_argv(udev_device_get_udev(dev), arg, &argc, argv); - return builtins[cmd]->cmd(dev, argc, argv, test); -} - -int udev_builtin_add_property(struct udev_device *dev, bool test, const char *key, const char *val) -{ - struct udev_list_entry *entry; - - entry = udev_device_add_property(dev, key, val); - /* store in db, skip private keys */ - if (key[0] != '.') - udev_list_entry_set_num(entry, true); - - info(udev_device_get_udev(dev), "%s=%s\n", key, val); - if (test) - printf("%s=%s\n", key, val); - return 0; -} diff --git a/udev/udev-ctrl.c b/udev/udev-ctrl.c deleted file mode 100644 index fab1108de0..0000000000 --- a/udev/udev-ctrl.c +++ /dev/null @@ -1,494 +0,0 @@ -/* - * 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 <errno.h> -#include <stdio.h> -#include <stdlib.h> -#include <stddef.h> -#include <string.h> -#include <unistd.h> -#include <sys/types.h> -#include <sys/poll.h> -#include <sys/socket.h> -#include <sys/un.h> - -#include "udev.h" - -/* wire protocol magic must match */ -#define UDEV_CTRL_MAGIC 0xdead1dea - -enum udev_ctrl_msg_type { - UDEV_CTRL_UNKNOWN, - UDEV_CTRL_SET_LOG_LEVEL, - UDEV_CTRL_STOP_EXEC_QUEUE, - UDEV_CTRL_START_EXEC_QUEUE, - UDEV_CTRL_RELOAD, - UDEV_CTRL_SET_ENV, - UDEV_CTRL_SET_CHILDREN_MAX, - UDEV_CTRL_PING, - UDEV_CTRL_EXIT, -}; - -struct udev_ctrl_msg_wire { - char version[16]; - unsigned int magic; - enum udev_ctrl_msg_type type; - union { - int intval; - char buf[256]; - }; -}; - -struct udev_ctrl_msg { - int refcount; - struct udev_ctrl_connection *conn; - struct udev_ctrl_msg_wire ctrl_msg_wire; -}; - -struct udev_ctrl { - int refcount; - struct udev *udev; - int sock; - struct sockaddr_un saddr; - socklen_t addrlen; - bool bound; - bool cleanup_socket; - bool connected; -}; - -struct udev_ctrl_connection { - int refcount; - struct udev_ctrl *uctrl; - int sock; -}; - -struct udev_ctrl *udev_ctrl_new_from_fd(struct udev *udev, int fd) -{ - struct udev_ctrl *uctrl; - - uctrl = calloc(1, sizeof(struct udev_ctrl)); - if (uctrl == NULL) - return NULL; - uctrl->refcount = 1; - uctrl->udev = udev; - - if (fd < 0) { - uctrl->sock = socket(AF_LOCAL, SOCK_SEQPACKET|SOCK_NONBLOCK|SOCK_CLOEXEC, 0); - if (uctrl->sock < 0) { - err(udev, "error getting socket: %m\n"); - udev_ctrl_unref(uctrl); - return NULL; - } - } else { - uctrl->bound = true; - uctrl->sock = fd; - } - - uctrl->saddr.sun_family = AF_LOCAL; - util_strscpyl(uctrl->saddr.sun_path, sizeof(uctrl->saddr.sun_path), - udev_get_run_path(udev), "/control", NULL); - uctrl->addrlen = offsetof(struct sockaddr_un, sun_path) + strlen(uctrl->saddr.sun_path); - return uctrl; -} - -struct udev_ctrl *udev_ctrl_new(struct udev *udev) -{ - return udev_ctrl_new_from_fd(udev, -1); -} - -int udev_ctrl_enable_receiving(struct udev_ctrl *uctrl) -{ - int err; - - if (!uctrl->bound) { - err = bind(uctrl->sock, (struct sockaddr *)&uctrl->saddr, uctrl->addrlen); - if (err < 0 && errno == EADDRINUSE) { - unlink(uctrl->saddr.sun_path); - err = bind(uctrl->sock, (struct sockaddr *)&uctrl->saddr, uctrl->addrlen); - } - - if (err < 0) { - err = -errno; - err(uctrl->udev, "bind failed: %m\n"); - return err; - } - - err = listen(uctrl->sock, 0); - if (err < 0) { - err = -errno; - err(uctrl->udev, "listen failed: %m\n"); - return err; - } - - uctrl->bound = true; - uctrl->cleanup_socket = true; - } - return 0; -} - -struct udev *udev_ctrl_get_udev(struct udev_ctrl *uctrl) -{ - return uctrl->udev; -} - -struct udev_ctrl *udev_ctrl_ref(struct udev_ctrl *uctrl) -{ - if (uctrl == NULL) - return NULL; - uctrl->refcount++; - return uctrl; -} - -struct udev_ctrl *udev_ctrl_unref(struct udev_ctrl *uctrl) -{ - if (uctrl == NULL) - return NULL; - uctrl->refcount--; - if (uctrl->refcount > 0) - return uctrl; - if (uctrl->sock >= 0) - close(uctrl->sock); - free(uctrl); - return NULL; -} - -int udev_ctrl_cleanup(struct udev_ctrl *uctrl) -{ - if (uctrl == NULL) - return 0; - if (uctrl->cleanup_socket) - unlink(uctrl->saddr.sun_path); - return 0; -} - -int udev_ctrl_get_fd(struct udev_ctrl *uctrl) -{ - if (uctrl == NULL) - return -EINVAL; - return uctrl->sock; -} - -struct udev_ctrl_connection *udev_ctrl_get_connection(struct udev_ctrl *uctrl) -{ - struct udev_ctrl_connection *conn; - struct ucred ucred; - socklen_t slen; - const int on = 1; - - conn = calloc(1, sizeof(struct udev_ctrl_connection)); - if (conn == NULL) - return NULL; - conn->refcount = 1; - conn->uctrl = uctrl; - - conn->sock = accept4(uctrl->sock, NULL, NULL, SOCK_CLOEXEC|SOCK_NONBLOCK); - if (conn->sock < 0) { - if (errno != EINTR) - err(uctrl->udev, "unable to receive ctrl connection: %m\n"); - goto err; - } - - /* check peer credential of connection */ - slen = sizeof(ucred); - if (getsockopt(conn->sock, SOL_SOCKET, SO_PEERCRED, &ucred, &slen) < 0) { - err(uctrl->udev, "unable to receive credentials of ctrl connection: %m\n"); - goto err; - } - if (ucred.uid > 0) { - err(uctrl->udev, "sender uid=%i, message ignored\n", ucred.uid); - goto err; - } - - /* enable receiving of the sender credentials in the messages */ - setsockopt(conn->sock, SOL_SOCKET, SO_PASSCRED, &on, sizeof(on)); - udev_ctrl_ref(uctrl); - return conn; -err: - if (conn->sock >= 0) - close(conn->sock); - free(conn); - return NULL; -} - -struct udev_ctrl_connection *udev_ctrl_connection_ref(struct udev_ctrl_connection *conn) -{ - if (conn == NULL) - return NULL; - conn->refcount++; - return conn; -} - -struct udev_ctrl_connection *udev_ctrl_connection_unref(struct udev_ctrl_connection *conn) -{ - if (conn == NULL) - return NULL; - conn->refcount--; - if (conn->refcount > 0) - return conn; - if (conn->sock >= 0) - close(conn->sock); - udev_ctrl_unref(conn->uctrl); - free(conn); - return NULL; -} - -static int ctrl_send(struct udev_ctrl *uctrl, enum udev_ctrl_msg_type type, int intval, const char *buf, int timeout) -{ - struct udev_ctrl_msg_wire ctrl_msg_wire; - int err = 0; - - memset(&ctrl_msg_wire, 0x00, sizeof(struct udev_ctrl_msg_wire)); - strcpy(ctrl_msg_wire.version, "udev-" VERSION); - ctrl_msg_wire.magic = UDEV_CTRL_MAGIC; - ctrl_msg_wire.type = type; - - if (buf != NULL) - util_strscpy(ctrl_msg_wire.buf, sizeof(ctrl_msg_wire.buf), buf); - else - ctrl_msg_wire.intval = intval; - - if (!uctrl->connected) { - if (connect(uctrl->sock, (struct sockaddr *)&uctrl->saddr, uctrl->addrlen) < 0) { - err = -errno; - goto out; - } - uctrl->connected = true; - } - if (send(uctrl->sock, &ctrl_msg_wire, sizeof(ctrl_msg_wire), 0) < 0) { - err = -errno; - goto out; - } - - /* wait for peer message handling or disconnect */ - for (;;) { - struct pollfd pfd[1]; - int r; - - pfd[0].fd = uctrl->sock; - pfd[0].events = POLLIN; - r = poll(pfd, 1, timeout * 1000); - if (r < 0) { - if (errno == EINTR) - continue; - err = -errno; - break; - } - - if (r > 0 && pfd[0].revents & POLLERR) { - err = -EIO; - break; - } - - if (r == 0) - err = -ETIMEDOUT; - break; - } -out: - return err; -} - -int udev_ctrl_send_set_log_level(struct udev_ctrl *uctrl, int priority, int timeout) -{ - return ctrl_send(uctrl, UDEV_CTRL_SET_LOG_LEVEL, priority, NULL, timeout); -} - -int udev_ctrl_send_stop_exec_queue(struct udev_ctrl *uctrl, int timeout) -{ - return ctrl_send(uctrl, UDEV_CTRL_STOP_EXEC_QUEUE, 0, NULL, timeout); -} - -int udev_ctrl_send_start_exec_queue(struct udev_ctrl *uctrl, int timeout) -{ - return ctrl_send(uctrl, UDEV_CTRL_START_EXEC_QUEUE, 0, NULL, timeout); -} - -int udev_ctrl_send_reload(struct udev_ctrl *uctrl, int timeout) -{ - return ctrl_send(uctrl, UDEV_CTRL_RELOAD, 0, NULL, timeout); -} - -int udev_ctrl_send_set_env(struct udev_ctrl *uctrl, const char *key, int timeout) -{ - return ctrl_send(uctrl, UDEV_CTRL_SET_ENV, 0, key, timeout); -} - -int udev_ctrl_send_set_children_max(struct udev_ctrl *uctrl, int count, int timeout) -{ - return ctrl_send(uctrl, UDEV_CTRL_SET_CHILDREN_MAX, count, NULL, timeout); -} - -int udev_ctrl_send_ping(struct udev_ctrl *uctrl, int timeout) -{ - return ctrl_send(uctrl, UDEV_CTRL_PING, 0, NULL, timeout); -} - -int udev_ctrl_send_exit(struct udev_ctrl *uctrl, int timeout) -{ - return ctrl_send(uctrl, UDEV_CTRL_EXIT, 0, NULL, timeout); -} - -struct udev_ctrl_msg *udev_ctrl_receive_msg(struct udev_ctrl_connection *conn) -{ - struct udev *udev = conn->uctrl->udev; - struct udev_ctrl_msg *uctrl_msg; - ssize_t size; - struct msghdr smsg; - struct cmsghdr *cmsg; - struct iovec iov; - struct ucred *cred; - char cred_msg[CMSG_SPACE(sizeof(struct ucred))]; - - uctrl_msg = calloc(1, sizeof(struct udev_ctrl_msg)); - if (uctrl_msg == NULL) - return NULL; - uctrl_msg->refcount = 1; - uctrl_msg->conn = conn; - udev_ctrl_connection_ref(conn); - - /* wait for the incoming message */ - for(;;) { - struct pollfd pfd[1]; - int r; - - pfd[0].fd = conn->sock; - pfd[0].events = POLLIN; - - r = poll(pfd, 1, 10000); - if (r < 0) { - if (errno == EINTR) - continue; - goto err; - } else if (r == 0) { - err(udev, "timeout waiting for ctrl message\n"); - goto err; - } else { - if (!(pfd[0].revents & POLLIN)) { - err(udev, "ctrl connection error: %m\n"); - goto err; - } - } - - break; - } - - iov.iov_base = &uctrl_msg->ctrl_msg_wire; - iov.iov_len = sizeof(struct udev_ctrl_msg_wire); - memset(&smsg, 0x00, sizeof(struct msghdr)); - smsg.msg_iov = &iov; - smsg.msg_iovlen = 1; - smsg.msg_control = cred_msg; - smsg.msg_controllen = sizeof(cred_msg); - size = recvmsg(conn->sock, &smsg, 0); - if (size < 0) { - err(udev, "unable to receive ctrl message: %m\n"); - goto err; - } - cmsg = CMSG_FIRSTHDR(&smsg); - cred = (struct ucred *) CMSG_DATA(cmsg); - - if (cmsg == NULL || cmsg->cmsg_type != SCM_CREDENTIALS) { - err(udev, "no sender credentials received, message ignored\n"); - goto err; - } - - if (cred->uid != 0) { - err(udev, "sender uid=%i, message ignored\n", cred->uid); - goto err; - } - - if (uctrl_msg->ctrl_msg_wire.magic != UDEV_CTRL_MAGIC) { - err(udev, "message magic 0x%08x doesn't match, ignore it\n", uctrl_msg->ctrl_msg_wire.magic); - goto err; - } - - dbg(udev, "created ctrl_msg %p (%i)\n", uctrl_msg, uctrl_msg->ctrl_msg_wire.type); - return uctrl_msg; -err: - udev_ctrl_msg_unref(uctrl_msg); - return NULL; -} - -struct udev_ctrl_msg *udev_ctrl_msg_ref(struct udev_ctrl_msg *ctrl_msg) -{ - if (ctrl_msg == NULL) - return NULL; - ctrl_msg->refcount++; - return ctrl_msg; -} - -struct udev_ctrl_msg *udev_ctrl_msg_unref(struct udev_ctrl_msg *ctrl_msg) -{ - if (ctrl_msg == NULL) - return NULL; - ctrl_msg->refcount--; - if (ctrl_msg->refcount > 0) - return ctrl_msg; - dbg(ctrl_msg->conn->uctrl->udev, "release ctrl_msg %p\n", ctrl_msg); - udev_ctrl_connection_unref(ctrl_msg->conn); - free(ctrl_msg); - return NULL; -} - -int udev_ctrl_get_set_log_level(struct udev_ctrl_msg *ctrl_msg) -{ - if (ctrl_msg->ctrl_msg_wire.type == UDEV_CTRL_SET_LOG_LEVEL) - return ctrl_msg->ctrl_msg_wire.intval; - return -1; -} - -int udev_ctrl_get_stop_exec_queue(struct udev_ctrl_msg *ctrl_msg) -{ - if (ctrl_msg->ctrl_msg_wire.type == UDEV_CTRL_STOP_EXEC_QUEUE) - return 1; - return -1; -} - -int udev_ctrl_get_start_exec_queue(struct udev_ctrl_msg *ctrl_msg) -{ - if (ctrl_msg->ctrl_msg_wire.type == UDEV_CTRL_START_EXEC_QUEUE) - return 1; - return -1; -} - -int udev_ctrl_get_reload(struct udev_ctrl_msg *ctrl_msg) -{ - if (ctrl_msg->ctrl_msg_wire.type == UDEV_CTRL_RELOAD) - return 1; - return -1; -} - -const char *udev_ctrl_get_set_env(struct udev_ctrl_msg *ctrl_msg) -{ - if (ctrl_msg->ctrl_msg_wire.type == UDEV_CTRL_SET_ENV) - return ctrl_msg->ctrl_msg_wire.buf; - return NULL; -} - -int udev_ctrl_get_set_children_max(struct udev_ctrl_msg *ctrl_msg) -{ - if (ctrl_msg->ctrl_msg_wire.type == UDEV_CTRL_SET_CHILDREN_MAX) - return ctrl_msg->ctrl_msg_wire.intval; - return -1; -} - -int udev_ctrl_get_ping(struct udev_ctrl_msg *ctrl_msg) -{ - if (ctrl_msg->ctrl_msg_wire.type == UDEV_CTRL_PING) - return 1; - return -1; -} - -int udev_ctrl_get_exit(struct udev_ctrl_msg *ctrl_msg) -{ - if (ctrl_msg->ctrl_msg_wire.type == UDEV_CTRL_EXIT) - return 1; - return -1; -} diff --git a/udev/udev-event.c b/udev/udev-event.c deleted file mode 100644 index 859d811bff..0000000000 --- a/udev/udev-event.c +++ /dev/null @@ -1,1005 +0,0 @@ -/* - * Copyright (C) 2003-2010 Kay Sievers <kay.sievers@vrfy.org> - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see <http://www.gnu.org/licenses/>. - */ - -#include <stdlib.h> -#include <stdio.h> -#include <stddef.h> -#include <unistd.h> -#include <fcntl.h> -#include <errno.h> -#include <ctype.h> -#include <string.h> -#include <time.h> -#include <net/if.h> -#include <sys/ioctl.h> -#include <sys/prctl.h> -#include <sys/poll.h> -#include <sys/epoll.h> -#include <sys/wait.h> -#include <sys/socket.h> -#include <sys/signalfd.h> -#include <linux/sockios.h> - -#include "udev.h" - -struct udev_event *udev_event_new(struct udev_device *dev) -{ - struct udev *udev = udev_device_get_udev(dev); - struct udev_event *event; - - event = calloc(1, sizeof(struct udev_event)); - if (event == NULL) - return NULL; - event->dev = dev; - event->udev = udev; - udev_list_init(udev, &event->run_list, false); - event->fd_signal = -1; - event->birth_usec = now_usec(); - event->timeout_usec = 60 * 1000 * 1000; - dbg(event->udev, "allocated event %p\n", event); - return event; -} - -void udev_event_unref(struct udev_event *event) -{ - if (event == NULL) - return; - udev_list_cleanup(&event->run_list); - free(event->program_result); - free(event->name); - dbg(event->udev, "free event %p\n", event); - free(event); -} - -size_t udev_event_apply_format(struct udev_event *event, const char *src, char *dest, size_t size) -{ - struct udev_device *dev = event->dev; - enum subst_type { - SUBST_UNKNOWN, - SUBST_DEVNODE, - SUBST_ATTR, - SUBST_ENV, - SUBST_KERNEL, - SUBST_KERNEL_NUMBER, - SUBST_DRIVER, - SUBST_DEVPATH, - SUBST_ID, - SUBST_MAJOR, - SUBST_MINOR, - SUBST_RESULT, - SUBST_PARENT, - SUBST_NAME, - SUBST_LINKS, - SUBST_ROOT, - SUBST_SYS, - }; - static const struct subst_map { - char *name; - char fmt; - enum subst_type type; - } map[] = { - { .name = "devnode", .fmt = 'N', .type = SUBST_DEVNODE }, - { .name = "tempnode", .fmt = 'N', .type = SUBST_DEVNODE }, - { .name = "attr", .fmt = 's', .type = SUBST_ATTR }, - { .name = "sysfs", .fmt = 's', .type = SUBST_ATTR }, - { .name = "env", .fmt = 'E', .type = SUBST_ENV }, - { .name = "kernel", .fmt = 'k', .type = SUBST_KERNEL }, - { .name = "number", .fmt = 'n', .type = SUBST_KERNEL_NUMBER }, - { .name = "driver", .fmt = 'd', .type = SUBST_DRIVER }, - { .name = "devpath", .fmt = 'p', .type = SUBST_DEVPATH }, - { .name = "id", .fmt = 'b', .type = SUBST_ID }, - { .name = "major", .fmt = 'M', .type = SUBST_MAJOR }, - { .name = "minor", .fmt = 'm', .type = SUBST_MINOR }, - { .name = "result", .fmt = 'c', .type = SUBST_RESULT }, - { .name = "parent", .fmt = 'P', .type = SUBST_PARENT }, - { .name = "name", .fmt = 'D', .type = SUBST_NAME }, - { .name = "links", .fmt = 'L', .type = SUBST_LINKS }, - { .name = "root", .fmt = 'r', .type = SUBST_ROOT }, - { .name = "sys", .fmt = 'S', .type = SUBST_SYS }, - }; - const char *from; - char *s; - size_t l; - - from = src; - s = dest; - l = size; - - for (;;) { - enum subst_type type = SUBST_UNKNOWN; - char attrbuf[UTIL_PATH_SIZE]; - char *attr = NULL; - - while (from[0] != '\0') { - if (from[0] == '$') { - /* substitute named variable */ - unsigned int i; - - if (from[1] == '$') { - from++; - goto copy; - } - - for (i = 0; i < ARRAY_SIZE(map); i++) { - if (strncmp(&from[1], map[i].name, strlen(map[i].name)) == 0) { - type = map[i].type; - from += strlen(map[i].name)+1; - dbg(event->udev, "will substitute format name '%s'\n", map[i].name); - goto subst; - } - } - } else if (from[0] == '%') { - /* substitute format char */ - unsigned int i; - - if (from[1] == '%') { - from++; - goto copy; - } - - for (i = 0; i < ARRAY_SIZE(map); i++) { - if (from[1] == map[i].fmt) { - type = map[i].type; - from += 2; - dbg(event->udev, "will substitute format char '%c'\n", map[i].fmt); - goto subst; - } - } - } -copy: - /* copy char */ - if (l == 0) - goto out; - s[0] = from[0]; - from++; - s++; - l--; - } - - goto out; -subst: - /* extract possible $format{attr} */ - if (from[0] == '{') { - unsigned int i; - - from++; - for (i = 0; from[i] != '}'; i++) { - if (from[i] == '\0') { - err(event->udev, "missing closing brace for format '%s'\n", src); - goto out; - } - } - if (i >= sizeof(attrbuf)) - goto out; - memcpy(attrbuf, from, i); - attrbuf[i] = '\0'; - from += i+1; - attr = attrbuf; - } else { - attr = NULL; - } - - switch (type) { - case SUBST_DEVPATH: - l = util_strpcpy(&s, l, udev_device_get_devpath(dev)); - dbg(event->udev, "substitute devpath '%s'\n", udev_device_get_devpath(dev)); - break; - case SUBST_KERNEL: - l = util_strpcpy(&s, l, udev_device_get_sysname(dev)); - dbg(event->udev, "substitute kernel name '%s'\n", udev_device_get_sysname(dev)); - break; - case SUBST_KERNEL_NUMBER: - if (udev_device_get_sysnum(dev) == NULL) - break; - l = util_strpcpy(&s, l, udev_device_get_sysnum(dev)); - dbg(event->udev, "substitute kernel number '%s'\n", udev_device_get_sysnum(dev)); - break; - case SUBST_ID: - if (event->dev_parent == NULL) - break; - l = util_strpcpy(&s, l, udev_device_get_sysname(event->dev_parent)); - dbg(event->udev, "substitute id '%s'\n", udev_device_get_sysname(event->dev_parent)); - break; - case SUBST_DRIVER: { - const char *driver; - - if (event->dev_parent == NULL) - break; - - driver = udev_device_get_driver(event->dev_parent); - if (driver == NULL) - break; - l = util_strpcpy(&s, l, driver); - dbg(event->udev, "substitute driver '%s'\n", driver); - break; - } - case SUBST_MAJOR: { - char num[UTIL_PATH_SIZE]; - - sprintf(num, "%d", major(udev_device_get_devnum(dev))); - l = util_strpcpy(&s, l, num); - dbg(event->udev, "substitute major number '%s'\n", num); - break; - } - case SUBST_MINOR: { - char num[UTIL_PATH_SIZE]; - - sprintf(num, "%d", minor(udev_device_get_devnum(dev))); - l = util_strpcpy(&s, l, num); - dbg(event->udev, "substitute minor number '%s'\n", num); - break; - } - case SUBST_RESULT: { - char *rest; - int i; - - if (event->program_result == NULL) - break; - /* get part part of the result string */ - i = 0; - if (attr != NULL) - i = strtoul(attr, &rest, 10); - if (i > 0) { - char result[UTIL_PATH_SIZE]; - char tmp[UTIL_PATH_SIZE]; - char *cpos; - - dbg(event->udev, "request part #%d of result string\n", i); - util_strscpy(result, sizeof(result), event->program_result); - cpos = result; - while (--i) { - while (cpos[0] != '\0' && !isspace(cpos[0])) - cpos++; - while (isspace(cpos[0])) - cpos++; - } - if (i > 0) { - err(event->udev, "requested part of result string not found\n"); - break; - } - util_strscpy(tmp, sizeof(tmp), cpos); - /* %{2+}c copies the whole string from the second part on */ - if (rest[0] != '+') { - cpos = strchr(tmp, ' '); - if (cpos) - cpos[0] = '\0'; - } - l = util_strpcpy(&s, l, tmp); - dbg(event->udev, "substitute part of result string '%s'\n", tmp); - } else { - l = util_strpcpy(&s, l, event->program_result); - dbg(event->udev, "substitute result string '%s'\n", event->program_result); - } - break; - } - case SUBST_ATTR: { - const char *value = NULL; - char vbuf[UTIL_NAME_SIZE]; - size_t len; - int count; - - if (attr == NULL) { - err(event->udev, "missing file parameter for attr\n"); - break; - } - - /* try to read the value specified by "[dmi/id]product_name" */ - if (util_resolve_subsys_kernel(event->udev, attr, vbuf, sizeof(vbuf), 1) == 0) - value = vbuf; - - /* try to read the attribute the device */ - if (value == NULL) - value = udev_device_get_sysattr_value(event->dev, attr); - - /* try to read the attribute of the parent device, other matches have selected */ - if (value == NULL && event->dev_parent != NULL && event->dev_parent != event->dev) - value = udev_device_get_sysattr_value(event->dev_parent, attr); - - if (value == NULL) - break; - - /* strip trailing whitespace, and replace unwanted characters */ - if (value != vbuf) - util_strscpy(vbuf, sizeof(vbuf), value); - len = strlen(vbuf); - while (len > 0 && isspace(vbuf[--len])) - vbuf[len] = '\0'; - count = util_replace_chars(vbuf, UDEV_ALLOWED_CHARS_INPUT); - if (count > 0) - info(event->udev, "%i character(s) replaced\n" , count); - l = util_strpcpy(&s, l, vbuf); - dbg(event->udev, "substitute sysfs value '%s'\n", vbuf); - break; - } - case SUBST_PARENT: { - struct udev_device *dev_parent; - const char *devnode; - - dev_parent = udev_device_get_parent(event->dev); - if (dev_parent == NULL) - break; - devnode = udev_device_get_devnode(dev_parent); - if (devnode != NULL) { - size_t devlen = strlen(udev_get_dev_path(event->udev))+1; - - l = util_strpcpy(&s, l, &devnode[devlen]); - dbg(event->udev, "found parent '%s', got node name '%s'\n", - udev_device_get_syspath(dev_parent), &devnode[devlen]); - } - break; - } - case SUBST_DEVNODE: - if (udev_device_get_devnode(dev) != NULL) - l = util_strpcpy(&s, l, udev_device_get_devnode(dev)); - break; - case SUBST_NAME: - if (event->name != NULL) { - l = util_strpcpy(&s, l, event->name); - dbg(event->udev, "substitute name '%s'\n", event->name); - } else { - l = util_strpcpy(&s, l, udev_device_get_sysname(dev)); - dbg(event->udev, "substitute sysname '%s'\n", udev_device_get_sysname(dev)); - } - break; - case SUBST_LINKS: { - size_t devlen = strlen(udev_get_dev_path(event->udev))+1; - struct udev_list_entry *list_entry; - - list_entry = udev_device_get_devlinks_list_entry(dev); - if (list_entry == NULL) - break; - l = util_strpcpy(&s, l, &udev_list_entry_get_name(list_entry)[devlen]); - udev_list_entry_foreach(list_entry, udev_list_entry_get_next(list_entry)) - l = util_strpcpyl(&s, l, " ", &udev_list_entry_get_name(list_entry)[devlen], NULL); - break; - } - case SUBST_ROOT: - l = util_strpcpy(&s, l, udev_get_dev_path(event->udev)); - dbg(event->udev, "substitute udev_root '%s'\n", udev_get_dev_path(event->udev)); - break; - case SUBST_SYS: - l = util_strpcpy(&s, l, udev_get_sys_path(event->udev)); - dbg(event->udev, "substitute sys_path '%s'\n", udev_get_sys_path(event->udev)); - break; - case SUBST_ENV: - if (attr == NULL) { - dbg(event->udev, "missing attribute\n"); - break; - } else { - const char *value; - - value = udev_device_get_property_value(event->dev, attr); - if (value == NULL) - break; - dbg(event->udev, "substitute env '%s=%s'\n", attr, value); - l = util_strpcpy(&s, l, value); - break; - } - default: - err(event->udev, "unknown substitution type=%i\n", type); - break; - } - } - -out: - s[0] = '\0'; - dbg(event->udev, "'%s' -> '%s' (%zu)\n", src, dest, l); - return l; -} - -static int spawn_exec(struct udev_event *event, - const char *cmd, char *const argv[], char **envp, const sigset_t *sigmask, - int fd_stdout, int fd_stderr) -{ - struct udev *udev = event->udev; - int err; - int fd; - - /* discard child output or connect to pipe */ - fd = open("/dev/null", O_RDWR); - if (fd >= 0) { - dup2(fd, STDIN_FILENO); - if (fd_stdout < 0) - dup2(fd, STDOUT_FILENO); - if (fd_stderr < 0) - dup2(fd, STDERR_FILENO); - close(fd); - } else { - err(udev, "open /dev/null failed: %m\n"); - } - - /* connect pipes to std{out,err} */ - if (fd_stdout >= 0) { - dup2(fd_stdout, STDOUT_FILENO); - close(fd_stdout); - } - if (fd_stderr >= 0) { - dup2(fd_stderr, STDERR_FILENO); - close(fd_stderr); - } - - /* terminate child in case parent goes away */ - prctl(PR_SET_PDEATHSIG, SIGTERM); - - /* restore original udev sigmask before exec */ - if (sigmask) - sigprocmask(SIG_SETMASK, sigmask, NULL); - - execve(argv[0], argv, envp); - - /* exec failed */ - err = -errno; - err(udev, "failed to execute '%s' '%s': %m\n", argv[0], cmd); - return err; -} - -static void spawn_read(struct udev_event *event, - const char *cmd, - int fd_stdout, int fd_stderr, - char *result, size_t ressize) -{ - struct udev *udev = event->udev; - size_t respos = 0; - int fd_ep = -1; - struct epoll_event ep_outpipe, ep_errpipe; - - /* read from child if requested */ - if (fd_stdout < 0 && fd_stderr < 0) - return; - - fd_ep = epoll_create1(EPOLL_CLOEXEC); - if (fd_ep < 0) { - err(udev, "error creating epoll fd: %m\n"); - goto out; - } - - if (fd_stdout >= 0) { - memset(&ep_outpipe, 0, sizeof(struct epoll_event)); - ep_outpipe.events = EPOLLIN; - ep_outpipe.data.ptr = &fd_stdout; - if (epoll_ctl(fd_ep, EPOLL_CTL_ADD, fd_stdout, &ep_outpipe) < 0) { - err(udev, "fail to add fd to epoll: %m\n"); - goto out; - } - } - - if (fd_stderr >= 0) { - memset(&ep_errpipe, 0, sizeof(struct epoll_event)); - ep_errpipe.events = EPOLLIN; - ep_errpipe.data.ptr = &fd_stderr; - if (epoll_ctl(fd_ep, EPOLL_CTL_ADD, fd_stderr, &ep_errpipe) < 0) { - err(udev, "fail to add fd to epoll: %m\n"); - goto out; - } - } - - /* read child output */ - while (fd_stdout >= 0 || fd_stderr >= 0) { - int timeout; - int fdcount; - struct epoll_event ev[4]; - int i; - - if (event->timeout_usec > 0) { - unsigned long long age_usec; - - age_usec = now_usec() - event->birth_usec; - if (age_usec >= event->timeout_usec) { - err(udev, "timeout '%s'\n", cmd); - goto out; - } - timeout = ((event->timeout_usec - age_usec) / 1000) + 1000; - } else { - timeout = -1; - } - - fdcount = epoll_wait(fd_ep, ev, ARRAY_SIZE(ev), timeout); - if (fdcount < 0) { - if (errno == EINTR) - continue; - err(udev, "failed to poll: %m\n"); - goto out; - } - if (fdcount == 0) { - err(udev, "timeout '%s'\n", cmd); - goto out; - } - - for (i = 0; i < fdcount; i++) { - int *fd = (int *)ev[i].data.ptr; - - if (ev[i].events & EPOLLIN) { - ssize_t count; - char buf[4096]; - - count = read(*fd, buf, sizeof(buf)-1); - if (count <= 0) - continue; - buf[count] = '\0'; - - /* store stdout result */ - if (result != NULL && *fd == fd_stdout) { - if (respos + count < ressize) { - memcpy(&result[respos], buf, count); - respos += count; - } else { - err(udev, "'%s' ressize %zd too short\n", cmd, ressize); - } - } - - /* log debug output only if we watch stderr */ - if (fd_stderr >= 0) { - char *pos; - char *line; - - pos = buf; - while ((line = strsep(&pos, "\n"))) { - if (pos != NULL || line[0] != '\0') - info(udev, "'%s'(%s) '%s'\n", cmd, *fd == fd_stdout ? "out" : "err" , line); - } - } - } else if (ev[i].events & EPOLLHUP) { - if (epoll_ctl(fd_ep, EPOLL_CTL_DEL, *fd, NULL) < 0) { - err(udev, "failed to remove fd from epoll: %m\n"); - goto out; - } - *fd = -1; - } - } - } - - /* return the child's stdout string */ - if (result != NULL) { - result[respos] = '\0'; - dbg(udev, "result='%s'\n", result); - } -out: - if (fd_ep >= 0) - close(fd_ep); -} - -static int spawn_wait(struct udev_event *event, const char *cmd, pid_t pid) -{ - struct udev *udev = event->udev; - struct pollfd pfd[1]; - int err = 0; - - pfd[0].events = POLLIN; - pfd[0].fd = event->fd_signal; - - while (pid > 0) { - int timeout; - int fdcount; - - if (event->timeout_usec > 0) { - unsigned long long age_usec; - - age_usec = now_usec() - event->birth_usec; - if (age_usec >= event->timeout_usec) - timeout = 1000; - else - timeout = ((event->timeout_usec - age_usec) / 1000) + 1000; - } else { - timeout = -1; - } - - fdcount = poll(pfd, 1, timeout); - if (fdcount < 0) { - if (errno == EINTR) - continue; - err = -errno; - err(udev, "failed to poll: %m\n"); - goto out; - } - if (fdcount == 0) { - err(udev, "timeout: killing '%s' [%u]\n", cmd, pid); - kill(pid, SIGKILL); - } - - if (pfd[0].revents & POLLIN) { - struct signalfd_siginfo fdsi; - int status; - ssize_t size; - - size = read(event->fd_signal, &fdsi, sizeof(struct signalfd_siginfo)); - if (size != sizeof(struct signalfd_siginfo)) - continue; - - switch (fdsi.ssi_signo) { - case SIGTERM: - event->sigterm = true; - break; - case SIGCHLD: - if (waitpid(pid, &status, WNOHANG) < 0) - break; - if (WIFEXITED(status)) { - info(udev, "'%s' [%u] exit with return code %i\n", cmd, pid, WEXITSTATUS(status)); - if (WEXITSTATUS(status) != 0) - err = -1; - } else if (WIFSIGNALED(status)) { - err(udev, "'%s' [%u] terminated by signal %i (%s)\n", cmd, pid, WTERMSIG(status), strsignal(WTERMSIG(status))); - err = -1; - } else if (WIFSTOPPED(status)) { - err(udev, "'%s' [%u] stopped\n", cmd, pid); - err = -1; - } else if (WIFCONTINUED(status)) { - err(udev, "'%s' [%u] continued\n", cmd, pid); - err = -1; - } else { - err(udev, "'%s' [%u] exit with status 0x%04x\n", cmd, pid, status); - err = -1; - } - pid = 0; - break; - } - } - } -out: - return err; -} - -int udev_build_argv(struct udev *udev, char *cmd, int *argc, char *argv[]) -{ - int i = 0; - char *pos; - - if (strchr(cmd, ' ') == NULL) { - argv[i++] = cmd; - goto out; - } - - pos = cmd; - while (pos != NULL && pos[0] != '\0') { - if (pos[0] == '\'') { - /* do not separate quotes */ - pos++; - argv[i] = strsep(&pos, "\'"); - if (pos != NULL) - while (pos[0] == ' ') - pos++; - } else { - argv[i] = strsep(&pos, " "); - if (pos != NULL) - while (pos[0] == ' ') - pos++; - } - dbg(udev, "argv[%i] '%s'\n", i, argv[i]); - i++; - } -out: - argv[i] = NULL; - if (argc) - *argc = i; - return 0; -} - -int udev_event_spawn(struct udev_event *event, - const char *cmd, char **envp, const sigset_t *sigmask, - char *result, size_t ressize) -{ - struct udev *udev = event->udev; - int outpipe[2] = {-1, -1}; - int errpipe[2] = {-1, -1}; - pid_t pid; - char arg[UTIL_PATH_SIZE]; - char *argv[128]; - char program[UTIL_PATH_SIZE]; - int err = 0; - - util_strscpy(arg, sizeof(arg), cmd); - udev_build_argv(event->udev, arg, NULL, argv); - - /* pipes from child to parent */ - if (result != NULL || udev_get_log_priority(udev) >= LOG_INFO) { - if (pipe2(outpipe, O_NONBLOCK) != 0) { - err = -errno; - err(udev, "pipe failed: %m\n"); - goto out; - } - } - if (udev_get_log_priority(udev) >= LOG_INFO) { - if (pipe2(errpipe, O_NONBLOCK) != 0) { - err = -errno; - err(udev, "pipe failed: %m\n"); - goto out; - } - } - - /* allow programs in /usr/lib/udev/ to be called without the path */ - if (argv[0][0] != '/') { - util_strscpyl(program, sizeof(program), PKGLIBEXECDIR "/", argv[0], NULL); - argv[0] = program; - } - - pid = fork(); - switch(pid) { - case 0: - /* child closes parent's ends of pipes */ - if (outpipe[READ_END] >= 0) { - close(outpipe[READ_END]); - outpipe[READ_END] = -1; - } - if (errpipe[READ_END] >= 0) { - close(errpipe[READ_END]); - errpipe[READ_END] = -1; - } - - info(udev, "starting '%s'\n", cmd); - - err = spawn_exec(event, cmd, argv, envp, sigmask, - outpipe[WRITE_END], errpipe[WRITE_END]); - - _exit(2 ); - case -1: - err(udev, "fork of '%s' failed: %m\n", cmd); - err = -1; - goto out; - default: - /* parent closed child's ends of pipes */ - if (outpipe[WRITE_END] >= 0) { - close(outpipe[WRITE_END]); - outpipe[WRITE_END] = -1; - } - if (errpipe[WRITE_END] >= 0) { - close(errpipe[WRITE_END]); - errpipe[WRITE_END] = -1; - } - - spawn_read(event, cmd, - outpipe[READ_END], errpipe[READ_END], - result, ressize); - - err = spawn_wait(event, cmd, pid); - } - -out: - if (outpipe[READ_END] >= 0) - close(outpipe[READ_END]); - if (outpipe[WRITE_END] >= 0) - close(outpipe[WRITE_END]); - if (errpipe[READ_END] >= 0) - close(errpipe[READ_END]); - if (errpipe[WRITE_END] >= 0) - close(errpipe[WRITE_END]); - return err; -} - -static void rename_netif_kernel_log(struct ifreq ifr) -{ - int klog; - FILE *f; - - klog = open("/dev/kmsg", O_WRONLY); - if (klog < 0) - return; - - f = fdopen(klog, "w"); - if (f == NULL) { - close(klog); - return; - } - - fprintf(f, "<30>udevd[%u]: renamed network interface %s to %s\n", - getpid(), ifr.ifr_name, ifr.ifr_newname); - fclose(f); -} - -static int rename_netif(struct udev_event *event) -{ - struct udev_device *dev = event->dev; - int sk; - struct ifreq ifr; - int loop; - int err; - - info(event->udev, "changing net interface name from '%s' to '%s'\n", - udev_device_get_sysname(dev), event->name); - - sk = socket(PF_INET, SOCK_DGRAM, 0); - if (sk < 0) { - err = -errno; - err(event->udev, "error opening socket: %m\n"); - return err; - } - - memset(&ifr, 0x00, sizeof(struct ifreq)); - util_strscpy(ifr.ifr_name, IFNAMSIZ, udev_device_get_sysname(dev)); - util_strscpy(ifr.ifr_newname, IFNAMSIZ, event->name); - err = ioctl(sk, SIOCSIFNAME, &ifr); - if (err == 0) { - rename_netif_kernel_log(ifr); - goto out; - } - - /* keep trying if the destination interface name already exists */ - err = -errno; - if (err != -EEXIST) - goto out; - - /* free our own name, another process may wait for us */ - snprintf(ifr.ifr_newname, IFNAMSIZ, "rename%u", udev_device_get_ifindex(dev)); - err = ioctl(sk, SIOCSIFNAME, &ifr); - if (err < 0) { - err = -errno; - goto out; - } - - /* log temporary name */ - rename_netif_kernel_log(ifr); - - /* wait a maximum of 90 seconds for our target to become available */ - util_strscpy(ifr.ifr_name, IFNAMSIZ, ifr.ifr_newname); - util_strscpy(ifr.ifr_newname, IFNAMSIZ, event->name); - loop = 90 * 20; - while (loop--) { - const struct timespec duration = { 0, 1000 * 1000 * 1000 / 20 }; - - dbg(event->udev, "wait for netif '%s' to become free, loop=%i\n", - event->name, (90 * 20) - loop); - nanosleep(&duration, NULL); - - err = ioctl(sk, SIOCSIFNAME, &ifr); - if (err == 0) { - rename_netif_kernel_log(ifr); - break; - } - err = -errno; - if (err != -EEXIST) - break; - } - -out: - if (err < 0) - err(event->udev, "error changing net interface name %s to %s: %m\n", ifr.ifr_name, ifr.ifr_newname); - close(sk); - return err; -} - -int udev_event_execute_rules(struct udev_event *event, struct udev_rules *rules, const sigset_t *sigmask) -{ - struct udev_device *dev = event->dev; - int err = 0; - - if (udev_device_get_subsystem(dev) == NULL) - return -1; - - if (strcmp(udev_device_get_action(dev), "remove") == 0) { - udev_device_read_db(dev, NULL); - udev_device_delete_db(dev); - udev_device_tag_index(dev, NULL, false); - - if (major(udev_device_get_devnum(dev)) != 0) - udev_watch_end(event->udev, dev); - - udev_rules_apply_to_event(rules, event, sigmask); - - if (major(udev_device_get_devnum(dev)) != 0) - err = udev_node_remove(dev); - } else { - event->dev_db = udev_device_new_from_syspath(event->udev, udev_device_get_syspath(dev)); - if (event->dev_db != NULL) { - udev_device_read_db(event->dev_db, NULL); - udev_device_set_info_loaded(event->dev_db); - - /* disable watch during event processing */ - if (major(udev_device_get_devnum(dev)) != 0) - udev_watch_end(event->udev, event->dev_db); - } - - udev_rules_apply_to_event(rules, event, sigmask); - - /* rename a new network interface, if needed */ - if (udev_device_get_ifindex(dev) > 0 && strcmp(udev_device_get_action(dev), "add") == 0 && - event->name != NULL && strcmp(event->name, udev_device_get_sysname(dev)) != 0) { - char syspath[UTIL_PATH_SIZE]; - char *pos; - - err = rename_netif(event); - if (err == 0) { - info(event->udev, "renamed netif to '%s'\n", event->name); - - /* remember old name */ - udev_device_add_property(dev, "INTERFACE_OLD", udev_device_get_sysname(dev)); - - /* now change the devpath, because the kernel device name has changed */ - util_strscpy(syspath, sizeof(syspath), udev_device_get_syspath(dev)); - pos = strrchr(syspath, '/'); - if (pos != NULL) { - pos++; - util_strscpy(pos, sizeof(syspath) - (pos - syspath), event->name); - udev_device_set_syspath(event->dev, syspath); - udev_device_add_property(dev, "INTERFACE", udev_device_get_sysname(dev)); - info(event->udev, "changed devpath to '%s'\n", udev_device_get_devpath(dev)); - } - } - } - - if (major(udev_device_get_devnum(dev)) != 0) { - /* remove/update possible left-over symlinks from old database entry */ - if (event->dev_db != NULL) - udev_node_update_old_links(dev, event->dev_db); - - if (!event->mode_set) { - if (udev_device_get_devnode_mode(dev) > 0) { - /* kernel supplied value */ - event->mode = udev_device_get_devnode_mode(dev); - } else if (event->gid > 0) { - /* default 0660 if a group is assigned */ - event->mode = 0660; - } else { - /* default 0600 */ - event->mode = 0600; - } - } - - err = udev_node_add(dev, event->mode, event->uid, event->gid); - } - - /* preserve old, or get new initialization timestamp */ - if (event->dev_db != NULL && udev_device_get_usec_initialized(event->dev_db) > 0) - udev_device_set_usec_initialized(event->dev, udev_device_get_usec_initialized(event->dev_db)); - else if (udev_device_get_usec_initialized(event->dev) == 0) - udev_device_set_usec_initialized(event->dev, now_usec()); - - /* (re)write database file */ - udev_device_update_db(dev); - udev_device_tag_index(dev, event->dev_db, true); - udev_device_set_is_initialized(dev); - - udev_device_unref(event->dev_db); - event->dev_db = NULL; - } -out: - return err; -} - -int udev_event_execute_run(struct udev_event *event, const sigset_t *sigmask) -{ - struct udev_list_entry *list_entry; - int err = 0; - - dbg(event->udev, "executing run list\n"); - udev_list_entry_foreach(list_entry, udev_list_get_entry(&event->run_list)) { - const char *cmd = udev_list_entry_get_name(list_entry); - - if (strncmp(cmd, "socket:", strlen("socket:")) == 0) { - struct udev_monitor *monitor; - - monitor = udev_monitor_new_from_socket(event->udev, &cmd[strlen("socket:")]); - if (monitor == NULL) - continue; - udev_monitor_send_device(monitor, NULL, event->dev); - udev_monitor_unref(monitor); - } else { - char program[UTIL_PATH_SIZE]; - char **envp; - - if (event->exec_delay > 0) { - info(event->udev, "delay execution of '%s'\n", program); - sleep(event->exec_delay); - } - - udev_event_apply_format(event, cmd, program, sizeof(program)); - envp = udev_device_get_properties_envp(event->dev); - if (udev_event_spawn(event, program, envp, sigmask, NULL, 0) < 0) { - if (udev_list_entry_get_num(list_entry)) - err = -1; - } - } - } - return err; -} diff --git a/udev/udev-node.c b/udev/udev-node.c deleted file mode 100644 index 31046bd713..0000000000 --- a/udev/udev-node.c +++ /dev/null @@ -1,385 +0,0 @@ -/* - * Copyright (C) 2003-2010 Kay Sievers <kay.sievers@vrfy.org> - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see <http://www.gnu.org/licenses/>. - */ - -#include <stdlib.h> -#include <string.h> -#include <stdio.h> -#include <stddef.h> -#include <stdbool.h> -#include <fcntl.h> -#include <unistd.h> -#include <errno.h> -#include <grp.h> -#include <dirent.h> -#include <sys/time.h> -#include <sys/stat.h> -#include <sys/types.h> - -#include "udev.h" - -#define TMP_FILE_EXT ".udev-tmp" - -static int node_symlink(struct udev *udev, const char *node, const char *slink) -{ - struct stat stats; - char target[UTIL_PATH_SIZE]; - char *s; - size_t l; - char slink_tmp[UTIL_PATH_SIZE + sizeof(TMP_FILE_EXT)]; - int i = 0; - int tail = 0; - int err = 0; - - /* use relative link */ - target[0] = '\0'; - while (node[i] && (node[i] == slink[i])) { - if (node[i] == '/') - tail = i+1; - i++; - } - s = target; - l = sizeof(target); - while (slink[i] != '\0') { - if (slink[i] == '/') - l = util_strpcpy(&s, l, "../"); - i++; - } - l = util_strscpy(s, l, &node[tail]); - if (l == 0) { - err = -EINVAL; - goto exit; - } - - /* preserve link with correct target, do not replace node of other device */ - if (lstat(slink, &stats) == 0) { - if (S_ISBLK(stats.st_mode) || S_ISCHR(stats.st_mode)) { - struct stat stats2; - - info(udev, "found existing node instead of symlink '%s'\n", slink); - if (lstat(node, &stats2) == 0) { - if ((stats.st_mode & S_IFMT) == (stats2.st_mode & S_IFMT) && - stats.st_rdev == stats2.st_rdev && stats.st_ino != stats2.st_ino) { - info(udev, "replace device node '%s' with symlink to our node '%s'\n", - slink, node); - } else { - err(udev, "device node '%s' already exists, " - "link to '%s' will not overwrite it\n", - slink, node); - goto exit; - } - } - } else if (S_ISLNK(stats.st_mode)) { - char buf[UTIL_PATH_SIZE]; - int len; - - dbg(udev, "found existing symlink '%s'\n", slink); - len = readlink(slink, buf, sizeof(buf)); - if (len > 0 && len < (int)sizeof(buf)) { - buf[len] = '\0'; - if (strcmp(target, buf) == 0) { - info(udev, "preserve already existing symlink '%s' to '%s'\n", - slink, target); - udev_selinux_lsetfilecon(udev, slink, S_IFLNK); - utimensat(AT_FDCWD, slink, NULL, AT_SYMLINK_NOFOLLOW); - goto exit; - } - } - } - } else { - info(udev, "creating symlink '%s' to '%s'\n", slink, target); - do { - err = util_create_path_selinux(udev, slink); - if (err != 0 && err != -ENOENT) - break; - udev_selinux_setfscreatecon(udev, slink, S_IFLNK); - err = symlink(target, slink); - if (err != 0) - err = -errno; - udev_selinux_resetfscreatecon(udev); - } while (err == -ENOENT); - if (err == 0) - goto exit; - } - - info(udev, "atomically replace '%s'\n", slink); - util_strscpyl(slink_tmp, sizeof(slink_tmp), slink, TMP_FILE_EXT, NULL); - unlink(slink_tmp); - do { - err = util_create_path_selinux(udev, slink_tmp); - if (err != 0 && err != -ENOENT) - break; - udev_selinux_setfscreatecon(udev, slink_tmp, S_IFLNK); - err = symlink(target, slink_tmp); - if (err != 0) - err = -errno; - udev_selinux_resetfscreatecon(udev); - } while (err == -ENOENT); - if (err != 0) { - err(udev, "symlink '%s' '%s' failed: %m\n", target, slink_tmp); - goto exit; - } - err = rename(slink_tmp, slink); - if (err != 0) { - err(udev, "rename '%s' '%s' failed: %m\n", slink_tmp, slink); - unlink(slink_tmp); - } -exit: - return err; -} - -/* find device node of device with highest priority */ -static const char *link_find_prioritized(struct udev_device *dev, bool add, const char *stackdir, char *buf, size_t bufsize) -{ - struct udev *udev = udev_device_get_udev(dev); - DIR *dir; - int priority = 0; - const char *target = NULL; - - if (add) { - priority = udev_device_get_devlink_priority(dev); - util_strscpy(buf, bufsize, udev_device_get_devnode(dev)); - target = buf; - } - - dir = opendir(stackdir); - if (dir == NULL) - return target; - for (;;) { - struct udev_device *dev_db; - struct dirent *dent; - - dent = readdir(dir); - if (dent == NULL || dent->d_name[0] == '\0') - break; - if (dent->d_name[0] == '.') - continue; - - info(udev, "found '%s' claiming '%s'\n", dent->d_name, stackdir); - - /* did we find ourself? */ - if (strcmp(dent->d_name, udev_device_get_id_filename(dev)) == 0) - continue; - - dev_db = udev_device_new_from_id_filename(udev, dent->d_name); - if (dev_db != NULL) { - const char *devnode; - - devnode = udev_device_get_devnode(dev_db); - if (devnode != NULL) { - dbg(udev, "compare priority of '%s'(%i) > '%s'(%i)\n", target, priority, - udev_device_get_devnode(dev_db), udev_device_get_devlink_priority(dev_db)); - if (target == NULL || udev_device_get_devlink_priority(dev_db) > priority) { - info(udev, "'%s' claims priority %i for '%s'\n", - udev_device_get_syspath(dev_db), udev_device_get_devlink_priority(dev_db), stackdir); - priority = udev_device_get_devlink_priority(dev_db); - util_strscpy(buf, bufsize, devnode); - target = buf; - } - } - udev_device_unref(dev_db); - } - } - closedir(dir); - return target; -} - -/* manage "stack of names" with possibly specified device priorities */ -static void link_update(struct udev_device *dev, const char *slink, bool add) -{ - struct udev *udev = udev_device_get_udev(dev); - char name_enc[UTIL_PATH_SIZE]; - char filename[UTIL_PATH_SIZE * 2]; - char dirname[UTIL_PATH_SIZE]; - const char *target; - char buf[UTIL_PATH_SIZE]; - - dbg(udev, "update symlink '%s' of '%s'\n", slink, udev_device_get_syspath(dev)); - - util_path_encode(&slink[strlen(udev_get_dev_path(udev))+1], name_enc, sizeof(name_enc)); - util_strscpyl(dirname, sizeof(dirname), udev_get_run_path(udev), "/links/", name_enc, NULL); - util_strscpyl(filename, sizeof(filename), dirname, "/", udev_device_get_id_filename(dev), NULL); - - if (!add) { - dbg(udev, "removing index: '%s'\n", filename); - if (unlink(filename) == 0) - rmdir(dirname); - } - - target = link_find_prioritized(dev, add, dirname, buf, sizeof(buf)); - if (target == NULL) { - info(udev, "no reference left, remove '%s'\n", slink); - if (unlink(slink) == 0) - util_delete_path(udev, slink); - } else { - info(udev, "creating link '%s' to '%s'\n", slink, target); - node_symlink(udev, target, slink); - } - - if (add) { - int err; - - dbg(udev, "creating index: '%s'\n", filename); - do { - int fd; - - err = util_create_path(udev, filename); - if (err != 0 && err != -ENOENT) - break; - fd = open(filename, O_WRONLY|O_CREAT|O_CLOEXEC|O_TRUNC|O_NOFOLLOW, 0444); - if (fd >= 0) - close(fd); - else - err = -errno; - } while (err == -ENOENT); - } -} - -void udev_node_update_old_links(struct udev_device *dev, struct udev_device *dev_old) -{ - struct udev *udev = udev_device_get_udev(dev); - struct udev_list_entry *list_entry; - - /* update possible left-over symlinks */ - udev_list_entry_foreach(list_entry, udev_device_get_devlinks_list_entry(dev_old)) { - const char *name = udev_list_entry_get_name(list_entry); - struct udev_list_entry *list_entry_current; - int found; - - /* check if old link name still belongs to this device */ - found = 0; - udev_list_entry_foreach(list_entry_current, udev_device_get_devlinks_list_entry(dev)) { - const char *name_current = udev_list_entry_get_name(list_entry_current); - - if (strcmp(name, name_current) == 0) { - found = 1; - break; - } - } - if (found) - continue; - - info(udev, "update old name, '%s' no longer belonging to '%s'\n", - name, udev_device_get_devpath(dev)); - link_update(dev, name, 0); - } -} - -static int node_fixup(struct udev_device *dev, mode_t mode, uid_t uid, gid_t gid) -{ - struct udev *udev = udev_device_get_udev(dev); - const char *devnode = udev_device_get_devnode(dev); - dev_t devnum = udev_device_get_devnum(dev); - struct stat stats; - int err = 0; - - if (strcmp(udev_device_get_subsystem(dev), "block") == 0) - mode |= S_IFBLK; - else - mode |= S_IFCHR; - - if (lstat(devnode, &stats) != 0) { - err = -errno; - info(udev, "can not stat() node '%s' (%m)\n", devnode); - goto out; - } - - if (((stats.st_mode & S_IFMT) != (mode & S_IFMT)) || (stats.st_rdev != devnum)) { - err = -EEXIST; - info(udev, "found node '%s' with non-matching devnum %s, skip handling\n", - udev_device_get_devnode(dev), udev_device_get_id_filename(dev)); - goto out; - } - - if ((stats.st_mode & 0777) != (mode & 0777) || stats.st_uid != uid || stats.st_gid != gid) { - info(udev, "set permissions %s, %#o, uid=%u, gid=%u\n", devnode, mode, uid, gid); - chmod(devnode, mode); - chown(devnode, uid, gid); - } else { - info(udev, "preserve permissions %s, %#o, uid=%u, gid=%u\n", devnode, mode, uid, gid); - } - - /* - * Set initial selinux file context only on add events. - * We set the proper context on bootup (triger) or for newly - * added devices, but we don't change it later, in case - * something else has set a custom context in the meantime. - */ - if (strcmp(udev_device_get_action(dev), "add") == 0) - udev_selinux_lsetfilecon(udev, devnode, mode); - - /* always update timestamp when we re-use the node, like on media change events */ - utimensat(AT_FDCWD, devnode, NULL, 0); -out: - return err; -} - -int udev_node_add(struct udev_device *dev, mode_t mode, uid_t uid, gid_t gid) -{ - struct udev *udev = udev_device_get_udev(dev); - char filename[UTIL_PATH_SIZE]; - struct udev_list_entry *list_entry; - int err = 0; - - info(udev, "handling device node '%s', devnum=%s, mode=%#o, uid=%d, gid=%d\n", - udev_device_get_devnode(dev), udev_device_get_id_filename(dev), mode, uid, gid); - - err = node_fixup(dev, mode, uid, gid); - if (err < 0) - goto exit; - - /* always add /dev/{block,char}/$major:$minor */ - snprintf(filename, sizeof(filename), "%s/%s/%u:%u", - udev_get_dev_path(udev), - strcmp(udev_device_get_subsystem(dev), "block") == 0 ? "block" : "char", - major(udev_device_get_devnum(dev)), minor(udev_device_get_devnum(dev))); - node_symlink(udev, udev_device_get_devnode(dev), filename); - - /* create/update symlinks, add symlinks to name index */ - udev_list_entry_foreach(list_entry, udev_device_get_devlinks_list_entry(dev)) { - if (udev_list_entry_get_num(list_entry)) - /* simple unmanaged link name */ - node_symlink(udev, udev_device_get_devnode(dev), udev_list_entry_get_name(list_entry)); - else - link_update(dev, udev_list_entry_get_name(list_entry), 1); - } -exit: - return err; -} - -int udev_node_remove(struct udev_device *dev) -{ - struct udev *udev = udev_device_get_udev(dev); - struct udev_list_entry *list_entry; - const char *devnode; - struct stat stats; - struct udev_device *dev_check; - char filename[UTIL_PATH_SIZE]; - int err = 0; - - /* remove/update symlinks, remove symlinks from name index */ - udev_list_entry_foreach(list_entry, udev_device_get_devlinks_list_entry(dev)) - link_update(dev, udev_list_entry_get_name(list_entry), 0); - - /* remove /dev/{block,char}/$major:$minor */ - snprintf(filename, sizeof(filename), "%s/%s/%u:%u", - udev_get_dev_path(udev), - strcmp(udev_device_get_subsystem(dev), "block") == 0 ? "block" : "char", - major(udev_device_get_devnum(dev)), minor(udev_device_get_devnum(dev))); - unlink(filename); -out: - return err; -} diff --git a/udev/udev-rules.c b/udev/udev-rules.c deleted file mode 100644 index 7e79545124..0000000000 --- a/udev/udev-rules.c +++ /dev/null @@ -1,2753 +0,0 @@ -/* - * Copyright (C) 2003-2010 Kay Sievers <kay.sievers@vrfy.org> - * Copyright (C) 2008 Alan Jenkins <alan-jenkins@tuffmail.co.uk> - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see <http://www.gnu.org/licenses/>. - */ - -#include <stddef.h> -#include <limits.h> -#include <stdlib.h> -#include <stdbool.h> -#include <string.h> -#include <stdio.h> -#include <fcntl.h> -#include <ctype.h> -#include <unistd.h> -#include <errno.h> -#include <dirent.h> -#include <fnmatch.h> -#include <time.h> - -#include "udev.h" - -#define PREALLOC_TOKEN 2048 -#define PREALLOC_STRBUF 32 * 1024 -#define PREALLOC_TRIE 256 - -struct uid_gid { - unsigned int name_off; - union { - uid_t uid; - gid_t gid; - }; -}; - -struct trie_node { - /* this node's first child */ - unsigned int child_idx; - /* the next child of our parent node's child list */ - unsigned int next_child_idx; - /* this node's last child (shortcut for append) */ - unsigned int last_child_idx; - unsigned int value_off; - unsigned short value_len; - unsigned char key; -}; - -struct udev_rules { - struct udev *udev; - int resolve_names; - - /* every key in the rules file becomes a token */ - struct token *tokens; - unsigned int token_cur; - unsigned int token_max; - - /* all key strings are copied to a single string buffer */ - char *buf; - size_t buf_cur; - size_t buf_max; - unsigned int buf_count; - - /* during rule parsing, strings are indexed to find duplicates */ - struct trie_node *trie_nodes; - unsigned int trie_nodes_cur; - unsigned int trie_nodes_max; - - /* during rule parsing, uid/gid lookup results are cached */ - struct uid_gid *uids; - unsigned int uids_cur; - unsigned int uids_max; - struct uid_gid *gids; - unsigned int gids_cur; - unsigned int gids_max; -}; - -/* KEY=="", KEY!="", KEY+="", KEY="", KEY:="" */ -enum operation_type { - OP_UNSET, - - OP_MATCH, - OP_NOMATCH, - OP_MATCH_MAX, - - OP_ADD, - OP_ASSIGN, - OP_ASSIGN_FINAL, -}; - -enum string_glob_type { - GL_UNSET, - GL_PLAIN, /* no special chars */ - GL_GLOB, /* shell globs ?,*,[] */ - GL_SPLIT, /* multi-value A|B */ - GL_SPLIT_GLOB, /* multi-value with glob A*|B* */ - GL_SOMETHING, /* commonly used "?*" */ -}; - -enum string_subst_type { - SB_UNSET, - SB_NONE, - SB_FORMAT, - SB_SUBSYS, -}; - -/* tokens of a rule are sorted/handled in this order */ -enum token_type { - TK_UNSET, - TK_RULE, - - TK_M_ACTION, /* val */ - TK_M_DEVPATH, /* val */ - TK_M_KERNEL, /* val */ - TK_M_DEVLINK, /* val */ - TK_M_NAME, /* val */ - TK_M_ENV, /* val, attr */ - TK_M_TAG, /* val */ - TK_M_SUBSYSTEM, /* val */ - TK_M_DRIVER, /* val */ - TK_M_WAITFOR, /* val */ - TK_M_ATTR, /* val, attr */ - - TK_M_PARENTS_MIN, - TK_M_KERNELS, /* val */ - TK_M_SUBSYSTEMS, /* val */ - TK_M_DRIVERS, /* val */ - TK_M_ATTRS, /* val, attr */ - TK_M_TAGS, /* val */ - TK_M_PARENTS_MAX, - - TK_M_TEST, /* val, mode_t */ - TK_M_EVENT_TIMEOUT, /* int */ - TK_M_PROGRAM, /* val */ - TK_M_IMPORT_FILE, /* val */ - TK_M_IMPORT_PROG, /* val */ - TK_M_IMPORT_BUILTIN, /* val */ - TK_M_IMPORT_DB, /* val */ - TK_M_IMPORT_CMDLINE, /* val */ - TK_M_IMPORT_PARENT, /* val */ - TK_M_RESULT, /* val */ - TK_M_MAX, - - TK_A_STRING_ESCAPE_NONE, - TK_A_STRING_ESCAPE_REPLACE, - TK_A_DB_PERSIST, - TK_A_INOTIFY_WATCH, /* int */ - TK_A_DEVLINK_PRIO, /* int */ - TK_A_OWNER, /* val */ - TK_A_GROUP, /* val */ - TK_A_MODE, /* val */ - TK_A_OWNER_ID, /* uid_t */ - TK_A_GROUP_ID, /* gid_t */ - TK_A_MODE_ID, /* mode_t */ - TK_A_STATIC_NODE, /* val */ - TK_A_ENV, /* val, attr */ - TK_A_TAG, /* val */ - TK_A_NAME, /* val */ - TK_A_DEVLINK, /* val */ - TK_A_ATTR, /* val, attr */ - TK_A_RUN, /* val, bool */ - TK_A_GOTO, /* size_t */ - - TK_END, -}; - -/* we try to pack stuff in a way that we take only 12 bytes per token */ -struct token { - union { - unsigned char type; /* same in rule and key */ - struct { - enum token_type type:8; - bool can_set_name:1; - bool has_static_node:1; - unsigned int unused:6; - unsigned short token_count; - unsigned int label_off; - unsigned short filename_off; - unsigned short filename_line; - } rule; - struct { - enum token_type type:8; - enum operation_type op:8; - enum string_glob_type glob:8; - enum string_subst_type subst:4; - enum string_subst_type attrsubst:4; - unsigned int value_off; - union { - unsigned int attr_off; - int devlink_unique; - unsigned int rule_goto; - mode_t mode; - uid_t uid; - gid_t gid; - int devlink_prio; - int event_timeout; - int watch; - enum udev_builtin_cmd builtin_cmd; - }; - } key; - }; -}; - -#define MAX_TK 64 -struct rule_tmp { - struct udev_rules *rules; - struct token rule; - struct token token[MAX_TK]; - unsigned int token_cur; -}; - -#ifdef ENABLE_DEBUG -static const char *operation_str(enum operation_type type) -{ - static const char *operation_strs[] = { - [OP_UNSET] = "UNSET", - [OP_MATCH] = "match", - [OP_NOMATCH] = "nomatch", - [OP_MATCH_MAX] = "MATCH_MAX", - - [OP_ADD] = "add", - [OP_ASSIGN] = "assign", - [OP_ASSIGN_FINAL] = "assign-final", -} ; - - return operation_strs[type]; -} - -static const char *string_glob_str(enum string_glob_type type) -{ - static const char *string_glob_strs[] = { - [GL_UNSET] = "UNSET", - [GL_PLAIN] = "plain", - [GL_GLOB] = "glob", - [GL_SPLIT] = "split", - [GL_SPLIT_GLOB] = "split-glob", - [GL_SOMETHING] = "split-glob", - }; - - return string_glob_strs[type]; -} - -static const char *token_str(enum token_type type) -{ - static const char *token_strs[] = { - [TK_UNSET] = "UNSET", - [TK_RULE] = "RULE", - - [TK_M_ACTION] = "M ACTION", - [TK_M_DEVPATH] = "M DEVPATH", - [TK_M_KERNEL] = "M KERNEL", - [TK_M_DEVLINK] = "M DEVLINK", - [TK_M_NAME] = "M NAME", - [TK_M_ENV] = "M ENV", - [TK_M_TAG] = "M TAG", - [TK_M_SUBSYSTEM] = "M SUBSYSTEM", - [TK_M_DRIVER] = "M DRIVER", - [TK_M_WAITFOR] = "M WAITFOR", - [TK_M_ATTR] = "M ATTR", - - [TK_M_PARENTS_MIN] = "M PARENTS_MIN", - [TK_M_KERNELS] = "M KERNELS", - [TK_M_SUBSYSTEMS] = "M SUBSYSTEMS", - [TK_M_DRIVERS] = "M DRIVERS", - [TK_M_ATTRS] = "M ATTRS", - [TK_M_TAGS] = "M TAGS", - [TK_M_PARENTS_MAX] = "M PARENTS_MAX", - - [TK_M_TEST] = "M TEST", - [TK_M_EVENT_TIMEOUT] = "M EVENT_TIMEOUT", - [TK_M_PROGRAM] = "M PROGRAM", - [TK_M_IMPORT_FILE] = "M IMPORT_FILE", - [TK_M_IMPORT_PROG] = "M IMPORT_PROG", - [TK_M_IMPORT_BUILTIN] = "M IMPORT_BUILTIN", - [TK_M_IMPORT_DB] = "M IMPORT_DB", - [TK_M_IMPORT_CMDLINE] = "M IMPORT_CMDLINE", - [TK_M_IMPORT_PARENT] = "M IMPORT_PARENT", - [TK_M_RESULT] = "M RESULT", - [TK_M_MAX] = "M MAX", - - [TK_A_STRING_ESCAPE_NONE] = "A STRING_ESCAPE_NONE", - [TK_A_STRING_ESCAPE_REPLACE] = "A STRING_ESCAPE_REPLACE", - [TK_A_DB_PERSIST] = "A DB_PERSIST", - [TK_A_INOTIFY_WATCH] = "A INOTIFY_WATCH", - [TK_A_DEVLINK_PRIO] = "A DEVLINK_PRIO", - [TK_A_OWNER] = "A OWNER", - [TK_A_GROUP] = "A GROUP", - [TK_A_MODE] = "A MODE", - [TK_A_OWNER_ID] = "A OWNER_ID", - [TK_A_GROUP_ID] = "A GROUP_ID", - [TK_A_STATIC_NODE] = "A STATIC_NODE", - [TK_A_MODE_ID] = "A MODE_ID", - [TK_A_ENV] = "A ENV", - [TK_A_TAG] = "A ENV", - [TK_A_NAME] = "A NAME", - [TK_A_DEVLINK] = "A DEVLINK", - [TK_A_ATTR] = "A ATTR", - [TK_A_RUN] = "A RUN", - [TK_A_GOTO] = "A GOTO", - - [TK_END] = "END", - }; - - return token_strs[type]; -} - -static void dump_token(struct udev_rules *rules, struct token *token) -{ - enum token_type type = token->type; - enum operation_type op = token->key.op; - enum string_glob_type glob = token->key.glob; - const char *value = &rules->buf[token->key.value_off]; - const char *attr = &rules->buf[token->key.attr_off]; - - switch (type) { - case TK_RULE: - { - const char *tks_ptr = (char *)rules->tokens; - const char *tk_ptr = (char *)token; - unsigned int idx = (tk_ptr - tks_ptr) / sizeof(struct token); - - dbg(rules->udev, "* RULE %s:%u, token: %u, count: %u, label: '%s'\n", - &rules->buf[token->rule.filename_off], token->rule.filename_line, - idx, token->rule.token_count, - &rules->buf[token->rule.label_off]); - break; - } - case TK_M_ACTION: - case TK_M_DEVPATH: - case TK_M_KERNEL: - case TK_M_SUBSYSTEM: - case TK_M_DRIVER: - case TK_M_WAITFOR: - case TK_M_DEVLINK: - case TK_M_NAME: - case TK_M_KERNELS: - case TK_M_SUBSYSTEMS: - case TK_M_DRIVERS: - case TK_M_TAGS: - case TK_M_PROGRAM: - case TK_M_IMPORT_FILE: - case TK_M_IMPORT_PROG: - case TK_M_IMPORT_DB: - case TK_M_IMPORT_CMDLINE: - case TK_M_IMPORT_PARENT: - case TK_M_RESULT: - case TK_A_NAME: - case TK_A_DEVLINK: - case TK_A_OWNER: - case TK_A_GROUP: - case TK_A_MODE: - case TK_A_RUN: - dbg(rules->udev, "%s %s '%s'(%s)\n", - token_str(type), operation_str(op), value, string_glob_str(glob)); - break; - case TK_M_IMPORT_BUILTIN: - dbg(rules->udev, "%s %i '%s'\n", token_str(type), token->key.builtin_cmd, value); - break; - case TK_M_ATTR: - case TK_M_ATTRS: - case TK_M_ENV: - case TK_A_ATTR: - case TK_A_ENV: - dbg(rules->udev, "%s %s '%s' '%s'(%s)\n", - token_str(type), operation_str(op), attr, value, string_glob_str(glob)); - break; - case TK_M_TAG: - case TK_A_TAG: - dbg(rules->udev, "%s %s '%s'\n", token_str(type), operation_str(op), value); - break; - case TK_A_STRING_ESCAPE_NONE: - case TK_A_STRING_ESCAPE_REPLACE: - case TK_A_DB_PERSIST: - dbg(rules->udev, "%s\n", token_str(type)); - break; - case TK_M_TEST: - dbg(rules->udev, "%s %s '%s'(%s) %#o\n", - token_str(type), operation_str(op), value, string_glob_str(glob), token->key.mode); - break; - case TK_A_INOTIFY_WATCH: - dbg(rules->udev, "%s %u\n", token_str(type), token->key.watch); - break; - case TK_A_DEVLINK_PRIO: - dbg(rules->udev, "%s %u\n", token_str(type), token->key.devlink_prio); - break; - case TK_A_OWNER_ID: - dbg(rules->udev, "%s %s %u\n", token_str(type), operation_str(op), token->key.uid); - break; - case TK_A_GROUP_ID: - dbg(rules->udev, "%s %s %u\n", token_str(type), operation_str(op), token->key.gid); - break; - case TK_A_MODE_ID: - dbg(rules->udev, "%s %s %#o\n", token_str(type), operation_str(op), token->key.mode); - break; - case TK_A_STATIC_NODE: - dbg(rules->udev, "%s '%s'\n", token_str(type), value); - break; - case TK_M_EVENT_TIMEOUT: - dbg(rules->udev, "%s %u\n", token_str(type), token->key.event_timeout); - break; - case TK_A_GOTO: - dbg(rules->udev, "%s '%s' %u\n", token_str(type), value, token->key.rule_goto); - break; - case TK_END: - dbg(rules->udev, "* %s\n", token_str(type)); - break; - case TK_M_PARENTS_MIN: - case TK_M_PARENTS_MAX: - case TK_M_MAX: - case TK_UNSET: - dbg(rules->udev, "unknown type %u\n", type); - break; - } -} - -static void dump_rules(struct udev_rules *rules) -{ - unsigned int i; - - dbg(rules->udev, "dumping %u (%zu bytes) tokens, %u (%zu bytes) strings\n", - rules->token_cur, - rules->token_cur * sizeof(struct token), - rules->buf_count, - rules->buf_cur); - for(i = 0; i < rules->token_cur; i++) - dump_token(rules, &rules->tokens[i]); -} -#else -static inline const char *operation_str(enum operation_type type) { return NULL; } -static inline const char *token_str(enum token_type type) { return NULL; } -static inline void dump_token(struct udev_rules *rules, struct token *token) {} -static inline void dump_rules(struct udev_rules *rules) {} -#endif /* ENABLE_DEBUG */ - -static int add_new_string(struct udev_rules *rules, const char *str, size_t bytes) -{ - int off; - - /* grow buffer if needed */ - if (rules->buf_cur + bytes+1 >= rules->buf_max) { - char *buf; - unsigned int add; - - /* double the buffer size */ - add = rules->buf_max; - if (add < bytes * 8) - add = bytes * 8; - - buf = realloc(rules->buf, rules->buf_max + add); - if (buf == NULL) - return -1; - dbg(rules->udev, "extend buffer from %zu to %zu\n", rules->buf_max, rules->buf_max + add); - rules->buf = buf; - rules->buf_max += add; - } - off = rules->buf_cur; - memcpy(&rules->buf[rules->buf_cur], str, bytes); - rules->buf_cur += bytes; - rules->buf_count++; - return off; -} - -static int add_string(struct udev_rules *rules, const char *str) -{ - unsigned int node_idx; - struct trie_node *new_node; - unsigned int new_node_idx; - unsigned char key; - unsigned short len; - unsigned int depth; - unsigned int off; - struct trie_node *parent; - - /* walk trie, start from last character of str to find matching tails */ - len = strlen(str); - key = str[len-1]; - node_idx = 0; - for (depth = 0; depth <= len; depth++) { - struct trie_node *node; - unsigned int child_idx; - - node = &rules->trie_nodes[node_idx]; - off = node->value_off + node->value_len - len; - - /* match against current node */ - if (depth == len || (node->value_len >= len && memcmp(&rules->buf[off], str, len) == 0)) - return off; - - /* lookup child node */ - key = str[len - 1 - depth]; - child_idx = node->child_idx; - while (child_idx > 0) { - struct trie_node *child; - - child = &rules->trie_nodes[child_idx]; - if (child->key == key) - break; - child_idx = child->next_child_idx; - } - if (child_idx == 0) - break; - node_idx = child_idx; - } - - /* string not found, add it */ - off = add_new_string(rules, str, len + 1); - - /* grow trie nodes if needed */ - if (rules->trie_nodes_cur >= rules->trie_nodes_max) { - struct trie_node *nodes; - unsigned int add; - - /* double the buffer size */ - add = rules->trie_nodes_max; - if (add < 8) - add = 8; - - nodes = realloc(rules->trie_nodes, (rules->trie_nodes_max + add) * sizeof(struct trie_node)); - if (nodes == NULL) - return -1; - dbg(rules->udev, "extend trie nodes from %u to %u\n", - rules->trie_nodes_max, rules->trie_nodes_max + add); - rules->trie_nodes = nodes; - rules->trie_nodes_max += add; - } - - /* get a new node */ - new_node_idx = rules->trie_nodes_cur; - rules->trie_nodes_cur++; - new_node = &rules->trie_nodes[new_node_idx]; - memset(new_node, 0x00, sizeof(struct trie_node)); - new_node->value_off = off; - new_node->value_len = len; - new_node->key = key; - - /* join the parent's child list */ - parent = &rules->trie_nodes[node_idx]; - if (parent->child_idx == 0) { - parent->child_idx = new_node_idx; - } else { - struct trie_node *last_child; - - last_child = &rules->trie_nodes[parent->last_child_idx]; - last_child->next_child_idx = new_node_idx; - } - parent->last_child_idx = new_node_idx; - return off; -} - -static int add_token(struct udev_rules *rules, struct token *token) -{ - /* grow buffer if needed */ - if (rules->token_cur+1 >= rules->token_max) { - struct token *tokens; - unsigned int add; - - /* double the buffer size */ - add = rules->token_max; - if (add < 8) - add = 8; - - tokens = realloc(rules->tokens, (rules->token_max + add ) * sizeof(struct token)); - if (tokens == NULL) - return -1; - dbg(rules->udev, "extend tokens from %u to %u\n", rules->token_max, rules->token_max + add); - rules->tokens = tokens; - rules->token_max += add; - } - memcpy(&rules->tokens[rules->token_cur], token, sizeof(struct token)); - rules->token_cur++; - return 0; -} - -static uid_t add_uid(struct udev_rules *rules, const char *owner) -{ - unsigned int i; - uid_t uid; - unsigned int off; - - /* lookup, if we know it already */ - for (i = 0; i < rules->uids_cur; i++) { - off = rules->uids[i].name_off; - if (strcmp(&rules->buf[off], owner) == 0) { - uid = rules->uids[i].uid; - dbg(rules->udev, "return existing %u for '%s'\n", uid, owner); - return uid; - } - } - uid = util_lookup_user(rules->udev, owner); - - /* grow buffer if needed */ - if (rules->uids_cur+1 >= rules->uids_max) { - struct uid_gid *uids; - unsigned int add; - - /* double the buffer size */ - add = rules->uids_max; - if (add < 1) - add = 8; - - uids = realloc(rules->uids, (rules->uids_max + add ) * sizeof(struct uid_gid)); - if (uids == NULL) - return uid; - dbg(rules->udev, "extend uids from %u to %u\n", rules->uids_max, rules->uids_max + add); - rules->uids = uids; - rules->uids_max += add; - } - rules->uids[rules->uids_cur].uid = uid; - off = add_string(rules, owner); - if (off <= 0) - return uid; - rules->uids[rules->uids_cur].name_off = off; - rules->uids_cur++; - return uid; -} - -static gid_t add_gid(struct udev_rules *rules, const char *group) -{ - unsigned int i; - gid_t gid; - unsigned int off; - - /* lookup, if we know it already */ - for (i = 0; i < rules->gids_cur; i++) { - off = rules->gids[i].name_off; - if (strcmp(&rules->buf[off], group) == 0) { - gid = rules->gids[i].gid; - dbg(rules->udev, "return existing %u for '%s'\n", gid, group); - return gid; - } - } - gid = util_lookup_group(rules->udev, group); - - /* grow buffer if needed */ - if (rules->gids_cur+1 >= rules->gids_max) { - struct uid_gid *gids; - unsigned int add; - - /* double the buffer size */ - add = rules->gids_max; - if (add < 1) - add = 8; - - gids = realloc(rules->gids, (rules->gids_max + add ) * sizeof(struct uid_gid)); - if (gids == NULL) - return gid; - dbg(rules->udev, "extend gids from %u to %u\n", rules->gids_max, rules->gids_max + add); - rules->gids = gids; - rules->gids_max += add; - } - rules->gids[rules->gids_cur].gid = gid; - off = add_string(rules, group); - if (off <= 0) - return gid; - rules->gids[rules->gids_cur].name_off = off; - rules->gids_cur++; - return gid; -} - -static int import_property_from_string(struct udev_device *dev, char *line) -{ - struct udev *udev = udev_device_get_udev(dev); - char *key; - char *val; - size_t len; - - /* find key */ - key = line; - while (isspace(key[0])) - key++; - - /* comment or empty line */ - if (key[0] == '#' || key[0] == '\0') - return -1; - - /* split key/value */ - val = strchr(key, '='); - if (val == NULL) - return -1; - val[0] = '\0'; - val++; - - /* find value */ - while (isspace(val[0])) - val++; - - /* terminate key */ - len = strlen(key); - if (len == 0) - return -1; - while (isspace(key[len-1])) - len--; - key[len] = '\0'; - - /* terminate value */ - len = strlen(val); - if (len == 0) - return -1; - while (isspace(val[len-1])) - len--; - val[len] = '\0'; - - if (len == 0) - return -1; - - /* unquote */ - if (val[0] == '"' || val[0] == '\'') { - if (val[len-1] != val[0]) { - info(udev, "inconsistent quoting: '%s', skip\n", line); - return -1; - } - val[len-1] = '\0'; - val++; - } - - dbg(udev, "adding '%s'='%s'\n", key, val); - - /* handle device, renamed by external tool, returning new path */ - if (strcmp(key, "DEVPATH") == 0) { - char syspath[UTIL_PATH_SIZE]; - - info(udev, "updating devpath from '%s' to '%s'\n", - udev_device_get_devpath(dev), val); - util_strscpyl(syspath, sizeof(syspath), udev_get_sys_path(udev), val, NULL); - udev_device_set_syspath(dev, syspath); - } else { - struct udev_list_entry *entry; - - entry = udev_device_add_property(dev, key, val); - /* store in db, skip private keys */ - if (key[0] != '.') - udev_list_entry_set_num(entry, true); - } - return 0; -} - -static int import_file_into_properties(struct udev_device *dev, const char *filename) -{ - FILE *f; - char line[UTIL_LINE_SIZE]; - - f = fopen(filename, "r"); - if (f == NULL) - return -1; - while (fgets(line, sizeof(line), f) != NULL) - import_property_from_string(dev, line); - fclose(f); - return 0; -} - -static int import_program_into_properties(struct udev_event *event, const char *program, const sigset_t *sigmask) -{ - struct udev_device *dev = event->dev; - char **envp; - char result[UTIL_LINE_SIZE]; - char *line; - int err; - - envp = udev_device_get_properties_envp(dev); - err = udev_event_spawn(event, program, envp, sigmask, result, sizeof(result)); - if (err < 0) - return err; - - line = result; - while (line != NULL) { - char *pos; - - pos = strchr(line, '\n'); - if (pos != NULL) { - pos[0] = '\0'; - pos = &pos[1]; - } - import_property_from_string(dev, line); - line = pos; - } - return 0; -} - -static int import_parent_into_properties(struct udev_device *dev, const char *filter) -{ - struct udev *udev = udev_device_get_udev(dev); - struct udev_device *dev_parent; - struct udev_list_entry *list_entry; - - dev_parent = udev_device_get_parent(dev); - if (dev_parent == NULL) - return -1; - - dbg(udev, "found parent '%s', get the node name\n", udev_device_get_syspath(dev_parent)); - udev_list_entry_foreach(list_entry, udev_device_get_properties_list_entry(dev_parent)) { - const char *key = udev_list_entry_get_name(list_entry); - const char *val = udev_list_entry_get_value(list_entry); - - if (fnmatch(filter, key, 0) == 0) { - struct udev_list_entry *entry; - - dbg(udev, "import key '%s=%s'\n", key, val); - entry = udev_device_add_property(dev, key, val); - /* store in db, skip private keys */ - if (key[0] != '.') - udev_list_entry_set_num(entry, true); - } - } - return 0; -} - -#define WAIT_LOOP_PER_SECOND 50 -static int wait_for_file(struct udev_device *dev, const char *file, int timeout) -{ - struct udev *udev = udev_device_get_udev(dev); - char filepath[UTIL_PATH_SIZE]; - char devicepath[UTIL_PATH_SIZE]; - struct stat stats; - int loop = timeout * WAIT_LOOP_PER_SECOND; - - /* a relative path is a device attribute */ - devicepath[0] = '\0'; - if (file[0] != '/') { - util_strscpyl(devicepath, sizeof(devicepath), - udev_get_sys_path(udev), udev_device_get_devpath(dev), NULL); - util_strscpyl(filepath, sizeof(filepath), devicepath, "/", file, NULL); - file = filepath; - } - - dbg(udev, "will wait %i sec for '%s'\n", timeout, file); - while (--loop) { - const struct timespec duration = { 0, 1000 * 1000 * 1000 / WAIT_LOOP_PER_SECOND }; - - /* lookup file */ - if (stat(file, &stats) == 0) { - info(udev, "file '%s' appeared after %i loops\n", file, (timeout * WAIT_LOOP_PER_SECOND) - loop-1); - return 0; - } - /* make sure, the device did not disappear in the meantime */ - if (devicepath[0] != '\0' && stat(devicepath, &stats) != 0) { - info(udev, "device disappeared while waiting for '%s'\n", file); - return -2; - } - info(udev, "wait for '%s' for %i mseconds\n", file, 1000 / WAIT_LOOP_PER_SECOND); - nanosleep(&duration, NULL); - } - info(udev, "waiting for '%s' failed\n", file); - return -1; -} - -static int attr_subst_subdir(char *attr, size_t len) -{ - bool found = false; - - if (strstr(attr, "/*/")) { - char *pos; - char dirname[UTIL_PATH_SIZE]; - const char *tail; - DIR *dir; - - util_strscpy(dirname, sizeof(dirname), attr); - pos = strstr(dirname, "/*/"); - if (pos == NULL) - return -1; - pos[0] = '\0'; - tail = &pos[2]; - dir = opendir(dirname); - if (dir != NULL) { - struct dirent *dent; - - for (dent = readdir(dir); dent != NULL; dent = readdir(dir)) { - struct stat stats; - - if (dent->d_name[0] == '.') - continue; - util_strscpyl(attr, len, dirname, "/", dent->d_name, tail, NULL); - if (stat(attr, &stats) == 0) { - found = true; - break; - } - } - closedir(dir); - } - } - - return found; -} - -static int get_key(struct udev *udev, char **line, char **key, enum operation_type *op, char **value) -{ - char *linepos; - char *temp; - - linepos = *line; - if (linepos == NULL || linepos[0] == '\0') - return -1; - - /* skip whitespace */ - while (isspace(linepos[0]) || linepos[0] == ',') - linepos++; - - /* get the key */ - if (linepos[0] == '\0') - return -1; - *key = linepos; - - for (;;) { - linepos++; - if (linepos[0] == '\0') - return -1; - if (isspace(linepos[0])) - break; - if (linepos[0] == '=') - break; - if ((linepos[0] == '+') || (linepos[0] == '!') || (linepos[0] == ':')) - if (linepos[1] == '=') - break; - } - - /* remember end of key */ - temp = linepos; - - /* skip whitespace after key */ - while (isspace(linepos[0])) - linepos++; - if (linepos[0] == '\0') - return -1; - - /* get operation type */ - if (linepos[0] == '=' && linepos[1] == '=') { - *op = OP_MATCH; - linepos += 2; - } else if (linepos[0] == '!' && linepos[1] == '=') { - *op = OP_NOMATCH; - linepos += 2; - } else if (linepos[0] == '+' && linepos[1] == '=') { - *op = OP_ADD; - linepos += 2; - } else if (linepos[0] == '=') { - *op = OP_ASSIGN; - linepos++; - } else if (linepos[0] == ':' && linepos[1] == '=') { - *op = OP_ASSIGN_FINAL; - linepos += 2; - } else - return -1; - - /* terminate key */ - temp[0] = '\0'; - - /* skip whitespace after operator */ - while (isspace(linepos[0])) - linepos++; - if (linepos[0] == '\0') - return -1; - - /* get the value */ - if (linepos[0] == '"') - linepos++; - else - return -1; - *value = linepos; - - /* terminate */ - temp = strchr(linepos, '"'); - if (!temp) - return -1; - temp[0] = '\0'; - temp++; - dbg(udev, "%s '%s'-'%s'\n", operation_str(*op), *key, *value); - - /* move line to next key */ - *line = temp; - return 0; -} - -/* extract possible KEY{attr} */ -static char *get_key_attribute(struct udev *udev, char *str) -{ - char *pos; - char *attr; - - attr = strchr(str, '{'); - if (attr != NULL) { - attr++; - pos = strchr(attr, '}'); - if (pos == NULL) { - err(udev, "missing closing brace for format\n"); - return NULL; - } - pos[0] = '\0'; - dbg(udev, "attribute='%s'\n", attr); - return attr; - } - return NULL; -} - -static int rule_add_key(struct rule_tmp *rule_tmp, enum token_type type, - enum operation_type op, - const char *value, const void *data) -{ - struct token *token = &rule_tmp->token[rule_tmp->token_cur]; - const char *attr = NULL; - - memset(token, 0x00, sizeof(struct token)); - - switch (type) { - case TK_M_ACTION: - case TK_M_DEVPATH: - case TK_M_KERNEL: - case TK_M_SUBSYSTEM: - case TK_M_DRIVER: - case TK_M_WAITFOR: - case TK_M_DEVLINK: - case TK_M_NAME: - case TK_M_KERNELS: - case TK_M_SUBSYSTEMS: - case TK_M_DRIVERS: - case TK_M_TAGS: - case TK_M_PROGRAM: - case TK_M_IMPORT_FILE: - case TK_M_IMPORT_PROG: - case TK_M_IMPORT_DB: - case TK_M_IMPORT_CMDLINE: - case TK_M_IMPORT_PARENT: - case TK_M_RESULT: - case TK_A_OWNER: - case TK_A_GROUP: - case TK_A_MODE: - case TK_A_NAME: - case TK_A_GOTO: - case TK_M_TAG: - case TK_A_TAG: - token->key.value_off = add_string(rule_tmp->rules, value); - break; - case TK_M_IMPORT_BUILTIN: - token->key.value_off = add_string(rule_tmp->rules, value); - token->key.builtin_cmd = *(enum udev_builtin_cmd *)data; - break; - case TK_M_ENV: - case TK_M_ATTR: - case TK_M_ATTRS: - case TK_A_ATTR: - case TK_A_ENV: - attr = data; - token->key.value_off = add_string(rule_tmp->rules, value); - token->key.attr_off = add_string(rule_tmp->rules, attr); - break; - case TK_A_DEVLINK: - token->key.value_off = add_string(rule_tmp->rules, value); - token->key.devlink_unique = *(int *)data; - break; - case TK_M_TEST: - token->key.value_off = add_string(rule_tmp->rules, value); - if (data != NULL) - token->key.mode = *(mode_t *)data; - break; - case TK_A_STRING_ESCAPE_NONE: - case TK_A_STRING_ESCAPE_REPLACE: - case TK_A_DB_PERSIST: - break; - case TK_A_RUN: - token->key.value_off = add_string(rule_tmp->rules, value); - break; - case TK_A_INOTIFY_WATCH: - case TK_A_DEVLINK_PRIO: - token->key.devlink_prio = *(int *)data; - break; - case TK_A_OWNER_ID: - token->key.uid = *(uid_t *)data; - break; - case TK_A_GROUP_ID: - token->key.gid = *(gid_t *)data; - break; - case TK_A_MODE_ID: - token->key.mode = *(mode_t *)data; - break; - case TK_A_STATIC_NODE: - token->key.value_off = add_string(rule_tmp->rules, value); - break; - case TK_M_EVENT_TIMEOUT: - token->key.event_timeout = *(int *)data; - break; - case TK_RULE: - case TK_M_PARENTS_MIN: - case TK_M_PARENTS_MAX: - case TK_M_MAX: - case TK_END: - case TK_UNSET: - err(rule_tmp->rules->udev, "wrong type %u\n", type); - return -1; - } - - if (value != NULL && type < TK_M_MAX) { - /* check if we need to split or call fnmatch() while matching rules */ - enum string_glob_type glob; - int has_split; - int has_glob; - - has_split = (strchr(value, '|') != NULL); - has_glob = (strchr(value, '*') != NULL || strchr(value, '?') != NULL || strchr(value, '[') != NULL); - if (has_split && has_glob) { - glob = GL_SPLIT_GLOB; - } else if (has_split) { - glob = GL_SPLIT; - } else if (has_glob) { - if (strcmp(value, "?*") == 0) - glob = GL_SOMETHING; - else - glob = GL_GLOB; - } else { - glob = GL_PLAIN; - } - token->key.glob = glob; - } - - if (value != NULL && type > TK_M_MAX) { - /* check if assigned value has substitution chars */ - if (value[0] == '[') - token->key.subst = SB_SUBSYS; - else if (strchr(value, '%') != NULL || strchr(value, '$') != NULL) - token->key.subst = SB_FORMAT; - else - token->key.subst = SB_NONE; - } - - if (attr != NULL) { - /* check if property/attribut name has substitution chars */ - if (attr[0] == '[') - token->key.attrsubst = SB_SUBSYS; - else if (strchr(attr, '%') != NULL || strchr(attr, '$') != NULL) - token->key.attrsubst = SB_FORMAT; - else - token->key.attrsubst = SB_NONE; - } - - token->key.type = type; - token->key.op = op; - rule_tmp->token_cur++; - if (rule_tmp->token_cur >= ARRAY_SIZE(rule_tmp->token)) { - err(rule_tmp->rules->udev, "temporary rule array too small\n"); - return -1; - } - return 0; -} - -static int sort_token(struct udev_rules *rules, struct rule_tmp *rule_tmp) -{ - unsigned int i; - unsigned int start = 0; - unsigned int end = rule_tmp->token_cur; - - for (i = 0; i < rule_tmp->token_cur; i++) { - enum token_type next_val = TK_UNSET; - unsigned int next_idx = 0; - unsigned int j; - - /* find smallest value */ - for (j = start; j < end; j++) { - if (rule_tmp->token[j].type == TK_UNSET) - continue; - if (next_val == TK_UNSET || rule_tmp->token[j].type < next_val) { - next_val = rule_tmp->token[j].type; - next_idx = j; - } - } - - /* add token and mark done */ - if (add_token(rules, &rule_tmp->token[next_idx]) != 0) - return -1; - rule_tmp->token[next_idx].type = TK_UNSET; - - /* shrink range */ - if (next_idx == start) - start++; - if (next_idx+1 == end) - end--; - } - return 0; -} - -static int add_rule(struct udev_rules *rules, char *line, - const char *filename, unsigned int filename_off, unsigned int lineno) -{ - char *linepos; - char *attr; - struct rule_tmp rule_tmp; - - memset(&rule_tmp, 0x00, sizeof(struct rule_tmp)); - rule_tmp.rules = rules; - rule_tmp.rule.type = TK_RULE; - rule_tmp.rule.rule.filename_off = filename_off; - rule_tmp.rule.rule.filename_line = lineno; - - linepos = line; - for (;;) { - char *key; - char *value; - enum operation_type op; - - if (get_key(rules->udev, &linepos, &key, &op, &value) != 0) - break; - - if (strcmp(key, "ACTION") == 0) { - if (op > OP_MATCH_MAX) { - err(rules->udev, "invalid ACTION operation\n"); - goto invalid; - } - rule_add_key(&rule_tmp, TK_M_ACTION, op, value, NULL); - continue; - } - - if (strcmp(key, "DEVPATH") == 0) { - if (op > OP_MATCH_MAX) { - err(rules->udev, "invalid DEVPATH operation\n"); - goto invalid; - } - rule_add_key(&rule_tmp, TK_M_DEVPATH, op, value, NULL); - continue; - } - - if (strcmp(key, "KERNEL") == 0) { - if (op > OP_MATCH_MAX) { - err(rules->udev, "invalid KERNEL operation\n"); - goto invalid; - } - rule_add_key(&rule_tmp, TK_M_KERNEL, op, value, NULL); - continue; - } - - if (strcmp(key, "SUBSYSTEM") == 0) { - if (op > OP_MATCH_MAX) { - err(rules->udev, "invalid SUBSYSTEM operation\n"); - goto invalid; - } - /* bus, class, subsystem events should all be the same */ - if (strcmp(value, "subsystem") == 0 || - strcmp(value, "bus") == 0 || - strcmp(value, "class") == 0) { - if (strcmp(value, "bus") == 0 || strcmp(value, "class") == 0) - err(rules->udev, "'%s' must be specified as 'subsystem' \n" - "please fix it in %s:%u", value, filename, lineno); - rule_add_key(&rule_tmp, TK_M_SUBSYSTEM, op, "subsystem|class|bus", NULL); - } else - rule_add_key(&rule_tmp, TK_M_SUBSYSTEM, op, value, NULL); - continue; - } - - if (strcmp(key, "DRIVER") == 0) { - if (op > OP_MATCH_MAX) { - err(rules->udev, "invalid DRIVER operation\n"); - goto invalid; - } - rule_add_key(&rule_tmp, TK_M_DRIVER, op, value, NULL); - continue; - } - - if (strncmp(key, "ATTR{", sizeof("ATTR{")-1) == 0) { - attr = get_key_attribute(rules->udev, key + sizeof("ATTR")-1); - if (attr == NULL) { - err(rules->udev, "error parsing ATTR attribute\n"); - goto invalid; - } - if (op < OP_MATCH_MAX) { - rule_add_key(&rule_tmp, TK_M_ATTR, op, value, attr); - } else { - rule_add_key(&rule_tmp, TK_A_ATTR, op, value, attr); - } - continue; - } - - if (strcmp(key, "KERNELS") == 0) { - if (op > OP_MATCH_MAX) { - err(rules->udev, "invalid KERNELS operation\n"); - goto invalid; - } - rule_add_key(&rule_tmp, TK_M_KERNELS, op, value, NULL); - continue; - } - - if (strcmp(key, "SUBSYSTEMS") == 0) { - if (op > OP_MATCH_MAX) { - err(rules->udev, "invalid SUBSYSTEMS operation\n"); - goto invalid; - } - rule_add_key(&rule_tmp, TK_M_SUBSYSTEMS, op, value, NULL); - continue; - } - - if (strcmp(key, "DRIVERS") == 0) { - if (op > OP_MATCH_MAX) { - err(rules->udev, "invalid DRIVERS operation\n"); - goto invalid; - } - rule_add_key(&rule_tmp, TK_M_DRIVERS, op, value, NULL); - continue; - } - - if (strncmp(key, "ATTRS{", sizeof("ATTRS{")-1) == 0) { - if (op > OP_MATCH_MAX) { - err(rules->udev, "invalid ATTRS operation\n"); - goto invalid; - } - attr = get_key_attribute(rules->udev, key + sizeof("ATTRS")-1); - if (attr == NULL) { - err(rules->udev, "error parsing ATTRS attribute\n"); - goto invalid; - } - if (strncmp(attr, "device/", 7) == 0) - err(rules->udev, "the 'device' link may not be available in a future kernel, " - "please fix it in %s:%u", filename, lineno); - else if (strstr(attr, "../") != NULL) - err(rules->udev, "do not reference parent sysfs directories directly, " - "it may break with a future kernel, please fix it in %s:%u", filename, lineno); - rule_add_key(&rule_tmp, TK_M_ATTRS, op, value, attr); - continue; - } - - if (strcmp(key, "TAGS") == 0) { - if (op > OP_MATCH_MAX) { - err(rules->udev, "invalid TAGS operation\n"); - goto invalid; - } - rule_add_key(&rule_tmp, TK_M_TAGS, op, value, NULL); - continue; - } - - if (strncmp(key, "ENV{", sizeof("ENV{")-1) == 0) { - attr = get_key_attribute(rules->udev, key + sizeof("ENV")-1); - if (attr == NULL) { - err(rules->udev, "error parsing ENV attribute\n"); - goto invalid; - } - if (op < OP_MATCH_MAX) { - if (rule_add_key(&rule_tmp, TK_M_ENV, op, value, attr) != 0) - goto invalid; - } else { - static const char *blacklist[] = { - "ACTION", - "SUBSYSTEM", - "DEVTYPE", - "MAJOR", - "MINOR", - "DRIVER", - "IFINDEX", - "DEVNAME", - "DEVLINKS", - "DEVPATH", - "TAGS", - }; - unsigned int i; - - for (i = 0; i < ARRAY_SIZE(blacklist); i++) - if (strcmp(attr, blacklist[i]) == 0) { - err(rules->udev, "invalid ENV attribute, '%s' can not be set %s:%u\n", attr, filename, lineno); - continue; - } - if (rule_add_key(&rule_tmp, TK_A_ENV, op, value, attr) != 0) - goto invalid; - } - continue; - } - - if (strcmp(key, "TAG") == 0) { - if (op < OP_MATCH_MAX) - rule_add_key(&rule_tmp, TK_M_TAG, op, value, NULL); - else - rule_add_key(&rule_tmp, TK_A_TAG, op, value, NULL); - continue; - } - - if (strcmp(key, "PROGRAM") == 0) { - rule_add_key(&rule_tmp, TK_M_PROGRAM, op, value, NULL); - continue; - } - - if (strcmp(key, "RESULT") == 0) { - if (op > OP_MATCH_MAX) { - err(rules->udev, "invalid RESULT operation\n"); - goto invalid; - } - rule_add_key(&rule_tmp, TK_M_RESULT, op, value, NULL); - continue; - } - - if (strncmp(key, "IMPORT", sizeof("IMPORT")-1) == 0) { - attr = get_key_attribute(rules->udev, key + sizeof("IMPORT")-1); - if (attr == NULL) { - err(rules->udev, "IMPORT{} type missing, ignoring IMPORT %s:%u\n", filename, lineno); - continue; - } - if (strstr(attr, "program")) { - /* find known built-in command */ - if (value[0] != '/') { - enum udev_builtin_cmd cmd; - - cmd = udev_builtin_lookup(value); - if (cmd < UDEV_BUILTIN_MAX) { - info(rules->udev, "IMPORT found builtin '%s', replacing %s:%u\n", - value, filename, lineno); - rule_add_key(&rule_tmp, TK_M_IMPORT_BUILTIN, op, value, &cmd); - continue; - } - } - dbg(rules->udev, "IMPORT will be executed\n"); - rule_add_key(&rule_tmp, TK_M_IMPORT_PROG, op, value, NULL); - } else if (strstr(attr, "builtin")) { - enum udev_builtin_cmd cmd = udev_builtin_lookup(value); - - dbg(rules->udev, "IMPORT execute builtin\n"); - if (cmd < UDEV_BUILTIN_MAX) - rule_add_key(&rule_tmp, TK_M_IMPORT_BUILTIN, op, value, &cmd); - else - err(rules->udev, "IMPORT{builtin}: '%s' unknown %s:%u\n", value, filename, lineno); - } else if (strstr(attr, "file")) { - dbg(rules->udev, "IMPORT will be included as file\n"); - rule_add_key(&rule_tmp, TK_M_IMPORT_FILE, op, value, NULL); - } else if (strstr(attr, "db")) { - dbg(rules->udev, "IMPORT will include db values\n"); - rule_add_key(&rule_tmp, TK_M_IMPORT_DB, op, value, NULL); - } else if (strstr(attr, "cmdline")) { - dbg(rules->udev, "IMPORT will include db values\n"); - rule_add_key(&rule_tmp, TK_M_IMPORT_CMDLINE, op, value, NULL); - } else if (strstr(attr, "parent")) { - dbg(rules->udev, "IMPORT will include the parent values\n"); - rule_add_key(&rule_tmp, TK_M_IMPORT_PARENT, op, value, NULL); - } - continue; - } - - if (strncmp(key, "TEST", sizeof("TEST")-1) == 0) { - mode_t mode = 0; - - if (op > OP_MATCH_MAX) { - err(rules->udev, "invalid TEST operation\n"); - goto invalid; - } - attr = get_key_attribute(rules->udev, key + sizeof("TEST")-1); - if (attr != NULL) { - mode = strtol(attr, NULL, 8); - rule_add_key(&rule_tmp, TK_M_TEST, op, value, &mode); - } else { - rule_add_key(&rule_tmp, TK_M_TEST, op, value, NULL); - } - continue; - } - - if (strcmp(key, "RUN") == 0) { - rule_add_key(&rule_tmp, TK_A_RUN, op, value, NULL); - continue; - } - - if (strcmp(key, "WAIT_FOR") == 0 || strcmp(key, "WAIT_FOR_SYSFS") == 0) { - rule_add_key(&rule_tmp, TK_M_WAITFOR, 0, value, NULL); - continue; - } - - if (strcmp(key, "LABEL") == 0) { - rule_tmp.rule.rule.label_off = add_string(rules, value); - continue; - } - - if (strcmp(key, "GOTO") == 0) { - rule_add_key(&rule_tmp, TK_A_GOTO, 0, value, NULL); - continue; - } - - if (strncmp(key, "NAME", sizeof("NAME")-1) == 0) { - if (op < OP_MATCH_MAX) { - rule_add_key(&rule_tmp, TK_M_NAME, op, value, NULL); - } else { - if (strcmp(value, "%k") == 0) { - err(rules->udev, "NAME=\"%%k\" is ignored, because it breaks kernel supplied names, " - "please remove it from %s:%u\n", filename, lineno); - continue; - } - if (value[0] == '\0') { - info(rules->udev, "NAME=\"\" is ignored, because udev will not delete any device nodes, " - "please remove it from %s:%u\n", filename, lineno); - continue; - } - rule_add_key(&rule_tmp, TK_A_NAME, op, value, NULL); - } - rule_tmp.rule.rule.can_set_name = true; - continue; - } - - if (strncmp(key, "SYMLINK", sizeof("SYMLINK")-1) == 0) { - if (op < OP_MATCH_MAX) { - rule_add_key(&rule_tmp, TK_M_DEVLINK, op, value, NULL); - } else { - int flag = 0; - - attr = get_key_attribute(rules->udev, key + sizeof("SYMLINK")-1); - if (attr != NULL && strstr(attr, "unique") != NULL) - flag = 1; - rule_add_key(&rule_tmp, TK_A_DEVLINK, op, value, &flag); - } - rule_tmp.rule.rule.can_set_name = true; - continue; - } - - if (strcmp(key, "OWNER") == 0) { - uid_t uid; - char *endptr; - - uid = strtoul(value, &endptr, 10); - if (endptr[0] == '\0') { - rule_add_key(&rule_tmp, TK_A_OWNER_ID, op, NULL, &uid); - } else if ((rules->resolve_names > 0) && strchr("$%", value[0]) == NULL) { - uid = add_uid(rules, value); - rule_add_key(&rule_tmp, TK_A_OWNER_ID, op, NULL, &uid); - } else if (rules->resolve_names >= 0) { - rule_add_key(&rule_tmp, TK_A_OWNER, op, value, NULL); - } - rule_tmp.rule.rule.can_set_name = true; - continue; - } - - if (strcmp(key, "GROUP") == 0) { - gid_t gid; - char *endptr; - - gid = strtoul(value, &endptr, 10); - if (endptr[0] == '\0') { - rule_add_key(&rule_tmp, TK_A_GROUP_ID, op, NULL, &gid); - } else if ((rules->resolve_names > 0) && strchr("$%", value[0]) == NULL) { - gid = add_gid(rules, value); - rule_add_key(&rule_tmp, TK_A_GROUP_ID, op, NULL, &gid); - } else if (rules->resolve_names >= 0) { - rule_add_key(&rule_tmp, TK_A_GROUP, op, value, NULL); - } - rule_tmp.rule.rule.can_set_name = true; - continue; - } - - if (strcmp(key, "MODE") == 0) { - mode_t mode; - char *endptr; - - mode = strtol(value, &endptr, 8); - if (endptr[0] == '\0') - rule_add_key(&rule_tmp, TK_A_MODE_ID, op, NULL, &mode); - else - rule_add_key(&rule_tmp, TK_A_MODE, op, value, NULL); - rule_tmp.rule.rule.can_set_name = true; - continue; - } - - if (strcmp(key, "OPTIONS") == 0) { - const char *pos; - - pos = strstr(value, "link_priority="); - if (pos != NULL) { - int prio = atoi(&pos[strlen("link_priority=")]); - - rule_add_key(&rule_tmp, TK_A_DEVLINK_PRIO, op, NULL, &prio); - dbg(rules->udev, "link priority=%i\n", prio); - } - - pos = strstr(value, "event_timeout="); - if (pos != NULL) { - int tout = atoi(&pos[strlen("event_timeout=")]); - - rule_add_key(&rule_tmp, TK_M_EVENT_TIMEOUT, op, NULL, &tout); - dbg(rules->udev, "event timeout=%i\n", tout); - } - - pos = strstr(value, "string_escape="); - if (pos != NULL) { - pos = &pos[strlen("string_escape=")]; - if (strncmp(pos, "none", strlen("none")) == 0) - rule_add_key(&rule_tmp, TK_A_STRING_ESCAPE_NONE, op, NULL, NULL); - else if (strncmp(pos, "replace", strlen("replace")) == 0) - rule_add_key(&rule_tmp, TK_A_STRING_ESCAPE_REPLACE, op, NULL, NULL); - } - - pos = strstr(value, "db_persist"); - if (pos != NULL) - rule_add_key(&rule_tmp, TK_A_DB_PERSIST, op, NULL, NULL); - - pos = strstr(value, "nowatch"); - if (pos != NULL) { - const int off = 0; - - rule_add_key(&rule_tmp, TK_A_INOTIFY_WATCH, op, NULL, &off); - dbg(rules->udev, "inotify watch of device disabled\n"); - } else { - pos = strstr(value, "watch"); - if (pos != NULL) { - const int on = 1; - - rule_add_key(&rule_tmp, TK_A_INOTIFY_WATCH, op, NULL, &on); - dbg(rules->udev, "inotify watch of device requested\n"); - } - } - - pos = strstr(value, "static_node="); - if (pos != NULL) { - rule_add_key(&rule_tmp, TK_A_STATIC_NODE, op, &pos[strlen("static_node=")], NULL); - rule_tmp.rule.rule.has_static_node = true; - } - - continue; - } - - err(rules->udev, "unknown key '%s' in %s:%u\n", key, filename, lineno); - goto invalid; - } - - /* add rule token */ - rule_tmp.rule.rule.token_count = 1 + rule_tmp.token_cur; - if (add_token(rules, &rule_tmp.rule) != 0) - goto invalid; - - /* add tokens to list, sorted by type */ - if (sort_token(rules, &rule_tmp) != 0) - goto invalid; - - return 0; -invalid: - err(rules->udev, "invalid rule '%s:%u'\n", filename, lineno); - return -1; -} - -static int parse_file(struct udev_rules *rules, const char *filename, unsigned short filename_off) -{ - FILE *f; - unsigned int first_token; - char line[UTIL_LINE_SIZE]; - int line_nr = 0; - unsigned int i; - - info(rules->udev, "reading '%s' as rules file\n", filename); - - f = fopen(filename, "r"); - if (f == NULL) - return -1; - - first_token = rules->token_cur; - - while (fgets(line, sizeof(line), f) != NULL) { - char *key; - size_t len; - - /* skip whitespace */ - line_nr++; - key = line; - while (isspace(key[0])) - key++; - - /* comment */ - if (key[0] == '#') - continue; - - len = strlen(line); - if (len < 3) - continue; - - /* continue reading if backslash+newline is found */ - while (line[len-2] == '\\') { - if (fgets(&line[len-2], (sizeof(line)-len)+2, f) == NULL) - break; - if (strlen(&line[len-2]) < 2) - break; - line_nr++; - len = strlen(line); - } - - if (len+1 >= sizeof(line)) { - err(rules->udev, "line too long '%s':%u, ignored\n", filename, line_nr); - continue; - } - add_rule(rules, key, filename, filename_off, line_nr); - } - fclose(f); - - /* link GOTOs to LABEL rules in this file to be able to fast-forward */ - for (i = first_token+1; i < rules->token_cur; i++) { - if (rules->tokens[i].type == TK_A_GOTO) { - char *label = &rules->buf[rules->tokens[i].key.value_off]; - unsigned int j; - - for (j = i+1; j < rules->token_cur; j++) { - if (rules->tokens[j].type != TK_RULE) - continue; - if (rules->tokens[j].rule.label_off == 0) - continue; - if (strcmp(label, &rules->buf[rules->tokens[j].rule.label_off]) != 0) - continue; - rules->tokens[i].key.rule_goto = j; - break; - } - if (rules->tokens[i].key.rule_goto == 0) - err(rules->udev, "GOTO '%s' has no matching label in: '%s'\n", label, filename); - } - } - return 0; -} - -static int add_matching_files(struct udev *udev, struct udev_list *file_list, const char *dirname, const char *suffix) -{ - DIR *dir; - struct dirent *dent; - char filename[UTIL_PATH_SIZE]; - - dbg(udev, "open directory '%s'\n", dirname); - dir = opendir(dirname); - if (dir == NULL) { - info(udev, "unable to open '%s': %m\n", dirname); - return -1; - } - - for (dent = readdir(dir); dent != NULL; dent = readdir(dir)) { - if (dent->d_name[0] == '.') - continue; - - /* look for file matching with specified suffix */ - if (suffix != NULL) { - const char *ext; - - ext = strrchr(dent->d_name, '.'); - if (ext == NULL) - continue; - if (strcmp(ext, suffix) != 0) - continue; - } - util_strscpyl(filename, sizeof(filename), dirname, "/", dent->d_name, NULL); - dbg(udev, "put file '%s' into list\n", filename); - /* - * the basename is the key, the filename the value - * identical basenames from different directories overwrite each other - * entries are sorted after basename - */ - udev_list_entry_add(file_list, dent->d_name, filename); - } - - closedir(dir); - return 0; -} - -struct udev_rules *udev_rules_new(struct udev *udev, int resolve_names) -{ - struct udev_rules *rules; - struct udev_list file_list; - struct udev_list_entry *file_loop; - struct token end_token; - char **s; - - rules = calloc(1, sizeof(struct udev_rules)); - if (rules == NULL) - return NULL; - rules->udev = udev; - rules->resolve_names = resolve_names; - udev_list_init(udev, &file_list, true); - - /* init token array and string buffer */ - rules->tokens = malloc(PREALLOC_TOKEN * sizeof(struct token)); - if (rules->tokens == NULL) { - free(rules); - return NULL; - } - rules->token_max = PREALLOC_TOKEN; - - rules->buf = malloc(PREALLOC_STRBUF); - if (rules->buf == NULL) { - free(rules->tokens); - free(rules); - return NULL; - } - rules->buf_max = PREALLOC_STRBUF; - /* offset 0 is always '\0' */ - rules->buf[0] = '\0'; - rules->buf_cur = 1; - dbg(udev, "prealloc %zu bytes tokens (%u * %zu bytes), %zu bytes buffer\n", - rules->token_max * sizeof(struct token), rules->token_max, sizeof(struct token), rules->buf_max); - - rules->trie_nodes = malloc(PREALLOC_TRIE * sizeof(struct trie_node)); - if (rules->trie_nodes == NULL) { - free(rules->buf); - free(rules->tokens); - free(rules); - return NULL; - } - rules->trie_nodes_max = PREALLOC_TRIE; - /* offset 0 is the trie root, with an empty string */ - memset(rules->trie_nodes, 0x00, sizeof(struct trie_node)); - rules->trie_nodes_cur = 1; - - for (udev_get_rules_path(udev, &s, NULL); *s != NULL; s++) - add_matching_files(udev, &file_list, *s, ".rules"); - - /* add all filenames to the string buffer */ - udev_list_entry_foreach(file_loop, udev_list_get_entry(&file_list)) { - const char *filename = udev_list_entry_get_value(file_loop); - unsigned int filename_off; - - filename_off = add_string(rules, filename); - /* the offset in the rule is limited to unsigned short */ - if (filename_off < USHRT_MAX) - udev_list_entry_set_num(file_loop, filename_off); - } - - /* parse all rules files */ - udev_list_entry_foreach(file_loop, udev_list_get_entry(&file_list)) { - const char *filename = udev_list_entry_get_value(file_loop); - unsigned int filename_off = udev_list_entry_get_num(file_loop); - struct stat st; - - if (stat(filename, &st) != 0) { - err(udev, "can not find '%s': %m\n", filename); - continue; - } - if (S_ISREG(st.st_mode) && st.st_size <= 0) { - info(udev, "ignore empty '%s'\n", filename); - continue; - } - if (S_ISCHR(st.st_mode)) { - info(udev, "ignore masked '%s'\n", filename); - continue; - } - parse_file(rules, filename, filename_off); - } - udev_list_cleanup(&file_list); - - memset(&end_token, 0x00, sizeof(struct token)); - end_token.type = TK_END; - add_token(rules, &end_token); - - /* shrink allocated token and string buffer */ - if (rules->token_cur < rules->token_max) { - struct token *tokens; - - tokens = realloc(rules->tokens, rules->token_cur * sizeof(struct token)); - if (tokens != NULL || rules->token_cur == 0) { - rules->tokens = tokens; - rules->token_max = rules->token_cur; - } - } - if (rules->buf_cur < rules->buf_max) { - char *buf; - - buf = realloc(rules->buf, rules->buf_cur); - if (buf != NULL || rules->buf_cur == 0) { - rules->buf = buf; - rules->buf_max = rules->buf_cur; - } - } - info(udev, "rules use %zu bytes tokens (%u * %zu bytes), %zu bytes buffer\n", - rules->token_max * sizeof(struct token), rules->token_max, sizeof(struct token), rules->buf_max); - info(udev, "temporary index used %zu bytes (%u * %zu bytes)\n", - rules->trie_nodes_cur * sizeof(struct trie_node), - rules->trie_nodes_cur, sizeof(struct trie_node)); - - /* cleanup trie */ - free(rules->trie_nodes); - rules->trie_nodes = NULL; - rules->trie_nodes_cur = 0; - rules->trie_nodes_max = 0; - - /* cleanup uid/gid cache */ - free(rules->uids); - rules->uids = NULL; - rules->uids_cur = 0; - rules->uids_max = 0; - free(rules->gids); - rules->gids = NULL; - rules->gids_cur = 0; - rules->gids_max = 0; - - dump_rules(rules); - return rules; -} - -struct udev_rules *udev_rules_unref(struct udev_rules *rules) -{ - if (rules == NULL) - return NULL; - free(rules->tokens); - free(rules->buf); - free(rules->trie_nodes); - free(rules->uids); - free(rules->gids); - free(rules); - return NULL; -} - -static int match_key(struct udev_rules *rules, struct token *token, const char *val) -{ - char *key_value = &rules->buf[token->key.value_off]; - char *pos; - bool match = false; - - if (val == NULL) - val = ""; - - switch (token->key.glob) { - case GL_PLAIN: - match = (strcmp(key_value, val) == 0); - break; - case GL_GLOB: - match = (fnmatch(key_value, val, 0) == 0); - break; - case GL_SPLIT: - { - const char *split; - size_t len; - - split = &rules->buf[token->key.value_off]; - len = strlen(val); - for (;;) { - const char *next; - - next = strchr(split, '|'); - if (next != NULL) { - size_t matchlen = (size_t)(next - split); - - match = (matchlen == len && strncmp(split, val, matchlen) == 0); - if (match) - break; - } else { - match = (strcmp(split, val) == 0); - break; - } - split = &next[1]; - } - break; - } - case GL_SPLIT_GLOB: - { - char value[UTIL_PATH_SIZE]; - - util_strscpy(value, sizeof(value), &rules->buf[token->key.value_off]); - key_value = value; - while (key_value != NULL) { - pos = strchr(key_value, '|'); - if (pos != NULL) { - pos[0] = '\0'; - pos = &pos[1]; - } - dbg(rules->udev, "match %s '%s' <-> '%s'\n", token_str(token->type), key_value, val); - match = (fnmatch(key_value, val, 0) == 0); - if (match) - break; - key_value = pos; - } - break; - } - case GL_SOMETHING: - match = (val[0] != '\0'); - break; - case GL_UNSET: - return -1; - } - - if (match && (token->key.op == OP_MATCH)) { - dbg(rules->udev, "%s is true (matching value)\n", token_str(token->type)); - return 0; - } - if (!match && (token->key.op == OP_NOMATCH)) { - dbg(rules->udev, "%s is true (non-matching value)\n", token_str(token->type)); - return 0; - } - dbg(rules->udev, "%s is not true\n", token_str(token->type)); - return -1; -} - -static int match_attr(struct udev_rules *rules, struct udev_device *dev, struct udev_event *event, struct token *cur) -{ - const char *name; - char nbuf[UTIL_NAME_SIZE]; - const char *value; - char vbuf[UTIL_NAME_SIZE]; - size_t len; - - name = &rules->buf[cur->key.attr_off]; - switch (cur->key.attrsubst) { - case SB_FORMAT: - udev_event_apply_format(event, name, nbuf, sizeof(nbuf)); - name = nbuf; - /* fall through */ - case SB_NONE: - value = udev_device_get_sysattr_value(dev, name); - if (value == NULL) - return -1; - break; - case SB_SUBSYS: - if (util_resolve_subsys_kernel(event->udev, name, vbuf, sizeof(vbuf), 1) != 0) - return -1; - value = vbuf; - break; - default: - return -1; - } - - /* remove trailing whitespace, if not asked to match for it */ - len = strlen(value); - if (len > 0 && isspace(value[len-1])) { - const char *key_value; - size_t klen; - - key_value = &rules->buf[cur->key.value_off]; - klen = strlen(key_value); - if (klen > 0 && !isspace(key_value[klen-1])) { - if (value != vbuf) { - util_strscpy(vbuf, sizeof(vbuf), value); - value = vbuf; - } - while (len > 0 && isspace(vbuf[--len])) - vbuf[len] = '\0'; - dbg(rules->udev, "removed trailing whitespace from '%s'\n", value); - } - } - - return match_key(rules, cur, value); -} - -enum escape_type { - ESCAPE_UNSET, - ESCAPE_NONE, - ESCAPE_REPLACE, -}; - -int udev_rules_apply_to_event(struct udev_rules *rules, struct udev_event *event, const sigset_t *sigmask) -{ - struct token *cur; - struct token *rule; - enum escape_type esc = ESCAPE_UNSET; - bool can_set_name; - - if (rules->tokens == NULL) - return -1; - - can_set_name = ((strcmp(udev_device_get_action(event->dev), "remove") != 0) && - (major(udev_device_get_devnum(event->dev)) > 0 || - udev_device_get_ifindex(event->dev) > 0)); - - /* loop through token list, match, run actions or forward to next rule */ - cur = &rules->tokens[0]; - rule = cur; - for (;;) { - dump_token(rules, cur); - switch (cur->type) { - case TK_RULE: - /* current rule */ - rule = cur; - /* possibly skip rules which want to set NAME, SYMLINK, OWNER, GROUP, MODE */ - if (!can_set_name && rule->rule.can_set_name) - goto nomatch; - esc = ESCAPE_UNSET; - break; - case TK_M_ACTION: - if (match_key(rules, cur, udev_device_get_action(event->dev)) != 0) - goto nomatch; - break; - case TK_M_DEVPATH: - if (match_key(rules, cur, udev_device_get_devpath(event->dev)) != 0) - goto nomatch; - break; - case TK_M_KERNEL: - if (match_key(rules, cur, udev_device_get_sysname(event->dev)) != 0) - goto nomatch; - break; - case TK_M_DEVLINK: { - size_t devlen = strlen(udev_get_dev_path(event->udev))+1; - struct udev_list_entry *list_entry; - bool match = false; - - udev_list_entry_foreach(list_entry, udev_device_get_devlinks_list_entry(event->dev)) { - const char *devlink; - - devlink = &udev_list_entry_get_name(list_entry)[devlen]; - if (match_key(rules, cur, devlink) == 0) { - match = true; - break; - } - } - if (!match) - goto nomatch; - break; - } - case TK_M_NAME: - if (match_key(rules, cur, event->name) != 0) - goto nomatch; - break; - case TK_M_ENV: { - const char *key_name = &rules->buf[cur->key.attr_off]; - const char *value; - - value = udev_device_get_property_value(event->dev, key_name); - if (value == NULL) { - dbg(event->udev, "ENV{%s} is not set, treat as empty\n", key_name); - value = ""; - } - if (match_key(rules, cur, value)) - goto nomatch; - break; - } - case TK_M_TAG: { - struct udev_list_entry *list_entry; - bool match = false; - - udev_list_entry_foreach(list_entry, udev_device_get_tags_list_entry(event->dev)) { - if (strcmp(&rules->buf[cur->key.value_off], udev_list_entry_get_name(list_entry)) == 0) { - match = true; - break; - } - } - if (!match && (cur->key.op != OP_NOMATCH)) - goto nomatch; - break; - } - case TK_M_SUBSYSTEM: - if (match_key(rules, cur, udev_device_get_subsystem(event->dev)) != 0) - goto nomatch; - break; - case TK_M_DRIVER: - if (match_key(rules, cur, udev_device_get_driver(event->dev)) != 0) - goto nomatch; - break; - case TK_M_WAITFOR: { - char filename[UTIL_PATH_SIZE]; - int found; - - udev_event_apply_format(event, &rules->buf[cur->key.value_off], filename, sizeof(filename)); - found = (wait_for_file(event->dev, filename, 10) == 0); - if (!found && (cur->key.op != OP_NOMATCH)) - goto nomatch; - break; - } - case TK_M_ATTR: - if (match_attr(rules, event->dev, event, cur) != 0) - goto nomatch; - break; - case TK_M_KERNELS: - case TK_M_SUBSYSTEMS: - case TK_M_DRIVERS: - case TK_M_ATTRS: - case TK_M_TAGS: { - struct token *next; - - /* get whole sequence of parent matches */ - next = cur; - while (next->type > TK_M_PARENTS_MIN && next->type < TK_M_PARENTS_MAX) - next++; - - /* loop over parents */ - event->dev_parent = event->dev; - for (;;) { - struct token *key; - - dbg(event->udev, "parent: '%s'\n", udev_device_get_syspath(event->dev_parent)); - /* loop over sequence of parent match keys */ - for (key = cur; key < next; key++ ) { - dump_token(rules, key); - switch(key->type) { - case TK_M_KERNELS: - if (match_key(rules, key, udev_device_get_sysname(event->dev_parent)) != 0) - goto try_parent; - break; - case TK_M_SUBSYSTEMS: - if (match_key(rules, key, udev_device_get_subsystem(event->dev_parent)) != 0) - goto try_parent; - break; - case TK_M_DRIVERS: - if (match_key(rules, key, udev_device_get_driver(event->dev_parent)) != 0) - goto try_parent; - break; - case TK_M_ATTRS: - if (match_attr(rules, event->dev_parent, event, key) != 0) - goto try_parent; - break; - case TK_M_TAGS: { - bool match = udev_device_has_tag(event->dev_parent, &rules->buf[cur->key.value_off]); - - if (match && key->key.op == OP_NOMATCH) - goto try_parent; - if (!match && key->key.op == OP_MATCH) - goto try_parent; - break; - } - default: - goto nomatch; - } - dbg(event->udev, "parent key matched\n"); - } - dbg(event->udev, "all parent keys matched\n"); - break; - - try_parent: - event->dev_parent = udev_device_get_parent(event->dev_parent); - if (event->dev_parent == NULL) - goto nomatch; - } - /* move behind our sequence of parent match keys */ - cur = next; - continue; - } - case TK_M_TEST: { - char filename[UTIL_PATH_SIZE]; - struct stat statbuf; - int match; - - udev_event_apply_format(event, &rules->buf[cur->key.value_off], filename, sizeof(filename)); - if (util_resolve_subsys_kernel(event->udev, filename, filename, sizeof(filename), 0) != 0) { - if (filename[0] != '/') { - char tmp[UTIL_PATH_SIZE]; - - util_strscpy(tmp, sizeof(tmp), filename); - util_strscpyl(filename, sizeof(filename), - udev_device_get_syspath(event->dev), "/", tmp, NULL); - } - } - attr_subst_subdir(filename, sizeof(filename)); - - match = (stat(filename, &statbuf) == 0); - dbg(event->udev, "'%s' %s", filename, match ? "exists\n" : "does not exist\n"); - if (match && cur->key.mode > 0) { - match = ((statbuf.st_mode & cur->key.mode) > 0); - dbg(event->udev, "'%s' has mode=%#o and %s %#o\n", filename, statbuf.st_mode, - match ? "matches" : "does not match", cur->key.mode); - } - if (match && cur->key.op == OP_NOMATCH) - goto nomatch; - if (!match && cur->key.op == OP_MATCH) - goto nomatch; - break; - } - case TK_M_EVENT_TIMEOUT: - info(event->udev, "OPTIONS event_timeout=%u\n", cur->key.event_timeout); - event->timeout_usec = cur->key.event_timeout * 1000 * 1000; - break; - case TK_M_PROGRAM: { - char program[UTIL_PATH_SIZE]; - char **envp; - char result[UTIL_PATH_SIZE]; - - free(event->program_result); - event->program_result = NULL; - udev_event_apply_format(event, &rules->buf[cur->key.value_off], program, sizeof(program)); - envp = udev_device_get_properties_envp(event->dev); - info(event->udev, "PROGRAM '%s' %s:%u\n", - program, - &rules->buf[rule->rule.filename_off], - rule->rule.filename_line); - - if (udev_event_spawn(event, program, envp, sigmask, result, sizeof(result)) < 0) { - if (cur->key.op != OP_NOMATCH) - goto nomatch; - } else { - int count; - - util_remove_trailing_chars(result, '\n'); - if (esc == ESCAPE_UNSET || esc == ESCAPE_REPLACE) { - count = util_replace_chars(result, UDEV_ALLOWED_CHARS_INPUT); - if (count > 0) - info(event->udev, "%i character(s) replaced\n" , count); - } - event->program_result = strdup(result); - dbg(event->udev, "storing result '%s'\n", event->program_result); - if (cur->key.op == OP_NOMATCH) - goto nomatch; - } - break; - } - case TK_M_IMPORT_FILE: { - char import[UTIL_PATH_SIZE]; - - udev_event_apply_format(event, &rules->buf[cur->key.value_off], import, sizeof(import)); - if (import_file_into_properties(event->dev, import) != 0) - if (cur->key.op != OP_NOMATCH) - goto nomatch; - break; - } - case TK_M_IMPORT_PROG: { - char import[UTIL_PATH_SIZE]; - - udev_event_apply_format(event, &rules->buf[cur->key.value_off], import, sizeof(import)); - info(event->udev, "IMPORT '%s' %s:%u\n", - import, - &rules->buf[rule->rule.filename_off], - rule->rule.filename_line); - - if (import_program_into_properties(event, import, sigmask) != 0) - if (cur->key.op != OP_NOMATCH) - goto nomatch; - break; - } - case TK_M_IMPORT_BUILTIN: { - char command[UTIL_PATH_SIZE]; - - if (udev_builtin_run_once(cur->key.builtin_cmd)) { - /* check if we ran already */ - if (event->builtin_run & (1 << cur->key.builtin_cmd)) { - info(event->udev, "IMPORT builtin skip '%s' %s:%u\n", - udev_builtin_name(cur->key.builtin_cmd), - &rules->buf[rule->rule.filename_off], - rule->rule.filename_line); - /* return the result from earlier run */ - if (event->builtin_ret & (1 << cur->key.builtin_cmd)) - if (cur->key.op != OP_NOMATCH) - goto nomatch; - break; - } - /* mark as ran */ - event->builtin_run |= (1 << cur->key.builtin_cmd); - } - - udev_event_apply_format(event, &rules->buf[cur->key.value_off], command, sizeof(command)); - info(event->udev, "IMPORT builtin '%s' %s:%u\n", - udev_builtin_name(cur->key.builtin_cmd), - &rules->buf[rule->rule.filename_off], - rule->rule.filename_line); - - if (udev_builtin_run(event->dev, cur->key.builtin_cmd, command, false) != 0) { - /* remember failure */ - info(rules->udev, "IMPORT builtin '%s' returned non-zero\n", - udev_builtin_name(cur->key.builtin_cmd)); - event->builtin_ret |= (1 << cur->key.builtin_cmd); - if (cur->key.op != OP_NOMATCH) - goto nomatch; - } - break; - } - case TK_M_IMPORT_DB: { - const char *key = &rules->buf[cur->key.value_off]; - const char *value; - - value = udev_device_get_property_value(event->dev_db, key); - if (value != NULL) { - struct udev_list_entry *entry; - - entry = udev_device_add_property(event->dev, key, value); - udev_list_entry_set_num(entry, true); - } else { - if (cur->key.op != OP_NOMATCH) - goto nomatch; - } - break; - } - case TK_M_IMPORT_CMDLINE: { - FILE *f; - bool imported = false; - - f = fopen("/proc/cmdline", "r"); - if (f != NULL) { - char cmdline[4096]; - - if (fgets(cmdline, sizeof(cmdline), f) != NULL) { - const char *key = &rules->buf[cur->key.value_off]; - char *pos; - - pos = strstr(cmdline, key); - if (pos != NULL) { - struct udev_list_entry *entry; - - pos += strlen(key); - if (pos[0] == '\0' || isspace(pos[0])) { - /* we import simple flags as 'FLAG=1' */ - entry = udev_device_add_property(event->dev, key, "1"); - udev_list_entry_set_num(entry, true); - imported = true; - } else if (pos[0] == '=') { - const char *value; - - pos++; - value = pos; - while (pos[0] != '\0' && !isspace(pos[0])) - pos++; - pos[0] = '\0'; - entry = udev_device_add_property(event->dev, key, value); - udev_list_entry_set_num(entry, true); - imported = true; - } - } - } - fclose(f); - } - if (!imported && cur->key.op != OP_NOMATCH) - goto nomatch; - break; - } - case TK_M_IMPORT_PARENT: { - char import[UTIL_PATH_SIZE]; - - udev_event_apply_format(event, &rules->buf[cur->key.value_off], import, sizeof(import)); - if (import_parent_into_properties(event->dev, import) != 0) - if (cur->key.op != OP_NOMATCH) - goto nomatch; - break; - } - case TK_M_RESULT: - if (match_key(rules, cur, event->program_result) != 0) - goto nomatch; - break; - case TK_A_STRING_ESCAPE_NONE: - esc = ESCAPE_NONE; - break; - case TK_A_STRING_ESCAPE_REPLACE: - esc = ESCAPE_REPLACE; - break; - case TK_A_DB_PERSIST: - udev_device_set_db_persist(event->dev); - break; - case TK_A_INOTIFY_WATCH: - if (event->inotify_watch_final) - break; - if (cur->key.op == OP_ASSIGN_FINAL) - event->inotify_watch_final = true; - event->inotify_watch = cur->key.watch; - break; - case TK_A_DEVLINK_PRIO: - udev_device_set_devlink_priority(event->dev, cur->key.devlink_prio); - break; - case TK_A_OWNER: { - char owner[UTIL_NAME_SIZE]; - - if (event->owner_final) - break; - if (cur->key.op == OP_ASSIGN_FINAL) - event->owner_final = true; - udev_event_apply_format(event, &rules->buf[cur->key.value_off], owner, sizeof(owner)); - event->uid = util_lookup_user(event->udev, owner); - info(event->udev, "OWNER %u %s:%u\n", - event->uid, - &rules->buf[rule->rule.filename_off], - rule->rule.filename_line); - break; - } - case TK_A_GROUP: { - char group[UTIL_NAME_SIZE]; - - if (event->group_final) - break; - if (cur->key.op == OP_ASSIGN_FINAL) - event->group_final = true; - udev_event_apply_format(event, &rules->buf[cur->key.value_off], group, sizeof(group)); - event->gid = util_lookup_group(event->udev, group); - info(event->udev, "GROUP %u %s:%u\n", - event->gid, - &rules->buf[rule->rule.filename_off], - rule->rule.filename_line); - break; - } - case TK_A_MODE: { - char mode_str[UTIL_NAME_SIZE]; - mode_t mode; - char *endptr; - - if (event->mode_final) - break; - udev_event_apply_format(event, &rules->buf[cur->key.value_off], mode_str, sizeof(mode_str)); - mode = strtol(mode_str, &endptr, 8); - if (endptr[0] != '\0') { - err(event->udev, "ignoring invalid mode '%s'\n", mode_str); - break; - } - if (cur->key.op == OP_ASSIGN_FINAL) - event->mode_final = true; - event->mode_set = true; - event->mode = mode; - info(event->udev, "MODE %#o %s:%u\n", - event->mode, - &rules->buf[rule->rule.filename_off], - rule->rule.filename_line); - break; - } - case TK_A_OWNER_ID: - if (event->owner_final) - break; - if (cur->key.op == OP_ASSIGN_FINAL) - event->owner_final = true; - event->uid = cur->key.uid; - info(event->udev, "OWNER %u %s:%u\n", - event->uid, - &rules->buf[rule->rule.filename_off], - rule->rule.filename_line); - break; - case TK_A_GROUP_ID: - if (event->group_final) - break; - if (cur->key.op == OP_ASSIGN_FINAL) - event->group_final = true; - event->gid = cur->key.gid; - info(event->udev, "GROUP %u %s:%u\n", - event->gid, - &rules->buf[rule->rule.filename_off], - rule->rule.filename_line); - break; - case TK_A_MODE_ID: - if (event->mode_final) - break; - if (cur->key.op == OP_ASSIGN_FINAL) - event->mode_final = true; - event->mode_set = true; - event->mode = cur->key.mode; - info(event->udev, "MODE %#o %s:%u\n", - event->mode, - &rules->buf[rule->rule.filename_off], - rule->rule.filename_line); - break; - case TK_A_ENV: { - const char *name = &rules->buf[cur->key.attr_off]; - char *value = &rules->buf[cur->key.value_off]; - - if (value[0] != '\0') { - char temp_value[UTIL_NAME_SIZE]; - struct udev_list_entry *entry; - - udev_event_apply_format(event, value, temp_value, sizeof(temp_value)); - entry = udev_device_add_property(event->dev, name, temp_value); - /* store in db, skip private keys */ - if (name[0] != '.') - udev_list_entry_set_num(entry, true); - } else { - udev_device_add_property(event->dev, name, NULL); - } - break; - } - case TK_A_TAG: { - char tag[UTIL_PATH_SIZE]; - const char *p; - - udev_event_apply_format(event, &rules->buf[cur->key.value_off], tag, sizeof(tag)); - if (cur->key.op == OP_ASSIGN || cur->key.op == OP_ASSIGN_FINAL) - udev_device_cleanup_tags_list(event->dev); - for (p = tag; *p != '\0'; p++) { - if ((*p >= 'a' && *p <= 'z') || - (*p >= 'A' && *p <= 'Z') || - (*p >= '0' && *p <= '9') || - *p == '-' || *p == '_') - continue; - err(event->udev, "ignoring invalid tag name '%s'\n", tag); - break; - } - udev_device_add_tag(event->dev, tag); - break; - } - case TK_A_NAME: { - const char *name = &rules->buf[cur->key.value_off]; - char name_str[UTIL_PATH_SIZE]; - int count; - - if (event->name_final) - break; - if (cur->key.op == OP_ASSIGN_FINAL) - event->name_final = true; - udev_event_apply_format(event, name, name_str, sizeof(name_str)); - if (esc == ESCAPE_UNSET || esc == ESCAPE_REPLACE) { - count = util_replace_chars(name_str, "/"); - if (count > 0) - info(event->udev, "%i character(s) replaced\n", count); - } - free(event->name); - event->name = strdup(name_str); - info(event->udev, "NAME '%s' %s:%u\n", - event->name, - &rules->buf[rule->rule.filename_off], - rule->rule.filename_line); - break; - } - case TK_A_DEVLINK: { - char temp[UTIL_PATH_SIZE]; - char filename[UTIL_PATH_SIZE]; - char *pos, *next; - int count = 0; - - if (event->devlink_final) - break; - if (major(udev_device_get_devnum(event->dev)) == 0) - break; - if (cur->key.op == OP_ASSIGN_FINAL) - event->devlink_final = true; - if (cur->key.op == OP_ASSIGN || cur->key.op == OP_ASSIGN_FINAL) - udev_device_cleanup_devlinks_list(event->dev); - - /* allow multiple symlinks separated by spaces */ - udev_event_apply_format(event, &rules->buf[cur->key.value_off], temp, sizeof(temp)); - if (esc == ESCAPE_UNSET) - count = util_replace_chars(temp, "/ "); - else if (esc == ESCAPE_REPLACE) - count = util_replace_chars(temp, "/"); - if (count > 0) - info(event->udev, "%i character(s) replaced\n" , count); - dbg(event->udev, "rule applied, added symlink(s) '%s'\n", temp); - pos = temp; - while (isspace(pos[0])) - pos++; - next = strchr(pos, ' '); - while (next != NULL) { - next[0] = '\0'; - info(event->udev, "LINK '%s' %s:%u\n", pos, - &rules->buf[rule->rule.filename_off], rule->rule.filename_line); - util_strscpyl(filename, sizeof(filename), udev_get_dev_path(event->udev), "/", pos, NULL); - udev_device_add_devlink(event->dev, filename, cur->key.devlink_unique); - while (isspace(next[1])) - next++; - pos = &next[1]; - next = strchr(pos, ' '); - } - if (pos[0] != '\0') { - info(event->udev, "LINK '%s' %s:%u\n", pos, - &rules->buf[rule->rule.filename_off], rule->rule.filename_line); - util_strscpyl(filename, sizeof(filename), udev_get_dev_path(event->udev), "/", pos, NULL); - udev_device_add_devlink(event->dev, filename, cur->key.devlink_unique); - } - break; - } - case TK_A_ATTR: { - const char *key_name = &rules->buf[cur->key.attr_off]; - char attr[UTIL_PATH_SIZE]; - char value[UTIL_NAME_SIZE]; - FILE *f; - - if (util_resolve_subsys_kernel(event->udev, key_name, attr, sizeof(attr), 0) != 0) - util_strscpyl(attr, sizeof(attr), udev_device_get_syspath(event->dev), "/", key_name, NULL); - attr_subst_subdir(attr, sizeof(attr)); - - udev_event_apply_format(event, &rules->buf[cur->key.value_off], value, sizeof(value)); - info(event->udev, "ATTR '%s' writing '%s' %s:%u\n", attr, value, - &rules->buf[rule->rule.filename_off], - rule->rule.filename_line); - f = fopen(attr, "w"); - if (f != NULL) { - if (fprintf(f, "%s", value) <= 0) - err(event->udev, "error writing ATTR{%s}: %m\n", attr); - fclose(f); - } else { - err(event->udev, "error opening ATTR{%s} for writing: %m\n", attr); - } - break; - } - case TK_A_RUN: { - if (cur->key.op == OP_ASSIGN || cur->key.op == OP_ASSIGN_FINAL) - udev_list_cleanup(&event->run_list); - info(event->udev, "RUN '%s' %s:%u\n", - &rules->buf[cur->key.value_off], - &rules->buf[rule->rule.filename_off], - rule->rule.filename_line); - udev_list_entry_add(&event->run_list, &rules->buf[cur->key.value_off], NULL); - break; - } - case TK_A_GOTO: - if (cur->key.rule_goto == 0) - break; - cur = &rules->tokens[cur->key.rule_goto]; - continue; - case TK_END: - return 0; - - case TK_M_PARENTS_MIN: - case TK_M_PARENTS_MAX: - case TK_M_MAX: - case TK_UNSET: - err(rules->udev, "wrong type %u\n", cur->type); - goto nomatch; - } - - cur++; - continue; - nomatch: - /* fast-forward to next rule */ - cur = rule + rule->rule.token_count; - dbg(rules->udev, "forward to rule: %u\n", - (unsigned int) (cur - rules->tokens)); - } -} - -void udev_rules_apply_static_dev_perms(struct udev_rules *rules) -{ - struct token *cur; - struct token *rule; - uid_t uid = 0; - gid_t gid = 0; - mode_t mode = 0; - - if (rules->tokens == NULL) - return; - - cur = &rules->tokens[0]; - rule = cur; - for (;;) { - switch (cur->type) { - case TK_RULE: - /* current rule */ - rule = cur; - - /* skip rules without a static_node tag */ - if (!rule->rule.has_static_node) - goto next; - - uid = 0; - gid = 0; - mode = 0; - break; - case TK_A_OWNER_ID: - uid = cur->key.uid; - break; - case TK_A_GROUP_ID: - gid = cur->key.gid; - break; - case TK_A_MODE_ID: - mode = cur->key.mode; - break; - case TK_A_STATIC_NODE: { - char filename[UTIL_PATH_SIZE]; - struct stat stats; - - /* we assure, that the permissions tokens are sorted before the static token */ - if (mode == 0 && uid == 0 && gid == 0) - goto next; - util_strscpyl(filename, sizeof(filename), udev_get_dev_path(rules->udev), "/", - &rules->buf[cur->key.value_off], NULL); - if (stat(filename, &stats) != 0) - goto next; - if (!S_ISBLK(stats.st_mode) && !S_ISCHR(stats.st_mode)) - goto next; - if (mode == 0) { - if (gid > 0) - mode = 0660; - else - mode = 0600; - } - if (mode != (stats.st_mode & 01777)) { - chmod(filename, mode); - info(rules->udev, "chmod '%s' %#o\n", filename, mode); - } - - if ((uid != 0 && uid != stats.st_uid) || (gid != 0 && gid != stats.st_gid)) { - chown(filename, uid, gid); - info(rules->udev, "chown '%s' %u %u\n", filename, uid, gid); - } - - utimensat(AT_FDCWD, filename, NULL, 0); - break; - } - case TK_END: - return; - } - - cur++; - continue; -next: - /* fast-forward to next rule */ - cur = rule + rule->rule.token_count; - continue; - } -} diff --git a/udev/udev-watch.c b/udev/udev-watch.c deleted file mode 100644 index 0ec8bfd627..0000000000 --- a/udev/udev-watch.c +++ /dev/null @@ -1,170 +0,0 @@ -/* - * Copyright (C) 2004-2010 Kay Sievers <kay.sievers@vrfy.org> - * Copyright (C) 2009 Canonical Ltd. - * Copyright (C) 2009 Scott James Remnant <scott@netsplit.com> - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see <http://www.gnu.org/licenses/>. - */ - -#include <sys/types.h> -#include <errno.h> -#include <fcntl.h> -#include <stdio.h> -#include <dirent.h> -#include <stddef.h> -#include <stdlib.h> -#include <string.h> -#include <unistd.h> -#include <sys/inotify.h> - -#include "udev.h" - -static int inotify_fd = -1; - -/* inotify descriptor, will be shared with rules directory; - * set to cloexec since we need our children to be able to add - * watches for us - */ -int udev_watch_init(struct udev *udev) -{ - inotify_fd = inotify_init1(IN_CLOEXEC); - if (inotify_fd < 0) - err(udev, "inotify_init failed: %m\n"); - return inotify_fd; -} - -/* move any old watches directory out of the way, and then restore - * the watches - */ -void udev_watch_restore(struct udev *udev) -{ - char filename[UTIL_PATH_SIZE], oldname[UTIL_PATH_SIZE]; - - if (inotify_fd < 0) - return; - - util_strscpyl(oldname, sizeof(oldname), udev_get_run_path(udev), "/watch.old", NULL); - util_strscpyl(filename, sizeof(filename), udev_get_run_path(udev), "/watch", NULL); - if (rename(filename, oldname) == 0) { - DIR *dir; - struct dirent *ent; - - dir = opendir(oldname); - if (dir == NULL) { - err(udev, "unable to open old watches dir '%s', old watches will not be restored: %m", oldname); - return; - } - - for (ent = readdir(dir); ent != NULL; ent = readdir(dir)) { - char device[UTIL_PATH_SIZE]; - char *s; - size_t l; - ssize_t len; - struct udev_device *dev; - - if (ent->d_name[0] == '.') - continue; - - s = device; - l = util_strpcpy(&s, sizeof(device), udev_get_sys_path(udev)); - len = readlinkat(dirfd(dir), ent->d_name, s, l); - if (len <= 0 || len == (ssize_t)l) - goto unlink; - s[len] = '\0'; - - dev = udev_device_new_from_id_filename(udev, s); - if (dev == NULL) - goto unlink; - - info(udev, "restoring old watch on '%s'\n", udev_device_get_devnode(dev)); - udev_watch_begin(udev, dev); - udev_device_unref(dev); -unlink: - unlinkat(dirfd(dir), ent->d_name, 0); - } - - closedir(dir); - rmdir(oldname); - - } else if (errno != ENOENT) { - err(udev, "unable to move watches dir '%s', old watches will not be restored: %m", filename); - } -} - -void udev_watch_begin(struct udev *udev, struct udev_device *dev) -{ - char filename[UTIL_PATH_SIZE]; - int wd; - - if (inotify_fd < 0) - return; - - info(udev, "adding watch on '%s'\n", udev_device_get_devnode(dev)); - wd = inotify_add_watch(inotify_fd, udev_device_get_devnode(dev), IN_CLOSE_WRITE); - if (wd < 0) { - err(udev, "inotify_add_watch(%d, %s, %o) failed: %m\n", - inotify_fd, udev_device_get_devnode(dev), IN_CLOSE_WRITE); - return; - } - - snprintf(filename, sizeof(filename), "%s/watch/%d", udev_get_run_path(udev), wd); - util_create_path(udev, filename); - unlink(filename); - symlink(udev_device_get_id_filename(dev), filename); - - udev_device_set_watch_handle(dev, wd); -} - -void udev_watch_end(struct udev *udev, struct udev_device *dev) -{ - int wd; - char filename[UTIL_PATH_SIZE]; - - if (inotify_fd < 0) - return; - - wd = udev_device_get_watch_handle(dev); - if (wd < 0) - return; - - info(udev, "removing watch on '%s'\n", udev_device_get_devnode(dev)); - inotify_rm_watch(inotify_fd, wd); - - snprintf(filename, sizeof(filename), "%s/watch/%d", udev_get_run_path(udev), wd); - unlink(filename); - - udev_device_set_watch_handle(dev, -1); -} - -struct udev_device *udev_watch_lookup(struct udev *udev, int wd) -{ - char filename[UTIL_PATH_SIZE]; - char majmin[UTIL_PATH_SIZE]; - char *s; - size_t l; - ssize_t len; - - if (inotify_fd < 0 || wd < 0) - return NULL; - - snprintf(filename, sizeof(filename), "%s/watch/%d", udev_get_run_path(udev), wd); - s = majmin; - l = util_strpcpy(&s, sizeof(majmin), udev_get_sys_path(udev)); - len = readlink(filename, s, l); - if (len <= 0 || (size_t)len == l) - return NULL; - s[len] = '\0'; - - return udev_device_new_from_id_filename(udev, s); -} diff --git a/udev/udev.h b/udev/udev.h deleted file mode 100644 index 56b1652c74..0000000000 --- a/udev/udev.h +++ /dev/null @@ -1,188 +0,0 @@ -/* - * Copyright (C) 2003 Greg Kroah-Hartman <greg@kroah.com> - * Copyright (C) 2003-2010 Kay Sievers <kay.sievers@vrfy.org> - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see <http://www.gnu.org/licenses/>. - */ - -#ifndef _UDEV_H_ -#define _UDEV_H_ - -#include <sys/types.h> -#include <sys/param.h> -#include <signal.h> - -#include "libudev.h" -#include "libudev-private.h" - -struct udev_event { - struct udev *udev; - struct udev_device *dev; - struct udev_device *dev_parent; - struct udev_device *dev_db; - char *name; - char *program_result; - mode_t mode; - uid_t uid; - gid_t gid; - struct udev_list run_list; - int exec_delay; - unsigned long long birth_usec; - unsigned long long timeout_usec; - int fd_signal; - unsigned int builtin_run; - unsigned int builtin_ret; - bool sigterm; - bool inotify_watch; - bool inotify_watch_final; - bool group_final; - bool owner_final; - bool mode_set; - bool mode_final; - bool name_final; - bool devlink_final; - bool run_final; -}; - -struct udev_watch { - struct udev_list_node node; - int handle; - char *name; -}; - -/* udev-rules.c */ -struct udev_rules; -struct udev_rules *udev_rules_new(struct udev *udev, int resolve_names); -struct udev_rules *udev_rules_unref(struct udev_rules *rules); -int udev_rules_apply_to_event(struct udev_rules *rules, struct udev_event *event, const sigset_t *sigmask); -void udev_rules_apply_static_dev_perms(struct udev_rules *rules); - -/* udev-event.c */ -struct udev_event *udev_event_new(struct udev_device *dev); -void udev_event_unref(struct udev_event *event); -size_t udev_event_apply_format(struct udev_event *event, const char *src, char *dest, size_t size); -int udev_event_apply_subsys_kernel(struct udev_event *event, const char *string, - char *result, size_t maxsize, int read_value); -int udev_event_spawn(struct udev_event *event, - const char *cmd, char **envp, const sigset_t *sigmask, - char *result, size_t ressize); -int udev_event_execute_rules(struct udev_event *event, struct udev_rules *rules, const sigset_t *sigset); -int udev_event_execute_run(struct udev_event *event, const sigset_t *sigset); -int udev_build_argv(struct udev *udev, char *cmd, int *argc, char *argv[]); - -/* udev-watch.c */ -int udev_watch_init(struct udev *udev); -void udev_watch_restore(struct udev *udev); -void udev_watch_begin(struct udev *udev, struct udev_device *dev); -void udev_watch_end(struct udev *udev, struct udev_device *dev); -struct udev_device *udev_watch_lookup(struct udev *udev, int wd); - -/* udev-node.c */ -int udev_node_add(struct udev_device *dev, mode_t mode, uid_t uid, gid_t gid); -int udev_node_remove(struct udev_device *dev); -void udev_node_update_old_links(struct udev_device *dev, struct udev_device *dev_old); - -/* udev-ctrl.c */ -struct udev_ctrl; -struct udev_ctrl *udev_ctrl_new(struct udev *udev); -struct udev_ctrl *udev_ctrl_new_from_fd(struct udev *udev, int fd); -int udev_ctrl_enable_receiving(struct udev_ctrl *uctrl); -struct udev_ctrl *udev_ctrl_ref(struct udev_ctrl *uctrl); -struct udev_ctrl *udev_ctrl_unref(struct udev_ctrl *uctrl); -int udev_ctrl_cleanup(struct udev_ctrl *uctrl); -struct udev *udev_ctrl_get_udev(struct udev_ctrl *uctrl); -int udev_ctrl_get_fd(struct udev_ctrl *uctrl); -int udev_ctrl_send_set_log_level(struct udev_ctrl *uctrl, int priority, int timeout); -int udev_ctrl_send_stop_exec_queue(struct udev_ctrl *uctrl, int timeout); -int udev_ctrl_send_start_exec_queue(struct udev_ctrl *uctrl, int timeout); -int udev_ctrl_send_reload(struct udev_ctrl *uctrl, int timeout); -int udev_ctrl_send_ping(struct udev_ctrl *uctrl, int timeout); -int udev_ctrl_send_exit(struct udev_ctrl *uctrl, int timeout); -int udev_ctrl_send_set_env(struct udev_ctrl *uctrl, const char *key, int timeout); -int udev_ctrl_send_set_children_max(struct udev_ctrl *uctrl, int count, int timeout); -struct udev_ctrl_connection; -struct udev_ctrl_connection *udev_ctrl_get_connection(struct udev_ctrl *uctrl); -struct udev_ctrl_connection *udev_ctrl_connection_ref(struct udev_ctrl_connection *conn); -struct udev_ctrl_connection *udev_ctrl_connection_unref(struct udev_ctrl_connection *conn); -struct udev_ctrl_msg; -struct udev_ctrl_msg *udev_ctrl_receive_msg(struct udev_ctrl_connection *conn); -struct udev_ctrl_msg *udev_ctrl_msg_ref(struct udev_ctrl_msg *ctrl_msg); -struct udev_ctrl_msg *udev_ctrl_msg_unref(struct udev_ctrl_msg *ctrl_msg); -int udev_ctrl_get_set_log_level(struct udev_ctrl_msg *ctrl_msg); -int udev_ctrl_get_stop_exec_queue(struct udev_ctrl_msg *ctrl_msg); -int udev_ctrl_get_start_exec_queue(struct udev_ctrl_msg *ctrl_msg); -int udev_ctrl_get_reload(struct udev_ctrl_msg *ctrl_msg); -int udev_ctrl_get_ping(struct udev_ctrl_msg *ctrl_msg); -int udev_ctrl_get_exit(struct udev_ctrl_msg *ctrl_msg); -const char *udev_ctrl_get_set_env(struct udev_ctrl_msg *ctrl_msg); -int udev_ctrl_get_set_children_max(struct udev_ctrl_msg *ctrl_msg); - -/* built-in commands */ -enum udev_builtin_cmd { - UDEV_BUILTIN_BLKID, - UDEV_BUILTIN_FIRMWARE, - UDEV_BUILTIN_INPUT_ID, - UDEV_BUILTIN_KMOD, - UDEV_BUILTIN_PATH_ID, - UDEV_BUILTIN_PCI_DB, - UDEV_BUILTIN_USB_DB, - UDEV_BUILTIN_USB_ID, - UDEV_BUILTIN_MAX -}; -struct udev_builtin { - const char *name; - int (*cmd)(struct udev_device *dev, int argc, char *argv[], bool test); - const char *help; - int (*init)(struct udev *udev); - void (*exit)(struct udev *udev); - bool (*validate)(struct udev *udev); - bool run_once; -}; -extern const struct udev_builtin udev_builtin_blkid; -extern const struct udev_builtin udev_builtin_firmware; -extern const struct udev_builtin udev_builtin_input_id; -extern const struct udev_builtin udev_builtin_kmod; -extern const struct udev_builtin udev_builtin_path_id; -extern const struct udev_builtin udev_builtin_pci_db; -extern const struct udev_builtin udev_builtin_usb_db; -extern const struct udev_builtin udev_builtin_usb_id; -int udev_builtin_init(struct udev *udev); -void udev_builtin_exit(struct udev *udev); -enum udev_builtin_cmd udev_builtin_lookup(const char *command); -const char *udev_builtin_name(enum udev_builtin_cmd cmd); -bool udev_builtin_run_once(enum udev_builtin_cmd cmd); -int udev_builtin_run(struct udev_device *dev, enum udev_builtin_cmd cmd, const char *command, bool test); -void udev_builtin_list(struct udev *udev); -int udev_builtin_add_property(struct udev_device *dev, bool test, const char *key, const char *val); - -/* udev logging */ -void udev_main_log(struct udev *udev, int priority, - const char *file, int line, const char *fn, - const char *format, va_list args); - -/* udevadm commands */ -struct udevadm_cmd { - const char *name; - int (*cmd)(struct udev *udev, int argc, char *argv[]); - const char *help; - int debug; -}; -extern const struct udevadm_cmd udevadm_info; -extern const struct udevadm_cmd udevadm_trigger; -extern const struct udevadm_cmd udevadm_settle; -extern const struct udevadm_cmd udevadm_control; -extern const struct udevadm_cmd udevadm_monitor; -extern const struct udevadm_cmd udevadm_test; -extern const struct udevadm_cmd udevadm_test_builtin; -#endif diff --git a/udev/udev.pc.in b/udev/udev.pc.in deleted file mode 100644 index 0b04c02ef6..0000000000 --- a/udev/udev.pc.in +++ /dev/null @@ -1,5 +0,0 @@ -Name: udev -Description: udev -Version: @VERSION@ - -udevdir=@pkglibexecdir@ diff --git a/udev/udev.xml b/udev/udev.xml deleted file mode 100644 index 79213b4bbe..0000000000 --- a/udev/udev.xml +++ /dev/null @@ -1,694 +0,0 @@ -<?xml version='1.0'?> -<?xml-stylesheet type="text/xsl" href="http://docbook.sourceforge.net/release/xsl/current/xhtml/docbook.xsl"?> -<!DOCTYPE refentry PUBLIC "-//OASIS//DTD DocBook XML V4.2//EN" - "http://www.oasis-open.org/docbook/xml/4.2/docbookx.dtd"> - -<refentry id="udev"> - <refentryinfo> - <title>udev</title> - <productname>udev</productname> - </refentryinfo> - - <refmeta> - <refentrytitle>udev</refentrytitle> - <manvolnum>7</manvolnum> - </refmeta> - - <refnamediv> - <refname>udev</refname> - <refpurpose>Linux dynamic device management</refpurpose> - </refnamediv> - - <refsect1><title>Description</title> - <para>udev supplies the system software with device events, manages permissions - of device nodes and may create additional symlinks in the <filename>/dev</filename> - directory, or renames network interfaces. The kernel usually just assigns unpredictable - device names based on the order of discovery. Meaningful symlinks or network device - names provide a way to reliably identify devices based on their properties or - current configuration.</para> - - <para>The udev daemon, <citerefentry><refentrytitle>udevd</refentrytitle> - <manvolnum>8</manvolnum></citerefentry>, receives device uevents directly from - the kernel whenever a device is added or removed from the system, or it changes its - state. When udev receives a device event, it matches its configured set of rules - against various device attributes to identify the device. Rules that match may - provide additional device information to be stored in the udev database or - to be used to create meaningful symlink names.</para> - - <para>All device information udev processes is stored in the udev database and - sent out to possible event subscribers. Access to all stored data and the event - sources is provided by the library libudev.</para> - </refsect1> - - <refsect1><title>Configuration</title> - <para>udev configuration files are placed in <filename>/etc/udev</filename> - and <filename>/usr/lib/udev</filename>. All empty lines or lines beginning with - '#' are ignored.</para> - - <refsect2><title>Configuration file</title> - <para>udev expects its main configuration file at <filename>/etc/udev/udev.conf</filename>. - It consists of a set of variables allowing the user to override default udev values. - The following variables can be set:</para> - <variablelist> - <varlistentry> - <term><option>udev_root</option></term> - <listitem> - <para>Specifies where to place the device nodes in the filesystem. - The default value is <filename>/dev</filename>.</para> - </listitem> - </varlistentry> - - <varlistentry> - <term><option>udev_log</option></term> - <listitem> - <para>The logging priority. Valid values are the numerical syslog priorities - or their textual representations: <option>err</option>, <option>info</option> - and <option>debug</option>.</para> - </listitem> - </varlistentry> - </variablelist> - </refsect2> - - <refsect2><title>Rules files</title> - <para>The udev rules are read from the files located in the - system rules directory <filename>/usr/lib/udev/rules.d</filename>, - the local administration directory <filename>/etc/udev/rules.d</filename> - and the volatile runtime directory <filename>/run/udev/rules.d</filename>. - All rules files are collectively sorted and processed in lexical order, - regardless of the directories in which they live. However, files with - identical file names replace each other. Files in <filename>/run</filename> - have the highest priority, files in <filename>/etc</filename> take precedence - over files with the same name in <filename>/lib</filename>. This can be - used to overwrite a system rules file if needed; a symlink in - <filename>/etc</filename> with the same name as a rules file in - <filename>/lib</filename>, pointing to <filename>/dev/null</filename>, - disables the rules file entirely.</para> - - <para>Rule files must have the extension <filename>.rules</filename>; other - extensions are ignored.</para> - - <para>Every line in the rules file contains at least one key-value pair. - There are two kind of keys: match and assignment. - If all match keys are matching against its value, the rule gets applied and the - assignment keys get the specified value assigned.</para> - - <para>A matching rule may rename a network interface, add symlinks - pointing to the device node, or run a specified program as part of - the event handling.</para> - - <para>A rule consists of a comma-separated list of one or more key-value pairs. - Each key has a distinct operation, depending on the used operator. Valid - operators are:</para> - <variablelist> - <varlistentry> - <term><option>==</option></term> - <listitem> - <para>Compare for equality.</para> - </listitem> - </varlistentry> - - <varlistentry> - <term><option>!=</option></term> - <listitem> - <para>Compare for inequality.</para> - </listitem> - </varlistentry> - - <varlistentry> - <term><option>=</option></term> - <listitem> - <para>Assign a value to a key. Keys that represent a list are reset - and only this single value is assigned.</para> - </listitem> - </varlistentry> - - <varlistentry> - <term><option>+=</option></term> - <listitem> - <para>Add the value to a key that holds a list of entries.</para> - </listitem> - </varlistentry> - - <varlistentry> - <term><option>:=</option></term> - <listitem> - <para>Assign a value to a key finally; disallow any later changes.</para> - </listitem> - </varlistentry> - </variablelist> - - <para>The following key names can be used to match against device properties. - Some of the keys also match against properties of the parent devices in sysfs, - not only the device that has generated the event. If multiple keys that match - a parent device are specified in a single rule, all these keys must match at - one and the same parent device.</para> - <variablelist> - <varlistentry> - <term><option>ACTION</option></term> - <listitem> - <para>Match the name of the event action.</para> - </listitem> - </varlistentry> - - <varlistentry> - <term><option>DEVPATH</option></term> - <listitem> - <para>Match the devpath of the event device.</para> - </listitem> - </varlistentry> - - <varlistentry> - <term><option>KERNEL</option></term> - <listitem> - <para>Match the name of the event device.</para> - </listitem> - </varlistentry> - - <varlistentry> - <term><option>NAME</option></term> - <listitem> - <para>Match the name of a network interface. It can be used once the - NAME key has been set in one of the preceding rules.</para> - </listitem> - </varlistentry> - - <varlistentry> - <term><option>SYMLINK</option></term> - <listitem> - <para>Match the name of a symlink targeting the node. It can - be used once a SYMLINK key has been set in one of the preceding - rules. There may be multiple symlinks; only one needs to match. - </para> - </listitem> - </varlistentry> - - <varlistentry> - <term><option>SUBSYSTEM</option></term> - <listitem> - <para>Match the subsystem of the event device.</para> - </listitem> - </varlistentry> - <varlistentry> - <term><option>DRIVER</option></term> - <listitem> - <para>Match the driver name of the event device. Only set this key for devices - which are bound to a driver at the time the event is generated.</para> - </listitem> - </varlistentry> - <varlistentry> - <term><option>ATTR{<replaceable>filename</replaceable>}</option></term> - <listitem> - <para>Match sysfs attribute values of the event device. Trailing - whitespace in the attribute values is ignored unless the specified match - value itself contains trailing whitespace. - </para> - </listitem> - </varlistentry> - - <varlistentry> - <term><option>KERNELS</option></term> - <listitem> - <para>Search the devpath upwards for a matching device name.</para> - </listitem> - </varlistentry> - - <varlistentry> - <term><option>SUBSYSTEMS</option></term> - <listitem> - <para>Search the devpath upwards for a matching device subsystem name.</para> - </listitem> - </varlistentry> - - <varlistentry> - <term><option>DRIVERS</option></term> - <listitem> - <para>Search the devpath upwards for a matching device driver name.</para> - </listitem> - </varlistentry> - - <varlistentry> - <term><option>ATTRS{<replaceable>filename</replaceable>}</option></term> - <listitem> - <para>Search the devpath upwards for a device with matching sysfs attribute values. - If multiple <option>ATTRS</option> matches are specified, all of them - must match on the same device. Trailing whitespace in the attribute values is ignored - unless the specified match value itself contains trailing whitespace.</para> - </listitem> - </varlistentry> - - <varlistentry> - <term><option>TAGS</option></term> - <listitem> - <para>Search the devpath upwards for a device with matching tag.</para> - </listitem> - </varlistentry> - - <varlistentry> - <term><option>ENV{<replaceable>key</replaceable>}</option></term> - <listitem> - <para>Match against a device property value.</para> - </listitem> - </varlistentry> - - <varlistentry> - <term><option>TAG</option></term> - <listitem> - <para>Match against a device tag.</para> - </listitem> - </varlistentry> - - <varlistentry> - <term><option>TEST{<replaceable>octal mode mask</replaceable>}</option></term> - <listitem> - <para>Test the existence of a file. An octal mode mask can be specified - if needed.</para> - </listitem> - </varlistentry> - - <varlistentry> - <term><option>PROGRAM</option></term> - <listitem> - <para>Execute a program to determine whether there - is a match; the key is true if the program returns - successfully. The device properties are made available to the - executed program in the environment. The program's stdout - is available in the RESULT key.</para> - </listitem> - </varlistentry> - - <varlistentry> - <term><option>RESULT</option></term> - <listitem> - <para>Match the returned string of the last PROGRAM call. This key can - be used in the same or in any later rule after a PROGRAM call.</para> - </listitem> - </varlistentry> - </variablelist> - - <para>Most of the fields support shell-style pattern matching. The following - pattern characters are supported:</para> - <variablelist> - <varlistentry> - <term><option>*</option></term> - <listitem> - <para>Matches zero or more characters.</para> - </listitem> - </varlistentry> - <varlistentry> - <term><option>?</option></term> - <listitem> - <para>Matches any single character.</para> - </listitem> - </varlistentry> - <varlistentry> - <term><option>[]</option></term> - <listitem> - <para>Matches any single character specified within the brackets. For - example, the pattern string 'tty[SR]' would match either 'ttyS' or 'ttyR'. - Ranges are also supported via the '-' character. - For example, to match on the range of all digits, the pattern [0-9] could - be used. If the first character following the '[' is a '!', any characters - not enclosed are matched.</para> - </listitem> - </varlistentry> - </variablelist> - - <para>The following keys can get values assigned:</para> - <variablelist> - <varlistentry> - <term><option>NAME</option></term> - <listitem> - <para>The name to use for a network interface. The name of a device node - can not be changed by udev, only additional symlinks can be created.</para> - </listitem> - </varlistentry> - - <varlistentry> - <term><option>SYMLINK</option></term> - <listitem> - <para>The name of a symlink targeting the node. Every matching rule adds - this value to the list of symlinks to be created. Multiple symlinks may be - specified by separating the names by the space character. In case multiple - devices claim the same name, the link always points to the device with - the highest link_priority. If the current device goes away, the links are - re-evaluated and the device with the next highest link_priority becomes the owner of - the link. If no link_priority is specified, the order of the devices (and - which one of them owns the link) is undefined. Also, symlink names must - never conflict with the kernel's default device node names, as that would - result in unpredictable behavior. - </para> - </listitem> - </varlistentry> - - <varlistentry> - <term><option>OWNER, GROUP, MODE</option></term> - <listitem> - <para>The permissions for the device node. Every specified value overwrites - the compiled-in default value.</para> - </listitem> - </varlistentry> - - <varlistentry> - <term><option>ATTR{<replaceable>key</replaceable>}</option></term> - <listitem> - <para>The value that should be written to a sysfs attribute of the - event device.</para> - </listitem> - </varlistentry> - - <varlistentry> - <term><option>ENV{<replaceable>key</replaceable>}</option></term> - <listitem> - <para>Set a device property value. Property names with a leading '.' - are neither stored in the database nor exported to events or - external tools (run by, say, the PROGRAM match key).</para> - </listitem> - </varlistentry> - - <varlistentry> - <term><option>TAG</option></term> - <listitem> - <para>Attach a tag to a device. This is used to filter events for users - of libudev's monitor functionality, or to enumerate a group of tagged - devices. The implementation can only work efficiently if only a few - tags are attached to a device. It is only meant to be used in - contexts with specific device filter requirements, and not as a - general-purpose flag. Excessive use might result in inefficient event - handling.</para> - </listitem> - </varlistentry> - - <varlistentry> - <term><option>RUN</option></term> - <listitem> - <para>Add a program to the list of programs to be executed for a specific - device. This can only be used for very short running tasks. Running an - event process for a long period of time may block all further events for - this or a dependent device. Long running tasks need to be immediately - detached from the event process itself.</para> - <para>If no absolute path is given, the program is expected to live in - /usr/lib/udev, otherwise the absolute path must be specified. The program - name and following arguments are separated by spaces. Single quotes can - be used to specify arguments with spaces.</para> - </listitem> - </varlistentry> - - <varlistentry> - <term><option>LABEL</option></term> - <listitem> - <para>A named label to which a GOTO may jump.</para> - </listitem> - </varlistentry> - - <varlistentry> - <term><option>GOTO</option></term> - <listitem> - <para>Jumps to the next LABEL with a matching name.</para> - </listitem> - </varlistentry> - - <varlistentry> - <term><option>IMPORT{<replaceable>type</replaceable>}</option></term> - <listitem> - <para>Import a set of variables as device properties, - depending on <replaceable>type</replaceable>:</para> - <variablelist> - <varlistentry> - <term><option>program</option></term> - <listitem> - <para>Execute an external program specified as the assigned value and - import its output, which must be in environment key - format. Path specification, command/argument separation, - and quoting work like in <option>RUN</option>.</para> - </listitem> - </varlistentry> - <varlistentry> - <term><option>file</option></term> - <listitem> - <para>Import a text file specified as the assigned value, the content - of which must be in environment key format.</para> - </listitem> - </varlistentry> - <varlistentry> - <term><option>db</option></term> - <listitem> - <para>Import a single property specified as the assigned value from the - current device database. This works only if the database is already populated - by an earlier event.</para> - </listitem> - </varlistentry> - <varlistentry> - <term><option>cmdline</option></term> - <listitem> - <para>Import a single property from the kernel command line. For simple flags - the value of the property is set to '1'.</para> - </listitem> - </varlistentry> - <varlistentry> - <term><option>parent</option></term> - <listitem> - <para>Import the stored keys from the parent device by reading - the database entry of the parent device. The value assigned to - <option>IMPORT{parent}</option> is used as a filter of key names - to import (with the same shell-style pattern matching used for - comparisons).</para> - </listitem> - </varlistentry> - </variablelist> - </listitem> - </varlistentry> - - <varlistentry> - <term><option>WAIT_FOR</option></term> - <listitem> - <para>Wait for a file to become available or until a timeout of - 10 seconds expires. The path is relative to the sysfs device; - if no path is specified, this waits for an attribute to appear.</para> - </listitem> - </varlistentry> - - <varlistentry> - <term><option>OPTIONS</option></term> - <listitem> - <para>Rule and device options:</para> - <variablelist> - <varlistentry> - <term><option>link_priority=<replaceable>value</replaceable></option></term> - <listitem> - <para>Specify the priority of the created symlinks. Devices with higher - priorities overwrite existing symlinks of other devices. The default is 0.</para> - </listitem> - </varlistentry> - <varlistentry> - <term><option>event_timeout=</option></term> - <listitem> - <para>Number of seconds an event waits for operations to finish before - giving up and terminating itself.</para> - </listitem> - </varlistentry> - <varlistentry> - <term><option>string_escape=<replaceable>none|replace</replaceable></option></term> - <listitem> - <para>Usually control and other possibly unsafe characters are replaced - in strings used for device naming. The mode of replacement can be specified - with this option.</para> - </listitem> - </varlistentry> - <varlistentry> - <term><option>static_node=</option></term> - <listitem> - <para>Apply the permissions specified in this rule to the static device node with - the specified name. Static device nodes might be provided by kernel modules - or copied from <filename>/usr/lib/udev/devices</filename>. These nodes might not have - a corresponding kernel device at the time udevd is started; they can trigger - automatic kernel module loading.</para> - </listitem> - </varlistentry> - <varlistentry> - <term><option>watch</option></term> - <listitem> - <para>Watch the device node with inotify; when the node is closed after being opened for - writing, a change uevent is synthesized.</para> - </listitem> - </varlistentry> - <varlistentry> - <term><option>nowatch</option></term> - <listitem> - <para>Disable the watching of a device node with inotify.</para> - </listitem> - </varlistentry> - </variablelist> - </listitem> - </varlistentry> - </variablelist> - - <para>The <option>NAME</option>, <option>SYMLINK</option>, <option>PROGRAM</option>, - <option>OWNER</option>, <option>GROUP</option>, <option>MODE</option> and <option>RUN</option> - fields support simple string substitutions. The <option>RUN</option> - substitutions are performed after all rules have been processed, right before the program - is executed, allowing for the use of device properties set by earlier matching - rules. For all other fields, substitutions are performed while the individual rule is - being processed. The available substitutions are:</para> - <variablelist> - <varlistentry> - <term><option>$kernel</option>, <option>%k</option></term> - <listitem> - <para>The kernel name for this device.</para> - </listitem> - </varlistentry> - - <varlistentry> - <term><option>$number</option>, <option>%n</option></term> - <listitem> - <para>The kernel number for this device. For example, 'sda3' has - kernel number of '3'</para> - </listitem> - </varlistentry> - - <varlistentry> - <term><option>$devpath</option>, <option>%p</option></term> - <listitem> - <para>The devpath of the device.</para> - </listitem> - </varlistentry> - - <varlistentry> - <term><option>$id</option>, <option>%b</option></term> - <listitem> - <para>The name of the device matched while searching the devpath upwards for - <option>SUBSYSTEMS</option>, <option>KERNELS</option>, <option>DRIVERS</option> and <option>ATTRS</option>. - </para> - </listitem> - </varlistentry> - - <varlistentry> - <term><option>$driver</option></term> - <listitem> - <para>The driver name of the device matched while searching the devpath upwards for - <option>SUBSYSTEMS</option>, <option>KERNELS</option>, <option>DRIVERS</option> and <option>ATTRS</option>. - </para> - </listitem> - </varlistentry> - - <varlistentry> - <term><option>$attr{<replaceable>file</replaceable>}</option>, <option>%s{<replaceable>file</replaceable>}</option></term> - <listitem> - <para>The value of a sysfs attribute found at the device where - all keys of the rule have matched. If the matching device does not have - such an attribute, and a previous KERNELS, SUBSYSTEMS, DRIVERS, or - ATTRS test selected a parent device, then the attribute from that - parent device is used.</para> - <para>If the attribute is a symlink, the last element of the symlink target is - returned as the value.</para> - </listitem> - </varlistentry> - - <varlistentry> - <term><option>$env{<replaceable>key</replaceable>}</option>, <option>%E{<replaceable>key</replaceable>}</option></term> - <listitem> - <para>A device property value.</para> - </listitem> - </varlistentry> - - <varlistentry> - <term><option>$major</option>, <option>%M</option></term> - <listitem> - <para>The kernel major number for the device.</para> - </listitem> - </varlistentry> - - <varlistentry> - <term><option>$minor</option>, <option>%m</option></term> - <listitem> - <para>The kernel minor number for the device.</para> - </listitem> - </varlistentry> - - <varlistentry> - <term><option>$result</option>, <option>%c</option></term> - <listitem> - <para>The string returned by the external program requested with PROGRAM. - A single part of the string, separated by a space character, may be selected - by specifying the part number as an attribute: <option>%c{N}</option>. - If the number is followed by the '+' character, this part plus all remaining parts - of the result string are substituted: <option>%c{N+}</option></para> - </listitem> - </varlistentry> - - <varlistentry> - <term><option>$parent</option>, <option>%P</option></term> - <listitem> - <para>The node name of the parent device.</para> - </listitem> - </varlistentry> - - <varlistentry> - <term><option>$name</option></term> - <listitem> - <para>The current name of the device. If not changed by a rule, it is the - name of the kernel device.</para> - </listitem> - </varlistentry> - - <varlistentry> - <term><option>$links</option></term> - <listitem> - <para>A space-separated list of the current symlinks. The value is - only set during a remove event or if an earlier rule assigned a value.</para> - </listitem> - </varlistentry> - - <varlistentry> - <term><option>$root</option>, <option>%r</option></term> - <listitem> - <para>The udev_root value.</para> - </listitem> - </varlistentry> - - <varlistentry> - <term><option>$sys</option>, <option>%S</option></term> - <listitem> - <para>The sysfs mount point.</para> - </listitem> - </varlistentry> - - <varlistentry> - <term><option>$devnode</option>, <option>%N</option></term> - <listitem> - <para>The name of the device node.</para> - </listitem> - </varlistentry> - - <varlistentry> - <term><option>%%</option></term> - <listitem> - <para>The '%' character itself.</para> - </listitem> - </varlistentry> - - <varlistentry> - <term><option>$$</option></term> - <listitem> - <para>The '$' character itself.</para> - </listitem> - </varlistentry> - </variablelist> - </refsect2> - </refsect1> - - <refsect1><title>Author</title> - <para>Written by Greg Kroah-Hartman <email>greg@kroah.com</email> and - Kay Sievers <email>kay.sievers@vrfy.org</email>. With much help from - Dan Stekloff and many others.</para> - </refsect1> - - <refsect1> - <title>See Also</title> - <para><citerefentry> - <refentrytitle>udevd</refentrytitle><manvolnum>8</manvolnum> - </citerefentry>, - <citerefentry> - <refentrytitle>udevadm</refentrytitle><manvolnum>8</manvolnum> - </citerefentry></para> - </refsect1> -</refentry> diff --git a/udev/udevadm-control.c b/udev/udevadm-control.c deleted file mode 100644 index dd1d5d783f..0000000000 --- a/udev/udevadm-control.c +++ /dev/null @@ -1,175 +0,0 @@ -/* - * Copyright (C) 2005-2011 Kay Sievers <kay.sievers@vrfy.org> - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - */ - -#include <time.h> -#include <errno.h> -#include <stdio.h> -#include <stdlib.h> -#include <stddef.h> -#include <string.h> -#include <unistd.h> -#include <getopt.h> -#include <sys/types.h> -#include <sys/socket.h> -#include <sys/wait.h> -#include <sys/un.h> - -#include "udev.h" - -static void print_help(void) -{ - printf("Usage: udevadm control COMMAND\n" - " --exit instruct the daemon to cleanup and exit\n" - " --log-priority=<level> set the udev log level for the daemon\n" - " --stop-exec-queue do not execute events, queue only\n" - " --start-exec-queue execute events, flush queue\n" - " --reload reload rules and databases\n" - " --property=<KEY>=<value> set a global property for all events\n" - " --children-max=<N> maximum number of children\n" - " --timeout=<seconds> maximum time to block for a reply\n" - " --help print this help text\n\n"); -} - -static int adm_control(struct udev *udev, int argc, char *argv[]) -{ - struct udev_ctrl *uctrl = NULL; - int timeout = 60; - int rc = 1; - - static const struct option options[] = { - { "exit", no_argument, NULL, 'e' }, - { "log-priority", required_argument, NULL, 'l' }, - { "stop-exec-queue", no_argument, NULL, 's' }, - { "start-exec-queue", no_argument, NULL, 'S' }, - { "reload", no_argument, NULL, 'R' }, - { "reload-rules", no_argument, NULL, 'R' }, - { "property", required_argument, NULL, 'p' }, - { "env", required_argument, NULL, 'p' }, - { "children-max", required_argument, NULL, 'm' }, - { "timeout", required_argument, NULL, 't' }, - { "help", no_argument, NULL, 'h' }, - {} - }; - - if (getuid() != 0) { - fprintf(stderr, "root privileges required\n"); - return 1; - } - - uctrl = udev_ctrl_new(udev); - if (uctrl == NULL) - return 2; - - for (;;) { - int option; - - option = getopt_long(argc, argv, "el:sSRp:m:h", options, NULL); - if (option == -1) - break; - - switch (option) { - case 'e': - if (udev_ctrl_send_exit(uctrl, timeout) < 0) - rc = 2; - else - rc = 0; - break; - case 'l': { - int i; - - i = util_log_priority(optarg); - if (i < 0) { - fprintf(stderr, "invalid number '%s'\n", optarg); - goto out; - } - if (udev_ctrl_send_set_log_level(uctrl, util_log_priority(optarg), timeout) < 0) - rc = 2; - else - rc = 0; - break; - } - case 's': - if (udev_ctrl_send_stop_exec_queue(uctrl, timeout) < 0) - rc = 2; - else - rc = 0; - break; - case 'S': - if (udev_ctrl_send_start_exec_queue(uctrl, timeout) < 0) - rc = 2; - else - rc = 0; - break; - case 'R': - if (udev_ctrl_send_reload(uctrl, timeout) < 0) - rc = 2; - else - rc = 0; - break; - case 'p': - if (strchr(optarg, '=') == NULL) { - fprintf(stderr, "expect <KEY>=<value> instead of '%s'\n", optarg); - goto out; - } - if (udev_ctrl_send_set_env(uctrl, optarg, timeout) < 0) - rc = 2; - else - rc = 0; - break; - case 'm': { - char *endp; - int i; - - i = strtoul(optarg, &endp, 0); - if (endp[0] != '\0' || i < 1) { - fprintf(stderr, "invalid number '%s'\n", optarg); - goto out; - } - if (udev_ctrl_send_set_children_max(uctrl, i, timeout) < 0) - rc = 2; - else - rc = 0; - break; - } - case 't': { - int seconds; - - seconds = atoi(optarg); - if (seconds >= 0) - timeout = seconds; - else - fprintf(stderr, "invalid timeout value\n"); - break; - } - case 'h': - print_help(); - rc = 0; - break; - } - } - - if (argv[optind] != NULL) - fprintf(stderr, "unknown option\n"); - else if (optind == 1) - fprintf(stderr, "missing option\n"); -out: - udev_ctrl_unref(uctrl); - return rc; -} - -const struct udevadm_cmd udevadm_control = { - .name = "control", - .cmd = adm_control, - .help = "control the udev daemon", -}; diff --git a/udev/udevadm-info.c b/udev/udevadm-info.c deleted file mode 100644 index f7e7e86b6a..0000000000 --- a/udev/udevadm-info.c +++ /dev/null @@ -1,568 +0,0 @@ -/* - * Copyright (C) 2004-2009 Kay Sievers <kay.sievers@vrfy.org> - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see <http://www.gnu.org/licenses/>. - */ - -#include <stdlib.h> -#include <string.h> -#include <stdio.h> -#include <stddef.h> -#include <ctype.h> -#include <stdarg.h> -#include <unistd.h> -#include <dirent.h> -#include <errno.h> -#include <getopt.h> -#include <fcntl.h> -#include <sys/stat.h> -#include <sys/types.h> - -#include "udev.h" - -static bool skip_attribute(const char *name) -{ - static const char const *skip[] = { - "uevent", - "dev", - "modalias", - "resource", - "driver", - "subsystem", - "module", - }; - unsigned int i; - - for (i = 0; i < ARRAY_SIZE(skip); i++) - if (strcmp(name, skip[i]) == 0) - return true; - return false; -} - -static void print_all_attributes(struct udev_device *device, const char *key) -{ - struct udev *udev = udev_device_get_udev(device); - struct udev_list_entry *sysattr; - - udev_list_entry_foreach(sysattr, udev_device_get_sysattr_list_entry(device)) { - const char *name; - const char *value; - size_t len; - - name = udev_list_entry_get_name(sysattr); - if (skip_attribute(name)) - continue; - - value = udev_device_get_sysattr_value(device, name); - if (value == NULL) - continue; - dbg(udev, "attr '%s'='%s'\n", name, value); - - /* skip any values that look like a path */ - if (value[0] == '/') - continue; - - /* skip nonprintable attributes */ - len = strlen(value); - while (len > 0 && isprint(value[len-1])) - len--; - if (len > 0) { - dbg(udev, "attribute value of '%s' non-printable, skip\n", name); - continue; - } - - printf(" %s{%s}==\"%s\"\n", key, name, value); - } - printf("\n"); -} - -static int print_device_chain(struct udev_device *device) -{ - struct udev_device *device_parent; - const char *str; - - printf("\n" - "Udevadm info starts with the device specified by the devpath and then\n" - "walks up the chain of parent devices. It prints for every device\n" - "found, all possible attributes in the udev rules key format.\n" - "A rule to match, can be composed by the attributes of the device\n" - "and the attributes from one single parent device.\n" - "\n"); - - printf(" looking at device '%s':\n", udev_device_get_devpath(device)); - printf(" KERNEL==\"%s\"\n", udev_device_get_sysname(device)); - str = udev_device_get_subsystem(device); - if (str == NULL) - str = ""; - printf(" SUBSYSTEM==\"%s\"\n", str); - str = udev_device_get_driver(device); - if (str == NULL) - str = ""; - printf(" DRIVER==\"%s\"\n", str); - print_all_attributes(device, "ATTR"); - - device_parent = device; - do { - device_parent = udev_device_get_parent(device_parent); - if (device_parent == NULL) - break; - printf(" looking at parent device '%s':\n", udev_device_get_devpath(device_parent)); - printf(" KERNELS==\"%s\"\n", udev_device_get_sysname(device_parent)); - str = udev_device_get_subsystem(device_parent); - if (str == NULL) - str = ""; - printf(" SUBSYSTEMS==\"%s\"\n", str); - str = udev_device_get_driver(device_parent); - if (str == NULL) - str = ""; - printf(" DRIVERS==\"%s\"\n", str); - print_all_attributes(device_parent, "ATTRS"); - } while (device_parent != NULL); - - return 0; -} - -static void print_record(struct udev_device *device) -{ - size_t len; - const char *str; - int i; - struct udev_list_entry *list_entry; - - printf("P: %s\n", udev_device_get_devpath(device)); - - len = strlen(udev_get_dev_path(udev_device_get_udev(device))); - str = udev_device_get_devnode(device); - if (str != NULL) - printf("N: %s\n", &str[len+1]); - - i = udev_device_get_devlink_priority(device); - if (i != 0) - printf("L: %i\n", i); - - udev_list_entry_foreach(list_entry, udev_device_get_devlinks_list_entry(device)) { - len = strlen(udev_get_dev_path(udev_device_get_udev(device))); - printf("S: %s\n", &udev_list_entry_get_name(list_entry)[len+1]); - } - - udev_list_entry_foreach(list_entry, udev_device_get_properties_list_entry(device)) - printf("E: %s=%s\n", - udev_list_entry_get_name(list_entry), - udev_list_entry_get_value(list_entry)); - printf("\n"); -} - -static int stat_device(const char *name, bool export, const char *prefix) -{ - struct stat statbuf; - - if (stat(name, &statbuf) != 0) - return -1; - - if (export) { - if (prefix == NULL) - prefix = "INFO_"; - printf("%sMAJOR=%d\n" - "%sMINOR=%d\n", - prefix, major(statbuf.st_dev), - prefix, minor(statbuf.st_dev)); - } else - printf("%d:%d\n", major(statbuf.st_dev), minor(statbuf.st_dev)); - return 0; -} - -static int export_devices(struct udev *udev) -{ - struct udev_enumerate *udev_enumerate; - struct udev_list_entry *list_entry; - - udev_enumerate = udev_enumerate_new(udev); - if (udev_enumerate == NULL) - return -1; - udev_enumerate_scan_devices(udev_enumerate); - udev_list_entry_foreach(list_entry, udev_enumerate_get_list_entry(udev_enumerate)) { - struct udev_device *device; - - device = udev_device_new_from_syspath(udev, udev_list_entry_get_name(list_entry)); - if (device != NULL) { - print_record(device); - udev_device_unref(device); - } - } - udev_enumerate_unref(udev_enumerate); - return 0; -} - -static void cleanup_dir(DIR *dir, mode_t mask, int depth) -{ - struct dirent *dent; - - if (depth <= 0) - return; - - for (dent = readdir(dir); dent != NULL; dent = readdir(dir)) { - struct stat stats; - - if (dent->d_name[0] == '.') - continue; - if (fstatat(dirfd(dir), dent->d_name, &stats, AT_SYMLINK_NOFOLLOW) != 0) - continue; - if ((stats.st_mode & mask) != 0) - continue; - if (S_ISDIR(stats.st_mode)) { - DIR *dir2; - - dir2 = fdopendir(openat(dirfd(dir), dent->d_name, O_RDONLY|O_NONBLOCK|O_DIRECTORY|O_CLOEXEC)); - if (dir2 != NULL) { - cleanup_dir(dir2, mask, depth-1); - closedir(dir2); - } - unlinkat(dirfd(dir), dent->d_name, AT_REMOVEDIR); - } else { - unlinkat(dirfd(dir), dent->d_name, 0); - } - } -} - -static void cleanup_db(struct udev *udev) -{ - char filename[UTIL_PATH_SIZE]; - DIR *dir; - - util_strscpyl(filename, sizeof(filename), udev_get_run_path(udev), "/queue.bin", NULL); - unlink(filename); - - util_strscpyl(filename, sizeof(filename), udev_get_run_path(udev), "/data", NULL); - dir = opendir(filename); - if (dir != NULL) { - cleanup_dir(dir, S_ISVTX, 1); - closedir(dir); - } - - util_strscpyl(filename, sizeof(filename), udev_get_run_path(udev), "/links", NULL); - dir = opendir(filename); - if (dir != NULL) { - cleanup_dir(dir, 0, 2); - closedir(dir); - } - - util_strscpyl(filename, sizeof(filename), udev_get_run_path(udev), "/tags", NULL); - dir = opendir(filename); - if (dir != NULL) { - cleanup_dir(dir, 0, 2); - closedir(dir); - } - - util_strscpyl(filename, sizeof(filename), udev_get_run_path(udev), "/watch", NULL); - dir = opendir(filename); - if (dir != NULL) { - cleanup_dir(dir, 0, 1); - closedir(dir); - } - - util_strscpyl(filename, sizeof(filename), udev_get_run_path(udev), "/firmware-missing", NULL); - dir = opendir(filename); - if (dir != NULL) { - cleanup_dir(dir, 0, 1); - closedir(dir); - } -} - -static int uinfo(struct udev *udev, int argc, char *argv[]) -{ - struct udev_device *device = NULL; - bool root = 0; - bool export = 0; - const char *export_prefix = NULL; - char path[UTIL_PATH_SIZE]; - char name[UTIL_PATH_SIZE]; - struct udev_list_entry *list_entry; - int rc = 0; - - static const struct option options[] = { - { "name", required_argument, NULL, 'n' }, - { "path", required_argument, NULL, 'p' }, - { "query", required_argument, NULL, 'q' }, - { "attribute-walk", no_argument, NULL, 'a' }, - { "cleanup-db", no_argument, NULL, 'c' }, - { "export-db", no_argument, NULL, 'e' }, - { "root", no_argument, NULL, 'r' }, - { "run", no_argument, NULL, 'R' }, - { "device-id-of-file", required_argument, NULL, 'd' }, - { "export", no_argument, NULL, 'x' }, - { "export-prefix", required_argument, NULL, 'P' }, - { "version", no_argument, NULL, 'V' }, - { "help", no_argument, NULL, 'h' }, - {} - }; - - enum action_type { - ACTION_NONE, - ACTION_QUERY, - ACTION_ATTRIBUTE_WALK, - ACTION_ROOT, - ACTION_DEVICE_ID_FILE, - } action = ACTION_NONE; - - enum query_type { - QUERY_NONE, - QUERY_NAME, - QUERY_PATH, - QUERY_SYMLINK, - QUERY_PROPERTY, - QUERY_ALL, - } query = QUERY_NONE; - - for (;;) { - int option; - struct stat statbuf; - - option = getopt_long(argc, argv, "aced:n:p:q:rxP:RVh", options, NULL); - if (option == -1) - break; - - dbg(udev, "option '%c'\n", option); - switch (option) { - case 'n': - if (device != NULL) { - fprintf(stderr, "device already specified\n"); - rc = 2; - goto exit; - } - /* remove /dev if given */ - if (strncmp(optarg, udev_get_dev_path(udev), strlen(udev_get_dev_path(udev))) != 0) - util_strscpyl(name, sizeof(name), udev_get_dev_path(udev), "/", optarg, NULL); - else - util_strscpy(name, sizeof(name), optarg); - util_remove_trailing_chars(name, '/'); - if (stat(name, &statbuf) < 0) { - fprintf(stderr, "device node not found\n"); - rc = 2; - goto exit; - } else { - char type; - - if (S_ISBLK(statbuf.st_mode)) { - type = 'b'; - } else if (S_ISCHR(statbuf.st_mode)) { - type = 'c'; - } else { - fprintf(stderr, "device node has wrong file type\n"); - rc = 2; - goto exit; - } - device = udev_device_new_from_devnum(udev, type, statbuf.st_rdev); - if (device == NULL) { - fprintf(stderr, "device node not found\n"); - rc = 2; - goto exit; - } - } - break; - case 'p': - if (device != NULL) { - fprintf(stderr, "device already specified\n"); - rc = 2; - goto exit; - } - /* add sys dir if needed */ - if (strncmp(optarg, udev_get_sys_path(udev), strlen(udev_get_sys_path(udev))) != 0) - util_strscpyl(path, sizeof(path), udev_get_sys_path(udev), optarg, NULL); - else - util_strscpy(path, sizeof(path), optarg); - util_remove_trailing_chars(path, '/'); - device = udev_device_new_from_syspath(udev, path); - if (device == NULL) { - fprintf(stderr, "device path not found\n"); - rc = 2; - goto exit; - } - break; - case 'q': - action = ACTION_QUERY; - if (strcmp(optarg, "property") == 0 || strcmp(optarg, "env") == 0) { - query = QUERY_PROPERTY; - } else if (strcmp(optarg, "name") == 0) { - query = QUERY_NAME; - } else if (strcmp(optarg, "symlink") == 0) { - query = QUERY_SYMLINK; - } else if (strcmp(optarg, "path") == 0) { - query = QUERY_PATH; - } else if (strcmp(optarg, "all") == 0) { - query = QUERY_ALL; - } else { - fprintf(stderr, "unknown query type\n"); - rc = 3; - goto exit; - } - break; - case 'r': - if (action == ACTION_NONE) - action = ACTION_ROOT; - root = true; - break; - case 'R': - printf("%s\n", udev_get_run_path(udev)); - goto exit; - case 'd': - action = ACTION_DEVICE_ID_FILE; - util_strscpy(name, sizeof(name), optarg); - break; - case 'a': - action = ACTION_ATTRIBUTE_WALK; - break; - case 'e': - export_devices(udev); - goto exit; - case 'c': - cleanup_db(udev); - goto exit; - case 'x': - export = true; - break; - case 'P': - export_prefix = optarg; - break; - case 'V': - printf("%s\n", VERSION); - goto exit; - case 'h': - printf("Usage: udevadm info OPTIONS\n" - " --query=<type> query device information:\n" - " name name of device node\n" - " symlink pointing to node\n" - " path sys device path\n" - " property the device properties\n" - " all all values\n" - " --path=<syspath> sys device path used for query or attribute walk\n" - " --name=<name> node or symlink name used for query or attribute walk\n" - " --root prepend dev directory to path names\n" - " --attribute-walk print all key matches while walking along the chain\n" - " of parent devices\n" - " --device-id-of-file=<file> print major:minor of device containing this file\n" - " --export export key/value pairs\n" - " --export-prefix export the key name with a prefix\n" - " --export-db export the content of the udev database\n" - " --cleanup-db cleanup the udev database\n" - " --help\n\n"); - goto exit; - default: - rc = 1; - goto exit; - } - } - - switch (action) { - case ACTION_QUERY: - if (device == NULL) { - fprintf(stderr, "query needs a valid device specified by --path= or --name=\n"); - rc = 4; - goto exit; - } - - switch(query) { - case QUERY_NAME: { - const char *node = udev_device_get_devnode(device); - - if (node == NULL) { - fprintf(stderr, "no device node found\n"); - rc = 5; - goto exit; - } - - if (root) { - printf("%s\n", udev_device_get_devnode(device)); - } else { - size_t len = strlen(udev_get_dev_path(udev)); - - printf("%s\n", &udev_device_get_devnode(device)[len+1]); - } - break; - } - case QUERY_SYMLINK: - list_entry = udev_device_get_devlinks_list_entry(device); - while (list_entry != NULL) { - if (root) { - printf("%s", udev_list_entry_get_name(list_entry)); - } else { - size_t len; - - len = strlen(udev_get_dev_path(udev_device_get_udev(device))); - printf("%s", &udev_list_entry_get_name(list_entry)[len+1]); - } - list_entry = udev_list_entry_get_next(list_entry); - if (list_entry != NULL) - printf(" "); - } - printf("\n"); - break; - case QUERY_PATH: - printf("%s\n", udev_device_get_devpath(device)); - goto exit; - case QUERY_PROPERTY: - list_entry = udev_device_get_properties_list_entry(device); - while (list_entry != NULL) { - if (export) { - const char *prefix = export_prefix; - - if (prefix == NULL) - prefix = ""; - printf("%s%s='%s'\n", prefix, - udev_list_entry_get_name(list_entry), - udev_list_entry_get_value(list_entry)); - } else { - printf("%s=%s\n", udev_list_entry_get_name(list_entry), udev_list_entry_get_value(list_entry)); - } - list_entry = udev_list_entry_get_next(list_entry); - } - break; - case QUERY_ALL: - print_record(device); - break; - default: - fprintf(stderr, "unknown query type\n"); - break; - } - break; - case ACTION_ATTRIBUTE_WALK: - if (device == NULL) { - fprintf(stderr, "query needs a valid device specified by --path= or --name=\n"); - rc = 4; - goto exit; - } - print_device_chain(device); - break; - case ACTION_DEVICE_ID_FILE: - if (stat_device(name, export, export_prefix) != 0) - rc = 1; - break; - case ACTION_ROOT: - printf("%s\n", udev_get_dev_path(udev)); - break; - default: - fprintf(stderr, "missing option\n"); - rc = 1; - break; - } - -exit: - udev_device_unref(device); - return rc; -} - -const struct udevadm_cmd udevadm_info = { - .name = "info", - .cmd = uinfo, - .help = "query sysfs or the udev database", -}; diff --git a/udev/udevadm-monitor.c b/udev/udevadm-monitor.c deleted file mode 100644 index 64913dbd55..0000000000 --- a/udev/udevadm-monitor.c +++ /dev/null @@ -1,297 +0,0 @@ -/* - * Copyright (C) 2004-2010 Kay Sievers <kay.sievers@vrfy.org> - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see <http://www.gnu.org/licenses/>. - */ - -#include <unistd.h> -#include <stdio.h> -#include <stdlib.h> -#include <stddef.h> -#include <string.h> -#include <fcntl.h> -#include <errno.h> -#include <signal.h> -#include <getopt.h> -#include <time.h> -#include <sys/time.h> -#include <sys/socket.h> -#include <sys/un.h> -#include <sys/epoll.h> -#include <linux/types.h> -#include <linux/netlink.h> - -#include "udev.h" - -static bool udev_exit; - -static void sig_handler(int signum) -{ - if (signum == SIGINT || signum == SIGTERM) - udev_exit = true; -} - -static void print_device(struct udev_device *device, const char *source, int prop) -{ - struct timespec ts; - - clock_gettime(CLOCK_MONOTONIC, &ts); - printf("%-6s[%llu.%06u] %-8s %s (%s)\n", - source, - (unsigned long long) ts.tv_sec, (unsigned int) ts.tv_nsec/1000, - udev_device_get_action(device), - udev_device_get_devpath(device), - udev_device_get_subsystem(device)); - if (prop) { - struct udev_list_entry *list_entry; - - udev_list_entry_foreach(list_entry, udev_device_get_properties_list_entry(device)) - printf("%s=%s\n", - udev_list_entry_get_name(list_entry), - udev_list_entry_get_value(list_entry)); - printf("\n"); - } -} - -static int adm_monitor(struct udev *udev, int argc, char *argv[]) -{ - struct sigaction act; - sigset_t mask; - int option; - bool prop = false; - bool print_kernel = false; - bool print_udev = false; - struct udev_list subsystem_match_list; - struct udev_list tag_match_list; - struct udev_monitor *udev_monitor = NULL; - struct udev_monitor *kernel_monitor = NULL; - int fd_ep = -1; - int fd_kernel = -1, fd_udev = -1; - struct epoll_event ep_kernel, ep_udev; - int rc = 0; - - static const struct option options[] = { - { "property", no_argument, NULL, 'p' }, - { "environment", no_argument, NULL, 'e' }, - { "kernel", no_argument, NULL, 'k' }, - { "udev", no_argument, NULL, 'u' }, - { "subsystem-match", required_argument, NULL, 's' }, - { "tag-match", required_argument, NULL, 't' }, - { "help", no_argument, NULL, 'h' }, - {} - }; - - udev_list_init(udev, &subsystem_match_list, true); - udev_list_init(udev, &tag_match_list, true); - - for (;;) { - option = getopt_long(argc, argv, "pekus:t:h", options, NULL); - if (option == -1) - break; - - switch (option) { - case 'p': - case 'e': - prop = true; - break; - case 'k': - print_kernel = true; - break; - case 'u': - print_udev = true; - break; - case 's': - { - char subsys[UTIL_NAME_SIZE]; - char *devtype; - - util_strscpy(subsys, sizeof(subsys), optarg); - devtype = strchr(subsys, '/'); - if (devtype != NULL) { - devtype[0] = '\0'; - devtype++; - } - udev_list_entry_add(&subsystem_match_list, subsys, devtype); - break; - } - case 't': - udev_list_entry_add(&tag_match_list, optarg, NULL); - break; - case 'h': - printf("Usage: udevadm monitor [--property] [--kernel] [--udev] [--help]\n" - " --property print the event properties\n" - " --kernel print kernel uevents\n" - " --udev print udev events\n" - " --subsystem-match=<subsystem[/devtype]> filter events by subsystem\n" - " --tag-match=<tag> filter events by tag\n" - " --help\n\n"); - goto out; - default: - rc = 1; - goto out; - } - } - - if (!print_kernel && !print_udev) { - print_kernel = true; - print_udev = true; - } - - /* set signal handlers */ - memset(&act, 0x00, sizeof(struct sigaction)); - act.sa_handler = sig_handler; - sigemptyset(&act.sa_mask); - act.sa_flags = SA_RESTART; - sigaction(SIGINT, &act, NULL); - sigaction(SIGTERM, &act, NULL); - sigemptyset(&mask); - sigaddset(&mask, SIGINT); - sigaddset(&mask, SIGTERM); - sigprocmask(SIG_UNBLOCK, &mask, NULL); - - fd_ep = epoll_create1(EPOLL_CLOEXEC); - if (fd_ep < 0) { - err(udev, "error creating epoll fd: %m\n"); - goto out; - } - - printf("monitor will print the received events for:\n"); - if (print_udev) { - struct udev_list_entry *entry; - - udev_monitor = udev_monitor_new_from_netlink(udev, "udev"); - if (udev_monitor == NULL) { - fprintf(stderr, "error: unable to create netlink socket\n"); - rc = 1; - goto out; - } - udev_monitor_set_receive_buffer_size(udev_monitor, 128*1024*1024); - fd_udev = udev_monitor_get_fd(udev_monitor); - - udev_list_entry_foreach(entry, udev_list_get_entry(&subsystem_match_list)) { - const char *subsys = udev_list_entry_get_name(entry); - const char *devtype = udev_list_entry_get_value(entry); - - if (udev_monitor_filter_add_match_subsystem_devtype(udev_monitor, subsys, devtype) < 0) - fprintf(stderr, "error: unable to apply subsystem filter '%s'\n", subsys); - } - - udev_list_entry_foreach(entry, udev_list_get_entry(&tag_match_list)) { - const char *tag = udev_list_entry_get_name(entry); - - if (udev_monitor_filter_add_match_tag(udev_monitor, tag) < 0) - fprintf(stderr, "error: unable to apply tag filter '%s'\n", tag); - } - - if (udev_monitor_enable_receiving(udev_monitor) < 0) { - fprintf(stderr, "error: unable to subscribe to udev events\n"); - rc = 2; - goto out; - } - - memset(&ep_udev, 0, sizeof(struct epoll_event)); - ep_udev.events = EPOLLIN; - ep_udev.data.fd = fd_udev; - if (epoll_ctl(fd_ep, EPOLL_CTL_ADD, fd_udev, &ep_udev) < 0) { - err(udev, "fail to add fd to epoll: %m\n"); - goto out; - } - - printf("UDEV - the event which udev sends out after rule processing\n"); - } - - if (print_kernel) { - struct udev_list_entry *entry; - - kernel_monitor = udev_monitor_new_from_netlink(udev, "kernel"); - if (kernel_monitor == NULL) { - fprintf(stderr, "error: unable to create netlink socket\n"); - rc = 3; - goto out; - } - udev_monitor_set_receive_buffer_size(kernel_monitor, 128*1024*1024); - fd_kernel = udev_monitor_get_fd(kernel_monitor); - - udev_list_entry_foreach(entry, udev_list_get_entry(&subsystem_match_list)) { - const char *subsys = udev_list_entry_get_name(entry); - - if (udev_monitor_filter_add_match_subsystem_devtype(kernel_monitor, subsys, NULL) < 0) - fprintf(stderr, "error: unable to apply subsystem filter '%s'\n", subsys); - } - - if (udev_monitor_enable_receiving(kernel_monitor) < 0) { - fprintf(stderr, "error: unable to subscribe to kernel events\n"); - rc = 4; - goto out; - } - - memset(&ep_kernel, 0, sizeof(struct epoll_event)); - ep_kernel.events = EPOLLIN; - ep_kernel.data.fd = fd_kernel; - if (epoll_ctl(fd_ep, EPOLL_CTL_ADD, fd_kernel, &ep_kernel) < 0) { - err(udev, "fail to add fd to epoll: %m\n"); - goto out; - } - - printf("KERNEL - the kernel uevent\n"); - } - printf("\n"); - - while (!udev_exit) { - int fdcount; - struct epoll_event ev[4]; - int i; - - fdcount = epoll_wait(fd_ep, ev, ARRAY_SIZE(ev), -1); - if (fdcount < 0) { - if (errno != EINTR) - fprintf(stderr, "error receiving uevent message: %m\n"); - continue; - } - - for (i = 0; i < fdcount; i++) { - if (ev[i].data.fd == fd_kernel && ev[i].events & EPOLLIN) { - struct udev_device *device; - - device = udev_monitor_receive_device(kernel_monitor); - if (device == NULL) - continue; - print_device(device, "KERNEL", prop); - udev_device_unref(device); - } else if (ev[i].data.fd == fd_udev && ev[i].events & EPOLLIN) { - struct udev_device *device; - - device = udev_monitor_receive_device(udev_monitor); - if (device == NULL) - continue; - print_device(device, "UDEV", prop); - udev_device_unref(device); - } - } - } -out: - if (fd_ep >= 0) - close(fd_ep); - udev_monitor_unref(udev_monitor); - udev_monitor_unref(kernel_monitor); - udev_list_cleanup(&subsystem_match_list); - udev_list_cleanup(&tag_match_list); - return rc; -} - -const struct udevadm_cmd udevadm_monitor = { - .name = "monitor", - .cmd = adm_monitor, - .help = "listen to kernel and udev events", -}; diff --git a/udev/udevadm-settle.c b/udev/udevadm-settle.c deleted file mode 100644 index a59d7c39e5..0000000000 --- a/udev/udevadm-settle.c +++ /dev/null @@ -1,235 +0,0 @@ -/* - * Copyright (C) 2006-2009 Kay Sievers <kay@vrfy.org> - * Copyright (C) 2009 Canonical Ltd. - * Copyright (C) 2009 Scott James Remnant <scott@netsplit.com> - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see <http://www.gnu.org/licenses/>. - */ - -#include <stdlib.h> -#include <stddef.h> -#include <string.h> -#include <stdio.h> -#include <unistd.h> -#include <errno.h> -#include <dirent.h> -#include <fcntl.h> -#include <syslog.h> -#include <getopt.h> -#include <signal.h> -#include <time.h> -#include <sys/inotify.h> -#include <sys/poll.h> -#include <sys/stat.h> -#include <sys/types.h> - -#include "udev.h" - -static int adm_settle(struct udev *udev, int argc, char *argv[]) -{ - static const struct option options[] = { - { "seq-start", required_argument, NULL, 's' }, - { "seq-end", required_argument, NULL, 'e' }, - { "timeout", required_argument, NULL, 't' }, - { "exit-if-exists", required_argument, NULL, 'E' }, - { "quiet", no_argument, NULL, 'q' }, - { "help", no_argument, NULL, 'h' }, - {} - }; - unsigned long long start_usec = now_usec(); - unsigned long long start = 0; - unsigned long long end = 0; - int quiet = 0; - const char *exists = NULL; - unsigned int timeout = 120; - struct pollfd pfd[1]; - struct udev_queue *udev_queue = NULL; - int rc = EXIT_FAILURE; - - dbg(udev, "version %s\n", VERSION); - - for (;;) { - int option; - int seconds; - - option = getopt_long(argc, argv, "s:e:t:E:qh", options, NULL); - if (option == -1) - break; - - switch (option) { - case 's': - start = strtoull(optarg, NULL, 0); - break; - case 'e': - end = strtoull(optarg, NULL, 0); - break; - case 't': - seconds = atoi(optarg); - if (seconds >= 0) - timeout = seconds; - else - fprintf(stderr, "invalid timeout value\n"); - dbg(udev, "timeout=%i\n", timeout); - break; - case 'q': - quiet = 1; - break; - case 'E': - exists = optarg; - break; - case 'h': - printf("Usage: udevadm settle OPTIONS\n" - " --timeout=<seconds> maximum time to wait for events\n" - " --seq-start=<seqnum> first seqnum to wait for\n" - " --seq-end=<seqnum> last seqnum to wait for\n" - " --exit-if-exists=<file> stop waiting if file exists\n" - " --quiet do not print list after timeout\n" - " --help\n\n"); - exit(EXIT_SUCCESS); - default: - exit(EXIT_FAILURE); - } - } - - udev_queue = udev_queue_new(udev); - if (udev_queue == NULL) - exit(2); - - if (start > 0) { - unsigned long long kernel_seq; - - kernel_seq = udev_queue_get_kernel_seqnum(udev_queue); - - /* unless specified, the last event is the current kernel seqnum */ - if (end == 0) - end = udev_queue_get_kernel_seqnum(udev_queue); - - if (start > end) { - err(udev, "seq-start larger than seq-end, ignoring\n"); - start = 0; - end = 0; - } - - if (start > kernel_seq || end > kernel_seq) { - err(udev, "seq-start or seq-end larger than current kernel value, ignoring\n"); - start = 0; - end = 0; - } - info(udev, "start=%llu end=%llu current=%llu\n", start, end, kernel_seq); - } else { - if (end > 0) { - err(udev, "seq-end needs seq-start parameter, ignoring\n"); - end = 0; - } - } - - /* guarantee that the udev daemon isn't pre-processing */ - if (getuid() == 0) { - struct udev_ctrl *uctrl; - - uctrl = udev_ctrl_new(udev); - if (uctrl != NULL) { - if (udev_ctrl_send_ping(uctrl, timeout) < 0) { - info(udev, "no connection to daemon\n"); - udev_ctrl_unref(uctrl); - rc = EXIT_SUCCESS; - goto out; - } - udev_ctrl_unref(uctrl); - } - } - - pfd[0].events = POLLIN; - pfd[0].fd = inotify_init1(IN_CLOEXEC); - if (pfd[0].fd < 0) { - err(udev, "inotify_init failed: %m\n"); - } else { - if (inotify_add_watch(pfd[0].fd, udev_get_run_path(udev), IN_MOVED_TO) < 0) { - err(udev, "watching '%s' failed\n", udev_get_run_path(udev)); - close(pfd[0].fd); - pfd[0].fd = -1; - } - } - - for (;;) { - struct stat statbuf; - - if (exists != NULL && stat(exists, &statbuf) == 0) { - rc = EXIT_SUCCESS; - break; - } - - if (start > 0) { - /* if asked for, wait for a specific sequence of events */ - if (udev_queue_get_seqnum_sequence_is_finished(udev_queue, start, end) == 1) { - rc = EXIT_SUCCESS; - break; - } - } else { - /* exit if queue is empty */ - if (udev_queue_get_queue_is_empty(udev_queue)) { - rc = EXIT_SUCCESS; - break; - } - } - - if (pfd[0].fd >= 0) { - int delay; - - if (exists != NULL || start > 0) - delay = 100; - else - delay = 1000; - /* wake up after delay, or immediately after the queue is rebuilt */ - if (poll(pfd, 1, delay) > 0 && pfd[0].revents & POLLIN) { - char buf[sizeof(struct inotify_event) + PATH_MAX]; - - read(pfd[0].fd, buf, sizeof(buf)); - } - } else { - sleep(1); - } - - if (timeout > 0) { - unsigned long long age_usec; - - age_usec = now_usec() - start_usec; - if (age_usec / (1000 * 1000) >= timeout) { - struct udev_list_entry *list_entry; - - if (!quiet && udev_queue_get_queued_list_entry(udev_queue) != NULL) { - info(udev, "timeout waiting for udev queue\n"); - printf("\nudevadm settle - timeout of %i seconds reached, the event queue contains:\n", timeout); - udev_list_entry_foreach(list_entry, udev_queue_get_queued_list_entry(udev_queue)) - printf(" %s (%s)\n", - udev_list_entry_get_name(list_entry), - udev_list_entry_get_value(list_entry)); - } - - break; - } - } - } -out: - if (pfd[0].fd >= 0) - close(pfd[0].fd); - udev_queue_unref(udev_queue); - return rc; -} - -const struct udevadm_cmd udevadm_settle = { - .name = "settle", - .cmd = adm_settle, - .help = "wait for the event queue to finish", -}; diff --git a/udev/udevadm-test-builtin.c b/udev/udevadm-test-builtin.c deleted file mode 100644 index 253fcd0c8f..0000000000 --- a/udev/udevadm-test-builtin.c +++ /dev/null @@ -1,128 +0,0 @@ -/* - * Copyright (C) 2011 Kay Sievers <kay.sievers@vrfy.org> - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see <http://www.gnu.org/licenses/>. - */ - -#include <stdlib.h> -#include <stddef.h> -#include <string.h> -#include <stdio.h> -#include <unistd.h> -#include <errno.h> -#include <dirent.h> -#include <fcntl.h> -#include <syslog.h> -#include <getopt.h> -#include <signal.h> -#include <time.h> -#include <sys/inotify.h> -#include <sys/poll.h> -#include <sys/stat.h> -#include <sys/types.h> - -#include "udev.h" - -static void help(struct udev *udev) -{ - fprintf(stderr, "\n"); - fprintf(stderr, "Usage: udevadm builtin [--help] <command> <syspath>\n"); - udev_builtin_list(udev); - fprintf(stderr, "\n"); -} - -static int adm_builtin(struct udev *udev, int argc, char *argv[]) -{ - static const struct option options[] = { - { "help", no_argument, NULL, 'h' }, - {} - }; - char *command = NULL; - char *syspath = NULL; - char filename[UTIL_PATH_SIZE]; - struct udev_device *dev = NULL; - enum udev_builtin_cmd cmd; - int rc = EXIT_SUCCESS; - - dbg(udev, "version %s\n", VERSION); - - for (;;) { - int option; - - option = getopt_long(argc, argv, "h", options, NULL); - if (option == -1) - break; - - switch (option) { - case 'h': - help(udev); - goto out; - } - } - - command = argv[optind++]; - if (command == NULL) { - fprintf(stderr, "command missing\n"); - help(udev); - rc = 2; - goto out; - } - - syspath = argv[optind++]; - if (syspath == NULL) { - fprintf(stderr, "syspath missing\n\n"); - rc = 3; - goto out; - } - - udev_builtin_init(udev); - - cmd = udev_builtin_lookup(command); - if (cmd >= UDEV_BUILTIN_MAX) { - fprintf(stderr, "unknown command '%s'\n", command); - help(udev); - rc = 5; - goto out; - } - - /* add /sys if needed */ - if (strncmp(syspath, udev_get_sys_path(udev), strlen(udev_get_sys_path(udev))) != 0) - util_strscpyl(filename, sizeof(filename), udev_get_sys_path(udev), syspath, NULL); - else - util_strscpy(filename, sizeof(filename), syspath); - util_remove_trailing_chars(filename, '/'); - - dev = udev_device_new_from_syspath(udev, filename); - if (dev == NULL) { - fprintf(stderr, "unable to open device '%s'\n\n", filename); - rc = 4; - goto out; - } - - if (udev_builtin_run(dev, cmd, command, true) < 0) { - fprintf(stderr, "error executing '%s'\n\n", command); - rc = 6; - } -out: - udev_device_unref(dev); - udev_builtin_exit(udev); - return rc; -} - -const struct udevadm_cmd udevadm_test_builtin = { - .name = "test-builtin", - .cmd = adm_builtin, - .help = "test a built-in command", - .debug = true, -}; diff --git a/udev/udevadm-test.c b/udev/udevadm-test.c deleted file mode 100644 index 851500527f..0000000000 --- a/udev/udevadm-test.c +++ /dev/null @@ -1,173 +0,0 @@ -/* - * Copyright (C) 2003-2004 Greg Kroah-Hartman <greg@kroah.com> - * Copyright (C) 2004-2008 Kay Sievers <kay.sievers@vrfy.org> - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see <http://www.gnu.org/licenses/>. - */ - -#include <stdlib.h> -#include <string.h> -#include <stdio.h> -#include <stddef.h> -#include <unistd.h> -#include <errno.h> -#include <ctype.h> -#include <fcntl.h> -#include <signal.h> -#include <syslog.h> -#include <getopt.h> -#include <sys/signalfd.h> - -#include "udev.h" - -static int adm_test(struct udev *udev, int argc, char *argv[]) -{ - int resolve_names = 1; - char filename[UTIL_PATH_SIZE]; - const char *action = "add"; - const char *syspath = NULL; - struct udev_event *event = NULL; - struct udev_device *dev = NULL; - struct udev_rules *rules = NULL; - struct udev_list_entry *entry; - sigset_t mask, sigmask_orig; - int err; - int rc = 0; - - static const struct option options[] = { - { "action", required_argument, NULL, 'a' }, - { "resolve-names", required_argument, NULL, 'N' }, - { "help", no_argument, NULL, 'h' }, - {} - }; - - info(udev, "version %s\n", VERSION); - - for (;;) { - int option; - - option = getopt_long(argc, argv, "a:s:N:fh", options, NULL); - if (option == -1) - break; - - dbg(udev, "option '%c'\n", option); - switch (option) { - case 'a': - action = optarg; - break; - case 'N': - if (strcmp (optarg, "early") == 0) { - resolve_names = 1; - } else if (strcmp (optarg, "late") == 0) { - resolve_names = 0; - } else if (strcmp (optarg, "never") == 0) { - resolve_names = -1; - } else { - fprintf(stderr, "resolve-names must be early, late or never\n"); - err(udev, "resolve-names must be early, late or never\n"); - exit(EXIT_FAILURE); - } - break; - case 'h': - printf("Usage: udevadm test OPTIONS <syspath>\n" - " --action=<string> set action string\n" - " --help\n\n"); - exit(EXIT_SUCCESS); - default: - exit(EXIT_FAILURE); - } - } - syspath = argv[optind]; - - if (syspath == NULL) { - fprintf(stderr, "syspath parameter missing\n"); - rc = 2; - goto out; - } - - printf("This program is for debugging only, it does not run any program,\n" - "specified by a RUN key. It may show incorrect results, because\n" - "some values may be different, or not available at a simulation run.\n" - "\n"); - - sigprocmask(SIG_SETMASK, NULL, &sigmask_orig); - - udev_builtin_init(udev); - - rules = udev_rules_new(udev, resolve_names); - if (rules == NULL) { - fprintf(stderr, "error reading rules\n"); - rc = 3; - goto out; - } - - /* add /sys if needed */ - if (strncmp(syspath, udev_get_sys_path(udev), strlen(udev_get_sys_path(udev))) != 0) - util_strscpyl(filename, sizeof(filename), udev_get_sys_path(udev), syspath, NULL); - else - util_strscpy(filename, sizeof(filename), syspath); - util_remove_trailing_chars(filename, '/'); - - dev = udev_device_new_from_syspath(udev, filename); - if (dev == NULL) { - fprintf(stderr, "unable to open device '%s'\n", filename); - rc = 4; - goto out; - } - - /* skip reading of db, but read kernel parameters */ - udev_device_set_info_loaded(dev); - udev_device_read_uevent_file(dev); - - udev_device_set_action(dev, action); - event = udev_event_new(dev); - - sigfillset(&mask); - sigprocmask(SIG_SETMASK, &mask, &sigmask_orig); - event->fd_signal = signalfd(-1, &mask, SFD_NONBLOCK|SFD_CLOEXEC); - if (event->fd_signal < 0) { - fprintf(stderr, "error creating signalfd\n"); - rc = 5; - goto out; - } - - err = udev_event_execute_rules(event, rules, &sigmask_orig); - - udev_list_entry_foreach(entry, udev_device_get_properties_list_entry(dev)) - printf("%s=%s\n", udev_list_entry_get_name(entry), udev_list_entry_get_value(entry)); - - if (err == 0) { - udev_list_entry_foreach(entry, udev_list_get_entry(&event->run_list)) { - char program[UTIL_PATH_SIZE]; - - udev_event_apply_format(event, udev_list_entry_get_name(entry), program, sizeof(program)); - printf("run: '%s'\n", program); - } - } -out: - if (event != NULL && event->fd_signal >= 0) - close(event->fd_signal); - udev_event_unref(event); - udev_device_unref(dev); - udev_rules_unref(rules); - udev_builtin_exit(udev); - return rc; -} - -const struct udevadm_cmd udevadm_test = { - .name = "test", - .cmd = adm_test, - .help = "test an event run", - .debug = true, -}; diff --git a/udev/udevadm-trigger.c b/udev/udevadm-trigger.c deleted file mode 100644 index 2cee2297d4..0000000000 --- a/udev/udevadm-trigger.c +++ /dev/null @@ -1,232 +0,0 @@ -/* - * Copyright (C) 2008-2009 Kay Sievers <kay.sievers@vrfy.org> - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see <http://www.gnu.org/licenses/>. - */ - -#include <stdlib.h> -#include <stddef.h> -#include <string.h> -#include <stdio.h> -#include <unistd.h> -#include <getopt.h> -#include <errno.h> -#include <dirent.h> -#include <fcntl.h> -#include <syslog.h> -#include <fnmatch.h> -#include <sys/stat.h> -#include <sys/types.h> -#include <sys/socket.h> -#include <sys/un.h> - -#include "udev.h" - -static int verbose; -static int dry_run; - -static void exec_list(struct udev_enumerate *udev_enumerate, const char *action) -{ - struct udev *udev = udev_enumerate_get_udev(udev_enumerate); - struct udev_list_entry *entry; - - udev_list_entry_foreach(entry, udev_enumerate_get_list_entry(udev_enumerate)) { - char filename[UTIL_PATH_SIZE]; - int fd; - - if (verbose) - printf("%s\n", udev_list_entry_get_name(entry)); - if (dry_run) - continue; - util_strscpyl(filename, sizeof(filename), udev_list_entry_get_name(entry), "/uevent", NULL); - fd = open(filename, O_WRONLY); - if (fd < 0) { - dbg(udev, "error on opening %s: %m\n", filename); - continue; - } - if (write(fd, action, strlen(action)) < 0) - info(udev, "error writing '%s' to '%s': %m\n", action, filename); - close(fd); - } -} - -static const char *keyval(const char *str, const char **val, char *buf, size_t size) -{ - char *pos; - - util_strscpy(buf, size,str); - pos = strchr(buf, '='); - if (pos != NULL) { - pos[0] = 0; - pos++; - } - *val = pos; - return buf; -} - -static int adm_trigger(struct udev *udev, int argc, char *argv[]) -{ - static const struct option options[] = { - { "verbose", no_argument, NULL, 'v' }, - { "dry-run", no_argument, NULL, 'n' }, - { "type", required_argument, NULL, 't' }, - { "action", required_argument, NULL, 'c' }, - { "subsystem-match", required_argument, NULL, 's' }, - { "subsystem-nomatch", required_argument, NULL, 'S' }, - { "attr-match", required_argument, NULL, 'a' }, - { "attr-nomatch", required_argument, NULL, 'A' }, - { "property-match", required_argument, NULL, 'p' }, - { "tag-match", required_argument, NULL, 'g' }, - { "sysname-match", required_argument, NULL, 'y' }, - { "parent-match", required_argument, NULL, 'b' }, - { "help", no_argument, NULL, 'h' }, - {} - }; - enum { - TYPE_DEVICES, - TYPE_SUBSYSTEMS, - } device_type = TYPE_DEVICES; - const char *action = "change"; - struct udev_enumerate *udev_enumerate; - int rc = 0; - - dbg(udev, "version %s\n", VERSION); - udev_enumerate = udev_enumerate_new(udev); - if (udev_enumerate == NULL) { - rc = 1; - goto exit; - } - - for (;;) { - int option; - const char *key; - const char *val; - char buf[UTIL_PATH_SIZE]; - - option = getopt_long(argc, argv, "vng:o:t:hc:p:s:S:a:A:y:b:", options, NULL); - if (option == -1) - break; - - switch (option) { - case 'v': - verbose = 1; - break; - case 'n': - dry_run = 1; - break; - case 't': - if (strcmp(optarg, "devices") == 0) { - device_type = TYPE_DEVICES; - } else if (strcmp(optarg, "subsystems") == 0) { - device_type = TYPE_SUBSYSTEMS; - } else { - err(udev, "unknown type --type=%s\n", optarg); - rc = 2; - goto exit; - } - break; - case 'c': - action = optarg; - break; - case 's': - udev_enumerate_add_match_subsystem(udev_enumerate, optarg); - break; - case 'S': - udev_enumerate_add_nomatch_subsystem(udev_enumerate, optarg); - break; - case 'a': - key = keyval(optarg, &val, buf, sizeof(buf)); - udev_enumerate_add_match_sysattr(udev_enumerate, key, val); - break; - case 'A': - key = keyval(optarg, &val, buf, sizeof(buf)); - udev_enumerate_add_nomatch_sysattr(udev_enumerate, key, val); - break; - case 'p': - key = keyval(optarg, &val, buf, sizeof(buf)); - udev_enumerate_add_match_property(udev_enumerate, key, val); - break; - case 'g': - udev_enumerate_add_match_tag(udev_enumerate, optarg); - break; - case 'y': - udev_enumerate_add_match_sysname(udev_enumerate, optarg); - break; - case 'b': { - char path[UTIL_PATH_SIZE]; - struct udev_device *dev; - - /* add sys dir if needed */ - if (strncmp(optarg, udev_get_sys_path(udev), strlen(udev_get_sys_path(udev))) != 0) - util_strscpyl(path, sizeof(path), udev_get_sys_path(udev), optarg, NULL); - else - util_strscpy(path, sizeof(path), optarg); - util_remove_trailing_chars(path, '/'); - dev = udev_device_new_from_syspath(udev, path); - if (dev == NULL) { - err(udev, "unable to open the device '%s'\n", optarg); - rc = 2; - goto exit; - } - udev_enumerate_add_match_parent(udev_enumerate, dev); - /* drop reference immediately, enumerate pins the device as long as needed */ - udev_device_unref(dev); - break; - } - case 'h': - printf("Usage: udevadm trigger OPTIONS\n" - " --verbose print the list of devices while running\n" - " --dry-run do not actually trigger the events\n" - " --type= type of events to trigger\n" - " devices sys devices (default)\n" - " subsystems sys subsystems and drivers\n" - " --action=<action> event action value, default is \"change\"\n" - " --subsystem-match=<subsystem> trigger devices from a matching subsystem\n" - " --subsystem-nomatch=<subsystem> exclude devices from a matching subsystem\n" - " --attr-match=<file[=<value>]> trigger devices with a matching attribute\n" - " --attr-nomatch=<file[=<value>]> exclude devices with a matching attribute\n" - " --property-match=<key>=<value> trigger devices with a matching property\n" - " --tag-match=<key>=<value> trigger devices with a matching property\n" - " --sysname-match=<name> trigger devices with a matching name\n" - " --parent-match=<name> trigger devices with that parent device\n" - " --help\n\n"); - goto exit; - default: - rc = 1; - goto exit; - } - } - - switch (device_type) { - case TYPE_SUBSYSTEMS: - udev_enumerate_scan_subsystems(udev_enumerate); - exec_list(udev_enumerate, action); - goto exit; - case TYPE_DEVICES: - udev_enumerate_scan_devices(udev_enumerate); - exec_list(udev_enumerate, action); - goto exit; - default: - goto exit; - } -exit: - udev_enumerate_unref(udev_enumerate); - return rc; -} - -const struct udevadm_cmd udevadm_trigger = { - .name = "trigger", - .cmd = adm_trigger, - .help = "request events from the kernel", -}; diff --git a/udev/udevadm.c b/udev/udevadm.c deleted file mode 100644 index 5410f00c02..0000000000 --- a/udev/udevadm.c +++ /dev/null @@ -1,165 +0,0 @@ -/* - * Copyright (C) 2007-2009 Kay Sievers <kay.sievers@vrfy.org> - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see <http://www.gnu.org/licenses/>. - */ - -#include <unistd.h> -#include <stdio.h> -#include <stdlib.h> -#include <stddef.h> -#include <string.h> -#include <errno.h> -#include <getopt.h> - -#include "udev.h" - -static bool debug; - -void udev_main_log(struct udev *udev, int priority, - const char *file, int line, const char *fn, - const char *format, va_list args) -{ - if (debug) { - fprintf(stderr, "%s: ", fn); - vfprintf(stderr, format, args); - } else { - va_list args2; - - va_copy(args2, args); - vfprintf(stderr, format, args2); - va_end(args2); - vsyslog(priority, format, args); - } -} - -static int adm_version(struct udev *udev, int argc, char *argv[]) -{ - printf("%s\n", VERSION); - return 0; -} -static const struct udevadm_cmd udevadm_version = { - .name = "version", - .cmd = adm_version, -}; - -static int adm_help(struct udev *udev, int argc, char *argv[]); -static const struct udevadm_cmd udevadm_help = { - .name = "help", - .cmd = adm_help, -}; - -static const struct udevadm_cmd *udevadm_cmds[] = { - &udevadm_info, - &udevadm_trigger, - &udevadm_settle, - &udevadm_control, - &udevadm_monitor, - &udevadm_test, - &udevadm_test_builtin, - &udevadm_version, - &udevadm_help, -}; - -static int adm_help(struct udev *udev, int argc, char *argv[]) -{ - unsigned int i; - - fprintf(stderr, "Usage: udevadm [--help] [--version] [--debug] COMMAND [COMMAND OPTIONS]\n"); - for (i = 0; i < ARRAY_SIZE(udevadm_cmds); i++) - if (udevadm_cmds[i]->help != NULL) - printf(" %-12s %s\n", udevadm_cmds[i]->name, udevadm_cmds[i]->help); - fprintf(stderr, "\n"); - return 0; -} - -static int run_command(struct udev *udev, const struct udevadm_cmd *cmd, int argc, char *argv[]) -{ - if (cmd->debug) { - debug = true; - if (udev_get_log_priority(udev) < LOG_INFO) - udev_set_log_priority(udev, LOG_INFO); - } - info(udev, "calling: %s\n", cmd->name); - return cmd->cmd(udev, argc, argv); -} - -int main(int argc, char *argv[]) -{ - struct udev *udev; - static const struct option options[] = { - { "debug", no_argument, NULL, 'd' }, - { "help", no_argument, NULL, 'h' }, - { "version", no_argument, NULL, 'V' }, - {} - }; - const char *command; - unsigned int i; - int rc = 1; - - udev = udev_new(); - if (udev == NULL) - goto out; - - udev_log_init("udevadm"); - udev_set_log_fn(udev, udev_main_log); - udev_selinux_init(udev); - - for (;;) { - int option; - - option = getopt_long(argc, argv, "+dhV", options, NULL); - if (option == -1) - break; - - switch (option) { - case 'd': - debug = true; - if (udev_get_log_priority(udev) < LOG_INFO) - udev_set_log_priority(udev, LOG_INFO); - break; - case 'h': - rc = adm_help(udev, argc, argv); - goto out; - case 'V': - rc = adm_version(udev, argc, argv); - goto out; - default: - goto out; - } - } - command = argv[optind]; - - info(udev, "runtime dir '%s'\n", udev_get_run_path(udev)); - - if (command != NULL) - for (i = 0; i < ARRAY_SIZE(udevadm_cmds); i++) { - if (strcmp(udevadm_cmds[i]->name, command) == 0) { - argc -= optind; - argv += optind; - optind = 0; - rc = run_command(udev, udevadm_cmds[i], argc, argv); - goto out; - } - } - - fprintf(stderr, "missing or unknown command\n\n"); - adm_help(udev, argc, argv); - rc = 2; -out: - udev_selinux_exit(udev); - udev_unref(udev); - udev_log_close(); - return rc; -} diff --git a/udev/udevadm.xml b/udev/udevadm.xml deleted file mode 100644 index 455ce80ca9..0000000000 --- a/udev/udevadm.xml +++ /dev/null @@ -1,472 +0,0 @@ -<?xml version='1.0'?> -<?xml-stylesheet type="text/xsl" href="http://docbook.sourceforge.net/release/xsl/current/xhtml/docbook.xsl"?> -<!DOCTYPE refentry PUBLIC "-//OASIS//DTD DocBook XML V4.2//EN" - "http://www.oasis-open.org/docbook/xml/4.2/docbookx.dtd"> - -<refentry id="udevadm"> - <refentryinfo> - <title>udevadm</title> - <productname>udev</productname> - </refentryinfo> - - <refmeta> - <refentrytitle>udevadm</refentrytitle> - <manvolnum>8</manvolnum> - <refmiscinfo class="version"></refmiscinfo> - </refmeta> - - <refnamediv> - <refname>udevadm</refname><refpurpose>udev management tool</refpurpose> - </refnamediv> - - <refsynopsisdiv> - <cmdsynopsis> - <command>udevadm</command> - <arg><option>--debug</option></arg> - <arg><option>--version</option></arg> - <arg><option>--help</option></arg> - </cmdsynopsis> - <cmdsynopsis> - <command>udevadm info <replaceable>options</replaceable></command> - </cmdsynopsis> - <cmdsynopsis> - <command>udevadm trigger <optional>options</optional></command> - </cmdsynopsis> - <cmdsynopsis> - <command>udevadm settle <optional>options</optional></command> - </cmdsynopsis> - <cmdsynopsis> - <command>udevadm control <replaceable>command</replaceable></command> - </cmdsynopsis> - <cmdsynopsis> - <command>udevadm monitor <optional>options</optional></command> - </cmdsynopsis> - <cmdsynopsis> - <command>udevadm test <optional>options</optional> <replaceable>devpath</replaceable></command> - </cmdsynopsis> - <cmdsynopsis> - <command>udevadm test-builtin <optional>options</optional> <replaceable>command</replaceable> <replaceable>devpath</replaceable></command> - </cmdsynopsis> - </refsynopsisdiv> - - <refsect1><title>Description</title> - <para>udevadm expects a command and command specific options. It - controls the runtime behavior of udev, requests kernel events, - manages the event queue, and provides simple debugging mechanisms.</para> - </refsect1> - - <refsect1><title>OPTIONS</title> - <variablelist> - <varlistentry> - <term><option>--debug</option></term> - <listitem> - <para>Print debug messages to stderr.</para> - </listitem> - </varlistentry> - <varlistentry> - <term><option>--version</option></term> - <listitem> - <para>Print version number.</para> - </listitem> - </varlistentry> - <varlistentry> - <term><option>--help</option></term> - <listitem> - <para>Print help text.</para> - </listitem> - </varlistentry> - </variablelist> - - <refsect2><title>udevadm info <replaceable>options</replaceable></title> - <para>Queries the udev database for device information - stored in the udev database. It can also query the properties - of a device from its sysfs representation to help creating udev - rules that match this device.</para> - <variablelist> - <varlistentry> - <term><option>--query=<replaceable>type</replaceable></option></term> - <listitem> - <para>Query the database for specified type of device data. It needs the - <option>--path</option> or <option>--name</option> to identify the specified - device. Valid queries are: - <command>name</command>, <command>symlink</command>, <command>path</command>, - <command>property</command>, <command>all</command>.</para> - </listitem> - </varlistentry> - <varlistentry> - <term><option>--path=<replaceable>devpath</replaceable></option></term> - <listitem> - <para>The devpath of the device to query.</para> - </listitem> - </varlistentry> - <varlistentry> - <term><option>--name=<replaceable>file</replaceable></option></term> - <listitem> - <para>The name of the device node or a symlink to query</para> - </listitem> - </varlistentry> - <varlistentry> - <term><option>--root</option></term> - <listitem> - <para>The udev root directory: <filename>/dev</filename>. If used in conjunction - with a <command>name</command> or <command>symlink</command> query, the - query returns the absolute path including the root directory.</para> - </listitem> - </varlistentry> - <varlistentry> - <term><option>--run</option></term> - <listitem> - <para>The udev runtime directory: <filename>/run/udev</filename>.</para> - </listitem> - </varlistentry> - <varlistentry> - <term><option>--attribute-walk</option></term> - <listitem> - <para>Print all sysfs properties of the specified device that can be used - in udev rules to match the specified device. It prints all devices - along the chain, up to the root of sysfs that can be used in udev rules.</para> - </listitem> - </varlistentry> - <varlistentry> - <term><option>--export</option></term> - <listitem> - <para>Print output as key/value pairs. Values are enclosed in single quotes.</para> - </listitem> - </varlistentry> - <varlistentry> - <term><option>--export-prefix=<replaceable>name</replaceable></option></term> - <listitem> - <para>Add a prefix to the key name of exported values.</para> - </listitem> - </varlistentry> - <varlistentry> - <term><option>--device-id-of-file=<replaceable>file</replaceable></option></term> - <listitem> - <para>Print major/minor numbers of the underlying device, where the file - lives on.</para> - </listitem> - </varlistentry> - <varlistentry> - <term><option>--export-db</option></term> - <listitem> - <para>Export the content of the udev database.</para> - </listitem> - </varlistentry> - <varlistentry> - <term><option>--cleanup-db</option></term> - <listitem> - <para>Cleanup the udev database.</para> - </listitem> - </varlistentry> - <varlistentry> - <term><option>--version</option></term> - <listitem> - <para>Print version.</para> - </listitem> - </varlistentry> - <varlistentry> - <term><option>--help</option></term> - <listitem> - <para>Print help text.</para> - </listitem> - </varlistentry> - </variablelist> - </refsect2> - - <refsect2><title>udevadm trigger <optional>options</optional></title> - <para>Request device events from the kernel. Primarily used to replay events at system coldplug time.</para> - <variablelist> - <varlistentry> - <term><option>--verbose</option></term> - <listitem> - <para>Print the list of devices which will be triggered.</para> - </listitem> - </varlistentry> - <varlistentry> - <term><option>--dry-run</option></term> - <listitem> - <para>Do not actually trigger the event.</para> - </listitem> - </varlistentry> - <varlistentry> - <term><option>--type=<replaceable>type</replaceable></option></term> - <listitem> - <para>Trigger a specific type of devices. Valid types are: - <command>devices</command>, <command>subsystems</command>. - The default value is <command>devices</command>.</para> - </listitem> - </varlistentry> - <varlistentry> - <term><option>--action=<replaceable>action</replaceable></option></term> - <listitem> - <para>Type of event to be triggered. The default value is <command>change</command>.</para> - </listitem> - </varlistentry> - <varlistentry> - <term><option>--subsystem-match=<replaceable>subsystem</replaceable></option></term> - <listitem> - <para>Trigger events for devices which belong to a matching subsystem. This option - can be specified multiple times and supports shell style pattern matching.</para> - </listitem> - </varlistentry> - <varlistentry> - <term><option>--subsystem-nomatch=<replaceable>subsystem</replaceable></option></term> - <listitem> - <para>Do not trigger events for devices which belong to a matching subsystem. This option - can be specified multiple times and supports shell style pattern matching.</para> - </listitem> - </varlistentry> - <varlistentry> - <term><option>--attr-match=<replaceable>attribute</replaceable>=<replaceable>value</replaceable></option></term> - <listitem> - <para>Trigger events for devices with a matching sysfs attribute. If a value is specified - along with the attribute name, the content of the attribute is matched against the given - value using shell style pattern matching. If no value is specified, the existence of the - sysfs attribute is checked. This option can be specified multiple times.</para> - </listitem> - </varlistentry> - <varlistentry> - <term><option>--attr-nomatch=<replaceable>attribute</replaceable>=<replaceable>value</replaceable></option></term> - <listitem> - <para>Do not trigger events for devices with a matching sysfs attribute. If a value is - specified along with the attribute name, the content of the attribute is matched against - the given value using shell style pattern matching. If no value is specified, the existence - of the sysfs attribute is checked. This option can be specified multiple times.</para> - </listitem> - </varlistentry> - <varlistentry> - <term><option>--property-match=<replaceable>property</replaceable>=<replaceable>value</replaceable></option></term> - <listitem> - <para>Trigger events for devices with a matching property value. This option can be - specified multiple times and supports shell style pattern matching.</para> - </listitem> - </varlistentry> - <varlistentry> - <term><option>--tag-match=<replaceable>property</replaceable></option></term> - <listitem> - <para>Trigger events for devices with a matching tag. This option can be - specified multiple times.</para> - </listitem> - </varlistentry> - <varlistentry> - <term><option>--sysname-match=<replaceable>name</replaceable></option></term> - <listitem> - <para>Trigger events for devices with a matching sys device name. This option can be - specified multiple times and supports shell style pattern matching.</para> - </listitem> - </varlistentry> - <varlistentry> - <term><option>--parent-match=<replaceable>syspath</replaceable></option></term> - <listitem> - <para>Trigger events for all children of a given device.</para> - </listitem> - </varlistentry> - </variablelist> - </refsect2> - - <refsect2><title>udevadm settle <optional>options</optional></title> - <para>Watches the udev event queue, and exits if all current events are handled.</para> - <variablelist> - <varlistentry> - <term><option>--timeout=<replaceable>seconds</replaceable></option></term> - <listitem> - <para>Maximum number of seconds to wait for the event queue to become empty. - The default value is 120 seconds. A value of 0 will check if the queue is empty - and always return immediately.</para> - </listitem> - </varlistentry> - <varlistentry> - <term><option>--seq-start=<replaceable>seqnum</replaceable></option></term> - <listitem> - <para>Wait only for events after the given sequence number.</para> - </listitem> - </varlistentry> - <varlistentry> - <term><option>--seq-end=<replaceable>seqnum</replaceable></option></term> - <listitem> - <para>Wait only for events before the given sequence number.</para> - </listitem> - </varlistentry> - <varlistentry> - <term><option>--exit-if-exists=<replaceable>file</replaceable></option></term> - <listitem> - <para>Stop waiting if file exists.</para> - </listitem> - </varlistentry> - <varlistentry> - <term><option>--quiet</option></term> - <listitem> - <para>Do not print any output, like the remaining queue entries when reaching the timeout.</para> - </listitem> - </varlistentry> - <varlistentry> - <term><option>--help</option></term> - <listitem> - <para>Print help text.</para> - </listitem> - </varlistentry> - </variablelist> - </refsect2> - - <refsect2><title>udevadm control <replaceable>command</replaceable></title> - <para>Modify the internal state of the running udev daemon.</para> - <variablelist> - <varlistentry> - <term><option>--exit</option></term> - <listitem> - <para>Signal and wait for udevd to exit.</para> - </listitem> - </varlistentry> - <varlistentry> - <term><option>--log-priority=<replaceable>value</replaceable></option></term> - <listitem> - <para>Set the internal log level of udevd. Valid values are the numerical - syslog priorities or their textual representations: <option>err</option>, - <option>info</option> and <option>debug</option>.</para> - </listitem> - </varlistentry> - <varlistentry> - <term><option>--stop-exec-queue</option></term> - <listitem> - <para>Signal udevd to stop executing new events. Incoming events - will be queued.</para> - </listitem> - </varlistentry> - <varlistentry> - <term><option>--start-exec-queue</option></term> - <listitem> - <para>Signal udevd to enable the execution of events.</para> - </listitem> - </varlistentry> - <varlistentry> - <term><option>--reload</option></term> - <listitem> - <para>Signal udevd to reload the rules files and other databases like the kernel - module index. Reloading rules and databases does not apply any changes to already - existing devices; the new configuration will only be applied to new events.</para> - </listitem> - </varlistentry> - <varlistentry> - <term><option>--property=<replaceable>KEY</replaceable>=<replaceable>value</replaceable></option></term> - <listitem> - <para>Set a global property for all events.</para> - </listitem> - </varlistentry> - <varlistentry> - <term><option>--children-max=</option><replaceable>value</replaceable></term> - <listitem> - <para>Set the maximum number of events, udevd will handle at the - same time.</para> - </listitem> - </varlistentry> - <varlistentry> - <term><option>--timeout=</option><replaceable>seconds</replaceable></term> - <listitem> - <para>The maximum number seconds to wait for a reply from udevd.</para> - </listitem> - </varlistentry> - <varlistentry> - <term><option>--help</option></term> - <listitem> - <para>Print help text.</para> - </listitem> - </varlistentry> - </variablelist> - </refsect2> - - <refsect2><title>udevadm monitor <optional>options</optional></title> - <para>Listens to the kernel uevents and events sent out by a udev rule - and prints the devpath of the event to the console. It can be used to analyze the - event timing, by comparing the timestamps of the kernel uevent and the udev event. - </para> - <variablelist> - <varlistentry> - <term><option>--kernel</option></term> - <listitem> - <para>Print the kernel uevents.</para> - </listitem> - </varlistentry> - <varlistentry> - <term><option>--udev</option></term> - <listitem> - <para>Print the udev event after the rule processing.</para> - </listitem> - </varlistentry> - <varlistentry> - <term><option>--property</option></term> - <listitem> - <para>Also print the properties of the event.</para> - </listitem> - </varlistentry> - <varlistentry> - <term><option>--subsystem-match=<replaceable>string[/string]</replaceable></option></term> - <listitem> - <para>Filter events by subsystem[/devtype]. Only udev events with a matching subsystem value will pass.</para> - </listitem> - </varlistentry> - <varlistentry> - <term><option>--tag-match=<replaceable>string</replaceable></option></term> - <listitem> - <para>Filter events by property. Only udev events with a given tag attached will pass.</para> - </listitem> - </varlistentry> - <varlistentry> - <term><option>--help</option></term> - <listitem> - <para>Print help text.</para> - </listitem> - </varlistentry> - </variablelist> - </refsect2> - - <refsect2><title>udevadm test <optional>options</optional> <replaceable>devpath</replaceable></title> - <para>Simulate a udev event run for the given device, and print debug output.</para> - <variablelist> - <varlistentry> - <term><option>--action=<replaceable>string</replaceable></option></term> - <listitem> - <para>The action string.</para> - </listitem> - </varlistentry> - <varlistentry> - <term><option>--subsystem=<replaceable>string</replaceable></option></term> - <listitem> - <para>The subsystem string.</para> - </listitem> - </varlistentry> - <varlistentry> - <term><option>--help</option></term> - <listitem> - <para>Print help text.</para> - </listitem> - </varlistentry> - </variablelist> - </refsect2> - - <refsect2><title>udevadm test-builtin <optional>options</optional> <replaceable>command</replaceable> <replaceable>devpath</replaceable></title> - <para>Run a built-in command for the given device, and print debug output.</para> - <variablelist> - <varlistentry> - <term><option>--help</option></term> - <listitem> - <para>Print help text.</para> - </listitem> - </varlistentry> - </variablelist> - </refsect2> - </refsect1> - - <refsect1><title>Author</title> - <para>Written by Kay Sievers <email>kay.sievers@vrfy.org</email>.</para> - </refsect1> - - <refsect1> - <title>See Also</title> - <para><citerefentry> - <refentrytitle>udev</refentrytitle><manvolnum>7</manvolnum> - </citerefentry> - <citerefentry> - <refentrytitle>udevd</refentrytitle><manvolnum>8</manvolnum> - </citerefentry></para> - </refsect1> -</refentry> diff --git a/udev/udevd.c b/udev/udevd.c deleted file mode 100644 index 196e63fd0d..0000000000 --- a/udev/udevd.c +++ /dev/null @@ -1,1708 +0,0 @@ -/* - * Copyright (C) 2004-2011 Kay Sievers <kay.sievers@vrfy.org> - * Copyright (C) 2004 Chris Friesen <chris_friesen@sympatico.ca> - * Copyright (C) 2009 Canonical Ltd. - * Copyright (C) 2009 Scott James Remnant <scott@netsplit.com> - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see <http://www.gnu.org/licenses/>. - */ - -#include <stddef.h> -#include <signal.h> -#include <unistd.h> -#include <errno.h> -#include <stdio.h> -#include <stdlib.h> -#include <stdbool.h> -#include <string.h> -#include <ctype.h> -#include <fcntl.h> -#include <time.h> -#include <getopt.h> -#include <dirent.h> -#include <sys/time.h> -#include <sys/prctl.h> -#include <sys/socket.h> -#include <sys/un.h> -#include <sys/signalfd.h> -#include <sys/epoll.h> -#include <sys/poll.h> -#include <sys/wait.h> -#include <sys/stat.h> -#include <sys/ioctl.h> -#include <sys/inotify.h> -#include <sys/utsname.h> - -#include "udev.h" -#include "sd-daemon.h" - -static bool debug; - -void udev_main_log(struct udev *udev, int priority, - const char *file, int line, const char *fn, - const char *format, va_list args) -{ - if (debug) { - char buf[1024]; - struct timespec ts; - - vsnprintf(buf, sizeof(buf), format, args); - clock_gettime(CLOCK_MONOTONIC, &ts); - fprintf(stderr, "[%llu.%06u] [%u] %s: %s", - (unsigned long long) ts.tv_sec, (unsigned int) ts.tv_nsec/1000, - (int) getpid(), fn, buf); - } else { - vsyslog(priority, format, args); - } -} - -static struct udev_rules *rules; -static struct udev_queue_export *udev_queue_export; -static struct udev_ctrl *udev_ctrl; -static struct udev_monitor *monitor; -static int worker_watch[2] = { -1, -1 }; -static int fd_signal = -1; -static int fd_ep = -1; -static int fd_inotify = -1; -static bool stop_exec_queue; -static bool reload; -static int children; -static int children_max; -static int exec_delay; -static sigset_t sigmask_orig; -static UDEV_LIST(event_list); -static UDEV_LIST(worker_list); -static bool udev_exit; - -enum event_state { - EVENT_UNDEF, - EVENT_QUEUED, - EVENT_RUNNING, -}; - -struct event { - struct udev_list_node node; - struct udev *udev; - struct udev_device *dev; - enum event_state state; - int exitcode; - unsigned long long int delaying_seqnum; - unsigned long long int seqnum; - const char *devpath; - size_t devpath_len; - const char *devpath_old; - dev_t devnum; - bool is_block; - int ifindex; -}; - -static struct event *node_to_event(struct udev_list_node *node) -{ - char *event; - - event = (char *)node; - event -= offsetof(struct event, node); - return (struct event *)event; -} - -static void event_queue_cleanup(struct udev *udev, enum event_state type); - -enum worker_state { - WORKER_UNDEF, - WORKER_RUNNING, - WORKER_IDLE, - WORKER_KILLED, -}; - -struct worker { - struct udev_list_node node; - struct udev *udev; - int refcount; - pid_t pid; - struct udev_monitor *monitor; - enum worker_state state; - struct event *event; -}; - -/* passed from worker to main process */ -struct worker_message { - pid_t pid; - int exitcode; -}; - -static struct worker *node_to_worker(struct udev_list_node *node) -{ - char *worker; - - worker = (char *)node; - worker -= offsetof(struct worker, node); - return (struct worker *)worker; -} - -static void event_queue_delete(struct event *event, bool export) -{ - udev_list_node_remove(&event->node); - - if (export) { - udev_queue_export_device_finished(udev_queue_export, event->dev); - info(event->udev, "seq %llu done with %i\n", udev_device_get_seqnum(event->dev), event->exitcode); - } - udev_device_unref(event->dev); - free(event); -} - -static struct worker *worker_ref(struct worker *worker) -{ - worker->refcount++; - return worker; -} - -static void worker_cleanup(struct worker *worker) -{ - udev_list_node_remove(&worker->node); - udev_monitor_unref(worker->monitor); - children--; - free(worker); -} - -static void worker_unref(struct worker *worker) -{ - worker->refcount--; - if (worker->refcount > 0) - return; - info(worker->udev, "worker [%u] cleaned up\n", worker->pid); - worker_cleanup(worker); -} - -static void worker_list_cleanup(struct udev *udev) -{ - struct udev_list_node *loop, *tmp; - - udev_list_node_foreach_safe(loop, tmp, &worker_list) { - struct worker *worker = node_to_worker(loop); - - worker_cleanup(worker); - } -} - -static void worker_new(struct event *event) -{ - struct udev *udev = event->udev; - struct worker *worker; - struct udev_monitor *worker_monitor; - pid_t pid; - - /* listen for new events */ - worker_monitor = udev_monitor_new_from_netlink(udev, NULL); - if (worker_monitor == NULL) - return; - /* allow the main daemon netlink address to send devices to the worker */ - udev_monitor_allow_unicast_sender(worker_monitor, monitor); - udev_monitor_enable_receiving(worker_monitor); - - worker = calloc(1, sizeof(struct worker)); - if (worker == NULL) { - udev_monitor_unref(worker_monitor); - return; - } - /* worker + event reference */ - worker->refcount = 2; - worker->udev = udev; - - pid = fork(); - switch (pid) { - case 0: { - struct udev_device *dev = NULL; - int fd_monitor; - struct epoll_event ep_signal, ep_monitor; - sigset_t mask; - int rc = EXIT_SUCCESS; - - /* move initial device from queue */ - dev = event->dev; - event->dev = NULL; - - free(worker); - worker_list_cleanup(udev); - event_queue_cleanup(udev, EVENT_UNDEF); - udev_queue_export_unref(udev_queue_export); - udev_monitor_unref(monitor); - udev_ctrl_unref(udev_ctrl); - close(fd_signal); - close(fd_ep); - close(worker_watch[READ_END]); - - sigfillset(&mask); - fd_signal = signalfd(-1, &mask, SFD_NONBLOCK|SFD_CLOEXEC); - if (fd_signal < 0) { - err(udev, "error creating signalfd %m\n"); - rc = 2; - goto out; - } - - fd_ep = epoll_create1(EPOLL_CLOEXEC); - if (fd_ep < 0) { - err(udev, "error creating epoll fd: %m\n"); - rc = 3; - goto out; - } - - memset(&ep_signal, 0, sizeof(struct epoll_event)); - ep_signal.events = EPOLLIN; - ep_signal.data.fd = fd_signal; - - fd_monitor = udev_monitor_get_fd(worker_monitor); - memset(&ep_monitor, 0, sizeof(struct epoll_event)); - ep_monitor.events = EPOLLIN; - ep_monitor.data.fd = fd_monitor; - - if (epoll_ctl(fd_ep, EPOLL_CTL_ADD, fd_signal, &ep_signal) < 0 || - epoll_ctl(fd_ep, EPOLL_CTL_ADD, fd_monitor, &ep_monitor) < 0) { - err(udev, "fail to add fds to epoll: %m\n"); - rc = 4; - goto out; - } - - /* request TERM signal if parent exits */ - prctl(PR_SET_PDEATHSIG, SIGTERM); - - for (;;) { - struct udev_event *udev_event; - struct worker_message msg; - int err; - - info(udev, "seq %llu running\n", udev_device_get_seqnum(dev)); - udev_event = udev_event_new(dev); - if (udev_event == NULL) { - rc = 5; - goto out; - } - - /* needed for SIGCHLD/SIGTERM in spawn() */ - udev_event->fd_signal = fd_signal; - - if (exec_delay > 0) - udev_event->exec_delay = exec_delay; - - /* apply rules, create node, symlinks */ - err = udev_event_execute_rules(udev_event, rules, &sigmask_orig); - - if (err == 0) - udev_event_execute_run(udev_event, &sigmask_orig); - - /* apply/restore inotify watch */ - if (err == 0 && udev_event->inotify_watch) { - udev_watch_begin(udev, dev); - udev_device_update_db(dev); - } - - /* send processed event back to libudev listeners */ - udev_monitor_send_device(worker_monitor, NULL, dev); - - /* send udevd the result of the event execution */ - memset(&msg, 0, sizeof(struct worker_message)); - if (err != 0) - msg.exitcode = err; - msg.pid = getpid(); - send(worker_watch[WRITE_END], &msg, sizeof(struct worker_message), 0); - - info(udev, "seq %llu processed with %i\n", udev_device_get_seqnum(dev), err); - - udev_device_unref(dev); - dev = NULL; - - if (udev_event->sigterm) { - udev_event_unref(udev_event); - goto out; - } - - udev_event_unref(udev_event); - - /* wait for more device messages from main udevd, or term signal */ - while (dev == NULL) { - struct epoll_event ev[4]; - int fdcount; - int i; - - fdcount = epoll_wait(fd_ep, ev, ARRAY_SIZE(ev), -1); - if (fdcount < 0) { - if (errno == EINTR) - continue; - err = -errno; - err(udev, "failed to poll: %m\n"); - goto out; - } - - for (i = 0; i < fdcount; i++) { - if (ev[i].data.fd == fd_monitor && ev[i].events & EPOLLIN) { - dev = udev_monitor_receive_device(worker_monitor); - break; - } else if (ev[i].data.fd == fd_signal && ev[i].events & EPOLLIN) { - struct signalfd_siginfo fdsi; - ssize_t size; - - size = read(fd_signal, &fdsi, sizeof(struct signalfd_siginfo)); - if (size != sizeof(struct signalfd_siginfo)) - continue; - switch (fdsi.ssi_signo) { - case SIGTERM: - goto out; - } - } - } - } - } -out: - udev_device_unref(dev); - if (fd_signal >= 0) - close(fd_signal); - if (fd_ep >= 0) - close(fd_ep); - close(fd_inotify); - close(worker_watch[WRITE_END]); - udev_rules_unref(rules); - udev_monitor_unref(worker_monitor); - udev_unref(udev); - udev_log_close(); - exit(rc); - } - case -1: - udev_monitor_unref(worker_monitor); - event->state = EVENT_QUEUED; - free(worker); - err(udev, "fork of child failed: %m\n"); - break; - default: - /* close monitor, but keep address around */ - udev_monitor_disconnect(worker_monitor); - worker->monitor = worker_monitor; - worker->pid = pid; - worker->state = WORKER_RUNNING; - worker->event = event; - event->state = EVENT_RUNNING; - udev_list_node_append(&worker->node, &worker_list); - children++; - info(udev, "seq %llu forked new worker [%u]\n", udev_device_get_seqnum(event->dev), pid); - break; - } -} - -static void event_run(struct event *event) -{ - struct udev_list_node *loop; - - udev_list_node_foreach(loop, &worker_list) { - struct worker *worker = node_to_worker(loop); - ssize_t count; - - if (worker->state != WORKER_IDLE) - continue; - - count = udev_monitor_send_device(monitor, worker->monitor, event->dev); - if (count < 0) { - err(event->udev, "worker [%u] did not accept message %zi (%m), kill it\n", worker->pid, count); - kill(worker->pid, SIGKILL); - worker->state = WORKER_KILLED; - continue; - } - worker_ref(worker); - worker->event = event; - worker->state = WORKER_RUNNING; - event->state = EVENT_RUNNING; - return; - } - - if (children >= children_max) { - if (children_max > 1) - info(event->udev, "maximum number (%i) of children reached\n", children); - return; - } - - /* start new worker and pass initial device */ - worker_new(event); -} - -static int event_queue_insert(struct udev_device *dev) -{ - struct event *event; - - event = calloc(1, sizeof(struct event)); - if (event == NULL) - return -1; - - event->udev = udev_device_get_udev(dev); - event->dev = dev; - event->seqnum = udev_device_get_seqnum(dev); - event->devpath = udev_device_get_devpath(dev); - event->devpath_len = strlen(event->devpath); - event->devpath_old = udev_device_get_devpath_old(dev); - event->devnum = udev_device_get_devnum(dev); - event->is_block = (strcmp("block", udev_device_get_subsystem(dev)) == 0); - event->ifindex = udev_device_get_ifindex(dev); - - udev_queue_export_device_queued(udev_queue_export, dev); - info(event->udev, "seq %llu queued, '%s' '%s'\n", udev_device_get_seqnum(dev), - udev_device_get_action(dev), udev_device_get_subsystem(dev)); - - event->state = EVENT_QUEUED; - udev_list_node_append(&event->node, &event_list); - return 0; -} - -static void worker_kill(struct udev *udev, int retain) -{ - struct udev_list_node *loop; - int max; - - if (children <= retain) - return; - - max = children - retain; - - udev_list_node_foreach(loop, &worker_list) { - struct worker *worker = node_to_worker(loop); - - if (max-- <= 0) - break; - - if (worker->state == WORKER_KILLED) - continue; - - worker->state = WORKER_KILLED; - kill(worker->pid, SIGTERM); - } -} - -/* lookup event for identical, parent, child device */ -static bool is_devpath_busy(struct event *event) -{ - struct udev_list_node *loop; - size_t common; - - /* check if queue contains events we depend on */ - udev_list_node_foreach(loop, &event_list) { - struct event *loop_event = node_to_event(loop); - - /* we already found a later event, earlier can not block us, no need to check again */ - if (loop_event->seqnum < event->delaying_seqnum) - continue; - - /* event we checked earlier still exists, no need to check again */ - if (loop_event->seqnum == event->delaying_seqnum) - return true; - - /* found ourself, no later event can block us */ - if (loop_event->seqnum >= event->seqnum) - break; - - /* check major/minor */ - if (major(event->devnum) != 0 && event->devnum == loop_event->devnum && event->is_block == loop_event->is_block) - return true; - - /* check network device ifindex */ - if (event->ifindex != 0 && event->ifindex == loop_event->ifindex) - return true; - - /* check our old name */ - if (event->devpath_old != NULL && strcmp(loop_event->devpath, event->devpath_old) == 0) { - event->delaying_seqnum = loop_event->seqnum; - return true; - } - - /* compare devpath */ - common = MIN(loop_event->devpath_len, event->devpath_len); - - /* one devpath is contained in the other? */ - if (memcmp(loop_event->devpath, event->devpath, common) != 0) - continue; - - /* identical device event found */ - if (loop_event->devpath_len == event->devpath_len) { - /* devices names might have changed/swapped in the meantime */ - if (major(event->devnum) != 0 && (event->devnum != loop_event->devnum || event->is_block != loop_event->is_block)) - continue; - if (event->ifindex != 0 && event->ifindex != loop_event->ifindex) - continue; - event->delaying_seqnum = loop_event->seqnum; - return true; - } - - /* parent device event found */ - if (event->devpath[common] == '/') { - event->delaying_seqnum = loop_event->seqnum; - return true; - } - - /* child device event found */ - if (loop_event->devpath[common] == '/') { - event->delaying_seqnum = loop_event->seqnum; - return true; - } - - /* no matching device */ - continue; - } - - return false; -} - -static void event_queue_start(struct udev *udev) -{ - struct udev_list_node *loop; - - udev_list_node_foreach(loop, &event_list) { - struct event *event = node_to_event(loop); - - if (event->state != EVENT_QUEUED) - continue; - - /* do not start event if parent or child event is still running */ - if (is_devpath_busy(event)) { - dbg(udev, "delay seq %llu (%s)\n", event->seqnum, event->devpath); - continue; - } - - event_run(event); - } -} - -static void event_queue_cleanup(struct udev *udev, enum event_state match_type) -{ - struct udev_list_node *loop, *tmp; - - udev_list_node_foreach_safe(loop, tmp, &event_list) { - struct event *event = node_to_event(loop); - - if (match_type != EVENT_UNDEF && match_type != event->state) - continue; - - event_queue_delete(event, false); - } -} - -static void worker_returned(int fd_worker) -{ - for (;;) { - struct worker_message msg; - ssize_t size; - struct udev_list_node *loop; - - size = recv(fd_worker, &msg, sizeof(struct worker_message), MSG_DONTWAIT); - if (size != sizeof(struct worker_message)) - break; - - /* lookup worker who sent the signal */ - udev_list_node_foreach(loop, &worker_list) { - struct worker *worker = node_to_worker(loop); - - if (worker->pid != msg.pid) - continue; - - /* worker returned */ - worker->event->exitcode = msg.exitcode; - event_queue_delete(worker->event, true); - worker->event = NULL; - if (worker->state != WORKER_KILLED) - worker->state = WORKER_IDLE; - worker_unref(worker); - break; - } - } -} - -/* receive the udevd message from userspace */ -static struct udev_ctrl_connection *handle_ctrl_msg(struct udev_ctrl *uctrl) -{ - struct udev *udev = udev_ctrl_get_udev(uctrl); - struct udev_ctrl_connection *ctrl_conn; - struct udev_ctrl_msg *ctrl_msg = NULL; - const char *str; - int i; - - ctrl_conn = udev_ctrl_get_connection(uctrl); - if (ctrl_conn == NULL) - goto out; - - ctrl_msg = udev_ctrl_receive_msg(ctrl_conn); - if (ctrl_msg == NULL) - goto out; - - i = udev_ctrl_get_set_log_level(ctrl_msg); - if (i >= 0) { - info(udev, "udevd message (SET_LOG_PRIORITY) received, log_priority=%i\n", i); - udev_set_log_priority(udev, i); - worker_kill(udev, 0); - } - - if (udev_ctrl_get_stop_exec_queue(ctrl_msg) > 0) { - info(udev, "udevd message (STOP_EXEC_QUEUE) received\n"); - stop_exec_queue = true; - } - - if (udev_ctrl_get_start_exec_queue(ctrl_msg) > 0) { - info(udev, "udevd message (START_EXEC_QUEUE) received\n"); - stop_exec_queue = false; - } - - if (udev_ctrl_get_reload(ctrl_msg) > 0) { - info(udev, "udevd message (RELOAD) received\n"); - reload = true; - } - - str = udev_ctrl_get_set_env(ctrl_msg); - if (str != NULL) { - char *key; - - key = strdup(str); - if (key != NULL) { - char *val; - - val = strchr(key, '='); - if (val != NULL) { - val[0] = '\0'; - val = &val[1]; - if (val[0] == '\0') { - info(udev, "udevd message (ENV) received, unset '%s'\n", key); - udev_add_property(udev, key, NULL); - } else { - info(udev, "udevd message (ENV) received, set '%s=%s'\n", key, val); - udev_add_property(udev, key, val); - } - } else { - err(udev, "wrong key format '%s'\n", key); - } - free(key); - } - worker_kill(udev, 0); - } - - i = udev_ctrl_get_set_children_max(ctrl_msg); - if (i >= 0) { - info(udev, "udevd message (SET_MAX_CHILDREN) received, children_max=%i\n", i); - children_max = i; - } - - if (udev_ctrl_get_ping(ctrl_msg) > 0) - info(udev, "udevd message (SYNC) received\n"); - - if (udev_ctrl_get_exit(ctrl_msg) > 0) { - info(udev, "udevd message (EXIT) received\n"); - udev_exit = true; - /* keep reference to block the client until we exit */ - udev_ctrl_connection_ref(ctrl_conn); - } -out: - udev_ctrl_msg_unref(ctrl_msg); - return udev_ctrl_connection_unref(ctrl_conn); -} - -/* read inotify messages */ -static int handle_inotify(struct udev *udev) -{ - int nbytes, pos; - char *buf; - struct inotify_event *ev; - - if ((ioctl(fd_inotify, FIONREAD, &nbytes) < 0) || (nbytes <= 0)) - return 0; - - buf = malloc(nbytes); - if (buf == NULL) { - err(udev, "error getting buffer for inotify\n"); - return -1; - } - - nbytes = read(fd_inotify, buf, nbytes); - - for (pos = 0; pos < nbytes; pos += sizeof(struct inotify_event) + ev->len) { - struct udev_device *dev; - - ev = (struct inotify_event *)(buf + pos); - dev = udev_watch_lookup(udev, ev->wd); - if (dev != NULL) { - info(udev, "inotify event: %x for %s\n", ev->mask, udev_device_get_devnode(dev)); - if (ev->mask & IN_CLOSE_WRITE) { - char filename[UTIL_PATH_SIZE]; - int fd; - - info(udev, "device %s closed, synthesising 'change'\n", udev_device_get_devnode(dev)); - util_strscpyl(filename, sizeof(filename), udev_device_get_syspath(dev), "/uevent", NULL); - fd = open(filename, O_WRONLY); - if (fd >= 0) { - if (write(fd, "change", 6) < 0) - info(udev, "error writing uevent: %m\n"); - close(fd); - } - } - if (ev->mask & IN_IGNORED) - udev_watch_end(udev, dev); - - udev_device_unref(dev); - } - - } - - free(buf); - return 0; -} - -static void handle_signal(struct udev *udev, int signo) -{ - switch (signo) { - case SIGINT: - case SIGTERM: - udev_exit = true; - break; - case SIGCHLD: - for (;;) { - pid_t pid; - int status; - struct udev_list_node *loop, *tmp; - - pid = waitpid(-1, &status, WNOHANG); - if (pid <= 0) - break; - - udev_list_node_foreach_safe(loop, tmp, &worker_list) { - struct worker *worker = node_to_worker(loop); - - if (worker->pid != pid) - continue; - info(udev, "worker [%u] exit\n", pid); - - if (WIFEXITED(status)) { - if (WEXITSTATUS(status) != 0) - err(udev, "worker [%u] exit with return code %i\n", pid, WEXITSTATUS(status)); - } else if (WIFSIGNALED(status)) { - err(udev, "worker [%u] terminated by signal %i (%s)\n", - pid, WTERMSIG(status), strsignal(WTERMSIG(status))); - } else if (WIFSTOPPED(status)) { - err(udev, "worker [%u] stopped\n", pid); - } else if (WIFCONTINUED(status)) { - err(udev, "worker [%u] continued\n", pid); - } else { - err(udev, "worker [%u] exit with status 0x%04x\n", pid, status); - } - - if (!WIFEXITED(status) || WEXITSTATUS(status) != 0) { - if (worker->event != NULL) { - err(udev, "worker [%u] failed while handling '%s'\n", - pid, worker->event->devpath); - worker->event->exitcode = -32; - event_queue_delete(worker->event, true); - /* drop reference taken for state 'running' */ - worker_unref(worker); - } - } - worker_unref(worker); - break; - } - } - break; - case SIGHUP: - reload = true; - break; - } -} - -static void static_dev_create_from_modules(struct udev *udev) -{ - struct utsname kernel; - char modules[UTIL_PATH_SIZE]; - char buf[4096]; - FILE *f; - - uname(&kernel); - util_strscpyl(modules, sizeof(modules), "/lib/modules/", kernel.release, "/modules.devname", NULL); - f = fopen(modules, "r"); - if (f == NULL) - return; - - while (fgets(buf, sizeof(buf), f) != NULL) { - char *s; - const char *modname; - const char *devname; - const char *devno; - int maj, min; - char type; - mode_t mode; - char filename[UTIL_PATH_SIZE]; - - if (buf[0] == '#') - continue; - - modname = buf; - s = strchr(modname, ' '); - if (s == NULL) - continue; - s[0] = '\0'; - - devname = &s[1]; - s = strchr(devname, ' '); - if (s == NULL) - continue; - s[0] = '\0'; - - devno = &s[1]; - s = strchr(devno, ' '); - if (s == NULL) - s = strchr(devno, '\n'); - if (s != NULL) - s[0] = '\0'; - if (sscanf(devno, "%c%u:%u", &type, &maj, &min) != 3) - continue; - - if (type == 'c') - mode = S_IFCHR; - else if (type == 'b') - mode = S_IFBLK; - else - continue; - - util_strscpyl(filename, sizeof(filename), udev_get_dev_path(udev), "/", devname, NULL); - util_create_path_selinux(udev, filename); - udev_selinux_setfscreatecon(udev, filename, mode); - info(udev, "mknod '%s' %c%u:%u\n", filename, type, maj, min); - if (mknod(filename, mode, makedev(maj, min)) < 0 && errno == EEXIST) - utimensat(AT_FDCWD, filename, NULL, 0); - udev_selinux_resetfscreatecon(udev); - } - - fclose(f); -} - -static int copy_dev_dir(struct udev *udev, DIR *dir_from, DIR *dir_to, int maxdepth) -{ - struct dirent *dent; - - for (dent = readdir(dir_from); dent != NULL; dent = readdir(dir_from)) { - struct stat stats; - - if (dent->d_name[0] == '.') - continue; - if (fstatat(dirfd(dir_from), dent->d_name, &stats, AT_SYMLINK_NOFOLLOW) != 0) - continue; - - if (S_ISBLK(stats.st_mode) || S_ISCHR(stats.st_mode)) { - udev_selinux_setfscreateconat(udev, dirfd(dir_to), dent->d_name, stats.st_mode & 0777); - if (mknodat(dirfd(dir_to), dent->d_name, stats.st_mode, stats.st_rdev) == 0) { - fchmodat(dirfd(dir_to), dent->d_name, stats.st_mode & 0777, 0); - fchownat(dirfd(dir_to), dent->d_name, stats.st_uid, stats.st_gid, 0); - } else { - utimensat(dirfd(dir_to), dent->d_name, NULL, 0); - } - udev_selinux_resetfscreatecon(udev); - } else if (S_ISLNK(stats.st_mode)) { - char target[UTIL_PATH_SIZE]; - ssize_t len; - - len = readlinkat(dirfd(dir_from), dent->d_name, target, sizeof(target)); - if (len <= 0 || len == (ssize_t)sizeof(target)) - continue; - target[len] = '\0'; - udev_selinux_setfscreateconat(udev, dirfd(dir_to), dent->d_name, S_IFLNK); - if (symlinkat(target, dirfd(dir_to), dent->d_name) < 0 && errno == EEXIST) - utimensat(dirfd(dir_to), dent->d_name, NULL, AT_SYMLINK_NOFOLLOW); - udev_selinux_resetfscreatecon(udev); - } else if (S_ISDIR(stats.st_mode)) { - DIR *dir2_from, *dir2_to; - - if (maxdepth == 0) - continue; - - udev_selinux_setfscreateconat(udev, dirfd(dir_to), dent->d_name, S_IFDIR|0755); - mkdirat(dirfd(dir_to), dent->d_name, 0755); - udev_selinux_resetfscreatecon(udev); - - dir2_to = fdopendir(openat(dirfd(dir_to), dent->d_name, O_RDONLY|O_NONBLOCK|O_DIRECTORY|O_CLOEXEC)); - if (dir2_to == NULL) - continue; - - dir2_from = fdopendir(openat(dirfd(dir_from), dent->d_name, O_RDONLY|O_NONBLOCK|O_DIRECTORY|O_CLOEXEC)); - if (dir2_from == NULL) { - closedir(dir2_to); - continue; - } - - copy_dev_dir(udev, dir2_from, dir2_to, maxdepth-1); - - closedir(dir2_to); - closedir(dir2_from); - } - } - - return 0; -} - -static void static_dev_create_links(struct udev *udev, DIR *dir) -{ - struct stdlinks { - const char *link; - const char *target; - }; - static const struct stdlinks stdlinks[] = { - { "core", "/proc/kcore" }, - { "fd", "/proc/self/fd" }, - { "stdin", "/proc/self/fd/0" }, - { "stdout", "/proc/self/fd/1" }, - { "stderr", "/proc/self/fd/2" }, - }; - unsigned int i; - - for (i = 0; i < ARRAY_SIZE(stdlinks); i++) { - struct stat sb; - - if (stat(stdlinks[i].target, &sb) == 0) { - udev_selinux_setfscreateconat(udev, dirfd(dir), stdlinks[i].link, S_IFLNK); - if (symlinkat(stdlinks[i].target, dirfd(dir), stdlinks[i].link) < 0 && errno == EEXIST) - utimensat(dirfd(dir), stdlinks[i].link, NULL, AT_SYMLINK_NOFOLLOW); - udev_selinux_resetfscreatecon(udev); - } - } -} - -static void static_dev_create_from_devices(struct udev *udev, DIR *dir) -{ - DIR *dir_from; - - dir_from = opendir(PKGLIBEXECDIR "/devices"); - if (dir_from == NULL) - return; - copy_dev_dir(udev, dir_from, dir, 8); - closedir(dir_from); -} - -static void static_dev_create(struct udev *udev) -{ - DIR *dir; - - dir = opendir(udev_get_dev_path(udev)); - if (dir == NULL) - return; - - static_dev_create_links(udev, dir); - static_dev_create_from_devices(udev, dir); - - closedir(dir); -} - -static int mem_size_mb(void) -{ - FILE *f; - char buf[4096]; - long int memsize = -1; - - f = fopen("/proc/meminfo", "r"); - if (f == NULL) - return -1; - - while (fgets(buf, sizeof(buf), f) != NULL) { - long int value; - - if (sscanf(buf, "MemTotal: %ld kB", &value) == 1) { - memsize = value / 1024; - break; - } - } - - fclose(f); - return memsize; -} - -static int convert_db(struct udev *udev) -{ - char filename[UTIL_PATH_SIZE]; - FILE *f; - struct udev_enumerate *udev_enumerate; - struct udev_list_entry *list_entry; - - /* current database */ - util_strscpyl(filename, sizeof(filename), udev_get_run_path(udev), "/data", NULL); - if (access(filename, F_OK) >= 0) - return 0; - - /* make sure we do not get here again */ - util_create_path(udev, filename); - mkdir(filename, 0755); - - /* old database */ - util_strscpyl(filename, sizeof(filename), udev_get_dev_path(udev), "/.udev/db", NULL); - if (access(filename, F_OK) < 0) - return 0; - - f = fopen("/dev/kmsg", "w"); - if (f != NULL) { - fprintf(f, "<30>udevd[%u]: converting old udev database\n", getpid()); - fclose(f); - } - - udev_enumerate = udev_enumerate_new(udev); - if (udev_enumerate == NULL) - return -1; - udev_enumerate_scan_devices(udev_enumerate); - udev_list_entry_foreach(list_entry, udev_enumerate_get_list_entry(udev_enumerate)) { - struct udev_device *device; - - device = udev_device_new_from_syspath(udev, udev_list_entry_get_name(list_entry)); - if (device == NULL) - continue; - - /* try to find the old database for devices without a current one */ - if (udev_device_read_db(device, NULL) < 0) { - bool have_db; - const char *id; - struct stat stats; - char devpath[UTIL_PATH_SIZE]; - char from[UTIL_PATH_SIZE]; - - have_db = false; - - /* find database in old location */ - id = udev_device_get_id_filename(device); - util_strscpyl(from, sizeof(from), udev_get_dev_path(udev), "/.udev/db/", id, NULL); - if (lstat(from, &stats) == 0) { - if (!have_db) { - udev_device_read_db(device, from); - have_db = true; - } - unlink(from); - } - - /* find old database with $subsys:$sysname name */ - util_strscpyl(from, sizeof(from), udev_get_dev_path(udev), - "/.udev/db/", udev_device_get_subsystem(device), ":", - udev_device_get_sysname(device), NULL); - if (lstat(from, &stats) == 0) { - if (!have_db) { - udev_device_read_db(device, from); - have_db = true; - } - unlink(from); - } - - /* find old database with the encoded devpath name */ - util_path_encode(udev_device_get_devpath(device), devpath, sizeof(devpath)); - util_strscpyl(from, sizeof(from), udev_get_dev_path(udev), "/.udev/db/", devpath, NULL); - if (lstat(from, &stats) == 0) { - if (!have_db) { - udev_device_read_db(device, from); - have_db = true; - } - unlink(from); - } - - /* write out new database */ - if (have_db) - udev_device_update_db(device); - } - udev_device_unref(device); - } - udev_enumerate_unref(udev_enumerate); - return 0; -} - -static int systemd_fds(struct udev *udev, int *rctrl, int *rnetlink) -{ - int ctrl = -1, netlink = -1; - int fd, n; - - n = sd_listen_fds(true); - if (n <= 0) - return -1; - - for (fd = SD_LISTEN_FDS_START; fd < n + SD_LISTEN_FDS_START; fd++) { - if (sd_is_socket(fd, AF_LOCAL, SOCK_SEQPACKET, -1)) { - if (ctrl >= 0) - return -1; - ctrl = fd; - continue; - } - - if (sd_is_socket(fd, AF_NETLINK, SOCK_RAW, -1)) { - if (netlink >= 0) - return -1; - netlink = fd; - continue; - } - - return -1; - } - - if (ctrl < 0 || netlink < 0) - return -1; - - info(udev, "ctrl=%i netlink=%i\n", ctrl, netlink); - *rctrl = ctrl; - *rnetlink = netlink; - return 0; -} - -static bool check_rules_timestamp(struct udev *udev) -{ - char **p; - unsigned long long *stamp_usec; - int i, n; - bool changed = false; - - n = udev_get_rules_path(udev, &p, &stamp_usec); - for (i = 0; i < n; i++) { - struct stat stats; - - if (stat(p[i], &stats) < 0) - continue; - - if (stamp_usec[i] == ts_usec(&stats.st_mtim)) - continue; - - /* first check */ - if (stamp_usec[i] != 0) { - info(udev, "reload - timestamp of '%s' changed\n", p[i]); - changed = true; - } - - /* update timestamp */ - stamp_usec[i] = ts_usec(&stats.st_mtim); - } - - return changed; -} - -int main(int argc, char *argv[]) -{ - struct udev *udev; - FILE *f; - sigset_t mask; - int daemonize = false; - int resolve_names = 1; - static const struct option options[] = { - { "daemon", no_argument, NULL, 'd' }, - { "debug", no_argument, NULL, 'D' }, - { "children-max", required_argument, NULL, 'c' }, - { "exec-delay", required_argument, NULL, 'e' }, - { "resolve-names", required_argument, NULL, 'N' }, - { "help", no_argument, NULL, 'h' }, - { "version", no_argument, NULL, 'V' }, - {} - }; - int fd_ctrl = -1; - int fd_netlink = -1; - int fd_worker = -1; - struct epoll_event ep_ctrl, ep_inotify, ep_signal, ep_netlink, ep_worker; - struct udev_ctrl_connection *ctrl_conn = NULL; - char **s; - int rc = 1; - - udev = udev_new(); - if (udev == NULL) - goto exit; - - udev_log_init("udevd"); - udev_set_log_fn(udev, udev_main_log); - info(udev, "version %s\n", VERSION); - udev_selinux_init(udev); - - for (;;) { - int option; - - option = getopt_long(argc, argv, "c:deDtN:hV", options, NULL); - if (option == -1) - break; - - switch (option) { - case 'd': - daemonize = true; - break; - case 'c': - children_max = strtoul(optarg, NULL, 0); - break; - case 'e': - exec_delay = strtoul(optarg, NULL, 0); - break; - case 'D': - debug = true; - if (udev_get_log_priority(udev) < LOG_INFO) - udev_set_log_priority(udev, LOG_INFO); - break; - case 'N': - if (strcmp (optarg, "early") == 0) { - resolve_names = 1; - } else if (strcmp (optarg, "late") == 0) { - resolve_names = 0; - } else if (strcmp (optarg, "never") == 0) { - resolve_names = -1; - } else { - fprintf(stderr, "resolve-names must be early, late or never\n"); - err(udev, "resolve-names must be early, late or never\n"); - goto exit; - } - break; - case 'h': - printf("Usage: udevd OPTIONS\n" - " --daemon\n" - " --debug\n" - " --children-max=<maximum number of workers>\n" - " --exec-delay=<seconds to wait before executing RUN=>\n" - " --resolve-names=early|late|never\n" - " --version\n" - " --help\n" - "\n"); - goto exit; - case 'V': - printf("%s\n", VERSION); - goto exit; - default: - goto exit; - } - } - - /* - * read the kernel commandline, in case we need to get into debug mode - * udev.log-priority=<level> syslog priority - * udev.children-max=<number of workers> events are fully serialized if set to 1 - * - */ - f = fopen("/proc/cmdline", "r"); - if (f != NULL) { - char cmdline[4096]; - - if (fgets(cmdline, sizeof(cmdline), f) != NULL) { - char *pos; - - pos = strstr(cmdline, "udev.log-priority="); - if (pos != NULL) { - pos += strlen("udev.log-priority="); - udev_set_log_priority(udev, util_log_priority(pos)); - } - - pos = strstr(cmdline, "udev.children-max="); - if (pos != NULL) { - pos += strlen("udev.children-max="); - children_max = strtoul(pos, NULL, 0); - } - - pos = strstr(cmdline, "udev.exec-delay="); - if (pos != NULL) { - pos += strlen("udev.exec-delay="); - exec_delay = strtoul(pos, NULL, 0); - } - } - fclose(f); - } - - if (getuid() != 0) { - fprintf(stderr, "root privileges required\n"); - err(udev, "root privileges required\n"); - goto exit; - } - - /* set umask before creating any file/directory */ - chdir("/"); - umask(022); - - /* /run/udev */ - mkdir(udev_get_run_path(udev), 0755); - - /* create standard links, copy static nodes, create nodes from modules */ - static_dev_create(udev); - static_dev_create_from_modules(udev); - - /* before opening new files, make sure std{in,out,err} fds are in a sane state */ - if (daemonize) { - int fd; - - fd = open("/dev/null", O_RDWR); - if (fd >= 0) { - if (write(STDOUT_FILENO, 0, 0) < 0) - dup2(fd, STDOUT_FILENO); - if (write(STDERR_FILENO, 0, 0) < 0) - dup2(fd, STDERR_FILENO); - if (fd > STDERR_FILENO) - close(fd); - } else { - fprintf(stderr, "cannot open /dev/null\n"); - err(udev, "cannot open /dev/null\n"); - } - } - - if (systemd_fds(udev, &fd_ctrl, &fd_netlink) >= 0) { - /* get control and netlink socket from from systemd */ - udev_ctrl = udev_ctrl_new_from_fd(udev, fd_ctrl); - if (udev_ctrl == NULL) { - err(udev, "error taking over udev control socket"); - rc = 1; - goto exit; - } - - monitor = udev_monitor_new_from_netlink_fd(udev, "kernel", fd_netlink); - if (monitor == NULL) { - err(udev, "error taking over netlink socket\n"); - rc = 3; - goto exit; - } - } else { - /* open control and netlink socket */ - udev_ctrl = udev_ctrl_new(udev); - if (udev_ctrl == NULL) { - fprintf(stderr, "error initializing udev control socket"); - err(udev, "error initializing udev control socket"); - rc = 1; - goto exit; - } - fd_ctrl = udev_ctrl_get_fd(udev_ctrl); - - monitor = udev_monitor_new_from_netlink(udev, "kernel"); - if (monitor == NULL) { - fprintf(stderr, "error initializing netlink socket\n"); - err(udev, "error initializing netlink socket\n"); - rc = 3; - goto exit; - } - fd_netlink = udev_monitor_get_fd(monitor); - } - - if (udev_monitor_enable_receiving(monitor) < 0) { - fprintf(stderr, "error binding netlink socket\n"); - err(udev, "error binding netlink socket\n"); - rc = 3; - goto exit; - } - - if (udev_ctrl_enable_receiving(udev_ctrl) < 0) { - fprintf(stderr, "error binding udev control socket\n"); - err(udev, "error binding udev control socket\n"); - rc = 1; - goto exit; - } - - udev_monitor_set_receive_buffer_size(monitor, 128*1024*1024); - - /* create queue file before signalling 'ready', to make sure we block 'settle' */ - udev_queue_export = udev_queue_export_new(udev); - if (udev_queue_export == NULL) { - err(udev, "error creating queue file\n"); - goto exit; - } - - if (daemonize) { - pid_t pid; - int fd; - - pid = fork(); - switch (pid) { - case 0: - break; - case -1: - err(udev, "fork of daemon failed: %m\n"); - rc = 4; - goto exit; - default: - rc = EXIT_SUCCESS; - goto exit_daemonize; - } - - setsid(); - - fd = open("/proc/self/oom_score_adj", O_RDWR); - if (fd < 0) { - /* Fallback to old interface */ - fd = open("/proc/self/oom_adj", O_RDWR); - if (fd < 0) { - err(udev, "error disabling OOM: %m\n"); - } else { - /* OOM_DISABLE == -17 */ - write(fd, "-17", 3); - close(fd); - } - } else { - write(fd, "-1000", 5); - close(fd); - } - } else { - sd_notify(1, "READY=1"); - } - - f = fopen("/dev/kmsg", "w"); - if (f != NULL) { - fprintf(f, "<30>udevd[%u]: starting version " VERSION "\n", getpid()); - fclose(f); - } - - if (!debug) { - int fd; - - fd = open("/dev/null", O_RDWR); - if (fd >= 0) { - dup2(fd, STDIN_FILENO); - dup2(fd, STDOUT_FILENO); - dup2(fd, STDERR_FILENO); - close(fd); - } - } - - fd_inotify = udev_watch_init(udev); - if (fd_inotify < 0) { - fprintf(stderr, "error initializing inotify\n"); - err(udev, "error initializing inotify\n"); - rc = 4; - goto exit; - } - udev_watch_restore(udev); - - /* block and listen to all signals on signalfd */ - sigfillset(&mask); - sigprocmask(SIG_SETMASK, &mask, &sigmask_orig); - fd_signal = signalfd(-1, &mask, SFD_NONBLOCK|SFD_CLOEXEC); - if (fd_signal < 0) { - fprintf(stderr, "error creating signalfd\n"); - err(udev, "error creating signalfd\n"); - rc = 5; - goto exit; - } - - /* unnamed socket from workers to the main daemon */ - if (socketpair(AF_LOCAL, SOCK_DGRAM|SOCK_CLOEXEC, 0, worker_watch) < 0) { - fprintf(stderr, "error creating socketpair\n"); - err(udev, "error creating socketpair\n"); - rc = 6; - goto exit; - } - fd_worker = worker_watch[READ_END]; - - udev_builtin_init(udev); - - rules = udev_rules_new(udev, resolve_names); - if (rules == NULL) { - err(udev, "error reading rules\n"); - goto exit; - } - - memset(&ep_ctrl, 0, sizeof(struct epoll_event)); - ep_ctrl.events = EPOLLIN; - ep_ctrl.data.fd = fd_ctrl; - - memset(&ep_inotify, 0, sizeof(struct epoll_event)); - ep_inotify.events = EPOLLIN; - ep_inotify.data.fd = fd_inotify; - - memset(&ep_signal, 0, sizeof(struct epoll_event)); - ep_signal.events = EPOLLIN; - ep_signal.data.fd = fd_signal; - - memset(&ep_netlink, 0, sizeof(struct epoll_event)); - ep_netlink.events = EPOLLIN; - ep_netlink.data.fd = fd_netlink; - - memset(&ep_worker, 0, sizeof(struct epoll_event)); - ep_worker.events = EPOLLIN; - ep_worker.data.fd = fd_worker; - - fd_ep = epoll_create1(EPOLL_CLOEXEC); - if (fd_ep < 0) { - err(udev, "error creating epoll fd: %m\n"); - goto exit; - } - if (epoll_ctl(fd_ep, EPOLL_CTL_ADD, fd_ctrl, &ep_ctrl) < 0 || - epoll_ctl(fd_ep, EPOLL_CTL_ADD, fd_inotify, &ep_inotify) < 0 || - epoll_ctl(fd_ep, EPOLL_CTL_ADD, fd_signal, &ep_signal) < 0 || - epoll_ctl(fd_ep, EPOLL_CTL_ADD, fd_netlink, &ep_netlink) < 0 || - epoll_ctl(fd_ep, EPOLL_CTL_ADD, fd_worker, &ep_worker) < 0) { - err(udev, "fail to add fds to epoll: %m\n"); - goto exit; - } - - /* if needed, convert old database from earlier udev version */ - convert_db(udev); - - if (children_max <= 0) { - int memsize = mem_size_mb(); - - /* set value depending on the amount of RAM */ - if (memsize > 0) - children_max = 128 + (memsize / 8); - else - children_max = 128; - } - info(udev, "set children_max to %u\n", children_max); - - udev_rules_apply_static_dev_perms(rules); - - udev_list_node_init(&event_list); - udev_list_node_init(&worker_list); - - for (;;) { - static unsigned long long last_usec; - struct epoll_event ev[8]; - int fdcount; - int timeout; - bool is_worker, is_signal, is_inotify, is_netlink, is_ctrl; - int i; - - if (udev_exit) { - /* close sources of new events and discard buffered events */ - if (fd_ctrl >= 0) { - epoll_ctl(fd_ep, EPOLL_CTL_DEL, fd_ctrl, NULL); - fd_ctrl = -1; - } - if (monitor != NULL) { - epoll_ctl(fd_ep, EPOLL_CTL_DEL, fd_netlink, NULL); - udev_monitor_unref(monitor); - monitor = NULL; - } - if (fd_inotify >= 0) { - epoll_ctl(fd_ep, EPOLL_CTL_DEL, fd_inotify, NULL); - close(fd_inotify); - fd_inotify = -1; - } - - /* discard queued events and kill workers */ - event_queue_cleanup(udev, EVENT_QUEUED); - worker_kill(udev, 0); - - /* exit after all has cleaned up */ - if (udev_list_node_is_empty(&event_list) && udev_list_node_is_empty(&worker_list)) - break; - - /* timeout at exit for workers to finish */ - timeout = 60 * 1000; - } else if (udev_list_node_is_empty(&event_list) && children > 2) { - /* set timeout to kill idle workers */ - timeout = 3 * 1000; - } else { - timeout = -1; - } - fdcount = epoll_wait(fd_ep, ev, ARRAY_SIZE(ev), timeout); - if (fdcount < 0) - continue; - - if (fdcount == 0) { - if (udev_exit) { - info(udev, "timeout, giving up waiting for workers to finish\n"); - break; - } - - /* timeout - kill idle workers */ - worker_kill(udev, 2); - } - - is_worker = is_signal = is_inotify = is_netlink = is_ctrl = false; - for (i = 0; i < fdcount; i++) { - if (ev[i].data.fd == fd_worker && ev[i].events & EPOLLIN) - is_worker = true; - else if (ev[i].data.fd == fd_netlink && ev[i].events & EPOLLIN) - is_netlink = true; - else if (ev[i].data.fd == fd_signal && ev[i].events & EPOLLIN) - is_signal = true; - else if (ev[i].data.fd == fd_inotify && ev[i].events & EPOLLIN) - is_inotify = true; - else if (ev[i].data.fd == fd_ctrl && ev[i].events & EPOLLIN) - is_ctrl = true; - } - - /* check for changed config, every 3 seconds at most */ - if ((now_usec() - last_usec) > 3 * 1000 * 1000) { - if (check_rules_timestamp(udev)) - reload = true; - if (udev_builtin_validate(udev)) - reload = true; - - last_usec = now_usec(); - } - - /* reload requested, HUP signal received, rules changed, builtin changed */ - if (reload) { - worker_kill(udev, 0); - rules = udev_rules_unref(rules); - udev_builtin_exit(udev); - reload = 0; - } - - /* event has finished */ - if (is_worker) - worker_returned(fd_worker); - - if (is_netlink) { - struct udev_device *dev; - - dev = udev_monitor_receive_device(monitor); - if (dev != NULL) { - udev_device_set_usec_initialized(dev, now_usec()); - if (event_queue_insert(dev) < 0) - udev_device_unref(dev); - } - } - - /* start new events */ - if (!udev_list_node_is_empty(&event_list) && !udev_exit && !stop_exec_queue) { - if (rules == NULL) - rules = udev_rules_new(udev, resolve_names); - if (rules != NULL) - event_queue_start(udev); - } - - if (is_signal) { - struct signalfd_siginfo fdsi; - ssize_t size; - - size = read(fd_signal, &fdsi, sizeof(struct signalfd_siginfo)); - if (size == sizeof(struct signalfd_siginfo)) - handle_signal(udev, fdsi.ssi_signo); - } - - /* we are shutting down, the events below are not handled anymore */ - if (udev_exit) - continue; - - /* device node watch */ - if (is_inotify) - handle_inotify(udev); - - /* - * This needs to be after the inotify handling, to make sure, - * that the ping is send back after the possibly generated - * "change" events by the inotify device node watch. - * - * A single time we may receive a client connection which we need to - * keep open to block the client. It will be closed right before we - * exit. - */ - if (is_ctrl) - ctrl_conn = handle_ctrl_msg(udev_ctrl); - } - - rc = EXIT_SUCCESS; -exit: - udev_queue_export_cleanup(udev_queue_export); - udev_ctrl_cleanup(udev_ctrl); -exit_daemonize: - if (fd_ep >= 0) - close(fd_ep); - worker_list_cleanup(udev); - event_queue_cleanup(udev, EVENT_UNDEF); - udev_rules_unref(rules); - udev_builtin_exit(udev); - if (fd_signal >= 0) - close(fd_signal); - if (worker_watch[READ_END] >= 0) - close(worker_watch[READ_END]); - if (worker_watch[WRITE_END] >= 0) - close(worker_watch[WRITE_END]); - udev_monitor_unref(monitor); - udev_queue_export_unref(udev_queue_export); - udev_ctrl_connection_unref(ctrl_conn); - udev_ctrl_unref(udev_ctrl); - udev_selinux_exit(udev); - udev_unref(udev); - udev_log_close(); - return rc; -} diff --git a/udev/udevd.xml b/udev/udevd.xml deleted file mode 100644 index c516eb9793..0000000000 --- a/udev/udevd.xml +++ /dev/null @@ -1,151 +0,0 @@ -<?xml version='1.0'?> -<?xml-stylesheet type="text/xsl" href="http://docbook.sourceforge.net/release/xsl/current/xhtml/docbook.xsl"?> -<!DOCTYPE refentry PUBLIC "-//OASIS//DTD DocBook XML V4.2//EN" - "http://www.oasis-open.org/docbook/xml/4.2/docbookx.dtd"> - -<refentry id="udevd"> - <refentryinfo> - <title>udevd</title> - <productname>udev</productname> - </refentryinfo> - - <refmeta> - <refentrytitle>udevd</refentrytitle> - <manvolnum>8</manvolnum> - <refmiscinfo class="version"></refmiscinfo> - </refmeta> - - <refnamediv> - <refname>udevd</refname><refpurpose>event managing daemon</refpurpose> - </refnamediv> - - <refsynopsisdiv> - <cmdsynopsis> - <command>udevd</command> - <arg><option>--daemon</option></arg> - <arg><option>--debug</option></arg> - <arg><option>--children-max=</option></arg> - <arg><option>--exec-delay=</option></arg> - <arg><option>--resolve-names=early|late|never</option></arg> - <arg><option>--version</option></arg> - <arg><option>--help</option></arg> - </cmdsynopsis> - </refsynopsisdiv> - - <refsect1><title>Description</title> - <para>udevd listens to kernel uevents. For every event, udevd executes matching - instructions specified in udev rules. See <citerefentry> - <refentrytitle>udev</refentrytitle><manvolnum>7</manvolnum> - </citerefentry>.</para> - <para>On startup the content of the directory <filename>/usr/lib/udev/devices</filename> - is copied to <filename>/dev</filename>. If kernel modules specify static device - nodes, these nodes are created even without a corresponding kernel device, to - allow on-demand loading of kernel modules. Matching permissions specified in udev - rules are applied to these static device nodes.</para> - <para>The behavior of the running daemon can be changed with - <command>udevadm control</command>.</para> - </refsect1> - - <refsect1><title>Options</title> - <variablelist> - <varlistentry> - <term><option>--daemon</option></term> - <listitem> - <para>Detach and run in the background.</para> - </listitem> - </varlistentry> - <varlistentry> - <term><option>--debug</option></term> - <listitem> - <para>Print debug messages to stderr.</para> - </listitem> - </varlistentry> - <varlistentry> - <term><option>--children-max=</option></term> - <listitem> - <para>Limit the number of parallel executed events.</para> - </listitem> - </varlistentry> - <varlistentry> - <term><option>--exec-delay=</option></term> - <listitem> - <para>Number of seconds to delay the execution of RUN instructions. - This might be useful when debugging system crashes during coldplug - cause by loading non-working kernel modules.</para> - </listitem> - </varlistentry> - <varlistentry> - <term><option>--resolve-names=</option></term> - <listitem> - <para>Specify when udevd should resolve names of users and groups. - When set to <option>early</option> (the default) names will be - resolved when the rules are parsed. When set to - <option>late</option> names will be resolved for every event. - When set to <option>never</option> names will never be resolved - and all devices will be owned by root.</para> - </listitem> - </varlistentry> - <varlistentry> - <term><option>--version</option></term> - <listitem> - <para>Print version number.</para> - </listitem> - </varlistentry> - <varlistentry> - <term><option>--help</option></term> - <listitem> - <para>Print help text.</para> - </listitem> - </varlistentry> - </variablelist> - </refsect1> - - <refsect1><title>Environment</title> - <variablelist> - <varlistentry> - <term><varname>UDEV_LOG=</varname></term> - <listitem> - <para>Set the logging priority.</para> - </listitem> - </varlistentry> - </variablelist> - </refsect1> - - <refsect1><title>Kernel command line</title> - <variablelist> - <varlistentry> - <term><varname>udev.log-priority=</varname></term> - <listitem> - <para>Set the logging priority.</para> - </listitem> - </varlistentry> - <varlistentry> - <term><varname>udev.children-max=</varname></term> - <listitem> - <para>Limit the number of parallel executed events.</para> - </listitem> - </varlistentry> - <varlistentry> - <term><varname>udev.exec-delay=</varname></term> - <listitem> - <para>Number of seconds to delay the execution of RUN instructions. - This might be useful when debugging system crashes during coldplug - cause by loading non-working kernel modules.</para> - </listitem> - </varlistentry> - </variablelist> - </refsect1> - - <refsect1><title>Author</title> - <para>Written by Kay Sievers <email>kay.sievers@vrfy.org</email>.</para> - </refsect1> - - <refsect1> - <title>See Also</title> - <para><citerefentry> - <refentrytitle>udev</refentrytitle><manvolnum>7</manvolnum> - </citerefentry>, <citerefentry> - <refentrytitle>udevadm</refentrytitle><manvolnum>8</manvolnum> - </citerefentry></para> - </refsect1> -</refentry> |