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