#include <sys/time.h>
#include <sys/prctl.h>
#include <sys/socket.h>
+#include <sys/un.h>
#include <sys/signalfd.h>
#include <sys/select.h>
#include <sys/poll.h>
static bool reload_config;
static int children;
static int children_max;
+static int exec_delay;
static sigset_t orig_sigmask;
static struct udev_list_node event_list;
static struct udev_list_node worker_list;
/* set timeout to prevent hanging processes */
alarm(UDEV_EVENT_TIMEOUT);
+ if (exec_delay > 0)
+ udev_event->exec_delay = exec_delay;
+
/* apply rules, create node, symlinks */
err = udev_event_execute_rules(udev_event, rules);
}
if (!force && children >= children_max) {
- info(event->udev, "maximum number (%i) of children reached\n", children);
+ if (children_max > 1)
+ info(event->udev, "maximum number (%i) of children reached\n", children);
return;
}
return memsize;
}
+static int init_notify(const char *state)
+{
+ int fd = -1, r;
+ struct msghdr msghdr;
+ struct iovec iovec;
+ struct ucred *ucred;
+ union {
+ struct sockaddr sa;
+ struct sockaddr_un un;
+ } sockaddr;
+ union {
+ struct cmsghdr cmsghdr;
+ uint8_t buf[CMSG_SPACE(sizeof(struct ucred))];
+ } control;
+ const char *e;
+
+ if (!(e = getenv("NOTIFY_SOCKET"))) {
+ r = 0;
+ goto finish;
+ }
+
+ /* 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(&control, 0, sizeof(control));
+ control.cmsghdr.cmsg_level = SOL_SOCKET;
+ control.cmsghdr.cmsg_type = SCM_CREDENTIALS;
+ control.cmsghdr.cmsg_len = CMSG_LEN(sizeof(struct ucred));
+
+ ucred = (struct ucred*) CMSG_DATA(&control.cmsghdr);
+ ucred->pid = getpid();
+ ucred->uid = getuid();
+ ucred->gid = getgid();
+
+ memset(&msghdr, 0, sizeof(msghdr));
+ msghdr.msg_name = &sockaddr;
+ msghdr.msg_namelen = sizeof(struct sockaddr_un);
+ msghdr.msg_iov = &iovec;
+ msghdr.msg_iovlen = 1;
+ msghdr.msg_control = &control;
+ msghdr.msg_controllen = control.cmsghdr.cmsg_len;
+
+ if (sendmsg(fd, &msghdr, MSG_NOSIGNAL) < 0) {
+ r = -errno;
+ goto finish;
+ }
+
+ r = 0;
+
+finish:
+ if (fd >= 0)
+ close(fd);
+
+ return r;
+}
+
int main(int argc, char *argv[])
{
struct udev *udev;
{ "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' },
for (;;) {
int option;
- option = getopt_long(argc, argv, "dDthV", options, NULL);
+ option = getopt_long(argc, argv, "cdeDthV", options, NULL);
if (option == -1)
break;
daemonize = true;
break;
case 'c':
- children_max = strtoul(optarg, NULL, 10);
+ children_max = strtoul(optarg, NULL, 0);
+ break;
+ case 'e':
+ exec_delay = strtoul(optarg, NULL, 0);
break;
case 'D':
debug = true;
}
break;
case 'h':
- printf("Usage: udevd [--help] [--daemon] [--children-max] [--debug] "
- "[--resolve-names=early|late|never] [--version]\n");
+ 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);
}
}
+ /*
+ * 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;
}
- /* make sure std{in,out,err} fd's are in a sane state */
+ /* set umask before creating any file/directory */
+ chdir("/");
+ umask(022);
+
+ /* before opening new files, make sure std{in,out,err} fds are in a sane state */
fd = open("/dev/null", O_RDWR);
if (fd < 0) {
fprintf(stderr, "cannot open /dev/null\n");
if (write(STDERR_FILENO, 0, 0) < 0)
dup2(fd, STDERR_FILENO);
- /* init control socket, bind() ensures, that only one udevd instance is running */
udev_ctrl = udev_ctrl_new_from_socket(udev, UDEV_CTRL_SOCK_PATH);
if (udev_ctrl == NULL) {
fprintf(stderr, "error initializing control socket");
goto exit;
}
+ if (!debug) {
+ dup2(fd, STDIN_FILENO);
+ dup2(fd, STDOUT_FILENO);
+ dup2(fd, STDERR_FILENO);
+ }
+ if (fd > STDERR_FILENO)
+ close(fd);
+
if (daemonize) {
pid_t pid;
rc = 0;
goto exit;
}
+ } else {
+ init_notify("READY=1");
}
+ /* set scheduling priority for the main daemon process */
+ setpriority(PRIO_PROCESS, 0, UDEVD_PRIORITY);
+
+ setsid();
+
f = fopen("/dev/kmsg", "w");
if (f != NULL) {
fprintf(f, "<6>udev: starting version " VERSION "\n");
fclose(f);
}
- /* redirect std{out,err} */
- if (!debug) {
- dup2(fd, STDIN_FILENO);
- dup2(fd, STDOUT_FILENO);
- dup2(fd, STDERR_FILENO);
- }
- if (fd > STDERR_FILENO)
- close(fd);
-
- /* set scheduling priority for the daemon */
- setpriority(PRIO_PROCESS, 0, UDEVD_PRIORITY);
-
- chdir("/");
- umask(022);
- setsid();
-
/* OOM_DISABLE == -17 */
fd = open("/proc/self/oom_adj", O_RDWR);
if (fd < 0) {
}
if (children_max <= 0) {
- const char *value = getenv("UDEVD_CHILDREN_MAX");
-
- if (value) {
- children_max = strtoul(value, NULL, 10);
- } else {
- int memsize = mem_size_mb();
+ 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;
- }
+ /* 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);