udevd.c revision 34c00c915c6dd9d063551732169cb3c3126376ad
/*
* udevd.c - event listener and serializer
*
* Copyright (C) 2004-2005 Kay Sievers <kay.sievers@vrfy.org>
* Copyright (C) 2004 Chris Friesen <chris_friesen@sympatico.ca>
*
*
* under the terms of the GNU General Public License as published by the
* Free Software Foundation version 2 of the License.
*
* 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, write to the Free Software Foundation, Inc.,
* 675 Mass Ave, Cambridge, MA 02139, USA.
*
*/
#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 <dirent.h>
#include <fcntl.h>
#include "list.h"
#include "udev_libc_wrapper.h"
#include "udev.h"
#include "udev_version.h"
#include "udev_utils.h"
#include "udevd.h"
#include "logging.h"
/* global variables*/
static int udevd_sock;
static int uevent_netlink_sock;
static int pipefds[2];
static volatile int sigchilds_waiting;
static volatile int run_msg_q;
static volatile int sig_flag;
static volatile int udev_exit;
static int init_phase = 1;
static int run_exec_q;
static int stop_exec_q;
static LIST_HEAD(running_list);
static void exec_queue_manager(void);
static void msg_queue_manager(void);
static void user_sighandler(void);
static void reap_sigchilds(void);
static char *udev_bin;
static unsigned long long expected_seqnum;
static int event_timeout;
static int max_childs;
static int max_childs_running;
#ifdef USE_LOG
{
if (priority > udev_log_priority)
return;
}
#endif
static void msg_dump_queue(void)
{
#ifdef DEBUG
struct uevent_msg *msg;
#endif
}
{
}
/* orders the message in the queue by sequence number */
{
struct uevent_msg *loop_msg;
dbg("no SEQNUM, move straight to the exec queue");
run_exec_q = 1;
return;
}
/* store timestamp of queuing */
/* with the first event we provide a phase of shorter timeout */
if (init_phase) {
static long init_time;
if (init_time == 0)
init_phase = 0;
}
/* don't delay messages with timeout set */
run_exec_q = 1;
return;
}
/* sort message by sequence number into list */
break;
return;
}
}
/* run msg queue manager */
run_msg_q = 1;
return;
}
/* forks event and removes event from run queue when finished */
{
switch (pid) {
case 0:
/* child */
if (uevent_netlink_sock != -1)
err("exec of child failed");
_exit(1);
case -1:
err("fork of child failed");
break;
default:
/* get SIGCHLD in main loop */
info("seq %llu forked, pid %d, %ld seconds old",
}
}
static int running_processes(void)
{
int f;
static char buf[4096];
int len;
int running;
const char *pos;
if (f == -1)
return -1;
close(f);
if (len <= 0)
return -1;
else
return -1;
return -1;
return running;
}
/* return the number of process es in our session, count only until limit */
{
int running = 0;
if (!dir)
return -1;
/* read process info from /proc */
int f;
char procdir[64];
char line[256];
const char *pos;
char state;
int len;
continue;
if (f == -1)
continue;
close(f);
if (len <= 0)
continue;
else
/* skip ugly program name */
continue;
continue;
/* count only processes in our session */
continue;
/* count only running, no sleeping processes */
if (state != 'R')
continue;
running++;
break;
}
return running;
}
{
int i;
for (i = 0; i < PATH_SIZE; i++) {
/* identical device event found */
return 1;
/* parent device event found */
return 2;
/* child device event found */
return 3;
/* no matching event */
break;
}
return 0;
}
/* returns still running task for the same device, its parent or its physical device */
{
struct uevent_msg *loop_msg;
int childs_count = 0;
return 0;
/* skip any events with a timeout set */
return 0;
return 1;
}
continue;
dbg("%llu, child device event still running %llu (%s)",
return 2;
}
/* return running physical device event */
dbg("%llu, physical device event still running %llu (%s)",
return 3;
}
}
return 0;
}
/* exec queue management routine executes the events and serializes events in the same sequence */
static void exec_queue_manager(void)
{
struct uevent_msg *loop_msg;
struct uevent_msg *tmp_msg;
int running;
if (list_empty(&exec_list))
return;
running = running_processes();
if (running < 0)
/* check running processes in our session and possibly throttle */
if (running >= max_childs_running) {
if (running >= max_childs_running) {
dbg("delay seq %llu, cause too many processes already running",
return;
}
}
/* move event to run list */
running++;
} else
}
}
{
run_exec_q = 1;
dbg("moved seq %llu to exec, next expected is %llu",
}
/* msg queue management routine handles the timeouts and dispatches the events */
static void msg_queue_manager(void)
{
struct uevent_msg *loop_msg;
struct uevent_msg *tmp_msg;
long msg_age = 0;
int timeout = event_timeout;
/* move event with expected sequence to the exec list */
continue;
}
/* limit timeout during initialization phase */
if (init_phase) {
}
/* move event with expired timeout to the exec list */
goto recheck;
} else {
break;
}
}
/* set timeout for remaining queued events */
if (list_empty(&msg_list) == 0) {
}
}
{
int bufpos;
int i;
struct uevent_msg *msg;
int major = 0;
int minor = 0;
return NULL;
/* copy environment buffer and reconstruct envp */
bufpos = 0;
int keylen;
char *key;
/* remember some keys for further processing */
}
return msg;
}
/* receive the udevd message from userspace */
static struct uevent_msg *get_udevd_msg(void)
{
struct uevent_msg *msg;
int envbuf_size;
int *intval;
if (size < 0) {
dbg("unable to receive udevd message");
return NULL;
}
info("no sender credentials received, message ignored");
return NULL;
}
return NULL;
}
return NULL;
}
case UDEVD_UEVENT_UDEVSEND:
case UDEVD_UEVENT_INITSEND:
info("udevd event message received");
return NULL;
return msg;
case UDEVD_STOP_EXEC_QUEUE:
info("udevd message (STOP_EXEC_QUEUE) received");
stop_exec_q = 1;
break;
case UDEVD_START_EXEC_QUEUE:
info("udevd message (START_EXEC_QUEUE) received");
stop_exec_q = 0;
break;
case UDEVD_SET_LOG_LEVEL:
break;
case UDEVD_SET_MAX_CHILDS:
max_childs = *intval;
break;
default:
dbg("unknown message type");
}
return NULL;
}
/* receive the kernel user event message and do some sanity checks */
static struct uevent_msg *get_netlink_msg(void)
{
struct uevent_msg *msg;
int bufpos;
char *pos;
if (size < 0) {
dbg("unable to receive udevd message");
return NULL;
}
/* start of event payload */
return NULL;
/* validate message */
return NULL;
}
pos[0] = '\0';
return NULL;
}
return NULL;
}
return msg;
}
{
int rc;
switch (signum) {
case SIGINT:
case SIGTERM:
udev_exit = 1;
break;
case SIGALRM:
/* set flag, then write to pipe if needed */
run_msg_q = 1;
break;
case SIGCHLD:
/* set flag, then write to pipe if needed */
sigchilds_waiting = 1;
break;
}
/* if pipe is empty, write to pipe to force select to return,
* which will wakeup our mainloop
*/
if (!sig_flag) {
if (rc >= 0)
sig_flag = 1;
}
}
{
/* find msg associated with pid and delete it */
struct uevent_msg *msg;
/* we want to run the exec queue manager since there may
* be events waiting with the devpath of the one that
* just finished
*/
run_exec_q = 1;
return;
}
}
}
static void reap_sigchilds(void)
{
while(1) {
break;
}
}
/* just read everything from the pipe and clear the flag,
* the flags was set in the signal handler
*/
static void user_sighandler(void)
{
int sig;
while(1) {
if (rc < 0)
break;
sig_flag = 0;
}
}
static int init_udevd_socket(void)
{
struct sockaddr_un saddr;
const int feature_on = 1;
int retval;
/* use abstract namespace for socket path */
if (udevd_sock == -1) {
return -1;
}
/* set receive buffersize */
/* the bind takes care of ensuring only one copy running */
if (retval < 0) {
return -1;
}
/* enable receiving of the sender credentials */
return 0;
}
static int init_uevent_netlink_sock(void)
{
struct sockaddr_nl snl;
int retval;
if (uevent_netlink_sock == -1) {
return -1;
}
/* set receive buffersize */
sizeof(struct sockaddr_nl));
if (retval < 0) {
uevent_netlink_sock = -1;
return -1;
}
return 0;
}
{
int maxsockplus;
int retval;
int fd;
const char *value;
int uevent_netlink_active = 0;
int daemonize = 0;
int i;
logging_init("udevd");
if (getuid() != 0) {
err("need to be root, exit");
goto exit;
}
for (i = 1 ; i < argc; i++) {
info("will daemonize");
daemonize = 1;
}
info("will not execute events until START_EXEC_QUEUE is received");
stop_exec_q = 1;
}
}
if (daemonize) {
switch (pid) {
case 0:
dbg("damonized fork running");
break;
case -1:
err("fork of daemon failed");
goto exit;
default:
exit(0);
}
}
/* become session leader */
chdir("/");
/* set a reasonable scheduling priority for the daemon */
if (fd >= 0) {
if (fd > 2)
} else
/* setup signal handler pipe */
if (retval < 0) {
goto exit;
}
if (retval < 0) {
goto exit;
}
if (retval < 0)
if (retval < 0) {
goto exit;
}
if (retval < 0)
/* set signal handlers */
if (init_uevent_netlink_sock() < 0) {
dbg("uevent socket not available");
}
if (init_udevd_socket() < 0) {
if (errno == EADDRINUSE)
dbg("another udevd running, exit");
else
goto exit;
}
/* override of forked udev binary, used for testing */
else
/* init of expected_seqnum value */
if (value) {
}
/* timeout to wait for missing events */
if (value)
else
/* maximum limit of forked childs */
if (value)
else
/* start to throttle forking if maximum number of _running_ childs is reached */
if (value)
else
if (uevent_netlink_sock > 0)
while (!udev_exit) {
struct uevent_msg *msg;
if (retval < 0) {
continue;
}
/* get user socket message */
msg = get_udevd_msg();
if (msg) {
/* discard kernel messages if netlink is active */
dbg("skip uevent_helper message, netlink is active");
continue;
}
}
}
/* get kernel netlink message */
msg = get_netlink_msg();
if (msg) {
/* disable udevsend with first netlink message */
if (!uevent_netlink_active) {
info("uevent_nl message received, disable udevsend messages");
}
}
}
/* received a signal, clear our notification pipe */
/* forked child have returned */
if (sigchilds_waiting) {
sigchilds_waiting = 0;
}
if (run_msg_q) {
run_msg_q = 0;
}
if (run_exec_q) {
/* clean up running_list before calling exec_queue_manager() */
if (sigchilds_waiting) {
sigchilds_waiting = 0;
}
run_exec_q = 0;
if (!stop_exec_q)
}
}
exit:
if (udevd_sock > 0)
if (uevent_netlink_sock > 0)
return 0;
}