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