]> git.ozlabs.org Git - petitboot/blob - discover/device-handler.c
log discover mount failure
[petitboot] / discover / device-handler.c
1
2 #include <stdlib.h>
3 #include <unistd.h>
4 #include <string.h>
5 #include <errno.h>
6 #include <sys/stat.h>
7 #include <sys/wait.h>
8
9 #include <talloc/talloc.h>
10 #include <list/list.h>
11 #include <log/log.h>
12 #include <pb-protocol/pb-protocol.h>
13
14 #include "device-handler.h"
15 #include "discover-server.h"
16 #include "parser.h"
17 #include "udev.h"
18 #include "paths.h"
19
20 #define MOUNT_BIN "/bin/mount"
21
22 #define UMOUNT_BIN "/bin/umount"
23
24 struct device_handler {
25         struct discover_server *server;
26
27         struct device *devices;
28         int n_devices;
29
30         struct list contexts;
31 };
32
33 struct mount_map {
34         char *device_path;
35         char *mount_point;
36 };
37
38
39 static struct boot_option options[] = {
40         {
41                 .id = "1.1",
42                 .name = "meep one",
43                 .description = "meep description one",
44                 .icon_file = "meep.one.png",
45                 .boot_args = "root=/dev/sda1",
46         },
47 };
48
49 static struct device device = {
50         .id = "1",
51         .name = "meep",
52         .description = "meep description",
53         .icon_file = "meep.png",
54 };
55
56 int device_handler_get_current_devices(
57                 struct device_handler *handler __attribute__((unused)),
58                 const struct device **devices)
59
60 {
61         *devices = &device;
62         return 1;
63 }
64
65 static int mkdir_recursive(const char *dir)
66 {
67         struct stat statbuf;
68         char *str, *sep;
69         int mode = 0755;
70
71         if (!*dir)
72                 return 0;
73
74         if (!stat(dir, &statbuf)) {
75                 if (!S_ISDIR(statbuf.st_mode)) {
76                         pb_log("%s: %s exists, but isn't a directory\n",
77                                         __func__, dir);
78                         return -1;
79                 }
80                 return 0;
81         }
82
83         str = talloc_strdup(NULL, dir);
84         sep = strchr(*str == '/' ? str + 1 : str, '/');
85
86         while (1) {
87
88                 /* terminate the path at sep */
89                 if (sep)
90                         *sep = '\0';
91
92                 if (mkdir(str, mode) && errno != EEXIST) {
93                         pb_log("mkdir(%s): %s\n", str, strerror(errno));
94                         return -1;
95                 }
96
97                 if (!sep)
98                         break;
99
100                 /* reset dir to the full path */
101                 strcpy(str, dir);
102                 sep = strchr(sep + 1, '/');
103         }
104
105         talloc_free(str);
106
107         return 0;
108 }
109
110 static int rmdir_recursive(const char *base, const char *dir)
111 {
112         char *cur, *pos;
113
114         /* sanity check: make sure that dir is within base */
115         if (strncmp(base, dir, strlen(base)))
116                 return -1;
117
118         cur = talloc_strdup(NULL, dir);
119
120         while (strcmp(base, dir)) {
121
122                 rmdir(dir);
123
124                 /* null-terminate at the last slash */
125                 pos = strrchr(dir, '/');
126                 if (!pos)
127                         break;
128
129                 *pos = '\0';
130         }
131
132         talloc_free(cur);
133
134         return 0;
135 }
136
137 static void setup_device_links(struct discover_context *ctx)
138 {
139         struct link {
140                 char *env, *dir;
141         } *link, links[] = {
142                 {
143                         .env = "ID_FS_UUID",
144                         .dir = "disk/by-uuid"
145                 },
146                 {
147                         .env = "ID_FS_LABEL",
148                         .dir = "disk/by-label"
149                 },
150                 {
151                         .env = NULL
152                 }
153         };
154
155         for (link = links; link->env; link++) {
156                 char *enc, *dir, *path;
157                 const char *value;
158
159                 value = udev_event_param(ctx->event, link->env);
160                 if (!value || !*value)
161                         continue;
162
163                 enc = encode_label(ctx, value);
164                 dir = join_paths(ctx, mount_base(), link->dir);
165                 path = join_paths(ctx, dir, value);
166
167                 if (!mkdir_recursive(dir)) {
168                         unlink(path);
169                         if (symlink(ctx->mount_path, path)) {
170                                 pb_log("symlink(%s,%s): %s\n",
171                                                 ctx->mount_path, path,
172                                                 strerror(errno));
173                                 talloc_free(path);
174                         } else {
175                                 int i = ctx->n_links++;
176                                 ctx->links = talloc_realloc(ctx,
177                                                 ctx->links, char *,
178                                                 ctx->n_links);
179                                 ctx->links[i] = path;
180                         }
181
182                 }
183
184                 talloc_free(dir);
185                 talloc_free(enc);
186         }
187 }
188
189 static void remove_device_links(struct discover_context *ctx)
190 {
191         int i;
192
193         for (i = 0; i < ctx->n_links; i++)
194                 unlink(ctx->links[i]);
195 }
196
197 static int mount_device(struct discover_context *ctx)
198 {
199         const char *mountpoint;
200         int status;
201         pid_t pid;
202
203         if (!ctx->mount_path) {
204                 mountpoint = mountpoint_for_device(ctx->device_path);
205                 ctx->mount_path = talloc_strdup(ctx, mountpoint);
206         }
207
208         if (mkdir_recursive(ctx->mount_path))
209                 pb_log("couldn't create mount directory %s: %s\n",
210                                 ctx->mount_path, strerror(errno));
211
212         pid = fork();
213         if (pid == -1) {
214                 pb_log("%s: fork failed: %s\n", __func__, strerror(errno));
215                 goto out_rmdir;
216         }
217
218         if (pid == 0) {
219                 execl(MOUNT_BIN, MOUNT_BIN, ctx->device_path, ctx->mount_path,
220                                 "-o", "ro", NULL);
221                 exit(EXIT_FAILURE);
222         }
223
224         if (waitpid(pid, &status, 0) == -1) {
225                 pb_log("%s: waitpid failed: %s\n", __func__,
226                                 strerror(errno));
227                 goto out_rmdir;
228         }
229
230         if (!WIFEXITED(status) || WEXITSTATUS(status) != 0) {
231                 pb_log("%s: mount failed (%d): %s\n", __func__,
232                         WEXITSTATUS(status), ctx->event->device);
233                 goto out_rmdir;
234         }
235
236         setup_device_links(ctx);
237         return 0;
238
239 out_rmdir:
240         rmdir_recursive(mount_base(), ctx->mount_path);
241         return -1;
242 }
243
244 static int umount_device(struct discover_context *ctx)
245 {
246         int status;
247         pid_t pid;
248
249         remove_device_links(ctx);
250
251         pid = fork();
252         if (pid == -1) {
253                 pb_log("%s: fork failed: %s\n", __func__, strerror(errno));
254                 return -1;
255         }
256
257         if (pid == 0) {
258                 execl(UMOUNT_BIN, UMOUNT_BIN, ctx->mount_path, NULL);
259                 exit(EXIT_FAILURE);
260         }
261
262         if (waitpid(pid, &status, 0) == -1) {
263                 pb_log("%s: waitpid failed: %s\n", __func__,
264                                 strerror(errno));
265                 return -1;
266         }
267
268         if (!WIFEXITED(status) || WEXITSTATUS(status) != 0)
269                 return -1;
270
271         rmdir_recursive(mount_base(), ctx->mount_path);
272
273         return 0;
274 }
275
276 static struct discover_context *find_context(struct device_handler *handler,
277                 const char *id)
278 {
279         struct discover_context *ctx;
280
281         list_for_each_entry(&handler->contexts, ctx, list) {
282                 if (!strcmp(ctx->id, id))
283                         return ctx;
284         }
285
286         return NULL;
287 }
288
289
290 static int destroy_context(void *arg)
291 {
292         struct discover_context *ctx = arg;
293
294         list_remove(&ctx->list);
295         umount_device(ctx);
296
297         return 0;
298 }
299
300 static int handle_add_event(struct device_handler *handler,
301                 struct udev_event *event)
302 {
303         struct discover_context *ctx;
304         const char *devname;
305         int rc;
306
307         /* create our context */
308         ctx = talloc(handler, struct discover_context);
309         ctx->event = event;
310         ctx->mount_path = NULL;
311         ctx->links = NULL;
312         ctx->n_links = 0;
313
314         ctx->id = talloc_strdup(ctx, event->device);
315
316         devname = udev_event_param(ctx->event, "DEVNAME");
317         if (!devname) {
318                 pb_log("no devname for %s?\n", event->device);
319                 return 0;
320         }
321
322         ctx->device_path = talloc_strdup(ctx, devname);
323
324         rc = mount_device(ctx);
325         if (rc) {
326                 talloc_free(ctx);
327                 return 0;
328         }
329
330         list_add(&handler->contexts, &ctx->list);
331         talloc_set_destructor(ctx, destroy_context);
332
333         /* set up the top-level device */
334         ctx->device = talloc_zero(ctx, struct device);
335         ctx->device->id = talloc_strdup(ctx->device, ctx->id);
336         list_init(&ctx->device->boot_options);
337
338         /* run the parsers */
339         iterate_parsers(ctx);
340
341         discover_server_notify_add(handler->server, ctx->device);
342
343         return 0;
344 }
345
346 static int handle_remove_event(struct device_handler *handler,
347                 struct udev_event *event)
348 {
349         struct discover_context *ctx;
350
351         ctx = find_context(handler, event->device);
352         if (!ctx)
353                 return 0;
354
355         discover_server_notify_remove(handler->server, ctx->device);
356
357         talloc_free(ctx);
358
359         return 0;
360 }
361
362 int device_handler_event(struct device_handler *handler,
363                 struct udev_event *event)
364 {
365         int rc;
366
367         switch (event->action) {
368         case UDEV_ACTION_ADD:
369                 rc = handle_add_event(handler, event);
370                 break;
371
372         case UDEV_ACTION_REMOVE:
373                 rc = handle_remove_event(handler, event);
374                 break;
375         }
376
377         return rc;
378 }
379
380 struct device_handler *device_handler_init(struct discover_server *server)
381 {
382         struct device_handler *handler;
383         unsigned int i;
384
385         handler = talloc(NULL, struct device_handler);
386         handler->devices = NULL;
387         handler->n_devices = 0;
388         handler->server = server;
389
390         list_init(&handler->contexts);
391
392         /* set up our mount point base */
393         mkdir_recursive(mount_base());
394
395         /* setup out test objects */
396         list_init(&device.boot_options);
397
398         for (i = 0; i < sizeof(options) / sizeof(options[0]); i++)
399                 list_add(&device.boot_options, &options[i].list);
400
401         parser_init();
402
403         return handler;
404 }
405
406 void device_handler_destroy(struct device_handler *handler)
407 {
408         talloc_free(handler);
409 }
410