+ }
+}
+
+static void msg_move_exec(struct uevent_msg *msg)
+{
+ list_move_tail(&msg->node, &exec_list);
+ run_exec_q = 1;
+ expected_seqnum = msg->seqnum+1;
+ dbg("moved seq %llu to exec, next expected is %llu",
+ msg->seqnum, expected_seqnum);
+}
+
+/* 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;
+ struct sysinfo info;
+ long msg_age = 0;
+ static int timeout = EVENT_INIT_TIMEOUT_SEC;
+ static int init = 1;
+
+ dbg("msg queue manager, next expected is %llu", expected_seqnum);
+recheck:
+ list_for_each_entry_safe(loop_msg, tmp_msg, &msg_list, node) {
+ /* move event with expected sequence to the exec list */
+ if (loop_msg->seqnum == expected_seqnum) {
+ msg_move_exec(loop_msg);
+ continue;
+ }
+
+ /* see if we are in the initialization phase and wait for the very first events */
+ if (init && (info.uptime - startup_time >= INIT_TIME_SEC)) {
+ init = 0;
+ timeout = EVENT_TIMEOUT_SEC;
+ dbg("initialization phase passed, set timeout to %i seconds", EVENT_TIMEOUT_SEC);
+ }
+
+ /* move event with expired timeout to the exec list */
+ sysinfo(&info);
+ msg_age = info.uptime - loop_msg->queue_time;
+ dbg("seq %llu is %li seconds old", loop_msg->seqnum, msg_age);
+ if (msg_age >= timeout) {
+ msg_move_exec(loop_msg);
+ goto recheck;
+ } else {
+ break;
+ }
+ }
+
+ msg_dump_queue();
+
+ /* set timeout for remaining queued events */
+ if (list_empty(&msg_list) == 0) {
+ struct itimerval itv = {{0, 0}, {timeout - msg_age, 0}};
+ dbg("next event expires in %li seconds", timeout - msg_age);
+ setitimer(ITIMER_REAL, &itv, NULL);
+ }
+}
+
+static struct uevent_msg *get_msg_from_envbuf(const char *buf, int buf_size)
+{
+ int bufpos;
+ int i;
+ struct uevent_msg *msg;
+
+ msg = malloc(sizeof(struct uevent_msg) + buf_size);
+ if (msg == NULL)
+ return NULL;
+ memset(msg, 0x00, sizeof(struct uevent_msg) + buf_size);
+
+ /* copy environment buffer and reconstruct envp */
+ memcpy(msg->envbuf, buf, buf_size);
+ bufpos = 0;
+ for (i = 0; (bufpos < buf_size) && (i < UEVENT_NUM_ENVP-2); i++) {
+ int keylen;
+ char *key;
+
+ key = &msg->envbuf[bufpos];
+ keylen = strlen(key);
+ msg->envp[i] = key;
+ bufpos += keylen + 1;
+ dbg("add '%s' to msg.envp[%i]", msg->envp[i], i);
+
+ /* remember some keys for further processing */
+ if (strncmp(key, "ACTION=", 7) == 0)
+ msg->action = &key[7];
+
+ if (strncmp(key, "DEVPATH=", 8) == 0)
+ msg->devpath = &key[8];
+
+ if (strncmp(key, "SUBSYSTEM=", 10) == 0)
+ msg->subsystem = &key[10];
+
+ if (strncmp(key, "SEQNUM=", 7) == 0)
+ msg->seqnum = strtoull(&key[7], NULL, 10);
+
+ if (strncmp(key, "PHYSDEVPATH=", 12) == 0)
+ msg->physdevpath = &key[12];
+
+ if (strncmp(key, "TIMEOUT=", 8) == 0)
+ msg->timeout = strtoull(&key[8], NULL, 10);
+ }
+ msg->envp[i++] = "UDEVD_EVENT=1";
+ msg->envp[i] = NULL;
+
+ return msg;
+}
+
+/* receive the udevd message from userspace */
+static struct uevent_msg *get_udevd_msg(void)
+{
+ static struct udevd_msg usend_msg;
+ struct uevent_msg *msg;
+ ssize_t size;
+ struct msghdr smsg;
+ struct cmsghdr *cmsg;
+ struct iovec iov;
+ struct ucred *cred;
+ char cred_msg[CMSG_SPACE(sizeof(struct ucred))];
+ int envbuf_size;
+
+ memset(&usend_msg, 0x00, sizeof(struct udevd_msg));
+ iov.iov_base = &usend_msg;
+ iov.iov_len = sizeof(struct udevd_msg);
+
+ 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(udevd_sock, &smsg, 0);
+ if (size < 0) {
+ if (errno != EINTR)
+ dbg("unable to receive udevd message");
+ return NULL;
+ }
+ cmsg = CMSG_FIRSTHDR(&smsg);
+ cred = (struct ucred *) CMSG_DATA(cmsg);
+
+ if (cmsg == NULL || cmsg->cmsg_type != SCM_CREDENTIALS) {
+ info("no sender credentials received, message ignored");
+ return NULL;
+ }
+
+ if (cred->uid != 0) {
+ info("sender uid=%i, message ignored", cred->uid);
+ return NULL;
+ }
+
+ if (strncmp(usend_msg.magic, UDEV_MAGIC, sizeof(UDEV_MAGIC)) != 0 ) {
+ info("message magic '%s' doesn't match, ignore it", usend_msg.magic);
+ return NULL;
+ }
+
+switch (usend_msg.type) {
+ case UDEVD_UEVENT:
+ dbg("udevd message (UEVENT) received");
+ envbuf_size = size - offsetof(struct udevd_msg, envbuf);
+ dbg("envbuf_size=%i", envbuf_size);
+ msg = get_msg_from_envbuf(usend_msg.envbuf, envbuf_size);
+ if (msg == NULL)
+ return NULL;
+ return msg;
+ case UDEVD_STOP_EXEC_QUEUE:
+ dbg("udevd message (STOP_EXEC_QUEUE) received");
+ stop_exec_q = 1;