udevd.c revision 821d0ec803a72841f173739f5b713fe847edab75
/*
* udevd.c - hotplug event 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 udevsendsock;
static int pipefds[2];
static long startup_time;
static unsigned long long expected_seqnum = 0;
static volatile int sigchilds_waiting;
static volatile int run_msg_q;
static volatile int sig_flag;
static int run_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);
char *udev_bin;
#ifdef USE_LOG
{
if (priority > udev_log_priority)
return;
}
#endif
static void msg_dump_queue(void)
{
#ifdef DEBUG
struct hotplug_msg *msg;
#endif
}
{
}
/* orders the message in the queue by sequence number */
{
struct hotplug_msg *loop_msg;
dbg("no SEQNUM, move straight to the exec queue");
run_exec_q = 1;
return;
}
/* don't delay messages with timeout set */
run_exec_q = 1;
return;
}
/* sort message by sequence number into list */
break;
return;
}
}
/* store timestamp of queuing */
/* run msg queue manager */
run_msg_q = 1;
return;
}
/* forks event and removes event from run queue when finished */
{
switch (pid) {
case 0:
/* child */
err("exec of child failed");
_exit(1);
break;
case -1:
err("fork of child failed");
break;
default:
/* get SIGCHLD in main loop */
}
}
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 hotplug_msg *loop_msg;
return NULL;
/* skip any events with a timeout set */
return NULL;
continue;
return loop_msg;
/* return running physical device event */
return loop_msg;
}
return NULL;
}
/* exec queue management routine executes the events and serializes events in the same sequence */
static void exec_queue_manager(void)
{
struct hotplug_msg *loop_msg;
struct hotplug_msg *tmp_msg;
struct hotplug_msg *msg;
int running;
running = running_processes();
if (running < 0)
/* check running processes in our session and possibly throttle */
if (running >= THROTTLE_MAX_RUNNING_CHILDS) {
if (running >= THROTTLE_MAX_RUNNING_CHILDS) {
return;
}
}
if (!msg) {
/* move event to run list */
running++;
} else {
dbg("delay seq %llu (%s), cause seq %llu (%s) is still running",
}
}
}
{
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 hotplug_msg *loop_msg;
struct hotplug_msg *tmp_msg;
long msg_age = 0;
static int timeout = EVENT_INIT_TIMEOUT_SEC;
static int init = 1;
/* move event with expected sequence to the exec list */
continue;
}
/* see if we are in the initialization phase and wait for the very first events */
init = 0;
}
/* 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) {
}
}
/* receive the udevsend message and do some sanity checks */
static struct hotplug_msg *get_udevsend_msg(void)
{
static struct udevsend_msg usend_msg;
struct hotplug_msg *msg;
int bufpos;
int i;
int envbuf_size;
if (size < 0) {
dbg("unable to receive udevsend message");
return NULL;
}
info("no sender credentials received, message ignored");
return NULL;
}
return NULL;
}
return NULL;
}
return NULL;
/* copy environment buffer and reconstruct envp */
bufpos = 0;
int keylen;
char *key;
/* remember some keys for further processing */
}
return msg;
}
{
int rc;
switch (signum) {
case SIGINT:
case SIGTERM:
break;
case SIGALRM:
/* set flag, then write to pipe if needed */
run_msg_q = 1;
goto do_write;
break;
case SIGCHLD:
/* set flag, then write to pipe if needed */
sigchilds_waiting = 1;
goto do_write;
break;
}
/* if pipe is empty, write to pipe to force select to return
* immediately when it gets called
*/
if (!sig_flag) {
if (rc >= 0)
sig_flag = 1;
}
}
{
/* find msg associated with pid and delete it */
struct hotplug_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_udevsend_socket(void)
{
struct sockaddr_un saddr;
const int feature_on = 1;
int retval;
/* use abstract namespace for socket path */
if (udevsendsock == -1) {
return -1;
}
/* the bind takes care of ensuring only one copy running */
if (retval < 0) {
return -1;
}
/* enable receiving of the sender credentials */
return 0;
}
{
int maxsockplus;
int retval;
int fd;
const char *udevd_expected_seqnum;
logging_init("udevd");
if (getuid() != 0) {
err("need to be root, exit");
goto exit;
}
/* daemonize on request */
switch (pid) {
case 0:
dbg("damonized fork running");
break;
case -1:
err("fork of daemon failed");
goto exit;
default:
exit(0);
}
}
/* become session leader */
/* make sure we don't lock any path */
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_udevsend_socket() < 0) {
if (errno == EADDRINUSE)
dbg("another udevd running, exit");
else
goto exit;
}
/* possible override of udev binary, used for testing */
else
/* possible init of expected_seqnum value */
if (udevd_expected_seqnum != NULL) {
}
/* get current time to provide shorter timeout on startup */
while (1) {
struct hotplug_msg *msg;
if (retval < 0) {
continue;
}
msg = get_udevsend_msg();
if (msg)
}
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;
}
}
exit:
return 1;
}