#include <stdlib.h>
#include <string.h>
#include <ctype.h>
-#include <dirent.h>
#include <fcntl.h>
-#include <syslog.h>
#include <time.h>
#include <getopt.h>
#include <sys/select.h>
#include <sys/wait.h>
-#include <sys/types.h>
-#include <sys/socket.h>
#include <sys/stat.h>
#include <sys/ioctl.h>
#ifdef HAVE_INOTIFY
#endif
#include "udev.h"
-#include "udev_rules.h"
#define UDEVD_PRIORITY -4
#define UDEV_PRIORITY -2
}
static int debug_trace;
-static struct udev_rules rules;
+static struct udev_rules *rules;
static struct udev_ctrl *udev_ctrl;
static struct udev_monitor *kernel_monitor;
static int inotify_fd = -1;
-
static int signal_pipe[2] = {-1, -1};
static volatile int sigchilds_waiting;
static volatile int udev_exit;
static int run_exec_q;
static int stop_exec_q;
static int max_childs;
-
-static LIST_HEAD(exec_list);
-static LIST_HEAD(running_list);
+static int childs;
+static struct udev_list_node event_list;
enum event_state {
EVENT_QUEUED,
EVENT_FAILED,
};
+static struct udev_event *node_to_event(struct udev_list_node *node)
+{
+ char *event;
+
+ event = (char *)node;
+ event -= offsetof(struct udev_event, node);
+ return (struct udev_event *)event;
+}
+
static void export_event_state(struct udev_event *event, enum event_state state)
{
char filename[UTIL_PATH_SIZE];
switch (state) {
case EVENT_QUEUED:
- unlink(filename_failed);
- delete_path(event->udev, filename_failed);
- create_path(event->udev, filename);
+ if(unlink(filename_failed) == 0)
+ util_delete_path(event->udev, filename_failed);
+ util_create_path(event->udev, filename);
udev_selinux_setfscreatecon(event->udev, filename, S_IFLNK);
symlink(udev_device_get_devpath(event->dev), filename);
udev_selinux_resetfscreatecon(event->udev);
info(event->udev, "renamed devpath, moved failed state of '%s' to %s'\n",
udev_device_get_devpath_old(event->dev), udev_device_get_devpath(event->dev));
} else {
- unlink(filename_failed);
- delete_path(event->udev, filename_failed);
+ if (unlink(filename_failed) == 0)
+ util_delete_path(event->udev, filename_failed);
}
unlink(filename);
- delete_path(event->udev, filename);
+
+ /* clean up possibly empty queue directory */
+ if (udev_list_is_empty(&event_list))
+ util_delete_path(event->udev, filename);
break;
case EVENT_FAILED:
/* move failed event to the failed directory */
- create_path(event->udev, filename_failed);
+ util_create_path(event->udev, filename_failed);
rename(filename, filename_failed);
/* clean up possibly empty queue directory */
- delete_path(event->udev, filename);
+ if (udev_list_is_empty(&event_list))
+ util_delete_path(event->udev, filename);
break;
}
static void event_queue_delete(struct udev_event *event)
{
- list_del(&event->node);
+ udev_list_node_remove(&event->node);
/* mark as failed, if "add" event returns non-zero */
if (event->exitstatus && strcmp(udev_device_get_action(event->dev), "add") == 0)
struct sigaction act;
int err;
+ if (debug_trace) {
+ event->trace = 1;
+ fprintf(stderr, "fork %s (%llu)\n",
+ udev_device_get_syspath(event->dev),
+ udev_device_get_seqnum(event->dev));
+ }
+
pid = fork();
switch (pid) {
case 0:
alarm(UDEV_EVENT_TIMEOUT);
/* apply rules, create node, symlinks */
- err = udev_event_run(event, &rules);
+ err = udev_event_execute_rules(event, rules);
/* rules may change/disable the timeout */
if (udev_device_get_event_timeout(event->dev) >= 0)
/* execute RUN= */
if (err == 0 && !event->ignore_device && udev_get_run(event->udev))
- udev_rules_run(event);
- info(event->udev, "seq %llu finished with %i\n", udev_device_get_seqnum(event->dev), err);
+ udev_event_execute_run(event);
+
+ info(event->udev, "seq %llu exit with %i\n", udev_device_get_seqnum(event->dev), err);
logging_close();
if (err != 0)
exit(1);
udev_device_get_subsystem(event->dev),
time(NULL) - event->queue_time);
event->pid = pid;
+ childs++;
}
}
event->queue_time = time(NULL);
export_event_state(event, EVENT_QUEUED);
- info(event->udev, "seq %llu queued, '%s' '%s'\n", udev_device_get_seqnum(event->dev), udev_device_get_action(event->dev), udev_device_get_subsystem(event->dev));
+ info(event->udev, "seq %llu queued, '%s' '%s'\n", udev_device_get_seqnum(event->dev),
+ udev_device_get_action(event->dev), udev_device_get_subsystem(event->dev));
util_strlcpy(filename, udev_get_dev_path(event->udev), sizeof(filename));
util_strlcat(filename, "/.udev/uevent_seqnum", sizeof(filename));
close(fd);
}
- /* run one event after the other in debug mode */
- if (debug_trace) {
- list_add_tail(&event->node, &running_list);
- event_fork(event);
- waitpid(event->pid, NULL, 0);
- event_queue_delete(event);
- return;
- }
+
+ udev_list_node_append(&event->node, &event_list);
+ run_exec_q = 1;
/* run all events with a timeout set immediately */
if (udev_device_get_timeout(event->dev) > 0) {
- list_add_tail(&event->node, &running_list);
event_fork(event);
return;
}
-
- list_add_tail(&event->node, &exec_list);
- run_exec_q = 1;
}
static int mem_size_mb(void)
static int compare_devpath(const char *running, const char *waiting)
{
- int i;
+ int i = 0;
- for (i = 0; i < UTIL_PATH_SIZE; i++) {
- /* identical device event found */
- if (running[i] == '\0' && waiting[i] == '\0')
- return 1;
+ while (running[i] != '\0' && running[i] == waiting[i])
+ i++;
- /* parent device event found */
- if (running[i] == '\0' && waiting[i] == '/')
- return 2;
+ /* identical device event found */
+ if (running[i] == '\0' && waiting[i] == '\0')
+ return 1;
- /* child device event found */
- if (running[i] == '/' && waiting[i] == '\0')
- return 3;
+ /* parent device event found */
+ if (running[i] == '\0' && waiting[i] == '/')
+ return 2;
- /* no matching event */
- if (running[i] != waiting[i])
- break;
- }
+ /* child device event found */
+ if (running[i] == '/' && waiting[i] == '\0')
+ return 3;
+ /* no matching event */
return 0;
}
/* lookup event for identical, parent, child, or physical device */
-static int devpath_busy(struct udev_event *event, int limit)
+static int devpath_busy(struct udev_event *event)
{
- struct udev_event *loop_event;
- int childs_count = 0;
+ struct udev_list_node *loop;
- /* check exec-queue which may still contain delayed events we depend on */
- list_for_each_entry(loop_event, &exec_list, node) {
- /* skip ourself and all later events */
+ if (event->delaying_seqnum > 0) {
+ }
+ /* check if queue contains events we depend on */
+ udev_list_node_foreach(loop, &event_list) {
+ struct udev_event *loop_event = node_to_event(loop);
+
+ /* we already found a later event, earlier can not block us, no need to check again */
+ if (udev_device_get_seqnum(loop_event->dev) < event->delaying_seqnum)
+ continue;
+
+ /* event we checked earlier still exists, no need to check again */
+ if (udev_device_get_seqnum(loop_event->dev) == event->delaying_seqnum)
+ return 2;
+
+ /* found ourself, no later event can block us */
if (udev_device_get_seqnum(loop_event->dev) >= udev_device_get_seqnum(event->dev))
break;
/* check our old name */
if (udev_device_get_devpath_old(event->dev) != NULL)
- if (strcmp(udev_device_get_devpath(loop_event->dev), udev_device_get_devpath_old(event->dev)) == 0)
- return 2;
+ if (strcmp(udev_device_get_devpath(loop_event->dev), udev_device_get_devpath_old(event->dev)) == 0) {
+ event->delaying_seqnum = udev_device_get_seqnum(loop_event->dev);
+ return 3;
+ }
/* check identical, parent, or child device event */
if (compare_devpath(udev_device_get_devpath(loop_event->dev), udev_device_get_devpath(event->dev)) != 0) {
udev_device_get_seqnum(event->dev),
udev_device_get_seqnum(loop_event->dev),
udev_device_get_devpath(loop_event->dev));
- return 3;
- }
-
- /* check for our major:minor number */
- if (major(udev_device_get_devnum(event->dev)) > 0 &&
- udev_device_get_devnum(loop_event->dev) == udev_device_get_devnum(event->dev) &&
- strcmp(udev_device_get_subsystem(event->dev), udev_device_get_subsystem(loop_event->dev)) == 0) {
- dbg(event->udev, "%llu, device event still pending %llu (%d:%d)\n",
- udev_device_get_seqnum(event->dev),
- udev_device_get_seqnum(loop_event->dev),
- major(udev_device_get_devnum(loop_event->dev)), minor(udev_device_get_devnum(loop_event->dev)));
+ event->delaying_seqnum = udev_device_get_seqnum(loop_event->dev);
return 4;
}
- /* check physical device event (special case of parent) */
- if (udev_device_get_physdevpath(event->dev) != NULL &&
- strcmp(udev_device_get_action(event->dev), "add") == 0)
- if (compare_devpath(udev_device_get_devpath(loop_event->dev),
- udev_device_get_physdevpath(event->dev)) != 0) {
- dbg(event->udev, "%llu, physical device event still pending %llu (%s)\n",
- udev_device_get_seqnum(event->dev),
- udev_device_get_seqnum(loop_event->dev),
- udev_device_get_devpath(loop_event->dev));
- return 5;
- }
- }
-
- /* check run queue for still running events */
- list_for_each_entry(loop_event, &running_list, node) {
- childs_count++;
-
- if (childs_count++ >= limit) {
- info(event->udev, "%llu, maximum number (%i) of childs reached\n",
- udev_device_get_seqnum(event->dev), childs_count);
- return 1;
- }
-
- /* check our old name */
- if (udev_device_get_devpath_old(event->dev) != NULL)
- if (strcmp(udev_device_get_devpath(loop_event->dev), udev_device_get_devpath_old(event->dev)) == 0)
- return 2;
-
- /* check identical, parent, or child device event */
- if (compare_devpath(udev_device_get_devpath(loop_event->dev), udev_device_get_devpath(event->dev)) != 0) {
- dbg(event->udev, "%llu, device event still running %llu (%s)\n",
- udev_device_get_seqnum(event->dev),
- udev_device_get_seqnum(loop_event->dev),
- udev_device_get_devpath(loop_event->dev));
- return 3;
- }
-
/* check for our major:minor number */
if (major(udev_device_get_devnum(event->dev)) > 0 &&
udev_device_get_devnum(loop_event->dev) == udev_device_get_devnum(event->dev) &&
udev_device_get_seqnum(event->dev),
udev_device_get_seqnum(loop_event->dev),
major(udev_device_get_devnum(loop_event->dev)), minor(udev_device_get_devnum(loop_event->dev)));
- return 4;
+ event->delaying_seqnum = udev_device_get_seqnum(loop_event->dev);
+ return 5;
}
/* check physical device event (special case of parent) */
udev_device_get_seqnum(event->dev),
udev_device_get_seqnum(loop_event->dev),
udev_device_get_devpath(loop_event->dev));
- return 5;
+ event->delaying_seqnum = udev_device_get_seqnum(loop_event->dev);
+ return 6;
}
}
return 0;
/* serializes events for the identical and parent and child devices */
static void event_queue_manager(struct udev *udev)
{
- struct udev_event *loop_event;
- struct udev_event *tmp_event;
+ struct udev_list_node *loop;
+ struct udev_list_node *tmp;
- if (list_empty(&exec_list))
+ if (udev_list_is_empty(&event_list)) {
+ if (childs > 0) {
+ err(udev, "event list empty, but childs count is %i", childs);
+ childs = 0;
+ }
return;
+ }
+
+ udev_list_node_foreach_safe(loop, tmp, &event_list) {
+ struct udev_event *loop_event = node_to_event(loop);
+
+ if (childs >= max_childs) {
+ info(udev, "maximum number (%i) of childs reached\n", childs);
+ break;
+ }
- list_for_each_entry_safe(loop_event, tmp_event, &exec_list, node) {
- /* serialize and wait for parent or child events */
- if (devpath_busy(loop_event, max_childs) != 0) {
+ if (loop_event->pid != 0)
+ continue;
+
+ /* do not start event if parent or child event is still running */
+ if (devpath_busy(loop_event) != 0) {
dbg(udev, "delay seq %llu (%s)\n",
udev_device_get_seqnum(loop_event->dev),
udev_device_get_devpath(loop_event->dev));
continue;
}
- /* move event to run list */
- list_move_tail(&loop_event->node, &running_list);
event_fork(loop_event);
dbg(udev, "moved seq %llu to running list\n", udev_device_get_seqnum(loop_event->dev));
}
info(udev, "udevd message (SET_MAX_CHILDS) received, max_childs=%i\n", i);
max_childs = i;
}
-
udev_ctrl_msg_unref(ctrl_msg);
}
static void udev_done(int pid, int exitstatus)
{
- /* find event associated with pid and delete it */
- struct udev_event *event;
+ struct udev_list_node *loop;
- list_for_each_entry(event, &running_list, node) {
- if (event->pid == pid) {
- info(event->udev, "seq %llu, pid [%d] exit with %i, %ld seconds old\n",
- udev_device_get_seqnum(event->dev), event->pid,
- exitstatus, time(NULL) - event->queue_time);
- event->exitstatus = exitstatus;
- event_queue_delete(event);
-
- /* there may be events waiting with the same devpath */
+ /* find event associated with pid and delete it */
+ udev_list_node_foreach(loop, &event_list) {
+ struct udev_event *loop_event = node_to_event(loop);
+
+ if (loop_event->pid == pid) {
+ info(loop_event->udev, "seq %llu cleanup, pid [%d], status %i, %ld seconds old\n",
+ udev_device_get_seqnum(loop_event->dev), loop_event->pid,
+ exitstatus, time(NULL) - loop_event->queue_time);
+ loop_event->exitstatus = exitstatus;
+ if (debug_trace)
+ fprintf(stderr, "exit %s (%llu)\n",
+ udev_device_get_syspath(loop_event->dev),
+ udev_device_get_seqnum(loop_event->dev));
+ event_queue_delete(loop_event);
+ childs--;
+
+ /* there may be dependent events waiting */
run_exec_q = 1;
return;
}
}
util_strlcpy(filename, udev_get_dev_path(udev), sizeof(filename));
util_strlcat(filename, "/.udev/uevent_seqnum", sizeof(filename));
- create_path(udev, filename);
+ util_create_path(udev, filename);
fd = open(filename, O_WRONLY|O_TRUNC|O_CREAT, 0644);
if (fd >= 0) {
write(fd, seqnum, len);
logging_init("udevd");
udev_set_log_fn(udev, log_fn);
info(udev, "version %s\n", VERSION);
- selinux_init(udev);
+ udev_selinux_init(udev);
while (1) {
int option;
fprintf(stderr, "cannot open /dev/null\n");
err(udev, "cannot open /dev/null\n");
}
- if (fd > STDIN_FILENO)
- dup2(fd, STDIN_FILENO);
if (write(STDOUT_FILENO, 0, 0) < 0)
dup2(fd, STDOUT_FILENO);
if (write(STDERR_FILENO, 0, 0) < 0)
err(udev, "error initializing netlink socket\n");
rc = 3;
goto exit;
- } else {
- /* set receive buffersize */
- const int buffersize = 32 * 1024 * 1024;
-
- setsockopt(udev_monitor_get_fd(kernel_monitor),
- SOL_SOCKET, SO_RCVBUFFORCE, &buffersize, sizeof(buffersize));
}
+ udev_monitor_set_receive_buffer_size(kernel_monitor, 128*1024*1024);
err = pipe(signal_pipe);
if (err < 0) {
goto exit;
}
- udev_rules_init(udev, &rules, 1);
+ rules = udev_rules_new(udev, 1);
+ if (rules == NULL) {
+ err(udev, "error reading rules\n");
+ goto exit;
+ }
+ udev_list_init(&event_list);
export_initial_seqnum(udev);
if (daemonize) {
}
/* redirect std{out,err} */
- if (!debug) {
+ if (!debug && !debug_trace) {
+ dup2(fd, STDIN_FILENO);
dup2(fd, STDOUT_FILENO);
dup2(fd, STDERR_FILENO);
}
inotify_add_watch(inotify_fd, udev_get_rules_path(udev),
IN_CREATE | IN_DELETE | IN_MOVE | IN_CLOSE_WRITE);
} else {
- char filename[PATH_MAX];
+ char filename[UTIL_PATH_SIZE];
inotify_add_watch(inotify_fd, UDEV_PREFIX "/lib/udev/rules.d",
IN_CREATE | IN_DELETE | IN_MOVE | IN_CLOSE_WRITE);
else
err(udev, "inotify_init failed: %m\n");
- /* maximum limit of forked childs */
- value = getenv("UDEVD_MAX_CHILDS");
- if (value)
- max_childs = strtoul(value, NULL, 10);
- else {
+ /* in trace mode run one event after the other */
+ if (debug_trace) {
+ max_childs = 1;
+ } else {
int memsize = mem_size_mb();
if (memsize > 0)
max_childs = 128 + (memsize / 4);
else
max_childs = UDEVD_MAX_CHILDS;
}
+ /* possibly overwrite maximum limit of executed events */
+ value = getenv("UDEVD_MAX_CHILDS");
+ if (value)
+ max_childs = strtoul(value, NULL, 10);
info(udev, "initialize max_childs to %u\n", max_childs);
maxfd = udev_ctrl_get_fd(udev_ctrl);
maxfd = UDEV_MAX(maxfd, udev_monitor_get_fd(kernel_monitor));
maxfd = UDEV_MAX(maxfd, signal_pipe[READ_END]);
maxfd = UDEV_MAX(maxfd, inotify_fd);
-
while (!udev_exit) {
int fdcount;
FD_SET(udev_monitor_get_fd(kernel_monitor), &readfds);
if (inotify_fd >= 0)
FD_SET(inotify_fd, &readfds);
-
fdcount = select(maxfd+1, &readfds, NULL, NULL, NULL);
if (fdcount < 0) {
if (errno != EINTR)
/* rules changed, set by inotify or a HUP signal */
if (reload_config) {
+ struct udev_rules *rules_new;
+
reload_config = 0;
- udev_rules_cleanup(&rules);
- udev_rules_init(udev, &rules, 1);
+ rules_new = udev_rules_new(udev, 1);
+ if (rules_new != NULL) {
+ udev_rules_unref(rules);
+ rules = rules_new;
+ }
}
if (sigchilds_waiting) {
rc = 0;
exit:
- udev_rules_cleanup(&rules);
+ udev_rules_unref(rules);
if (signal_pipe[READ_END] >= 0)
close(signal_pipe[READ_END]);
close(inotify_fd);
udev_monitor_unref(kernel_monitor);
- selinux_exit(udev);
+ udev_selinux_exit(udev);
udev_unref(udev);
logging_close();
return rc;