9589bb20d16c0e8ac57f390138aafca4ccd0b2be
[petitboot] / discover / device-handler.c
1
2 #include <assert.h>
3 #include <stdlib.h>
4 #include <unistd.h>
5 #include <string.h>
6 #include <errno.h>
7 #include <sys/stat.h>
8 #include <sys/wait.h>
9
10 #include <talloc/talloc.h>
11 #include <list/list.h>
12 #include <log/log.h>
13 #include <pb-protocol/pb-protocol.h>
14 #include <system/system.h>
15
16 #include "device-handler.h"
17 #include "discover-server.h"
18 #include "event.h"
19 #include "parser.h"
20 #include "udev.h"
21 #include "paths.h"
22
23 #define MOUNT_BIN "/bin/mount"
24
25 #define UMOUNT_BIN "/bin/umount"
26
27 struct device_handler {
28         struct discover_server *server;
29
30         struct device **devices;
31         unsigned int n_devices;
32
33         struct list contexts;
34 };
35
36 /**
37  * device_handler_add - Add a device to the handler device array.
38  */
39
40 static void device_handler_add(struct device_handler *handler,
41         struct device *device)
42 {
43         handler->n_devices++;
44         handler->devices = talloc_realloc(handler, handler->devices,
45                 struct device *, handler->n_devices);
46         handler->devices[handler->n_devices - 1] = device;
47 }
48
49 /**
50  * device_handler_remove - Remove a device from the handler device array.
51  */
52
53 static void device_handler_remove(struct device_handler *handler,
54         struct device *device)
55 {
56         unsigned int i;
57
58         for (i = 0; i < handler->n_devices; i++)
59                 if (handler->devices[i] == device)
60                         break;
61
62         if (i == handler->n_devices) {
63                 assert(0 && "unknown device");
64                 return;
65         }
66
67         handler->n_devices--;
68         memmove(&handler->devices[i], &handler->devices[i + 1],
69                 (handler->n_devices - i) * sizeof(handler->devices[0]));
70         handler->devices = talloc_realloc(handler, handler->devices,
71                 struct device *, handler->n_devices);
72 }
73
74 /**
75  * device_handler_find - Find a handler device by id.
76  */
77
78 static struct device *device_handler_find(struct device_handler *handler,
79         const char *id)
80 {
81         unsigned int i;
82
83         assert(id);
84
85         for (i = 0; i < handler->n_devices; i++)
86                 if (handler->devices[i]->id
87                         && streq(handler->devices[i]->id, id))
88                         return handler->devices[i];
89
90         pb_log("%s: unknown device: %s\n", __func__, id);
91         return NULL;
92 }
93
94 /**
95  * device_handler_get_device_count - Get the count of current handler devices.
96  */
97
98 int device_handler_get_device_count(const struct device_handler *handler)
99 {
100         return handler->n_devices;
101 }
102
103 /**
104  * device_handler_get_device - Get a handler device by index.
105  */
106
107 const struct device *device_handler_get_device(
108         const struct device_handler *handler, unsigned int index)
109 {
110         if (index >= handler->n_devices) {
111                 assert(0 && "bad index");
112                 return NULL;
113         }
114
115         return handler->devices[index];
116 }
117
118 static void setup_device_links(struct discover_context *ctx)
119 {
120         struct link {
121                 char *env, *dir;
122         } *link, links[] = {
123                 {
124                         .env = "ID_FS_UUID",
125                         .dir = "disk/by-uuid"
126                 },
127                 {
128                         .env = "ID_FS_LABEL",
129                         .dir = "disk/by-label"
130                 },
131                 {
132                         .env = NULL
133                 }
134         };
135
136         for (link = links; link->env; link++) {
137                 char *enc, *dir, *path;
138                 const char *value;
139
140                 value = event_get_param(ctx->event, link->env);
141                 if (!value || !*value)
142                         continue;
143
144                 enc = encode_label(ctx, value);
145                 dir = join_paths(ctx, mount_base(), link->dir);
146                 path = join_paths(ctx, dir, value);
147
148                 if (!pb_mkdir_recursive(dir)) {
149                         unlink(path);
150                         if (symlink(ctx->mount_path, path)) {
151                                 pb_log("symlink(%s,%s): %s\n",
152                                                 ctx->mount_path, path,
153                                                 strerror(errno));
154                                 talloc_free(path);
155                         } else {
156                                 int i = ctx->n_links++;
157                                 ctx->links = talloc_realloc(ctx,
158                                                 ctx->links, char *,
159                                                 ctx->n_links);
160                                 ctx->links[i] = path;
161                         }
162
163                 }
164
165                 talloc_free(dir);
166                 talloc_free(enc);
167         }
168 }
169
170 static void remove_device_links(struct discover_context *ctx)
171 {
172         int i;
173
174         for (i = 0; i < ctx->n_links; i++)
175                 unlink(ctx->links[i]);
176 }
177
178 static int mount_device(struct discover_context *ctx)
179 {
180         const char *mountpoint;
181         const char *argv[6];
182
183         if (!ctx->mount_path) {
184                 mountpoint = mountpoint_for_device(ctx->device_path);
185                 ctx->mount_path = talloc_strdup(ctx, mountpoint);
186         }
187
188         if (pb_mkdir_recursive(ctx->mount_path))
189                 pb_log("couldn't create mount directory %s: %s\n",
190                                 ctx->mount_path, strerror(errno));
191
192         argv[0] = MOUNT_BIN;
193         argv[1] = ctx->device_path;
194         argv[2] = ctx->mount_path;
195         argv[3] = "-o";
196         argv[4] = "ro";
197         argv[5] = NULL;
198
199         if (pb_run_cmd(argv, 1, 0)) {
200
201                 /* Retry mount without ro option. */
202
203                 argv[0] = MOUNT_BIN;
204                 argv[1] = ctx->device_path;
205                 argv[2] = ctx->mount_path;
206                 argv[3] = NULL;
207
208                 if (pb_run_cmd(argv, 1, 0))
209                         goto out_rmdir;
210         }
211
212         setup_device_links(ctx);
213         return 0;
214
215 out_rmdir:
216         pb_rmdir_recursive(mount_base(), ctx->mount_path);
217         return -1;
218 }
219
220 static int umount_device(struct discover_context *ctx)
221 {
222         int status;
223         pid_t pid;
224
225         remove_device_links(ctx);
226
227         pid = fork();
228         if (pid == -1) {
229                 pb_log("%s: fork failed: %s\n", __func__, strerror(errno));
230                 return -1;
231         }
232
233         if (pid == 0) {
234                 execl(UMOUNT_BIN, UMOUNT_BIN, ctx->mount_path, NULL);
235                 exit(EXIT_FAILURE);
236         }
237
238         if (waitpid(pid, &status, 0) == -1) {
239                 pb_log("%s: waitpid failed: %s\n", __func__,
240                                 strerror(errno));
241                 return -1;
242         }
243
244         if (!WIFEXITED(status) || WEXITSTATUS(status) != 0)
245                 return -1;
246
247         pb_rmdir_recursive(mount_base(), ctx->mount_path);
248
249         return 0;
250 }
251
252 static struct discover_context *find_context(struct device_handler *handler,
253                 const char *id)
254 {
255         struct discover_context *ctx;
256
257         list_for_each_entry(&handler->contexts, ctx, list) {
258                 if (!strcmp(ctx->id, id))
259                         return ctx;
260         }
261
262         return NULL;
263 }
264
265 static int destroy_context(void *arg)
266 {
267         struct discover_context *ctx = arg;
268
269         list_remove(&ctx->list);
270         umount_device(ctx);
271
272         return 0;
273 }
274
275 static int handle_add_udev_event(struct device_handler *handler,
276                 struct event *event)
277 {
278         struct discover_context *ctx;
279         const char *devname;
280         int rc;
281
282         /* create our context */
283         ctx = talloc(handler, struct discover_context);
284         ctx->event = event;
285         ctx->mount_path = NULL;
286         ctx->links = NULL;
287         ctx->n_links = 0;
288
289         ctx->id = talloc_strdup(ctx, event->device);
290
291         devname = event_get_param(ctx->event, "DEVNAME");
292         assert(devname);
293         ctx->device_path = talloc_strdup(ctx, devname);
294
295         rc = mount_device(ctx);
296         if (rc) {
297                 talloc_free(ctx);
298                 return 0;
299         }
300
301         list_add(&handler->contexts, &ctx->list);
302         talloc_set_destructor(ctx, destroy_context);
303
304         /* set up the top-level device */
305         ctx->device = talloc_zero(ctx, struct device);
306         ctx->device->id = talloc_strdup(ctx->device, ctx->id);
307         list_init(&ctx->device->boot_options);
308
309         /* run the parsers */
310         iterate_parsers(ctx);
311
312         /* add device to handler device array */
313         device_handler_add(handler, ctx->device);
314
315         discover_server_notify_add(handler->server, ctx->device);
316
317         return 0;
318 }
319
320 static int handle_remove_udev_event(struct device_handler *handler,
321                 struct event *event)
322 {
323         struct discover_context *ctx;
324
325         ctx = find_context(handler, event->device);
326         if (!ctx)
327                 return 0;
328
329         discover_server_notify_remove(handler->server, ctx->device);
330
331         /* remove device from handler device array */
332         device_handler_remove(handler, ctx->device);
333
334         talloc_free(ctx);
335
336         return 0;
337 }
338
339 static int handle_add_user_event(struct device_handler *handler,
340                 struct event *event)
341 {
342         struct device *device;
343
344         assert(event->device);
345
346         device = talloc_zero(handler, struct device);
347
348         if (!device)
349                 goto fail;
350
351         device->id = talloc_strdup(device, event->device);
352         list_init(&device->boot_options);
353
354         parse_user_event(device, event);
355
356         discover_server_notify_add(handler->server, device);
357
358         /* add device to handler device array */
359         device_handler_add(handler, device);
360
361         return 0;
362
363 fail:
364         talloc_free(device);
365         return 0;
366 }
367
368 static int handle_remove_user_event(struct device_handler *handler,
369                 struct event *event)
370 {
371         struct device *device = device_handler_find(handler, event->device);
372
373         if (!device)
374                 return 0;
375
376         discover_server_notify_remove(handler->server, device);
377
378         /* remove device from handler device array */
379         device_handler_remove(handler, device);
380
381         talloc_free(device);
382         return 0;
383 }
384
385 typedef int (*event_handler)(struct device_handler *, struct event *);
386
387 static event_handler handlers[EVENT_TYPE_MAX][EVENT_ACTION_MAX] = {
388         [EVENT_TYPE_UDEV] = {
389                 [EVENT_ACTION_ADD]      = handle_add_udev_event,
390                 [EVENT_ACTION_REMOVE]   = handle_remove_udev_event,
391         },
392         [EVENT_TYPE_USER] = {
393                 [EVENT_ACTION_ADD]      = handle_add_user_event,
394                 [EVENT_ACTION_REMOVE]   = handle_remove_user_event,
395         }
396 };
397
398 int device_handler_event(struct device_handler *handler,
399                 struct event *event)
400 {
401         if (event->type >= EVENT_TYPE_MAX ||
402                         event->action >= EVENT_ACTION_MAX ||
403                         !handlers[event->type][event->action]) {
404                 pb_log("%s unknown type/action: %d/%d\n", __func__,
405                                 event->type, event->action);
406                 return 0;
407         }
408
409         return handlers[event->type][event->action](handler, event);
410 }
411
412 struct device_handler *device_handler_init(struct discover_server *server)
413 {
414         struct device_handler *handler;
415
416         handler = talloc(NULL, struct device_handler);
417         handler->devices = NULL;
418         handler->n_devices = 0;
419         handler->server = server;
420
421         list_init(&handler->contexts);
422
423         /* set up our mount point base */
424         pb_mkdir_recursive(mount_base());
425
426         parser_init();
427
428         return handler;
429 }
430
431 void device_handler_destroy(struct device_handler *handler)
432 {
433         talloc_free(handler);
434 }