#include <sys/types.h>
#include <sys/device.h>
#include <sys/wait.h>
#include <sys/socket.h>
#include <sys/ioctl.h>
#include <sys/poll.h>
#include <sys/queue.h>
#include <sys/un.h>
#include <err.h>
#include <errno.h>
#include <fcntl.h>
#include <libgen.h>
#include <regex.h>
#include <signal.h>
#include <stdarg.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <syslog.h>
#include <unistd.h>
#include <pthread.h>
#include <libprop/proplib.h>
#include <sys/udev.h>
#include "udevd.h"
int debugopt = 0;
static int udevfd;
static int hangup_ongoing = 0;
static struct pollfd fds[NFDS];
extern pthread_mutex_t monitor_lock;
extern TAILQ_HEAD(udev_monitor_list_head, udev_monitor) udev_monitor_list;
extern TAILQ_HEAD(pdev_array_list_head, pdev_array_entry) pdev_array_list;
static void usage(void);
int match_dev_dict(prop_dictionary_t, prop_dictionary_t);
prop_dictionary_t find_dev_dict(int64_t, prop_dictionary_t, int *);
void udev_read_event(int);
prop_array_t udev_getdevs(int);
static
void
usage(void)
{
fprintf(stderr, "usage: udevd [-d]\n");
exit(1);
}
int
match_dev_dict(prop_dictionary_t dict, prop_dictionary_t match_dict)
{
prop_number_t pn, pn2;
prop_string_t ps, ps2;
if (dict == NULL)
return 0;
if ((ps = prop_dictionary_get(dict, "name")) == NULL)
return 0;
if ((ps2 = prop_dictionary_get(match_dict, "name")) == NULL)
return 0;
if (!prop_string_equals(ps, ps2))
return 0;
if ((pn = prop_dictionary_get(dict, "devnum")) == NULL)
return 0;
if ((pn2 = prop_dictionary_get(match_dict, "devnum")) == NULL)
return 0;
if (!prop_number_equals(pn, pn2))
return 0;
if ((pn = prop_dictionary_get(dict, "kptr")) == NULL)
return 0;
if ((pn2 = prop_dictionary_get(match_dict, "kptr")) == NULL)
return 0;
if (!prop_number_equals(pn, pn2))
return 0;
return 1;
}
prop_dictionary_t
find_dev_dict(int64_t generation, prop_dictionary_t match_dict, int *idx)
{
struct pdev_array_entry *pae;
prop_array_t pa;
prop_object_iterator_t iter;
prop_dictionary_t dict;
int i = 0;
if (generation == -1)
pae = pdev_array_entry_get_last();
else
pae = pdev_array_entry_get(generation);
if (pae == NULL)
return NULL;
pa = pae->pdev_array;
iter = prop_array_iterator(pa);
if (iter == NULL) {
pdev_array_entry_unref(pae);
return NULL;
}
while ((dict = prop_object_iterator_next(iter)) != NULL) {
if (match_dev_dict(dict, match_dict))
break;
++i;
}
prop_object_iterator_release(iter);
if (idx != NULL)
*idx = i;
pdev_array_entry_unref(pae);
return dict;
}
void
udev_read_event(int fd)
{
struct pdev_array_entry *pae;
prop_dictionary_t dict, evdict, devdict;
prop_number_t pn;
prop_string_t ps;
prop_object_t po;
prop_array_t pa;
char *xml;
int n, idx, evtype;
size_t sz;
sz = 4096 * 1024;
xml = malloc(sz);
again:
if ((n = read(fd, xml, sz)) <= 0) {
if (errno == ENOMEM) {
sz <<= 2;
if ((xml = realloc(xml, sz)) == NULL) {
syslog(LOG_ERR, "could not realloc xml memory");
return;
}
goto again;
}
free(xml);
return;
}
dict = prop_dictionary_internalize(xml);
free(xml);
if (dict == NULL) {
syslog(LOG_ERR, "internalization of xml failed");
return;
}
pn = prop_dictionary_get(dict, "evtype");
if (pn == NULL) {
syslog(LOG_ERR, "read_event: no key evtype");
goto out;
}
evtype = prop_number_integer_value(pn);
evdict = prop_dictionary_get(dict, "evdict");
if (evdict == NULL) {
syslog(LOG_ERR, "read_event: no key evdict");
goto out;
}
switch (evtype) {
case UDEV_EVENT_ATTACH:
monitor_queue_event(dict);
pae = pdev_array_entry_get_last();
pa = prop_array_copy(pae->pdev_array);
pdev_array_entry_unref(pae);
if (pa == NULL)
goto out;
prop_array_add(pa, evdict);
pdev_array_entry_insert(pa);
break;
case UDEV_EVENT_DETACH:
monitor_queue_event(dict);
if ((devdict = find_dev_dict(-1, evdict, &idx)) == NULL)
goto out;
pae = pdev_array_entry_get_last();
pa = prop_array_copy(pae->pdev_array);
pdev_array_entry_unref(pae);
if (pa == NULL)
goto out;
prop_array_remove(pa, idx);
pdev_array_entry_insert(pa);
break;
case UDEV_EV_KEY_UPDATE:
if ((devdict = find_dev_dict(-1, evdict, NULL)) == NULL)
goto out;
if ((ps = prop_dictionary_get(evdict, "key")) == NULL)
goto out;
if ((po = prop_dictionary_get(evdict, "value")) == NULL)
goto out;
prop_dictionary_set(devdict, prop_string_cstring_nocopy(ps), po);
break;
case UDEV_EV_KEY_REMOVE:
if ((devdict = find_dev_dict(-1, evdict, NULL)) == NULL)
goto out;
if ((ps = prop_dictionary_get(evdict, "key")) == NULL)
goto out;
prop_dictionary_remove(devdict, prop_string_cstring_nocopy(ps));
break;
default:
syslog(LOG_ERR, "read_event: unknown evtype %d", evtype);
}
out:
prop_object_release(dict);
return;
}
prop_array_t
udev_getdevs(int devfd)
{
prop_dictionary_t pd, rpd;
prop_string_t ps;
prop_array_t pa;
pd = prop_dictionary_create();
if (pd == NULL) {
err(1, "prop_dictionary_create()");
}
ps = prop_string_create_cstring("getdevs");
if (ps == NULL) {
prop_object_release(pd);
err(1, "prop_string_create_cstring()");
}
if (prop_dictionary_set(pd, "command", ps) == false) {
prop_object_release(ps);
prop_object_release(pd);
err(1, "prop_dictionary_set()");
}
prop_object_release(ps);
if (prop_dictionary_sendrecv_ioctl(pd, devfd, UDEVPROP, &rpd) != 0)
err(1, "prop_array_recv_ioctl()");
prop_object_release(pd);
pa = prop_dictionary_get(rpd, "array");
if (pa == NULL)
goto out;
prop_object_retain(pa);
out:
prop_object_release(rpd);
return pa;
}
static void
killed(int sig __unused)
{
syslog(LOG_ERR, "udevd stopped");
unlink("/var/run/udevd.pid");
pdev_array_clean();
exit(0);
}
static void
hangup(int sig __unused)
{
FILE *pidf;
int s;
syslog(LOG_ERR, "udevd hangup+resume");
pidf = fopen("/var/run/udevd.pid", "w");
if (pidf != NULL) {
fprintf(pidf, "%ld\n", (long)getpid());
fclose(pidf);
}
hangup_ongoing = 1;
close(fds[UDEV_SOCKET_FD_IDX].fd);
pdev_array_clean();
s = init_local_server(LISTEN_SOCKET_FILE, SOCK_STREAM, 0);
if (s < 0)
err(1, "init_local_server");
fds[UDEV_SOCKET_FD_IDX].fd = s;
pdev_array_entry_insert(udev_getdevs(udevfd));
hangup_ongoing = 0;
}
int
ignore_signal(int signum)
{
struct sigaction act;
int ret;
act.sa_handler = SIG_IGN;
sigemptyset(&act.sa_mask);
act.sa_flags = 0;
ret = sigaction(signum, &act, NULL);
return ret;
}
static int
set_signal(int signum, sig_t sig_func)
{
struct sigaction act;
int ret;
act.sa_handler = sig_func;
sigemptyset(&act.sa_mask);
act.sa_flags = 0;
ret = sigaction(signum, &act, NULL);
return ret;
}
int main(int argc, char *argv[])
{
int error __unused, i, r, s;
FILE *pidf;
int ch = 0;
while ((ch = getopt(argc, argv, "d")) != -1) {
switch(ch) {
case 'd':
debugopt = 1;
break;
default:
usage();
}
}
argc -= optind;
argv += optind;
TAILQ_INIT(&pdev_array_list);
TAILQ_INIT(&udev_monitor_list);
r = ignore_signal(SIGPIPE);
if (r != 0)
err(1, "could not ignore_signal SIGPIPE");
r = pthread_mutex_init(&(monitor_lock), NULL);
if (r != 0)
err(1, "could not allocate a pthread_mutex");
if ((udevfd = open(UDEV_DEVICE_PATH, O_RDWR | O_NONBLOCK)) == -1)
err(1, "%s", UDEV_DEVICE_PATH);
unblock_descriptor(udevfd);
s = init_local_server(LISTEN_SOCKET_FILE, SOCK_STREAM, 0);
if (s < 0)
err(1, "init_local_server");
pidf = fopen("/var/run/udevd.pid", "w");
#if 0
if (pidf == NULL)
err(1, "pidfile");
#endif
set_signal(SIGTERM, killed);
set_signal(SIGHUP, hangup);
if (debugopt == 0)
if (daemon(0, 0) == -1)
err(1, "daemon");
if (pidf != NULL) {
fprintf(pidf, "%ld\n", (long)getpid());
fclose(pidf);
}
syslog(LOG_ERR, "udevd started");
pdev_array_entry_insert(udev_getdevs(udevfd));
memset(fds, 0 , sizeof(fds));
fds[UDEV_DEVICE_FD_IDX].fd = udevfd;
fds[UDEV_DEVICE_FD_IDX].events = POLLIN;
fds[UDEV_SOCKET_FD_IDX].fd = s;
fds[UDEV_SOCKET_FD_IDX].events = POLLIN | POLLPRI;
for (;;) {
r = poll(fds, NFDS, -1);
if (r < 0) {
if (hangup_ongoing == 0) {
if (errno == EINTR) {
usleep(5000);
continue;
} else {
err(1, "polling...");
}
} else {
usleep(20000);
continue;
}
}
for (i = 0; (i < NFDS) && (r > 0); i++) {
if (fds[i].revents == 0)
continue;
--r;
switch (i) {
case UDEV_DEVICE_FD_IDX:
udev_read_event(udevfd);
break;
case UDEV_SOCKET_FD_IDX:
handle_new_connection(s);
break;
default:
break;
}
}
}
syslog(LOG_ERR, "udevd is exiting normally");
return 0;
}