a5d9e03006dd2af346a1418296526a47aece88ca
[petitboot] / discover / udev.c
1
2 #define _GNU_SOURCE
3
4 #include <errno.h>
5 #include <stdio.h>
6 #include <stdlib.h>
7 #include <string.h>
8 #include <unistd.h>
9 #include <sys/socket.h>
10 #include <sys/types.h>
11 #include <sys/un.h>
12
13 #include <log/log.h>
14 #include <talloc/talloc.h>
15 #include <waiter/waiter.h>
16
17 #include "event.h"
18 #include "udev.h"
19 #include "pb-discover.h"
20 #include "device-handler.h"
21
22 #define PBOOT_DEVICE_SOCKET "/tmp/petitboot.udev"
23
24 #define max(a, b) ((a) > (b) ? (a) : (b))
25
26 struct udev {
27         struct device_handler *handler;
28         int socket;
29 };
30
31 static void udev_print_event(struct event *event)
32 {
33         const char *action, *params[] = {
34                 "DEVNAME",
35                 "DEVPATH",
36                 "ID_TYPE",
37                 "ID_BUS",
38                 "ID_FS_UUID",
39                 "ID_FS_LABEL",
40                 NULL,
41         };
42         int i;
43
44         action = event->action == EVENT_ACTION_ADD ? "add" : "remove";
45
46         pb_log("udev %s event:\n", action);
47         pb_log("\tdevice: %s\n", event->device);
48
49         for (i = 0; params[i]; i++)
50                 pb_log("\t%-12s => %s\n",
51                                 params[i], event_get_param(event, params[i]));
52 }
53
54 static void udev_handle_message(struct udev *udev, char *buf, int len)
55 {
56         int result;
57         struct event *event;
58         const char *devpath;
59
60         event = talloc(udev, struct event);
61         event->type = EVENT_TYPE_UDEV;
62
63         pb_log("%s\n", buf);
64
65         result = event_parse_ad_message(event, buf, len);
66
67         if (result)
68                 return;
69
70         udev_print_event(event);
71
72         /* Ignore ram, loop, and devices with no DEVNAME. */
73
74         devpath = event_get_param(event, "DEVPATH");
75
76         if (event_get_param(event, "DEVNAME")
77                 && !strstr(devpath, "virtual/block/loop")
78                 && !strstr(devpath, "virtual/block/ram")) {
79                 device_handler_event(udev->handler, event);
80         }
81
82         talloc_free(event);
83
84         return;
85 }
86
87 static int udev_process(void *arg)
88 {
89         struct udev *udev = arg;
90         char buf[4096];
91         int len;
92
93         len = recvfrom(udev->socket, buf, sizeof(buf), 0, NULL, NULL);
94
95         if (len < 0) {
96                 pb_log("udev socket read failed: %s", strerror(errno));
97                 return -1;
98         }
99
100         if (len == 0)
101                 return 0;
102
103         udev_handle_message(udev, buf, len);
104
105         return 0;
106 }
107
108 static int udev_destructor(void *p)
109 {
110         struct udev *udev = p;
111
112         if (udev->socket >= 0)
113                 close(udev->socket);
114
115         return 0;
116 }
117
118 struct udev *udev_init(struct device_handler *handler)
119 {
120         struct sockaddr_un addr;
121         struct udev *udev;
122
123         unlink(PBOOT_DEVICE_SOCKET);
124
125         udev = talloc(NULL, struct udev);
126
127         udev->handler = handler;
128
129         udev->socket = socket(PF_UNIX, SOCK_DGRAM, 0);
130         if (udev->socket < 0) {
131                 pb_log("Error creating udev socket: %s\n", strerror(errno));
132                 goto out_err;
133         }
134
135         talloc_set_destructor(udev, udev_destructor);
136
137         memset(&addr, 0, sizeof addr);
138         addr.sun_family = AF_UNIX;
139         strcpy(addr.sun_path, PBOOT_DEVICE_SOCKET);
140
141         if (bind(udev->socket, (struct sockaddr *)&addr, sizeof(addr))) {
142                 pb_log("Error binding udev socket: %s\n", strerror(errno));
143                 goto out_err;
144         }
145
146         waiter_register(udev->socket, WAIT_IN, udev_process, udev);
147
148         pb_log("%s: waiting on %s\n", __func__, PBOOT_DEVICE_SOCKET);
149
150         return udev;
151
152 out_err:
153         talloc_free(udev);
154         return NULL;
155 }
156
157 int udev_trigger(struct udev __attribute__((unused)) *udev)
158 {
159         int rc = system("/sbin/udevadm trigger --subsystem-match=block --action=add");
160
161         if (rc)
162                 pb_log("udev trigger failed: %d (%d)\n", rc, WEXITSTATUS(rc));
163
164         return WEXITSTATUS(rc);
165 }
166
167 void udev_destroy(struct udev *udev)
168 {
169         talloc_free(udev);
170 }