udevd.c revision 116254097ad3c07d9f7ed06042dbec7ba4f0f4fd
/*
* Copyright (C) 2004-2008 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>
*
* 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 <string.h>
#include <ctype.h>
#include <fcntl.h>
#include <time.h>
#include <getopt.h>
#include <dirent.h>
#ifdef HAVE_INOTIFY
#endif
#include "udev.h"
#define UDEVD_PRIORITY -4
#define UDEV_PRIORITY -2
/* maximum limit of forked childs */
#define UDEVD_MAX_CHILDS 256
static int debug;
{
if (debug) {
} else {
}
}
static void reap_sigchilds(void);
static int debug_trace;
static struct udev_rules *rules;
static struct udev_monitor *kernel_monitor;
static volatile int sigchilds_waiting;
static volatile int udev_exit;
static volatile int reload_config;
static volatile int signal_received;
static volatile pid_t settle_pid;
static int run_exec_q;
static int stop_exec_q;
static int max_childs;
static int childs;
static struct udev_list_node event_list;
enum event_state {
};
{
char *event;
return (struct udev_event *)event;
}
{
char filename[UTIL_PATH_SIZE];
char filename_failed[UTIL_PATH_SIZE];
/* location of queue file */
/* location of failed file */
switch (state) {
case EVENT_QUEUED:
if(unlink(filename_failed) == 0)
break;
case EVENT_FINISHED:
/* "move" event - rename failed file to current name, do not delete failed */
util_strlcat(filename_failed_old, udev_device_get_devpath_old(event->dev), sizeof(filename_failed_old));
} else {
if (unlink(filename_failed) == 0)
}
/* clean up possibly empty queue directory */
if (udev_list_is_empty(&event_list))
break;
case EVENT_FAILED:
/* move failed event to the failed directory */
/* clean up possibly empty queue directory */
if (udev_list_is_empty(&event_list))
break;
}
return;
}
{
/* mark as failed, if "add" event returns non-zero */
else
}
{
exit(1);
}
{
int err;
if (debug_trace) {
}
switch (pid) {
case 0:
/* child */
logging_init("udevd-event");
/* set signal handlers */
/* reset to default */
/* set timeout to prevent hanging processes */
/* apply rules, create node, symlinks */
/* execute RUN= */
}
/* send processed event back to the kernel netlink socket */
if (err != 0)
exit(1);
exit(0);
case -1:
break;
default:
/* get SIGCHLD in main loop */
pid,
childs++;
}
}
{
char filename[UTIL_PATH_SIZE];
int fd;
if (fd >= 0) {
char str[32];
int len;
}
run_exec_q = 1;
/* run all events with a timeout set immediately */
return;
}
}
static int mem_size_mb(void)
{
FILE* f;
char buf[4096];
long int memsize = -1;
if (f == NULL)
return -1;
long int value;
break;
}
}
fclose(f);
return memsize;
}
{
int i = 0;
i++;
/* identical device event found */
return 1;
/* parent device event found */
return 2;
/* child device event found */
return 3;
/* no matching event */
return 0;
}
/* lookup event for identical, parent, child, or physical device */
{
struct udev_list_node *loop;
if (event->delaying_seqnum > 0) {
}
/* check if queue contains events we depend on */
/* we already found a later event, earlier can not block us, no need to check again */
continue;
/* event we checked earlier still exists, no need to check again */
return 2;
/* found ourself, no later event can block us */
break;
/* check our old name */
if (strcmp(udev_device_get_devpath(loop_event->dev), udev_device_get_devpath_old(event->dev)) == 0) {
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) {
return 4;
}
/* check for our major:minor number */
return 5;
}
/* check physical device event (special case of parent) */
return 6;
}
}
return 0;
}
/* serializes events for the identical and parent and child devices */
{
struct udev_list_node *loop;
struct udev_list_node *tmp;
if (udev_list_is_empty(&event_list)) {
if (childs > 0) {
childs = 0;
}
return;
}
if (childs >= max_childs) {
break;
}
if (loop_event->pid != 0)
continue;
/* do not start event if parent or child event is still running */
if (devpath_busy(loop_event) != 0) {
continue;
}
/* retry if events finished in the meantime */
if (sigchilds_waiting) {
sigchilds_waiting = 0;
goto start_over;
}
}
}
/* receive the udevd message from userspace */
{
struct udev_ctrl_msg *ctrl_msg;
const char *str;
int i;
return;
if (i >= 0) {
}
if (udev_ctrl_get_stop_exec_queue(ctrl_msg) > 0) {
stop_exec_q = 1;
}
if (udev_ctrl_get_start_exec_queue(ctrl_msg) > 0) {
stop_exec_q = 0;
}
if (udev_ctrl_get_reload_rules(ctrl_msg) > 0) {
reload_config = 1;
}
char *key;
char *val;
val[0] = '\0';
if (val[0] == '\0') {
} else {
}
} else {
}
}
}
if (i >= 0) {
max_childs = i;
}
if (settle_pid > 0) {
}
}
/* read inotify messages */
{
char *buf;
struct inotify_event *ev;
return 0;
inotify_fd = -1;
return 0;
}
struct udev_device *dev;
reload_config = 1;
continue;
}
char filename[UTIL_PATH_SIZE];
int fd;
}
}
}
return 0;
}
{
switch (signum) {
case SIGINT:
case SIGTERM:
udev_exit = 1;
break;
case SIGCHLD:
/* set flag, then write to pipe if needed */
sigchilds_waiting = 1;
break;
case SIGHUP:
reload_config = 1;
break;
}
signal_received = 1;
}
{
struct udev_list_node *loop;
/* find event associated with pid and delete it */
if (debug_trace)
childs--;
/* there may be dependent events waiting */
run_exec_q = 1;
return;
}
}
}
static void reap_sigchilds(void)
{
int status;
while (1) {
if (pid <= 0)
break;
else if (WIFSIGNALED(status))
else
status = 0;
}
}
{
char dirname[UTIL_PATH_SIZE];
char filename[UTIL_PATH_SIZE];
while (1) {
break;
continue;
}
}
}
{
char filename[UTIL_PATH_SIZE];
int fd;
char seqnum[32];
if (fd >= 0) {
}
if (len <= 0) {
len = 3;
}
if (fd >= 0) {
}
}
{
int fd;
const char *value;
int daemonize = 0;
int resolve_names = 1;
{}
};
int rc = 1;
goto exit;
logging_init("udevd");
while (1) {
int option;
if (option == -1)
break;
switch (option) {
case 'd':
daemonize = 1;
break;
case 't':
debug_trace = 1;
break;
case 'D':
debug = 1;
break;
case 'N':
resolve_names = 1;
resolve_names = 0;
resolve_names = -1;
} else {
goto exit;
}
break;
case 'h':
printf("Usage: udevd [--help] [--daemon] [--debug-trace] [--debug] "
"[--resolve-names=early|late|never] [--version]\n");
goto exit;
case 'V':
goto exit;
default:
goto exit;
}
}
if (getuid() != 0) {
goto exit;
}
/* make sure std{in,out,err} fd's are in a sane state */
if (fd < 0) {
}
if (write(STDOUT_FILENO, 0, 0) < 0)
if (write(STDERR_FILENO, 0, 0) < 0)
/* init control socket, bind() ensures, that only one udevd instance is running */
rc = 1;
goto exit;
}
if (udev_ctrl_enable_receiving(udev_ctrl) < 0) {
rc = 1;
goto exit;
}
rc = 3;
goto exit;
}
goto exit;
}
if (daemonize) {
switch (pid) {
case 0:
break;
case -1:
rc = 4;
goto exit;
default:
rc = 0;
goto exit;
}
}
/* redirect std{out,err} */
if (!debug && !debug_trace) {
}
if (fd > STDERR_FILENO)
/* set scheduling priority for the daemon */
chdir("/");
umask(022);
setsid();
/* OOM_DISABLE == -17 */
if (fd < 0)
else {
}
if (fd > 0) {
char path[UTIL_PATH_SIZE];
const char *depr_str =
"<6>udev: deprecated sysfs layout; update the kernel or "
"disable CONFIG_SYSFS_DEPRECATED; some udev features will "
"not work correctly\n";
}
}
}
/* set signal handlers */
/* watch rules directory */
if (inotify_fd >= 0) {
} else {
char filename[UTIL_PATH_SIZE];
/* watch dynamic rules directory */
}
}
/* in trace mode run one event after the other */
if (debug_trace) {
max_childs = 1;
} else {
int memsize = mem_size_mb();
if (memsize > 0)
else
}
/* possibly overwrite maximum limit of executed events */
if (value)
while (!udev_exit) {
int nfds = 0;
int fdcount;
if (signal_received) {
goto handle_signals;
}
if (inotify_fd >= 0) {
}
if (fdcount < 0) {
goto handle_signals;
continue;
}
/* get control message */
/* get kernel uevent */
struct udev_device *dev;
struct udev_event *event;
else
}
}
/* rules directory inotify watch */
signal_received = 0;
/* rules changed, set by inotify or a HUP signal */
if (reload_config) {
struct udev_rules *rules_new;
reload_config = 0;
}
}
if (sigchilds_waiting) {
sigchilds_waiting = 0;
}
if (run_exec_q) {
run_exec_q = 0;
if (!stop_exec_q)
}
if (settle_pid > 0) {
settle_pid = 0;
}
}
rc = 0;
exit:
if (inotify_fd >= 0)
return rc;
}