X-Git-Url: https://www.chiark.greenend.org.uk/ucgi/~ianmdlvl/git?p=elogind.git;a=blobdiff_plain;f=libudev%2Flibudev-ctrl.c;h=61e02433834e5a73e1d88748652230fd38925c1c;hp=e47b2b66dc6eb02910e1bad1e26243b4c86a74d8;hb=1547687a83334b056fdf06ed0c5676e948eafb30;hpb=48a9b173e88738ff4eefb3519f1d27711b417c8d diff --git a/libudev/libudev-ctrl.c b/libudev/libudev-ctrl.c index e47b2b66d..61e024338 100644 --- a/libudev/libudev-ctrl.c +++ b/libudev/libudev-ctrl.c @@ -16,6 +16,7 @@ #include #include #include +#include #include #include @@ -32,9 +33,9 @@ enum udev_ctrl_msg_type { UDEV_CTRL_START_EXEC_QUEUE, UDEV_CTRL_RELOAD_RULES, UDEV_CTRL_SET_ENV, - UDEV_CTRL_SET_MAX_CHILDS, - UDEV_CTRL_SET_MAX_CHILDS_RUNNING, - UDEV_CTRL_SETTLE, + UDEV_CTRL_SET_CHILDREN_MAX, + UDEV_CTRL_PING, + UDEV_CTRL_EXIT, }; struct udev_ctrl_msg_wire { @@ -49,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 { @@ -60,9 +60,17 @@ struct udev_ctrl { int sock; struct sockaddr_un saddr; socklen_t addrlen; + bool bound; + bool connected; }; -struct udev_ctrl *udev_ctrl_new_from_socket(struct udev *udev, const char *socket_path) +struct udev_ctrl_connection { + int refcount; + struct udev_ctrl *uctrl; + int sock; +}; + +static struct udev_ctrl *udev_ctrl_new(struct udev *udev) { struct udev_ctrl *uctrl; @@ -71,12 +79,27 @@ struct udev_ctrl *udev_ctrl_new_from_socket(struct udev *udev, const char *socke return NULL; uctrl->refcount = 1; uctrl->udev = udev; + return uctrl; +} + +struct udev_ctrl *udev_ctrl_new_from_socket_fd(struct udev *udev, const char *socket_path, int fd) +{ + struct udev_ctrl *uctrl; - uctrl->sock = socket(AF_LOCAL, SOCK_DGRAM, 0); - if (uctrl->sock < 0) { - err(udev, "error getting socket: %m\n"); - udev_ctrl_unref(uctrl); + uctrl = udev_ctrl_new(udev); + if (uctrl == NULL) return NULL; + + if (fd < 0) { + uctrl->sock = socket(AF_LOCAL, SOCK_SEQPACKET|SOCK_NONBLOCK|SOCK_CLOEXEC, 0); + if (uctrl->sock < 0) { + err(udev, "error getting socket: %m\n"); + udev_ctrl_unref(uctrl); + return NULL; + } + } else { + uctrl->bound = true; + uctrl->sock = fd; } uctrl->saddr.sun_family = AF_LOCAL; @@ -85,23 +108,35 @@ struct udev_ctrl *udev_ctrl_new_from_socket(struct udev *udev, const char *socke /* translate leading '@' to abstract namespace */ if (uctrl->saddr.sun_path[0] == '@') uctrl->saddr.sun_path[0] = '\0'; - return uctrl; } +struct udev_ctrl *udev_ctrl_new_from_socket(struct udev *udev, const char *socket_path) +{ + return udev_ctrl_new_from_socket_fd(udev, socket_path, -1); +} + int udev_ctrl_enable_receiving(struct udev_ctrl *uctrl) { int err; - const int feature_on = 1; - err= bind(uctrl->sock, (struct sockaddr *)&uctrl->saddr, uctrl->addrlen); - if (err < 0) { - err(uctrl->udev, "bind failed: %m\n"); - return err; + if (!uctrl->bound) { + 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; + } + + uctrl->bound = true; } - - /* enable receiving of the sender credentials */ - setsockopt(uctrl->sock, SOL_SOCKET, SO_PASSCRED, &feature_on, sizeof(feature_on)); return 0; } @@ -118,16 +153,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) @@ -137,10 +173,57 @@ 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|SOCK_NONBLOCK); + if (conn->sock < 0) { + if (errno != EINTR) + err(uctrl->udev, "unable to receive ctrl connection: %m\n"); + 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); @@ -152,51 +235,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_max_childs(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_MAX_CHILDS, 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; @@ -209,43 +330,40 @@ 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; + udev_ctrl_connection_ref(conn); iov.iov_base = &uctrl_msg->ctrl_msg_wire; iov.iov_len = sizeof(struct udev_ctrl_msg_wire); - memset(&smsg, 0x00, sizeof(struct msghdr)); smsg.msg_iov = &iov; smsg.msg_iovlen = 1; 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); return uctrl_msg; err: udev_ctrl_msg_unref(uctrl_msg); @@ -260,15 +378,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) @@ -306,16 +426,23 @@ const char *udev_ctrl_get_set_env(struct udev_ctrl_msg *ctrl_msg) return NULL; } -int udev_ctrl_get_set_max_childs(struct udev_ctrl_msg *ctrl_msg) +int udev_ctrl_get_set_children_max(struct udev_ctrl_msg *ctrl_msg) { - if (ctrl_msg->ctrl_msg_wire.type == UDEV_CTRL_SET_MAX_CHILDS) + if (ctrl_msg->ctrl_msg_wire.type == UDEV_CTRL_SET_CHILDREN_MAX) return ctrl_msg->ctrl_msg_wire.intval; 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; }