+ struct sockaddr_nl snl;
+ const int buffersize = 16 * 1024 * 1024;
+ int retval;
+
+ memset(&snl, 0x00, sizeof(struct sockaddr_nl));
+ snl.nl_family = AF_NETLINK;
+ snl.nl_pid = getpid();
+ snl.nl_groups = 0xffffffff;
+
+ uevent_netlink_sock = socket(PF_NETLINK, SOCK_DGRAM, NETLINK_KOBJECT_UEVENT);
+ if (uevent_netlink_sock == -1) {
+ err("error getting socket: %s", strerror(errno));
+ return -1;
+ }
+
+ /* set receive buffersize */
+ setsockopt(uevent_netlink_sock, SOL_SOCKET, SO_RCVBUFFORCE, &buffersize, sizeof(buffersize));
+
+ retval = bind(uevent_netlink_sock, (struct sockaddr *) &snl, sizeof(struct sockaddr_nl));
+ if (retval < 0) {
+ err("bind failed: %s", strerror(errno));
+ close(uevent_netlink_sock);
+ uevent_netlink_sock = -1;
+ return -1;
+ }
+ return 0;
+}
+
+int main(int argc, char *argv[], char *envp[])
+{
+ int retval;
+ int fd;
+ struct sigaction act;
+ fd_set readfds;
+ const char *value;
+ int daemonize = 0;
+ int i;
+ int rc = 0;
+ int maxfd;
+
+ /* redirect std fd's, if the kernel forks us, we don't have them at all */
+ fd = open("/dev/null", O_RDWR);
+ if (fd >= 0) {
+ if (fd != STDIN_FILENO)
+ dup2(fd, STDIN_FILENO);
+ if (fd != STDOUT_FILENO)
+ dup2(fd, STDOUT_FILENO);
+ if (fd != STDERR_FILENO)
+ dup2(fd, STDERR_FILENO);
+ if (fd > STDERR_FILENO)
+ close(fd);
+ }
+
+ logging_init("udevd");
+ if (fd < 0)
+ err("fatal, could not open /dev/null: %s", strerror(errno));
+
+ udev_init_config();
+ dbg("version %s", UDEV_VERSION);
+
+ if (getuid() != 0) {
+ err("need to be root, exit");
+ goto exit;
+ }
+
+ /* parse commandline options */
+ for (i = 1 ; i < argc; i++) {
+ char *arg = argv[i];
+ if (strcmp(arg, "--daemon") == 0 || strcmp(arg, "-d") == 0) {
+ info("will daemonize");
+ daemonize = 1;
+ }
+ if (strcmp(arg, "--stop-exec-queue") == 0) {
+ info("will not execute events until START_EXEC_QUEUE is received");
+ stop_exec_q = 1;
+ }
+ }
+
+ /* init sockets to receive events */
+ if (init_udevd_socket() < 0) {
+ if (errno == EADDRINUSE) {
+ err("another udevd running, exit");
+ rc = 1;
+ } else {
+ err("error initializing udevd socket: %s", strerror(errno));
+ rc = 2;
+ }
+ goto exit;
+ }
+
+ if (init_uevent_netlink_sock() < 0) {
+ err("uevent socket not available");
+ rc = 3;
+ goto exit;
+ }
+
+ /* parse the rules and keep it in memory */
+ udev_rules_init(&rules, 1);
+
+ if (daemonize) {
+ pid_t pid;
+
+ pid = fork();
+ switch (pid) {
+ case 0:
+ dbg("daemonized fork running");
+ break;
+ case -1:
+ err("fork of daemon failed: %s", strerror(errno));
+ rc = 4;
+ goto exit;
+ default:
+ dbg("child [%u] running, parent exits", pid);
+ goto exit;
+ }
+ }
+
+ /* set scheduling priority for the daemon */
+ setpriority(PRIO_PROCESS, 0, UDEVD_PRIORITY);
+
+ chdir("/");
+ umask(022);
+
+ /* become session leader */
+ sid = setsid();
+ dbg("our session is %d", sid);
+
+ /* OOM_DISABLE == -17 */
+ fd = open("/proc/self/oom_adj", O_RDWR);
+ if (fd < 0)
+ err("error disabling OOM: %s", strerror(errno));
+ else {
+ write(fd, "-17", 3);
+ close(fd);
+ }
+
+ /* setup signal handler pipe */
+ retval = pipe(signal_pipe);
+ if (retval < 0) {
+ err("error getting pipes: %s", strerror(errno));
+ goto exit;
+ }
+ retval = fcntl(signal_pipe[READ_END], F_SETFL, O_NONBLOCK);
+ if (retval < 0) {
+ err("error fcntl on read pipe: %s", strerror(errno));
+ goto exit;
+ }
+ retval = fcntl(signal_pipe[WRITE_END], F_SETFL, O_NONBLOCK);
+ if (retval < 0) {
+ err("error fcntl on write pipe: %s", strerror(errno));
+ goto exit;
+ }
+
+ /* set signal handlers */
+ memset(&act, 0x00, sizeof(struct sigaction));
+ act.sa_handler = (void (*)(int)) sig_handler;
+ sigemptyset(&act.sa_mask);
+ act.sa_flags = SA_RESTART;
+ sigaction(SIGINT, &act, NULL);
+ sigaction(SIGTERM, &act, NULL);
+ sigaction(SIGCHLD, &act, NULL);
+ sigaction(SIGHUP, &act, NULL);
+
+ /* watch rules directory */
+ inotify_fd = inotify_init();
+ if (inotify_fd > 0)
+ inotify_add_watch(inotify_fd, udev_rules_filename, IN_CREATE | IN_DELETE | IN_MOVE | IN_CLOSE_WRITE);
+
+ /* maximum limit of forked childs */
+ value = getenv("UDEVD_MAX_CHILDS");
+ if (value)
+ max_childs = strtoul(value, NULL, 10);
+ else
+ max_childs = UDEVD_MAX_CHILDS;
+ info("initialize max_childs to %u", max_childs);
+
+ /* start to throttle forking if maximum number of _running_ childs is reached */
+ value = getenv("UDEVD_MAX_CHILDS_RUNNING");
+ if (value)
+ max_childs_running = strtoull(value, NULL, 10);
+ else
+ max_childs_running = UDEVD_MAX_CHILDS_RUNNING;
+ info("initialize max_childs_running to %u", max_childs_running);
+
+ /* clear environment for forked event processes */
+ clearenv();
+
+ /* export log_priority , as called programs may want to follow that setting */
+ sprintf(udev_log, "UDEV_LOG=%i", udev_log_priority);
+ putenv(udev_log);
+
+ maxfd = udevd_sock;
+ maxfd = UDEV_MAX(maxfd, uevent_netlink_sock);
+ maxfd = UDEV_MAX(maxfd, signal_pipe[READ_END]);
+ maxfd = UDEV_MAX(maxfd, inotify_fd);
+
+ while (!udev_exit) {
+ struct uevent_msg *msg;
+ int fdcount;
+
+ FD_ZERO(&readfds);
+ FD_SET(signal_pipe[READ_END], &readfds);
+ FD_SET(udevd_sock, &readfds);
+ FD_SET(uevent_netlink_sock, &readfds);
+ if (inotify_fd > 0)
+ FD_SET(inotify_fd, &readfds);
+
+ fdcount = select(maxfd+1, &readfds, NULL, NULL, NULL);
+ if (fdcount < 0) {
+ if (errno != EINTR)
+ err("error in select: %s", strerror(errno));
+ continue;
+ }
+
+ /* get user socket message */
+ if (FD_ISSET(udevd_sock, &readfds)) {
+ msg = get_udevd_msg();
+ if (msg)
+ msg_queue_insert(msg);
+ }
+
+ /* get kernel netlink message */
+ if (FD_ISSET(uevent_netlink_sock, &readfds)) {
+ msg = get_netlink_msg();
+ if (msg)
+ msg_queue_insert(msg);
+ }
+
+ /* received a signal, clear our notification pipe */
+ if (FD_ISSET(signal_pipe[READ_END], &readfds)) {
+ char buf[256];
+
+ read(signal_pipe[READ_END], &buf, sizeof(buf));
+ }
+
+ /* rules directory inotify watch */
+ if ((inotify_fd > 0) && FD_ISSET(inotify_fd, &readfds)) {
+ int nbytes;
+
+ /* discard all possible events, we can just reload the config */
+ if ((ioctl(inotify_fd, FIONREAD, &nbytes) == 0) && nbytes) {
+ char *buf;
+
+ reload_config = 1;
+ buf = malloc(nbytes);
+ if (!buf) {
+ err("error getting buffer for inotify, disable watching");
+ close(inotify_fd);
+ inotify_fd = -1;
+ }
+ read(inotify_fd, buf, nbytes);
+ free(buf);
+ }
+ }
+
+ /* rules changed, set by inotify or a signal*/
+ if (reload_config) {
+ reload_config = 0;
+ udev_rules_close(&rules);
+ udev_rules_init(&rules, 1);
+ }
+
+ /* forked child has returned */
+ if (sigchilds_waiting) {
+ sigchilds_waiting = 0;
+ reap_sigchilds();
+ }
+
+ if (run_exec_q) {
+ run_exec_q = 0;
+ if (!stop_exec_q)
+ msg_queue_manager();
+ }
+ }
+
+exit:
+ udev_rules_close(&rules);
+
+ if (signal_pipe[READ_END] > 0)
+ close(signal_pipe[READ_END]);
+ if (signal_pipe[WRITE_END] > 0)
+ close(signal_pipe[WRITE_END]);
+
+ if (udevd_sock > 0)
+ close(udevd_sock);
+ if (inotify_fd > 0)
+ close(inotify_fd);
+ if (uevent_netlink_sock > 0)
+ close(uevent_netlink_sock);