5d40ebe99642711e6f17d9f4b2d113e94f7bfc4c
[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 <pb-protocol/pb-protocol.h>
11
12 #include "device-handler.h"
13 #include "udev.h"
14 #include "log.h"
15 #include "paths.h"
16
17 #define MOUNT_BIN "/bin/mount"
18
19 struct device_handler {
20         struct discover_server *server;
21
22         struct device *devices;
23         int n_devices;
24 };
25
26 struct discover_context {
27         char *device_path;
28         char *mount_path;
29         struct udev_event *event;
30         struct device *device;
31         char **links;
32         int n_links;
33 };
34
35 struct mount_map {
36         char *device_path;
37         char *mount_point;
38 };
39
40
41 static struct boot_option options[] = {
42         {
43                 .id = "1.1",
44                 .name = "meep one",
45                 .description = "meep description one",
46                 .icon_file = "meep.one.png",
47                 .boot_args = "root=/dev/sda1",
48         },
49 };
50
51 static struct device device = {
52         .id = "1",
53         .name = "meep",
54         .description = "meep description",
55         .icon_file = "meep.png",
56         .n_options = 1,
57         .options = options,
58 };
59
60 int device_handler_get_current_devices(struct device_handler *handler,
61                 struct device **devices)
62
63 {
64         *devices = &device;
65         return 1;
66 }
67
68 static int mkdir_recursive(const char *dir)
69 {
70         struct stat statbuf;
71         char *str, *sep;
72         int mode = 0755;
73
74         if (!*dir)
75                 return 0;
76
77         if (!stat(dir, &statbuf)) {
78                 if (!S_ISDIR(statbuf.st_mode)) {
79                         pb_log("%s: %s exists, but isn't a directory\n",
80                                         __func__, dir);
81                         return -1;
82                 }
83                 return 0;
84         }
85
86         str = talloc_strdup(NULL, dir);
87         sep = strchr(*str == '/' ? str + 1 : str, '/');
88
89         while (1) {
90
91                 /* terminate the path at sep */
92                 if (sep)
93                         *sep = '\0';
94
95                 if (mkdir(str, mode) && errno != EEXIST) {
96                         pb_log("mkdir(%s): %s\n", str, strerror(errno));
97                         return -1;
98                 }
99
100                 if (!sep)
101                         break;
102
103                 /* reset dir to the full path */
104                 strcpy(str, dir);
105                 sep = strchr(sep + 1, '/');
106         }
107
108         talloc_free(str);
109
110         return 0;
111 }
112
113 static void setup_device_links(struct discover_context *ctx)
114 {
115         struct link {
116                 char *env, *dir;
117         } *link, links[] = {
118                 {
119                         .env = "ID_FS_UUID",
120                         .dir = "disk/by-uuid"
121                 },
122                 {
123                         .env = "ID_FS_LABEL",
124                         .dir = "disk/by-label"
125                 },
126                 {
127                         .env = NULL
128                 }
129         };
130
131         for (link = links; link->env; link++) {
132                 char *enc, *dir, *path;
133                 const char *value;
134
135                 value = udev_event_param(ctx->event, link->env);
136                 if (!value)
137                         continue;
138
139                 enc = encode_label(ctx, value);
140                 dir = join_paths(ctx, mount_base(), link->dir);
141                 path = join_paths(ctx, dir, value);
142
143                 if (!mkdir_recursive(dir)) {
144                         unlink(path);
145                         if (symlink(ctx->mount_path, path)) {
146                                 pb_log("symlink(%s,%s): %s\n",
147                                                 ctx->mount_path, path,
148                                                 strerror(errno));
149                                 talloc_free(path);
150                         } else {
151                                 int i = ctx->n_links++;
152                                 ctx->links = talloc_realloc(ctx,
153                                                 ctx->links, char *,
154                                                 ctx->n_links);
155                                 ctx->links[i] = path;
156                         }
157
158                 }
159
160                 talloc_free(dir);
161                 talloc_free(enc);
162         }
163 }
164
165 static int mount_device(struct discover_context *ctx)
166 {
167         const char *mountpoint;
168         struct stat statbuf;
169         int status;
170         pid_t pid;
171
172         if (!ctx->mount_path) {
173                 mountpoint = mountpoint_for_device(ctx->device_path);
174                 ctx->mount_path = talloc_strdup(ctx, mountpoint);
175         }
176
177         if (stat(ctx->mount_path, &statbuf)) {
178                 if (mkdir(ctx->mount_path, 0755)) {
179                         pb_log("couldn't create mount directory %s: %s\n",
180                                         ctx->mount_path, strerror(errno));
181                         return -1;
182                 }
183         } else {
184                 if (!S_ISDIR(statbuf.st_mode)) {
185                         pb_log("mountpoint %s exists, but isn't a directory\n",
186                                         ctx->mount_path);
187                         return -1;
188                 }
189         }
190
191         pid = fork();
192         if (pid == -1) {
193                 pb_log("%s: fork failed: %s\n", __func__, strerror(errno));
194                 return -1;
195         }
196
197         if (pid == 0) {
198                 execl(MOUNT_BIN, MOUNT_BIN, ctx->device_path, ctx->mount_path,
199                                 "-o", "ro", NULL);
200                 exit(EXIT_FAILURE);
201         }
202
203         if (waitpid(pid, &status, 0) == -1) {
204                 pb_log("%s: waitpid failed: %s\n", __func__,
205                                 strerror(errno));
206                 return -1;
207         }
208
209         if (!WIFEXITED(status) || WEXITSTATUS(status) != 0)
210                 return -1;
211
212         setup_device_links(ctx);
213         return 0;
214 }
215
216 static int handle_add_event(struct device_handler *handler,
217                 struct udev_event *event)
218 {
219         struct discover_context *ctx;
220         const char *devname;
221         int rc;
222
223         /* create our context */
224         ctx = talloc(NULL, struct discover_context);
225         ctx->event = event;
226         ctx->mount_path = NULL;
227         ctx->links = NULL;
228         ctx->n_links = 0;
229
230         devname = udev_event_param(ctx->event, "DEVNAME");
231         if (!devname) {
232                 pb_log("no devname for %s?\n", event->device);
233                 return 0;
234         }
235
236         ctx->device_path = talloc_strdup(ctx, devname);
237
238         rc = mount_device(ctx);
239         if (rc) {
240                 pb_log("mount_device failed for %s\n", event->device);
241                 talloc_free(ctx);
242                 return 0;
243         }
244
245         talloc_free(ctx);
246
247         return 0;
248 }
249
250 static int handle_remove_event(struct device_handler *handler,
251                 struct udev_event *event)
252 {
253         return 0;
254 }
255
256 int device_handler_event(struct device_handler *handler,
257                 struct udev_event *event)
258 {
259         int rc;
260
261         switch (event->action) {
262         case UDEV_ACTION_ADD:
263                 rc = handle_add_event(handler, event);
264                 break;
265
266         case UDEV_ACTION_REMOVE:
267                 rc = handle_remove_event(handler, event);
268                 break;
269         }
270
271         return rc;
272 }
273
274 struct device_handler *device_handler_init(struct discover_server *server)
275 {
276         struct device_handler *handler;
277
278         handler = talloc(NULL, struct device_handler);
279         handler->devices = NULL;
280         handler->n_devices = 0;
281
282         /* set up our mount point base */
283         mkdir_recursive(mount_base());
284
285         return handler;
286 }
287
288 void device_handler_destroy(struct device_handler *devices)
289 {
290         talloc_free(devices);
291 }
292