mirror of
https://github.com/systemd/systemd
synced 2024-09-06 16:56:43 +00:00
udevadm: control - add --exit
This commit is contained in:
parent
9602509521
commit
ff2c503df0
4
TODO
4
TODO
|
@ -1,4 +1,6 @@
|
|||
- bind control socket in systemd
|
||||
- use inotify() in settle
|
||||
|
||||
- do not write age/configured in initramfs
|
||||
|
||||
- remove deprecated trigger --type=failed logic
|
||||
|
||||
|
|
|
@ -3,4 +3,4 @@ Description=udev Kernel Device Manager Socket
|
|||
DefaultDependencies=no
|
||||
|
||||
[Socket]
|
||||
ListenDatagram=@/org/kernel/udev/udevd
|
||||
ListenSequentialPacket=@/org/kernel/udev/udevd
|
||||
|
|
|
@ -16,6 +16,7 @@
|
|||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/poll.h>
|
||||
#include <sys/socket.h>
|
||||
#include <sys/un.h>
|
||||
|
||||
|
@ -33,7 +34,8 @@ enum udev_ctrl_msg_type {
|
|||
UDEV_CTRL_RELOAD_RULES,
|
||||
UDEV_CTRL_SET_ENV,
|
||||
UDEV_CTRL_SET_CHILDREN_MAX,
|
||||
UDEV_CTRL_SETTLE,
|
||||
UDEV_CTRL_PING,
|
||||
UDEV_CTRL_EXIT,
|
||||
};
|
||||
|
||||
struct udev_ctrl_msg_wire {
|
||||
|
@ -48,9 +50,8 @@ struct udev_ctrl_msg_wire {
|
|||
|
||||
struct udev_ctrl_msg {
|
||||
int refcount;
|
||||
struct udev_ctrl *uctrl;
|
||||
struct udev_ctrl_connection *conn;
|
||||
struct udev_ctrl_msg_wire ctrl_msg_wire;
|
||||
pid_t pid;
|
||||
};
|
||||
|
||||
struct udev_ctrl {
|
||||
|
@ -59,6 +60,13 @@ struct udev_ctrl {
|
|||
int sock;
|
||||
struct sockaddr_un saddr;
|
||||
socklen_t addrlen;
|
||||
bool connected;
|
||||
};
|
||||
|
||||
struct udev_ctrl_connection {
|
||||
int refcount;
|
||||
struct udev_ctrl *uctrl;
|
||||
int sock;
|
||||
};
|
||||
|
||||
static struct udev_ctrl *udev_ctrl_new(struct udev *udev)
|
||||
|
@ -81,7 +89,7 @@ struct udev_ctrl *udev_ctrl_new_from_socket(struct udev *udev, const char *socke
|
|||
if (uctrl == NULL)
|
||||
return NULL;
|
||||
|
||||
uctrl->sock = socket(AF_LOCAL, SOCK_DGRAM, 0);
|
||||
uctrl->sock = socket(AF_LOCAL, SOCK_SEQPACKET, 0);
|
||||
if (uctrl->sock < 0) {
|
||||
err(udev, "error getting socket: %m\n");
|
||||
udev_ctrl_unref(uctrl);
|
||||
|
@ -112,18 +120,21 @@ struct udev_ctrl *udev_ctrl_new_from_fd(struct udev *udev, int fd)
|
|||
int udev_ctrl_enable_receiving(struct udev_ctrl *uctrl)
|
||||
{
|
||||
int err;
|
||||
const int on = 1;
|
||||
|
||||
if (uctrl->addrlen > 0) {
|
||||
err = bind(uctrl->sock, (struct sockaddr *)&uctrl->saddr, uctrl->addrlen);
|
||||
if (err < 0) {
|
||||
err = -errno;
|
||||
err(uctrl->udev, "bind failed: %m\n");
|
||||
return err;
|
||||
}
|
||||
err = listen(uctrl->sock, 0);
|
||||
if (err < 0) {
|
||||
err = -errno;
|
||||
err(uctrl->udev, "listen failed: %m\n");
|
||||
return err;
|
||||
}
|
||||
}
|
||||
|
||||
/* enable receiving of the sender credentials */
|
||||
setsockopt(uctrl->sock, SOL_SOCKET, SO_PASSCRED, &on, sizeof(on));
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -140,16 +151,17 @@ struct udev_ctrl *udev_ctrl_ref(struct udev_ctrl *uctrl)
|
|||
return uctrl;
|
||||
}
|
||||
|
||||
void udev_ctrl_unref(struct udev_ctrl *uctrl)
|
||||
struct udev_ctrl *udev_ctrl_unref(struct udev_ctrl *uctrl)
|
||||
{
|
||||
if (uctrl == NULL)
|
||||
return;
|
||||
return NULL;
|
||||
uctrl->refcount--;
|
||||
if (uctrl->refcount > 0)
|
||||
return;
|
||||
return uctrl;
|
||||
if (uctrl->sock >= 0)
|
||||
close(uctrl->sock);
|
||||
free(uctrl);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
int udev_ctrl_get_fd(struct udev_ctrl *uctrl)
|
||||
|
@ -159,10 +171,55 @@ int udev_ctrl_get_fd(struct udev_ctrl *uctrl)
|
|||
return uctrl->sock;
|
||||
}
|
||||
|
||||
static int ctrl_send(struct udev_ctrl *uctrl, enum udev_ctrl_msg_type type, int intval, const char *buf)
|
||||
struct udev_ctrl_connection *udev_ctrl_get_connection(struct udev_ctrl *uctrl)
|
||||
{
|
||||
struct udev_ctrl_connection *conn;
|
||||
const int on = 1;
|
||||
|
||||
conn = calloc(1, sizeof(struct udev_ctrl_connection));
|
||||
if (conn == NULL)
|
||||
return NULL;
|
||||
conn->refcount = 1;
|
||||
conn->uctrl = uctrl;
|
||||
|
||||
conn->sock = accept4(uctrl->sock, NULL, NULL, SOCK_CLOEXEC);
|
||||
if (conn->sock < 0) {
|
||||
free(conn);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* enable receiving of the sender credentials */
|
||||
setsockopt(conn->sock, SOL_SOCKET, SO_PASSCRED, &on, sizeof(on));
|
||||
udev_ctrl_ref(uctrl);
|
||||
return conn;
|
||||
}
|
||||
|
||||
struct udev_ctrl_connection *udev_ctrl_connection_ref(struct udev_ctrl_connection *conn)
|
||||
{
|
||||
if (conn == NULL)
|
||||
return NULL;
|
||||
conn->refcount++;
|
||||
return conn;
|
||||
}
|
||||
|
||||
struct udev_ctrl_connection *udev_ctrl_connection_unref(struct udev_ctrl_connection *conn)
|
||||
{
|
||||
if (conn == NULL)
|
||||
return NULL;
|
||||
conn->refcount--;
|
||||
if (conn->refcount > 0)
|
||||
return conn;
|
||||
if (conn->sock >= 0)
|
||||
close(conn->sock);
|
||||
udev_ctrl_unref(conn->uctrl);
|
||||
free(conn);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static int ctrl_send(struct udev_ctrl *uctrl, enum udev_ctrl_msg_type type, int intval, const char *buf, int timeout)
|
||||
{
|
||||
struct udev_ctrl_msg_wire ctrl_msg_wire;
|
||||
int err;
|
||||
int err = 0;
|
||||
|
||||
memset(&ctrl_msg_wire, 0x00, sizeof(struct udev_ctrl_msg_wire));
|
||||
strcpy(ctrl_msg_wire.version, "udev-" VERSION);
|
||||
|
@ -174,51 +231,89 @@ static int ctrl_send(struct udev_ctrl *uctrl, enum udev_ctrl_msg_type type, int
|
|||
else
|
||||
ctrl_msg_wire.intval = intval;
|
||||
|
||||
err = sendto(uctrl->sock, &ctrl_msg_wire, sizeof(ctrl_msg_wire), 0,
|
||||
(struct sockaddr *)&uctrl->saddr, uctrl->addrlen);
|
||||
if (err == -1) {
|
||||
err(uctrl->udev, "error sending message: %m\n");
|
||||
if (!uctrl->connected) {
|
||||
if (connect(uctrl->sock, (struct sockaddr *)&uctrl->saddr, uctrl->addrlen) < 0) {
|
||||
err = -errno;
|
||||
goto out;
|
||||
}
|
||||
uctrl->connected = true;
|
||||
}
|
||||
if (send(uctrl->sock, &ctrl_msg_wire, sizeof(ctrl_msg_wire), 0) < 0) {
|
||||
err = -errno;
|
||||
goto out;
|
||||
}
|
||||
|
||||
/* wait for peer message handling or disconnect */
|
||||
for (;;) {
|
||||
struct pollfd pfd[1];
|
||||
int r;
|
||||
|
||||
pfd[0].fd = uctrl->sock;
|
||||
pfd[0].events = POLLIN;
|
||||
r = poll(pfd, 1, timeout * 1000);
|
||||
if (r < 0) {
|
||||
if (errno == EINTR)
|
||||
continue;
|
||||
err = -errno;
|
||||
break;
|
||||
}
|
||||
|
||||
if (r > 0 && pfd[0].revents & POLLERR) {
|
||||
err = -EIO;
|
||||
break;
|
||||
}
|
||||
|
||||
if (r == 0)
|
||||
err = -ETIMEDOUT;
|
||||
break;
|
||||
}
|
||||
out:
|
||||
return err;
|
||||
}
|
||||
|
||||
int udev_ctrl_send_set_log_level(struct udev_ctrl *uctrl, int priority)
|
||||
int udev_ctrl_send_set_log_level(struct udev_ctrl *uctrl, int priority, int timeout)
|
||||
{
|
||||
return ctrl_send(uctrl, UDEV_CTRL_SET_LOG_LEVEL, priority, NULL);
|
||||
return ctrl_send(uctrl, UDEV_CTRL_SET_LOG_LEVEL, priority, NULL, timeout);
|
||||
}
|
||||
|
||||
int udev_ctrl_send_stop_exec_queue(struct udev_ctrl *uctrl)
|
||||
int udev_ctrl_send_stop_exec_queue(struct udev_ctrl *uctrl, int timeout)
|
||||
{
|
||||
return ctrl_send(uctrl, UDEV_CTRL_STOP_EXEC_QUEUE, 0, NULL);
|
||||
return ctrl_send(uctrl, UDEV_CTRL_STOP_EXEC_QUEUE, 0, NULL, timeout);
|
||||
}
|
||||
|
||||
int udev_ctrl_send_start_exec_queue(struct udev_ctrl *uctrl)
|
||||
int udev_ctrl_send_start_exec_queue(struct udev_ctrl *uctrl, int timeout)
|
||||
{
|
||||
return ctrl_send(uctrl, UDEV_CTRL_START_EXEC_QUEUE, 0, NULL);
|
||||
return ctrl_send(uctrl, UDEV_CTRL_START_EXEC_QUEUE, 0, NULL, timeout);
|
||||
}
|
||||
|
||||
int udev_ctrl_send_reload_rules(struct udev_ctrl *uctrl)
|
||||
int udev_ctrl_send_reload_rules(struct udev_ctrl *uctrl, int timeout)
|
||||
{
|
||||
return ctrl_send(uctrl, UDEV_CTRL_RELOAD_RULES, 0, NULL);
|
||||
return ctrl_send(uctrl, UDEV_CTRL_RELOAD_RULES, 0, NULL, timeout);
|
||||
}
|
||||
|
||||
int udev_ctrl_send_set_env(struct udev_ctrl *uctrl, const char *key)
|
||||
int udev_ctrl_send_set_env(struct udev_ctrl *uctrl, const char *key, int timeout)
|
||||
{
|
||||
return ctrl_send(uctrl, UDEV_CTRL_SET_ENV, 0, key);
|
||||
return ctrl_send(uctrl, UDEV_CTRL_SET_ENV, 0, key, timeout);
|
||||
}
|
||||
|
||||
int udev_ctrl_send_set_children_max(struct udev_ctrl *uctrl, int count)
|
||||
int udev_ctrl_send_set_children_max(struct udev_ctrl *uctrl, int count, int timeout)
|
||||
{
|
||||
return ctrl_send(uctrl, UDEV_CTRL_SET_CHILDREN_MAX, count, NULL);
|
||||
return ctrl_send(uctrl, UDEV_CTRL_SET_CHILDREN_MAX, count, NULL, timeout);
|
||||
}
|
||||
|
||||
int udev_ctrl_send_settle(struct udev_ctrl *uctrl)
|
||||
int udev_ctrl_send_ping(struct udev_ctrl *uctrl, int timeout)
|
||||
{
|
||||
return ctrl_send(uctrl, UDEV_CTRL_SETTLE, 0, NULL);
|
||||
return ctrl_send(uctrl, UDEV_CTRL_PING, 0, NULL, timeout);
|
||||
}
|
||||
|
||||
struct udev_ctrl_msg *udev_ctrl_receive_msg(struct udev_ctrl *uctrl)
|
||||
int udev_ctrl_send_exit(struct udev_ctrl *uctrl, int timeout)
|
||||
{
|
||||
return ctrl_send(uctrl, UDEV_CTRL_EXIT, 0, NULL, timeout);
|
||||
}
|
||||
|
||||
struct udev_ctrl_msg *udev_ctrl_receive_msg(struct udev_ctrl_connection *conn)
|
||||
{
|
||||
struct udev *udev = conn->uctrl->udev;
|
||||
struct udev_ctrl_msg *uctrl_msg;
|
||||
ssize_t size;
|
||||
struct msghdr smsg;
|
||||
|
@ -231,7 +326,7 @@ struct udev_ctrl_msg *udev_ctrl_receive_msg(struct udev_ctrl *uctrl)
|
|||
if (uctrl_msg == NULL)
|
||||
return NULL;
|
||||
uctrl_msg->refcount = 1;
|
||||
uctrl_msg->uctrl = uctrl;
|
||||
uctrl_msg->conn = conn;
|
||||
|
||||
iov.iov_base = &uctrl_msg->ctrl_msg_wire;
|
||||
iov.iov_len = sizeof(struct udev_ctrl_msg_wire);
|
||||
|
@ -242,32 +337,31 @@ struct udev_ctrl_msg *udev_ctrl_receive_msg(struct udev_ctrl *uctrl)
|
|||
smsg.msg_control = cred_msg;
|
||||
smsg.msg_controllen = sizeof(cred_msg);
|
||||
|
||||
size = recvmsg(uctrl->sock, &smsg, 0);
|
||||
size = recvmsg(conn->sock, &smsg, 0);
|
||||
if (size < 0) {
|
||||
err(uctrl->udev, "unable to receive user udevd message: %m\n");
|
||||
err(udev, "unable to receive user udevd message: %m\n");
|
||||
goto err;
|
||||
}
|
||||
cmsg = CMSG_FIRSTHDR(&smsg);
|
||||
cred = (struct ucred *) CMSG_DATA(cmsg);
|
||||
|
||||
if (cmsg == NULL || cmsg->cmsg_type != SCM_CREDENTIALS) {
|
||||
err(uctrl->udev, "no sender credentials received, message ignored\n");
|
||||
err(udev, "no sender credentials received, message ignored\n");
|
||||
goto err;
|
||||
}
|
||||
|
||||
if (cred->uid != 0) {
|
||||
err(uctrl->udev, "sender uid=%i, message ignored\n", cred->uid);
|
||||
err(udev, "sender uid=%i, message ignored\n", cred->uid);
|
||||
goto err;
|
||||
}
|
||||
|
||||
uctrl_msg->pid = cred->pid;
|
||||
|
||||
if (uctrl_msg->ctrl_msg_wire.magic != UDEV_CTRL_MAGIC) {
|
||||
err(uctrl->udev, "message magic 0x%08x doesn't match, ignore it\n", uctrl_msg->ctrl_msg_wire.magic);
|
||||
err(udev, "message magic 0x%08x doesn't match, ignore it\n", uctrl_msg->ctrl_msg_wire.magic);
|
||||
goto err;
|
||||
}
|
||||
|
||||
dbg(uctrl->udev, "created ctrl_msg %p (%i)\n", uctrl_msg, uctrl_msg->ctrl_msg_wire.type);
|
||||
dbg(udev, "created ctrl_msg %p (%i)\n", uctrl_msg, uctrl_msg->ctrl_msg_wire.type);
|
||||
udev_ctrl_connection_ref(conn);
|
||||
return uctrl_msg;
|
||||
err:
|
||||
udev_ctrl_msg_unref(uctrl_msg);
|
||||
|
@ -282,15 +376,17 @@ struct udev_ctrl_msg *udev_ctrl_msg_ref(struct udev_ctrl_msg *ctrl_msg)
|
|||
return ctrl_msg;
|
||||
}
|
||||
|
||||
void udev_ctrl_msg_unref(struct udev_ctrl_msg *ctrl_msg)
|
||||
struct udev_ctrl_msg *udev_ctrl_msg_unref(struct udev_ctrl_msg *ctrl_msg)
|
||||
{
|
||||
if (ctrl_msg == NULL)
|
||||
return;
|
||||
return NULL;
|
||||
ctrl_msg->refcount--;
|
||||
if (ctrl_msg->refcount > 0)
|
||||
return;
|
||||
dbg(ctrl_msg->uctrl->udev, "release ctrl_msg %p\n", ctrl_msg);
|
||||
return ctrl_msg;
|
||||
dbg(ctrl_msg->conn->uctrl->udev, "release ctrl_msg %p\n", ctrl_msg);
|
||||
udev_ctrl_connection_unref(ctrl_msg->conn);
|
||||
free(ctrl_msg);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
int udev_ctrl_get_set_log_level(struct udev_ctrl_msg *ctrl_msg)
|
||||
|
@ -335,9 +431,16 @@ int udev_ctrl_get_set_children_max(struct udev_ctrl_msg *ctrl_msg)
|
|||
return -1;
|
||||
}
|
||||
|
||||
pid_t udev_ctrl_get_settle(struct udev_ctrl_msg *ctrl_msg)
|
||||
int udev_ctrl_get_ping(struct udev_ctrl_msg *ctrl_msg)
|
||||
{
|
||||
if (ctrl_msg->ctrl_msg_wire.type == UDEV_CTRL_SETTLE)
|
||||
return ctrl_msg->pid;
|
||||
if (ctrl_msg->ctrl_msg_wire.type == UDEV_CTRL_PING)
|
||||
return 1;
|
||||
return -1;
|
||||
}
|
||||
|
||||
int udev_ctrl_get_exit(struct udev_ctrl_msg *ctrl_msg)
|
||||
{
|
||||
if (ctrl_msg->ctrl_msg_wire.type == UDEV_CTRL_EXIT)
|
||||
return 1;
|
||||
return -1;
|
||||
}
|
||||
|
|
|
@ -127,26 +127,31 @@ struct udev_ctrl *udev_ctrl_new_from_socket(struct udev *udev, const char *socke
|
|||
struct udev_ctrl *udev_ctrl_new_from_fd(struct udev *udev, int fd);
|
||||
int udev_ctrl_enable_receiving(struct udev_ctrl *uctrl);
|
||||
struct udev_ctrl *udev_ctrl_ref(struct udev_ctrl *uctrl);
|
||||
void udev_ctrl_unref(struct udev_ctrl *uctrl);
|
||||
struct udev_ctrl *udev_ctrl_unref(struct udev_ctrl *uctrl);
|
||||
struct udev *udev_ctrl_get_udev(struct udev_ctrl *uctrl);
|
||||
int udev_ctrl_get_fd(struct udev_ctrl *uctrl);
|
||||
int udev_ctrl_send_set_log_level(struct udev_ctrl *uctrl, int priority);
|
||||
int udev_ctrl_send_stop_exec_queue(struct udev_ctrl *uctrl);
|
||||
int udev_ctrl_send_start_exec_queue(struct udev_ctrl *uctrl);
|
||||
int udev_ctrl_send_reload_rules(struct udev_ctrl *uctrl);
|
||||
int udev_ctrl_send_settle(struct udev_ctrl *uctrl);
|
||||
int udev_ctrl_send_set_env(struct udev_ctrl *uctrl, const char *key);
|
||||
int udev_ctrl_send_set_children_max(struct udev_ctrl *uctrl, int count);
|
||||
int udev_ctrl_send_set_log_level(struct udev_ctrl *uctrl, int priority, int timeout);
|
||||
int udev_ctrl_send_stop_exec_queue(struct udev_ctrl *uctrl, int timeout);
|
||||
int udev_ctrl_send_start_exec_queue(struct udev_ctrl *uctrl, int timeout);
|
||||
int udev_ctrl_send_reload_rules(struct udev_ctrl *uctrl, int timeout);
|
||||
int udev_ctrl_send_ping(struct udev_ctrl *uctrl, int timeout);
|
||||
int udev_ctrl_send_exit(struct udev_ctrl *uctrl, int timeout);
|
||||
int udev_ctrl_send_set_env(struct udev_ctrl *uctrl, const char *key, int timeout);
|
||||
int udev_ctrl_send_set_children_max(struct udev_ctrl *uctrl, int count, int timeout);
|
||||
struct udev_ctrl_connection;
|
||||
struct udev_ctrl_connection *udev_ctrl_get_connection(struct udev_ctrl *uctrl);
|
||||
struct udev_ctrl_connection *udev_ctrl_connection_ref(struct udev_ctrl_connection *conn);
|
||||
struct udev_ctrl_connection *udev_ctrl_connection_unref(struct udev_ctrl_connection *conn);
|
||||
struct udev_ctrl_msg;
|
||||
struct udev_ctrl_msg *udev_ctrl_msg(struct udev_ctrl *uctrl);
|
||||
struct udev_ctrl_msg *udev_ctrl_receive_msg(struct udev_ctrl *uctrl);
|
||||
struct udev_ctrl_msg *udev_ctrl_receive_msg(struct udev_ctrl_connection *conn);
|
||||
struct udev_ctrl_msg *udev_ctrl_msg_ref(struct udev_ctrl_msg *ctrl_msg);
|
||||
void udev_ctrl_msg_unref(struct udev_ctrl_msg *ctrl_msg);
|
||||
struct udev_ctrl_msg *udev_ctrl_msg_unref(struct udev_ctrl_msg *ctrl_msg);
|
||||
int udev_ctrl_get_set_log_level(struct udev_ctrl_msg *ctrl_msg);
|
||||
int udev_ctrl_get_stop_exec_queue(struct udev_ctrl_msg *ctrl_msg);
|
||||
int udev_ctrl_get_start_exec_queue(struct udev_ctrl_msg *ctrl_msg);
|
||||
int udev_ctrl_get_reload_rules(struct udev_ctrl_msg *ctrl_msg);
|
||||
pid_t udev_ctrl_get_settle(struct udev_ctrl_msg *ctrl_msg);
|
||||
int udev_ctrl_get_ping(struct udev_ctrl_msg *ctrl_msg);
|
||||
int udev_ctrl_get_exit(struct udev_ctrl_msg *ctrl_msg);
|
||||
const char *udev_ctrl_get_set_env(struct udev_ctrl_msg *ctrl_msg);
|
||||
int udev_ctrl_get_set_children_max(struct udev_ctrl_msg *ctrl_msg);
|
||||
|
||||
|
@ -154,6 +159,7 @@ int udev_ctrl_get_set_children_max(struct udev_ctrl_msg *ctrl_msg);
|
|||
struct udev_list_node {
|
||||
struct udev_list_node *next, *prev;
|
||||
};
|
||||
#define UDEV_LIST(list) struct udev_list_node list = { &(list), &(list) }
|
||||
void udev_list_init(struct udev_list_node *list);
|
||||
int udev_list_is_empty(struct udev_list_node *list);
|
||||
void udev_list_node_append(struct udev_list_node *new, struct udev_list_node *list);
|
||||
|
|
|
@ -30,21 +30,25 @@
|
|||
static void print_help(void)
|
||||
{
|
||||
printf("Usage: udevadm control COMMAND\n"
|
||||
" --exit instruct the daemon to cleanup and exit\n"
|
||||
" --log-priority=<level> set the udev log level for the daemon\n"
|
||||
" --stop-exec-queue keep udevd from executing events, queue only\n"
|
||||
" --start-exec-queue execute events, flush queue\n"
|
||||
" --reload-rules reloads the rules files\n"
|
||||
" --property=<KEY>=<value> set a global property for all events\n"
|
||||
" --children-max=<N> maximum number of children\n"
|
||||
" --timeout=<seconds> maximum time to block for a reply\n"
|
||||
" --help print this help text\n\n");
|
||||
}
|
||||
|
||||
int udevadm_control(struct udev *udev, int argc, char *argv[])
|
||||
{
|
||||
struct udev_ctrl *uctrl = NULL;
|
||||
int timeout = 60;
|
||||
int rc = 1;
|
||||
|
||||
static const struct option options[] = {
|
||||
{ "exit", no_argument, NULL, 'e' },
|
||||
{ "log-priority", required_argument, NULL, 'l' },
|
||||
{ "stop-exec-queue", no_argument, NULL, 's' },
|
||||
{ "start-exec-queue", no_argument, NULL, 'S' },
|
||||
|
@ -52,6 +56,7 @@ int udevadm_control(struct udev *udev, int argc, char *argv[])
|
|||
{ "property", required_argument, NULL, 'p' },
|
||||
{ "env", required_argument, NULL, 'p' },
|
||||
{ "children-max", required_argument, NULL, 'm' },
|
||||
{ "timeout", required_argument, NULL, 't' },
|
||||
{ "help", no_argument, NULL, 'h' },
|
||||
{}
|
||||
};
|
||||
|
@ -67,39 +72,46 @@ int udevadm_control(struct udev *udev, int argc, char *argv[])
|
|||
|
||||
for (;;) {
|
||||
int option;
|
||||
int i;
|
||||
char *endp;
|
||||
|
||||
option = getopt_long(argc, argv, "l:sSRp:m:h", options, NULL);
|
||||
option = getopt_long(argc, argv, "el:sSRp:m:h", options, NULL);
|
||||
if (option == -1)
|
||||
break;
|
||||
|
||||
switch (option) {
|
||||
case 'l':
|
||||
i = util_log_priority(optarg);
|
||||
if (i < 0) {
|
||||
fprintf(stderr, "invalid number '%s'\n", optarg);
|
||||
goto exit;
|
||||
}
|
||||
if (udev_ctrl_send_set_log_level(uctrl, util_log_priority(optarg)) < 0)
|
||||
case 'e':
|
||||
if (udev_ctrl_send_exit(uctrl, timeout) < 0)
|
||||
rc = 2;
|
||||
else
|
||||
rc = 0;
|
||||
break;
|
||||
case 'l': {
|
||||
int i;
|
||||
|
||||
i = util_log_priority(optarg);
|
||||
if (i < 0) {
|
||||
fprintf(stderr, "invalid number '%s'\n", optarg);
|
||||
goto out;
|
||||
}
|
||||
if (udev_ctrl_send_set_log_level(uctrl, util_log_priority(optarg), timeout) < 0)
|
||||
rc = 2;
|
||||
else
|
||||
rc = 0;
|
||||
break;
|
||||
}
|
||||
case 's':
|
||||
if (udev_ctrl_send_stop_exec_queue(uctrl) < 0)
|
||||
if (udev_ctrl_send_stop_exec_queue(uctrl, timeout) < 0)
|
||||
rc = 2;
|
||||
else
|
||||
rc = 0;
|
||||
break;
|
||||
case 'S':
|
||||
if (udev_ctrl_send_start_exec_queue(uctrl) < 0)
|
||||
if (udev_ctrl_send_start_exec_queue(uctrl, timeout) < 0)
|
||||
rc = 2;
|
||||
else
|
||||
rc = 0;
|
||||
break;
|
||||
case 'R':
|
||||
if (udev_ctrl_send_reload_rules(uctrl) < 0)
|
||||
if (udev_ctrl_send_reload_rules(uctrl, timeout) < 0)
|
||||
rc = 2;
|
||||
else
|
||||
rc = 0;
|
||||
|
@ -107,34 +119,45 @@ int udevadm_control(struct udev *udev, int argc, char *argv[])
|
|||
case 'p':
|
||||
if (strchr(optarg, '=') == NULL) {
|
||||
fprintf(stderr, "expect <KEY>=<value> instead of '%s'\n", optarg);
|
||||
goto exit;
|
||||
goto out;
|
||||
}
|
||||
if (udev_ctrl_send_set_env(uctrl, optarg) < 0)
|
||||
if (udev_ctrl_send_set_env(uctrl, optarg, timeout) < 0)
|
||||
rc = 2;
|
||||
else
|
||||
rc = 0;
|
||||
break;
|
||||
case 'm':
|
||||
case 'm': {
|
||||
char *endp;
|
||||
int i;
|
||||
|
||||
i = strtoul(optarg, &endp, 0);
|
||||
if (endp[0] != '\0' || i < 1) {
|
||||
fprintf(stderr, "invalid number '%s'\n", optarg);
|
||||
goto exit;
|
||||
goto out;
|
||||
}
|
||||
if (udev_ctrl_send_set_children_max(uctrl, i) < 0)
|
||||
if (udev_ctrl_send_set_children_max(uctrl, i, timeout) < 0)
|
||||
rc = 2;
|
||||
else
|
||||
rc = 0;
|
||||
break;
|
||||
}
|
||||
case 't': {
|
||||
int seconds;
|
||||
|
||||
seconds = atoi(optarg);
|
||||
if (seconds >= 0)
|
||||
timeout = seconds;
|
||||
else
|
||||
fprintf(stderr, "invalid timeout value\n");
|
||||
break;
|
||||
}
|
||||
case 'h':
|
||||
print_help();
|
||||
rc = 0;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (rc == 1)
|
||||
err(udev, "unrecognized command\n");
|
||||
exit:
|
||||
out:
|
||||
udev_ctrl_unref(uctrl);
|
||||
return rc;
|
||||
}
|
||||
|
|
|
@ -44,8 +44,6 @@ static void sig_handler(int signum)
|
|||
switch (signum) {
|
||||
case SIGALRM:
|
||||
is_timeout = 1;
|
||||
case SIGUSR1:
|
||||
;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -78,9 +76,7 @@ int udevadm_settle(struct udev *udev, int argc, char *argv[])
|
|||
sigemptyset (&act.sa_mask);
|
||||
act.sa_flags = 0;
|
||||
sigaction(SIGALRM, &act, NULL);
|
||||
sigaction(SIGUSR1, &act, NULL);
|
||||
sigemptyset(&mask);
|
||||
sigaddset(&mask, SIGUSR1);
|
||||
sigaddset(&mask, SIGALRM);
|
||||
sigprocmask(SIG_UNBLOCK, &mask, NULL);
|
||||
|
||||
|
@ -168,15 +164,12 @@ int udevadm_settle(struct udev *udev, int argc, char *argv[])
|
|||
|
||||
uctrl = udev_ctrl_new_from_socket(udev, UDEV_CTRL_SOCK_PATH);
|
||||
if (uctrl != NULL) {
|
||||
sigset_t oldmask;
|
||||
|
||||
sigemptyset(&mask);
|
||||
sigaddset(&mask, SIGUSR1);
|
||||
sigaddset(&mask, SIGALRM);
|
||||
sigprocmask(SIG_BLOCK, &mask, &oldmask);
|
||||
if (udev_ctrl_send_settle(uctrl) > 0)
|
||||
sigsuspend(&oldmask);
|
||||
sigprocmask(SIG_SETMASK, &oldmask, NULL);
|
||||
if (udev_ctrl_send_ping(uctrl, timeout) < 0) {
|
||||
info(udev, "no connection to daemon\n");
|
||||
udev_ctrl_unref(uctrl);
|
||||
rc = 0;
|
||||
goto out;
|
||||
}
|
||||
udev_ctrl_unref(uctrl);
|
||||
}
|
||||
}
|
||||
|
@ -223,7 +216,7 @@ int udevadm_settle(struct udev *udev, int argc, char *argv[])
|
|||
udev_list_entry_get_value(list_entry));
|
||||
}
|
||||
}
|
||||
|
||||
out:
|
||||
udev_queue_unref(udev_queue);
|
||||
return rc;
|
||||
}
|
||||
|
|
|
@ -296,6 +296,12 @@
|
|||
<refsect2><title>udevadm control <replaceable>command</replaceable></title>
|
||||
<para>Modify the internal state of the running udev daemon.</para>
|
||||
<variablelist>
|
||||
<varlistentry>
|
||||
<term><option>--exit</option></term>
|
||||
<listitem>
|
||||
<para>Signal and wait for udevd to exit.</para>
|
||||
</listitem>
|
||||
</varlistentry>
|
||||
<varlistentry>
|
||||
<term><option>--log-priority=<replaceable>value</replaceable></option></term>
|
||||
<listitem>
|
||||
|
@ -339,6 +345,12 @@
|
|||
same time.</para>
|
||||
</listitem>
|
||||
</varlistentry>
|
||||
<varlistentry>
|
||||
<term><option>--timeout=</option><replaceable>seconds</replaceable></term>
|
||||
<listitem>
|
||||
<para>The maximum number seonds to wait for a reply from udevd.</para>
|
||||
</listitem>
|
||||
</varlistentry>
|
||||
<varlistentry>
|
||||
<term><option>--help</option></term>
|
||||
<listitem>
|
||||
|
|
463
udev/udevd.c
463
udev/udevd.c
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright (C) 2004-2009 Kay Sievers <kay.sievers@vrfy.org>
|
||||
* Copyright (C) 2004-2011 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>
|
||||
|
@ -36,7 +36,7 @@
|
|||
#include <sys/socket.h>
|
||||
#include <sys/un.h>
|
||||
#include <sys/signalfd.h>
|
||||
#include <sys/select.h>
|
||||
#include <sys/epoll.h>
|
||||
#include <sys/poll.h>
|
||||
#include <sys/wait.h>
|
||||
#include <sys/stat.h>
|
||||
|
@ -76,34 +76,20 @@ static struct udev_queue_export *udev_queue_export;
|
|||
static struct udev_ctrl *udev_ctrl;
|
||||
static struct udev_monitor *monitor;
|
||||
static int worker_watch[2] = { -1, -1 };
|
||||
static pid_t settle_pid;
|
||||
static int fd_signal = -1;
|
||||
static int fd_ep = -1;
|
||||
static int fd_inotify = -1;
|
||||
static bool stop_exec_queue;
|
||||
static bool reload_config;
|
||||
static int children;
|
||||
static int children_max;
|
||||
static int exec_delay;
|
||||
static sigset_t orig_sigmask;
|
||||
static struct udev_list_node event_list;
|
||||
static struct udev_list_node worker_list;
|
||||
static UDEV_LIST(event_list);
|
||||
static UDEV_LIST(worker_list);
|
||||
static bool udev_exit;
|
||||
static volatile sig_atomic_t worker_exit;
|
||||
|
||||
enum poll_fd {
|
||||
FD_CONTROL,
|
||||
FD_NETLINK,
|
||||
FD_INOTIFY,
|
||||
FD_SIGNAL,
|
||||
FD_WORKER,
|
||||
};
|
||||
|
||||
static struct pollfd pfd[] = {
|
||||
[FD_NETLINK] = { .events = POLLIN, .fd = -1 },
|
||||
[FD_WORKER] = { .events = POLLIN, .fd = -1 },
|
||||
[FD_SIGNAL] = { .events = POLLIN, .fd = -1 },
|
||||
[FD_INOTIFY] = { .events = POLLIN, .fd = -1 },
|
||||
[FD_CONTROL] = { .events = POLLIN, .fd = -1 },
|
||||
};
|
||||
|
||||
enum event_state {
|
||||
EVENT_UNDEF,
|
||||
EVENT_QUEUED,
|
||||
|
@ -135,6 +121,8 @@ static struct event *node_to_event(struct udev_list_node *node)
|
|||
return (struct event *)event;
|
||||
}
|
||||
|
||||
static void event_queue_cleanup(struct udev *udev, enum event_state type);
|
||||
|
||||
enum worker_state {
|
||||
WORKER_UNDEF,
|
||||
WORKER_RUNNING,
|
||||
|
@ -167,17 +155,18 @@ static struct worker *node_to_worker(struct udev_list_node *node)
|
|||
return (struct worker *)worker;
|
||||
}
|
||||
|
||||
static void event_queue_delete(struct event *event)
|
||||
static void event_queue_delete(struct event *event, bool export)
|
||||
{
|
||||
udev_list_node_remove(&event->node);
|
||||
|
||||
/* mark as failed, if "add" event returns non-zero */
|
||||
if (event->exitcode != 0 && strcmp(udev_device_get_action(event->dev), "remove") != 0)
|
||||
udev_queue_export_device_failed(udev_queue_export, event->dev);
|
||||
else
|
||||
udev_queue_export_device_finished(udev_queue_export, event->dev);
|
||||
|
||||
info(event->udev, "seq %llu done with %i\n", udev_device_get_seqnum(event->dev), event->exitcode);
|
||||
if (export) {
|
||||
/* mark as failed, if "add" event returns non-zero */
|
||||
if (event->exitcode != 0 && strcmp(udev_device_get_action(event->dev), "remove") != 0)
|
||||
udev_queue_export_device_failed(udev_queue_export, event->dev);
|
||||
else
|
||||
udev_queue_export_device_finished(udev_queue_export, event->dev);
|
||||
info(event->udev, "seq %llu done with %i\n", udev_device_get_seqnum(event->dev), event->exitcode);
|
||||
}
|
||||
udev_device_unref(event->dev);
|
||||
free(event);
|
||||
}
|
||||
|
@ -200,28 +189,44 @@ static struct worker *worker_ref(struct worker *worker)
|
|||
return worker;
|
||||
}
|
||||
|
||||
static void worker_cleanup(struct worker *worker)
|
||||
{
|
||||
udev_list_node_remove(&worker->node);
|
||||
udev_monitor_unref(worker->monitor);
|
||||
children--;
|
||||
free(worker);
|
||||
}
|
||||
|
||||
static void worker_unref(struct worker *worker)
|
||||
{
|
||||
worker->refcount--;
|
||||
if (worker->refcount > 0)
|
||||
return;
|
||||
|
||||
udev_list_node_remove(&worker->node);
|
||||
udev_monitor_unref(worker->monitor);
|
||||
children--;
|
||||
info(worker->udev, "worker [%u] cleaned up\n", worker->pid);
|
||||
free(worker);
|
||||
worker_cleanup(worker);
|
||||
}
|
||||
|
||||
static void worker_list_cleanup(struct udev *udev)
|
||||
{
|
||||
struct udev_list_node *loop, *tmp;
|
||||
|
||||
udev_list_node_foreach_safe(loop, tmp, &worker_list) {
|
||||
struct worker *worker = node_to_worker(loop);
|
||||
|
||||
worker_cleanup(worker);
|
||||
}
|
||||
}
|
||||
|
||||
static void worker_new(struct event *event)
|
||||
{
|
||||
struct udev *udev = event->udev;
|
||||
struct worker *worker;
|
||||
struct udev_monitor *worker_monitor;
|
||||
pid_t pid;
|
||||
struct sigaction act;
|
||||
|
||||
/* listen for new events */
|
||||
worker_monitor = udev_monitor_new_from_netlink(event->udev, NULL);
|
||||
worker_monitor = udev_monitor_new_from_netlink(udev, NULL);
|
||||
if (worker_monitor == NULL)
|
||||
return;
|
||||
/* allow the main daemon netlink address to send devices to the worker */
|
||||
|
@ -235,7 +240,7 @@ static void worker_new(struct event *event)
|
|||
}
|
||||
/* worker + event reference */
|
||||
worker->refcount = 2;
|
||||
worker->udev = event->udev;
|
||||
worker->udev = udev;
|
||||
|
||||
pid = fork();
|
||||
switch (pid) {
|
||||
|
@ -247,10 +252,18 @@ static void worker_new(struct event *event)
|
|||
.events = POLLIN,
|
||||
};
|
||||
|
||||
/* move initial device from queue */
|
||||
dev = event->dev;
|
||||
event->dev = NULL;
|
||||
|
||||
free(worker);
|
||||
worker_list_cleanup(udev);
|
||||
event_queue_cleanup(udev, EVENT_UNDEF);
|
||||
udev_queue_export_unref(udev_queue_export);
|
||||
udev_monitor_unref(monitor);
|
||||
udev_ctrl_unref(udev_ctrl);
|
||||
close(pfd[FD_SIGNAL].fd);
|
||||
close(fd_signal);
|
||||
close(fd_ep);
|
||||
close(worker_watch[READ_END]);
|
||||
udev_log_close();
|
||||
udev_log_init("udevd-work");
|
||||
|
@ -274,16 +287,13 @@ static void worker_new(struct event *event)
|
|||
/* request TERM signal if parent exits */
|
||||
prctl(PR_SET_PDEATHSIG, SIGTERM);
|
||||
|
||||
/* initial device */
|
||||
dev = event->dev;
|
||||
|
||||
do {
|
||||
struct udev_event *udev_event;
|
||||
struct worker_message msg = {};
|
||||
int err;
|
||||
int failed = 0;
|
||||
|
||||
info(event->udev, "seq %llu running\n", udev_device_get_seqnum(dev));
|
||||
info(udev, "seq %llu running\n", udev_device_get_seqnum(dev));
|
||||
udev_event = udev_event_new(dev);
|
||||
if (udev_event == NULL)
|
||||
_exit(3);
|
||||
|
@ -308,7 +318,7 @@ static void worker_new(struct event *event)
|
|||
|
||||
/* apply/restore inotify watch */
|
||||
if (err == 0 && udev_event->inotify_watch) {
|
||||
udev_watch_begin(udev_event->udev, dev);
|
||||
udev_watch_begin(udev, dev);
|
||||
udev_device_update_db(dev);
|
||||
}
|
||||
|
||||
|
@ -323,7 +333,7 @@ static void worker_new(struct event *event)
|
|||
msg.pid = getpid();
|
||||
send(worker_watch[WRITE_END], &msg, sizeof(struct worker_message), 0);
|
||||
|
||||
info(event->udev, "seq %llu processed with %i\n", udev_device_get_seqnum(dev), err);
|
||||
info(udev, "seq %llu processed with %i\n", udev_device_get_seqnum(dev), err);
|
||||
udev_event_unref(udev_event);
|
||||
udev_device_unref(dev);
|
||||
dev = NULL;
|
||||
|
@ -344,15 +354,19 @@ static void worker_new(struct event *event)
|
|||
}
|
||||
} while (dev != NULL);
|
||||
|
||||
close(fd_inotify);
|
||||
close(worker_watch[WRITE_END]);
|
||||
udev_rules_unref(rules);
|
||||
udev_monitor_unref(worker_monitor);
|
||||
udev_unref(udev);
|
||||
udev_log_close();
|
||||
exit(0);
|
||||
exit(EXIT_SUCCESS);
|
||||
}
|
||||
case -1:
|
||||
udev_monitor_unref(worker_monitor);
|
||||
event->state = EVENT_QUEUED;
|
||||
free(worker);
|
||||
err(event->udev, "fork of child failed: %m\n");
|
||||
err(udev, "fork of child failed: %m\n");
|
||||
break;
|
||||
default:
|
||||
/* close monitor, but keep address around */
|
||||
|
@ -364,7 +378,7 @@ static void worker_new(struct event *event)
|
|||
event->state = EVENT_RUNNING;
|
||||
udev_list_node_append(&worker->node, &worker_list);
|
||||
children++;
|
||||
info(event->udev, "seq %llu forked new worker [%u]\n", udev_device_get_seqnum(event->dev), pid);
|
||||
info(udev, "seq %llu forked new worker [%u]\n", udev_device_get_seqnum(event->dev), pid);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
@ -535,7 +549,7 @@ static bool is_devpath_busy(struct event *event)
|
|||
return false;
|
||||
}
|
||||
|
||||
static void events_start(struct udev *udev)
|
||||
static void event_queue_start(struct udev *udev)
|
||||
{
|
||||
struct udev_list_node *loop;
|
||||
|
||||
|
@ -555,14 +569,28 @@ static void events_start(struct udev *udev)
|
|||
}
|
||||
}
|
||||
|
||||
static void worker_returned(void)
|
||||
static void event_queue_cleanup(struct udev *udev, enum event_state match_type)
|
||||
{
|
||||
struct udev_list_node *loop, *tmp;
|
||||
|
||||
udev_list_node_foreach_safe(loop, tmp, &event_list) {
|
||||
struct event *event = node_to_event(loop);
|
||||
|
||||
if (match_type != EVENT_UNDEF && match_type != event->state)
|
||||
continue;
|
||||
|
||||
event_queue_delete(event, false);
|
||||
}
|
||||
}
|
||||
|
||||
static void worker_returned(int fd_worker)
|
||||
{
|
||||
for (;;) {
|
||||
struct worker_message msg;
|
||||
ssize_t size;
|
||||
struct udev_list_node *loop;
|
||||
|
||||
size = recv(pfd[FD_WORKER].fd, &msg, sizeof(struct worker_message), MSG_DONTWAIT);
|
||||
size = recv(fd_worker, &msg, sizeof(struct worker_message), MSG_DONTWAIT);
|
||||
if (size != sizeof(struct worker_message))
|
||||
break;
|
||||
|
||||
|
@ -575,7 +603,7 @@ static void worker_returned(void)
|
|||
|
||||
/* worker returned */
|
||||
worker->event->exitcode = msg.exitcode;
|
||||
event_queue_delete(worker->event);
|
||||
event_queue_delete(worker->event, true);
|
||||
worker->event = NULL;
|
||||
if (worker->state != WORKER_KILLED)
|
||||
worker->state = WORKER_IDLE;
|
||||
|
@ -586,16 +614,21 @@ static void worker_returned(void)
|
|||
}
|
||||
|
||||
/* receive the udevd message from userspace */
|
||||
static void handle_ctrl_msg(struct udev_ctrl *uctrl)
|
||||
static struct udev_ctrl_connection *handle_ctrl_msg(struct udev_ctrl *uctrl)
|
||||
{
|
||||
struct udev *udev = udev_ctrl_get_udev(uctrl);
|
||||
struct udev_ctrl_msg *ctrl_msg;
|
||||
struct udev_ctrl_connection *ctrl_conn;
|
||||
struct udev_ctrl_msg *ctrl_msg = NULL;
|
||||
const char *str;
|
||||
int i;
|
||||
|
||||
ctrl_msg = udev_ctrl_receive_msg(uctrl);
|
||||
ctrl_conn = udev_ctrl_get_connection(uctrl);
|
||||
if (ctrl_conn == NULL)
|
||||
goto out;
|
||||
|
||||
ctrl_msg = udev_ctrl_receive_msg(ctrl_conn);
|
||||
if (ctrl_msg == NULL)
|
||||
return;
|
||||
goto out;
|
||||
|
||||
i = udev_ctrl_get_set_log_level(ctrl_msg);
|
||||
if (i >= 0) {
|
||||
|
@ -652,13 +685,18 @@ static void handle_ctrl_msg(struct udev_ctrl *uctrl)
|
|||
children_max = i;
|
||||
}
|
||||
|
||||
settle_pid = udev_ctrl_get_settle(ctrl_msg);
|
||||
if (settle_pid > 0) {
|
||||
info(udev, "udevd message (SETTLE) received\n");
|
||||
kill(settle_pid, SIGUSR1);
|
||||
settle_pid = 0;
|
||||
if (udev_ctrl_get_ping(ctrl_msg) > 0)
|
||||
info(udev, "udevd message (SYNC) received\n");
|
||||
|
||||
if (udev_ctrl_get_exit(ctrl_msg) > 0) {
|
||||
info(udev, "udevd message (EXIT) received\n");
|
||||
udev_exit = true;
|
||||
/* keep reference to block the client until we exit */
|
||||
udev_ctrl_connection_ref(ctrl_conn);
|
||||
}
|
||||
out:
|
||||
udev_ctrl_msg_unref(ctrl_msg);
|
||||
return udev_ctrl_connection_unref(ctrl_conn);
|
||||
}
|
||||
|
||||
/* read inotify messages */
|
||||
|
@ -668,7 +706,7 @@ static int handle_inotify(struct udev *udev)
|
|||
char *buf;
|
||||
struct inotify_event *ev;
|
||||
|
||||
if ((ioctl(pfd[FD_INOTIFY].fd, FIONREAD, &nbytes) < 0) || (nbytes <= 0))
|
||||
if ((ioctl(fd_inotify, FIONREAD, &nbytes) < 0) || (nbytes <= 0))
|
||||
return 0;
|
||||
|
||||
buf = malloc(nbytes);
|
||||
|
@ -677,7 +715,7 @@ static int handle_inotify(struct udev *udev)
|
|||
return -1;
|
||||
}
|
||||
|
||||
nbytes = read(pfd[FD_INOTIFY].fd, buf, nbytes);
|
||||
nbytes = read(fd_inotify, buf, nbytes);
|
||||
|
||||
for (pos = 0; pos < nbytes; pos += sizeof(struct inotify_event) + ev->len) {
|
||||
struct udev_device *dev;
|
||||
|
@ -751,7 +789,7 @@ static void handle_signal(struct udev *udev, int signo)
|
|||
if (worker->event != NULL) {
|
||||
err(udev, "worker [%u] failed while handling '%s'\n", pid, worker->event->devpath);
|
||||
worker->event->exitcode = -32;
|
||||
event_queue_delete(worker->event);
|
||||
event_queue_delete(worker->event, true);
|
||||
/* drop reference from running event */
|
||||
worker_unref(worker);
|
||||
}
|
||||
|
@ -1081,6 +1119,11 @@ int main(int argc, char *argv[])
|
|||
{ "version", no_argument, NULL, 'V' },
|
||||
{}
|
||||
};
|
||||
int fd_ctrl = -1;
|
||||
int fd_netlink = -1;
|
||||
int fd_worker = -1;
|
||||
struct epoll_event ep_ctrl, ep_inotify, ep_signal, ep_netlink, ep_worker;
|
||||
struct udev_ctrl_connection *ctrl_conn = NULL;
|
||||
int rc = 1;
|
||||
|
||||
udev = udev_new();
|
||||
|
@ -1229,7 +1272,7 @@ int main(int argc, char *argv[])
|
|||
dup2(fd, STDERR_FILENO);
|
||||
|
||||
/* udevadm control socket */
|
||||
if (sd_listen_fds(true) == 1 && sd_is_socket(SD_LISTEN_FDS_START, AF_LOCAL, SOCK_DGRAM, -1))
|
||||
if (sd_listen_fds(true) == 1 && sd_is_socket(SD_LISTEN_FDS_START, AF_LOCAL, SOCK_SEQPACKET, -1))
|
||||
udev_ctrl = udev_ctrl_new_from_fd(udev, SD_LISTEN_FDS_START);
|
||||
else
|
||||
udev_ctrl = udev_ctrl_new_from_socket(udev, UDEV_CTRL_SOCK_PATH);
|
||||
|
@ -1245,7 +1288,7 @@ int main(int argc, char *argv[])
|
|||
rc = 1;
|
||||
goto exit;
|
||||
}
|
||||
pfd[FD_CONTROL].fd = udev_ctrl_get_fd(udev_ctrl);
|
||||
fd_ctrl = udev_ctrl_get_fd(udev_ctrl);
|
||||
|
||||
monitor = udev_monitor_new_from_netlink(udev, "kernel");
|
||||
if (monitor == NULL || udev_monitor_enable_receiving(monitor) < 0) {
|
||||
|
@ -1255,81 +1298,7 @@ int main(int argc, char *argv[])
|
|||
goto exit;
|
||||
}
|
||||
udev_monitor_set_receive_buffer_size(monitor, 128*1024*1024);
|
||||
pfd[FD_NETLINK].fd = udev_monitor_get_fd(monitor);
|
||||
|
||||
pfd[FD_INOTIFY].fd = udev_watch_init(udev);
|
||||
if (pfd[FD_INOTIFY].fd < 0) {
|
||||
fprintf(stderr, "error initializing inotify\n");
|
||||
err(udev, "error initializing inotify\n");
|
||||
rc = 4;
|
||||
goto exit;
|
||||
}
|
||||
|
||||
if (udev_get_rules_path(udev) != NULL) {
|
||||
inotify_add_watch(pfd[FD_INOTIFY].fd, udev_get_rules_path(udev),
|
||||
IN_DELETE | IN_MOVE | IN_CLOSE_WRITE);
|
||||
} else {
|
||||
char filename[UTIL_PATH_SIZE];
|
||||
struct stat statbuf;
|
||||
|
||||
inotify_add_watch(pfd[FD_INOTIFY].fd, LIBEXECDIR "/rules.d",
|
||||
IN_DELETE | IN_MOVE | IN_CLOSE_WRITE);
|
||||
inotify_add_watch(pfd[FD_INOTIFY].fd, SYSCONFDIR "/udev/rules.d",
|
||||
IN_DELETE | IN_MOVE | IN_CLOSE_WRITE);
|
||||
|
||||
/* watch dynamic rules directory */
|
||||
util_strscpyl(filename, sizeof(filename), udev_get_run_path(udev), "/rules.d", NULL);
|
||||
if (stat(filename, &statbuf) != 0) {
|
||||
util_create_path(udev, filename);
|
||||
mkdir(filename, 0755);
|
||||
}
|
||||
inotify_add_watch(pfd[FD_INOTIFY].fd, filename,
|
||||
IN_DELETE | IN_MOVE | IN_CLOSE_WRITE);
|
||||
}
|
||||
udev_watch_restore(udev);
|
||||
|
||||
/* block and listen to all signals on signalfd */
|
||||
sigfillset(&mask);
|
||||
sigprocmask(SIG_SETMASK, &mask, &orig_sigmask);
|
||||
pfd[FD_SIGNAL].fd = signalfd(-1, &mask, 0);
|
||||
if (pfd[FD_SIGNAL].fd < 0) {
|
||||
fprintf(stderr, "error getting signalfd\n");
|
||||
err(udev, "error getting signalfd\n");
|
||||
rc = 5;
|
||||
goto exit;
|
||||
}
|
||||
|
||||
/* unnamed socket from workers to the main daemon */
|
||||
if (socketpair(AF_LOCAL, SOCK_DGRAM|SOCK_CLOEXEC, 0, worker_watch) < 0) {
|
||||
fprintf(stderr, "error getting socketpair\n");
|
||||
err(udev, "error getting socketpair\n");
|
||||
rc = 6;
|
||||
goto exit;
|
||||
}
|
||||
pfd[FD_WORKER].fd = worker_watch[READ_END];
|
||||
|
||||
rules = udev_rules_new(udev, resolve_names);
|
||||
if (rules == NULL) {
|
||||
err(udev, "error reading rules\n");
|
||||
goto exit;
|
||||
}
|
||||
|
||||
udev_queue_export = udev_queue_export_new(udev);
|
||||
if (udev_queue_export == NULL) {
|
||||
err(udev, "error creating queue file\n");
|
||||
goto exit;
|
||||
}
|
||||
|
||||
/* if needed, convert old database from earlier udev version */
|
||||
convert_db(udev);
|
||||
|
||||
if (!debug) {
|
||||
dup2(fd, STDIN_FILENO);
|
||||
dup2(fd, STDOUT_FILENO);
|
||||
dup2(fd, STDERR_FILENO);
|
||||
}
|
||||
if (fd > STDERR_FILENO)
|
||||
close(fd);
|
||||
fd_netlink = udev_monitor_get_fd(monitor);
|
||||
|
||||
if (daemonize) {
|
||||
pid_t pid;
|
||||
|
@ -1350,6 +1319,109 @@ int main(int argc, char *argv[])
|
|||
sd_notify(1, "READY=1");
|
||||
}
|
||||
|
||||
fd_inotify = udev_watch_init(udev);
|
||||
if (fd_inotify < 0) {
|
||||
fprintf(stderr, "error initializing inotify\n");
|
||||
err(udev, "error initializing inotify\n");
|
||||
rc = 4;
|
||||
goto exit;
|
||||
}
|
||||
|
||||
if (udev_get_rules_path(udev) != NULL) {
|
||||
inotify_add_watch(fd_inotify, udev_get_rules_path(udev),
|
||||
IN_DELETE | IN_MOVE | IN_CLOSE_WRITE);
|
||||
} else {
|
||||
char filename[UTIL_PATH_SIZE];
|
||||
struct stat statbuf;
|
||||
|
||||
inotify_add_watch(fd_inotify, LIBEXECDIR "/rules.d",
|
||||
IN_DELETE | IN_MOVE | IN_CLOSE_WRITE);
|
||||
inotify_add_watch(fd_inotify, SYSCONFDIR "/udev/rules.d",
|
||||
IN_DELETE | IN_MOVE | IN_CLOSE_WRITE);
|
||||
|
||||
/* watch dynamic rules directory */
|
||||
util_strscpyl(filename, sizeof(filename), udev_get_run_path(udev), "/rules.d", NULL);
|
||||
if (stat(filename, &statbuf) != 0) {
|
||||
util_create_path(udev, filename);
|
||||
mkdir(filename, 0755);
|
||||
}
|
||||
inotify_add_watch(fd_inotify, filename,
|
||||
IN_DELETE | IN_MOVE | IN_CLOSE_WRITE);
|
||||
}
|
||||
udev_watch_restore(udev);
|
||||
|
||||
/* block and listen to all signals on signalfd */
|
||||
sigfillset(&mask);
|
||||
sigprocmask(SIG_SETMASK, &mask, &orig_sigmask);
|
||||
fd_signal = signalfd(-1, &mask, SFD_CLOEXEC);
|
||||
if (fd_signal < 0) {
|
||||
fprintf(stderr, "error getting signalfd\n");
|
||||
err(udev, "error getting signalfd\n");
|
||||
rc = 5;
|
||||
goto exit;
|
||||
}
|
||||
|
||||
/* unnamed socket from workers to the main daemon */
|
||||
if (socketpair(AF_LOCAL, SOCK_DGRAM|SOCK_CLOEXEC, 0, worker_watch) < 0) {
|
||||
fprintf(stderr, "error getting socketpair\n");
|
||||
err(udev, "error getting socketpair\n");
|
||||
rc = 6;
|
||||
goto exit;
|
||||
}
|
||||
fd_worker = worker_watch[READ_END];
|
||||
|
||||
rules = udev_rules_new(udev, resolve_names);
|
||||
if (rules == NULL) {
|
||||
err(udev, "error reading rules\n");
|
||||
goto exit;
|
||||
}
|
||||
|
||||
udev_queue_export = udev_queue_export_new(udev);
|
||||
if (udev_queue_export == NULL) {
|
||||
err(udev, "error creating queue file\n");
|
||||
goto exit;
|
||||
}
|
||||
|
||||
memset(&ep_ctrl, 0, sizeof(struct epoll_event));
|
||||
ep_ctrl.events = EPOLLIN;
|
||||
ep_ctrl.data.fd = fd_ctrl;
|
||||
memset(&ep_inotify, 0, sizeof(struct epoll_event));
|
||||
ep_inotify.events = EPOLLIN;
|
||||
ep_inotify.data.fd = fd_inotify;
|
||||
memset(&ep_signal, 0, sizeof(struct epoll_event));
|
||||
ep_signal.events = EPOLLIN;
|
||||
ep_signal.data.fd = fd_signal;
|
||||
memset(&ep_netlink, 0, sizeof(struct epoll_event));
|
||||
ep_netlink.events = EPOLLIN;
|
||||
ep_netlink.data.fd = fd_netlink;
|
||||
memset(&ep_worker, 0, sizeof(struct epoll_event));
|
||||
ep_worker.events = EPOLLIN;
|
||||
ep_worker.data.fd = fd_worker;
|
||||
fd_ep = epoll_create1(EPOLL_CLOEXEC);
|
||||
if (fd_ep < 0) {
|
||||
err(udev, "error creating epoll fd: %m\n");
|
||||
goto exit;
|
||||
}
|
||||
if (epoll_ctl(fd_ep, EPOLL_CTL_ADD, fd_ctrl, &ep_ctrl) < 0 ||
|
||||
epoll_ctl(fd_ep, EPOLL_CTL_ADD, fd_inotify, &ep_inotify) < 0 ||
|
||||
epoll_ctl(fd_ep, EPOLL_CTL_ADD, fd_signal, &ep_signal) < 0 ||
|
||||
epoll_ctl(fd_ep, EPOLL_CTL_ADD, fd_netlink, &ep_netlink) < 0 ||
|
||||
epoll_ctl(fd_ep, EPOLL_CTL_ADD, fd_worker, &ep_worker) < 0) {
|
||||
err(udev, "fail to add fds to epoll: %m\n");
|
||||
goto exit;
|
||||
}
|
||||
|
||||
/* if needed, convert old database from earlier udev version */
|
||||
convert_db(udev);
|
||||
|
||||
if (!debug) {
|
||||
dup2(fd, STDIN_FILENO);
|
||||
dup2(fd, STDOUT_FILENO);
|
||||
dup2(fd, STDERR_FILENO);
|
||||
}
|
||||
if (fd > STDERR_FILENO)
|
||||
close(fd);
|
||||
|
||||
/* set scheduling priority for the main daemon process */
|
||||
setpriority(PRIO_PROCESS, 0, UDEVD_PRIORITY);
|
||||
|
||||
|
@ -1393,30 +1465,79 @@ int main(int argc, char *argv[])
|
|||
udev_list_init(&event_list);
|
||||
udev_list_init(&worker_list);
|
||||
|
||||
while (!udev_exit) {
|
||||
for (;;) {
|
||||
struct epoll_event ev[8];
|
||||
int fdcount;
|
||||
int timeout;
|
||||
bool is_worker, is_signal, is_inotify, is_netlink, is_ctrl;
|
||||
int i;
|
||||
|
||||
/* set timeout to kill idle workers */
|
||||
if (udev_list_is_empty(&event_list) && children > 2)
|
||||
if (udev_exit) {
|
||||
/* close sources of new events and discard buffered events */
|
||||
if (fd_ctrl >= 0) {
|
||||
epoll_ctl(fd_ep, EPOLL_CTL_DEL, fd_ctrl, NULL);
|
||||
fd_ctrl = -1;
|
||||
}
|
||||
if (monitor != NULL) {
|
||||
epoll_ctl(fd_ep, EPOLL_CTL_DEL, fd_netlink, NULL);
|
||||
udev_monitor_unref(monitor);
|
||||
monitor = NULL;
|
||||
}
|
||||
if (fd_inotify >= 0) {
|
||||
epoll_ctl(fd_ep, EPOLL_CTL_DEL, fd_inotify, NULL);
|
||||
close(fd_inotify);
|
||||
fd_inotify = -1;
|
||||
}
|
||||
|
||||
/* discard queued events and kill workers */
|
||||
event_queue_cleanup(udev, EVENT_QUEUED);
|
||||
worker_kill(udev, 0);
|
||||
|
||||
/* exit after all has cleaned up */
|
||||
if (udev_list_is_empty(&event_list) && udev_list_is_empty(&worker_list))
|
||||
break;
|
||||
|
||||
/* timeout at exit for workers to finish */
|
||||
timeout = 60 * 1000;
|
||||
} else if (udev_list_is_empty(&event_list) && children > 2) {
|
||||
/* set timeout to kill idle workers */
|
||||
timeout = 3 * 1000;
|
||||
else
|
||||
} else {
|
||||
timeout = -1;
|
||||
/* wait for events */
|
||||
fdcount = poll(pfd, ARRAY_SIZE(pfd), timeout);
|
||||
}
|
||||
fdcount = epoll_wait(fd_ep, ev, ARRAY_SIZE(ev), timeout);
|
||||
if (fdcount < 0)
|
||||
continue;
|
||||
|
||||
/* timeout - kill idle workers */
|
||||
if (fdcount == 0)
|
||||
if (fdcount == 0) {
|
||||
if (udev_exit) {
|
||||
info(udev, "timeout, giving up waiting for workers to finish\n");
|
||||
break;
|
||||
}
|
||||
|
||||
/* timeout - kill idle workers */
|
||||
worker_kill(udev, 2);
|
||||
}
|
||||
|
||||
is_worker = is_signal = is_inotify = is_netlink = is_ctrl = false;
|
||||
for (i = 0; i < fdcount; i++) {
|
||||
if (ev[i].data.fd == fd_worker && ev[i].events & EPOLLIN)
|
||||
is_worker = true;
|
||||
else if (ev[i].data.fd == fd_netlink && ev[i].events & EPOLLIN)
|
||||
is_netlink = true;
|
||||
else if (ev[i].data.fd == fd_signal && ev[i].events & EPOLLIN)
|
||||
is_signal = true;
|
||||
else if (ev[i].data.fd == fd_inotify && ev[i].events & EPOLLIN)
|
||||
is_inotify = true;
|
||||
else if (ev[i].data.fd == fd_ctrl && ev[i].events & EPOLLIN)
|
||||
is_ctrl = true;
|
||||
}
|
||||
|
||||
/* event has finished */
|
||||
if (pfd[FD_WORKER].revents & POLLIN)
|
||||
worker_returned();
|
||||
if (is_worker)
|
||||
worker_returned(fd_worker);
|
||||
|
||||
/* get kernel uevent */
|
||||
if (pfd[FD_NETLINK].revents & POLLIN) {
|
||||
if (is_netlink) {
|
||||
struct udev_device *dev;
|
||||
|
||||
dev = udev_monitor_receive_device(monitor);
|
||||
|
@ -1426,32 +1547,37 @@ int main(int argc, char *argv[])
|
|||
}
|
||||
|
||||
/* start new events */
|
||||
if (!udev_list_is_empty(&event_list) && !stop_exec_queue)
|
||||
events_start(udev);
|
||||
if (!udev_list_is_empty(&event_list) && !udev_exit && !stop_exec_queue)
|
||||
event_queue_start(udev);
|
||||
|
||||
/* get signal */
|
||||
if (pfd[FD_SIGNAL].revents & POLLIN) {
|
||||
if (is_signal) {
|
||||
struct signalfd_siginfo fdsi;
|
||||
ssize_t size;
|
||||
|
||||
size = read(pfd[FD_SIGNAL].fd, &fdsi, sizeof(struct signalfd_siginfo));
|
||||
size = read(fd_signal, &fdsi, sizeof(struct signalfd_siginfo));
|
||||
if (size == sizeof(struct signalfd_siginfo))
|
||||
handle_signal(udev, fdsi.ssi_signo);
|
||||
}
|
||||
|
||||
/* we are shutting down, the events below are not handled anymore */
|
||||
if (udev_exit)
|
||||
continue;
|
||||
|
||||
/* device node and rules directory inotify watch */
|
||||
if (pfd[FD_INOTIFY].revents & POLLIN)
|
||||
if (is_inotify)
|
||||
handle_inotify(udev);
|
||||
|
||||
/*
|
||||
* get control message
|
||||
*
|
||||
* This needs to be after the inotify handling, to make sure,
|
||||
* that the settle signal is send back after the possibly generated
|
||||
* that the ping is send back after the possibly generated
|
||||
* "change" events by the inotify device node watch.
|
||||
*
|
||||
* A single time we may receive a client connection which we need to
|
||||
* keep open to block the client. It will be closed right before we
|
||||
* exit.
|
||||
*/
|
||||
if (pfd[FD_CONTROL].revents & POLLIN)
|
||||
handle_ctrl_msg(udev_ctrl);
|
||||
if (is_ctrl)
|
||||
ctrl_conn = handle_ctrl_msg(udev_ctrl);
|
||||
|
||||
/* rules changed, set by inotify or a HUP signal */
|
||||
if (reload_config) {
|
||||
|
@ -1470,16 +1596,21 @@ int main(int argc, char *argv[])
|
|||
udev_queue_export_cleanup(udev_queue_export);
|
||||
rc = 0;
|
||||
exit:
|
||||
udev_queue_export_unref(udev_queue_export);
|
||||
if (fd_ep >= 0)
|
||||
close(fd_ep);
|
||||
worker_list_cleanup(udev);
|
||||
event_queue_cleanup(udev, EVENT_UNDEF);
|
||||
udev_rules_unref(rules);
|
||||
udev_ctrl_unref(udev_ctrl);
|
||||
if (pfd[FD_SIGNAL].fd >= 0)
|
||||
close(pfd[FD_SIGNAL].fd);
|
||||
if (fd_signal >= 0)
|
||||
close(fd_signal);
|
||||
if (worker_watch[READ_END] >= 0)
|
||||
close(worker_watch[READ_END]);
|
||||
if (worker_watch[WRITE_END] >= 0)
|
||||
close(worker_watch[WRITE_END]);
|
||||
udev_monitor_unref(monitor);
|
||||
udev_queue_export_unref(udev_queue_export);
|
||||
udev_ctrl_connection_unref(ctrl_conn);
|
||||
udev_ctrl_unref(udev_ctrl);
|
||||
udev_selinux_exit(udev);
|
||||
udev_unref(udev);
|
||||
udev_log_close();
|
||||
|
|
Loading…
Reference in a new issue