f9360860e4c8955813a741fbd5444646bbb819ce
[petitboot] / devices / udev-helper.c
1
2 #include <stdio.h>
3 #include <stdlib.h>
4 #include <stdint.h>
5 #include <unistd.h>
6 #include <sys/types.h>
7 #include <sys/stat.h>
8 #include <sys/socket.h>
9 #include <sys/wait.h>
10 #include <sys/un.h>
11 #include <fcntl.h>
12 #include <errno.h>
13 #include <string.h>
14 #include <asm/byteorder.h>
15
16 #include "udev-helper.h"
17
18 #define parser_dir "."
19
20 #define tmp_dir "/var/tmp/petitboot"
21 #define socket_file "/var/tmp/petitboot-dev"
22 #define mount_bin "/bin/mount"
23 #define umount_bin "/bin/umount"
24
25 extern struct parser native_parser;
26 static FILE *logf;
27 static int sock;
28
29 /* array of parsers, ordered by priority */
30 static struct parser *parsers[] = {
31         &native_parser,
32         NULL
33 };
34
35 #define log(...) fprintf(logf, __VA_ARGS__)
36
37 static void iterate_parsers(const char *devpath, const char *mountpoint)
38 {
39         int i;
40
41         log("trying parsers for %s@%s\n", devpath, mountpoint);
42
43         for (i = 0; parsers[i]; i++) {
44                 log("\ttrying parser '%s'\n", parsers[i]->name);
45                 /* just use a dummy device path for now */
46                 if (parsers[i]->parse(devpath, mountpoint))
47                         return;
48         }
49         log("\tno boot_options found\n");
50 }
51
52 static void print_boot_option(const struct boot_option *opt)
53 {
54         log("\tname: %s\n", opt->name);
55         log("\tdescription: %s\n", opt->description);
56         log("\tboot_image: %s\n", opt->boot_image_file);
57         log("\tboot_args: %s\n", opt->boot_args);
58
59 }
60
61 static void print_device(const struct device *dev)
62 {
63         log("\tid: %s\n", dev->name);
64         log("\tname: %s\n", dev->name);
65         log("\tdescription: %s\n", dev->description);
66         log("\tboot_image: %s\n", dev->icon_file);
67 }
68
69
70 void free_device(struct device *dev)
71 {
72         if (!dev)
73                 return;
74         if (dev->id)
75                 free(dev->id);
76         if (dev->name)
77                 free(dev->name);
78         if (dev->description)
79                 free(dev->description);
80         if (dev->icon_file)
81                 free(dev->icon_file);
82         free(dev);
83 }
84
85 void free_boot_option(struct boot_option *opt)
86 {
87         if (!opt)
88                 return;
89         if (opt->name)
90                 free(opt->name);
91         if (opt->description)
92                 free(opt->description);
93         if (opt->icon_file)
94                 free(opt->icon_file);
95         if (opt->boot_image_file)
96                 free(opt->boot_image_file);
97         if (opt->initrd_file)
98                 free(opt->initrd_file);
99         if (opt->boot_args)
100                 free(opt->boot_args);
101         free(opt);
102 }
103
104 static int write_action(int fd, enum device_action action)
105 {
106         uint8_t action_buf = action;
107         return write(fd, &action_buf, sizeof(action_buf)) != sizeof(action_buf);
108 }
109
110 static int write_string(int fd, const char *str)
111 {
112         int len, pos = 0;
113         uint32_t len_buf;
114
115         if (!str) {
116                 len_buf = 0;
117                 if (write(fd, &len_buf, sizeof(len_buf)) != sizeof(len_buf)) {
118                         log("write failed: %s\n", strerror(errno));
119                         return -1;
120                 }
121                 return 0;
122         }
123
124         len = strlen(str);
125         if (len > (1ull << (sizeof(len_buf) * 8 - 1))) {
126                 log("string too large\n");
127                 return -1;
128         }
129
130         len_buf = __cpu_to_be32(len);
131         if (write(fd, &len_buf, sizeof(len_buf)) != sizeof(len_buf)) {
132                 log("write failed: %s\n", strerror(errno));
133                 return -1;
134         }
135
136         while (pos < len) {
137                 int rc = write(fd, str, len - pos);
138                 if (rc <= 0) {
139                         log("write failed: %s\n", strerror(errno));
140                         return -1;
141                 }
142                 pos += rc;
143                 str += rc;
144         }
145
146         return 0;
147 }
148
149 int add_device(const struct device *dev)
150 {
151         int rc;
152
153         log("device added:\n");
154         print_device(dev);
155         rc = write_action(sock, DEV_ACTION_ADD_DEVICE) ||
156                 write_string(sock, dev->id) ||
157                 write_string(sock, dev->name) ||
158                 write_string(sock, dev->description) ||
159                 write_string(sock, dev->icon_file);
160
161         if (rc)
162                 log("error writing device %s to socket\n", dev->name);
163
164         return rc;
165 }
166
167 int add_boot_option(const struct boot_option *opt)
168 {
169         int rc;
170
171         log("boot option added:\n");
172         print_boot_option(opt);
173
174         rc = write_action(sock, DEV_ACTION_ADD_OPTION) ||
175                 write_string(sock, opt->id) ||
176                 write_string(sock, opt->name) ||
177                 write_string(sock, opt->description) ||
178                 write_string(sock, opt->icon_file) ||
179                 write_string(sock, opt->boot_image_file) ||
180                 write_string(sock, opt->initrd_file) ||
181                 write_string(sock, opt->boot_args);
182
183         if (rc)
184                 log("error writing boot option %s to socket\n", opt->name);
185
186         return rc;
187 }
188
189 int remove_device(const char *dev_path)
190 {
191         return write_action(sock, DEV_ACTION_REMOVE_DEVICE) ||
192                 write_string(sock, dev_path);
193 }
194
195 int connect_to_socket()
196 {
197 #if 1
198         int fd;
199         struct sockaddr_un addr;
200
201         fd = socket(PF_UNIX, SOCK_STREAM, 0);
202         if (fd == -1) {
203                 log("can't create socket: %s\n", strerror(errno));
204                 return -1;
205         }
206
207         addr.sun_family = AF_UNIX;
208         strcpy(addr.sun_path, socket_file);
209
210         if (connect(fd, (struct sockaddr *)&addr, sizeof(addr))) {
211                 log("can't connect to %s: %s\n",
212                                 addr.sun_path, strerror(errno));
213                 return -1;
214         }
215         sock = fd;
216
217         return 0;
218 #else
219         int fd;
220         fd = open("./debug_socket", O_WRONLY | O_CREAT, 0640);
221         if (fd < 0) {
222                 log("can't create output file: %s\n", strerror(errno));
223                 return -1;
224         }
225         sock = fd;
226         return 0;
227 #endif
228 }
229
230 #define template "mnt-XXXXXX"
231
232 static int mount_device(const char *dev_path, char *mount_path)
233 {
234         char *dir;
235         int pid, status, rc = -1;
236
237         /* create a unique mountpoint */
238         dir = malloc(strlen(tmp_dir) + 2 + strlen(template));
239         sprintf(dir, "%s/%s", tmp_dir, template);
240
241         if (!mkdtemp(dir)) {
242                 log("failed to create temporary directory in %s: %s",
243                                 tmp_dir, strerror(errno));
244                 goto out;
245         }
246
247         pid = fork();
248         if (pid == -1) {
249                 log("%s: fork failed: %s\n", __FUNCTION__, strerror(errno));
250                 goto out;
251         }
252
253         if (pid == 0) {
254                 execl(mount_bin, mount_bin, dev_path, dir, "-o", "ro", NULL);
255                 exit(EXIT_FAILURE);
256         }
257
258         if (waitpid(pid, &status, 0) == -1) {
259                 log("%s: waitpid failed: %s\n", __FUNCTION__, strerror(errno));
260                 goto out;
261         }
262
263         if (WIFEXITED(status) && WEXITSTATUS(status) == 0) {
264                 strcpy(mount_path, dir);
265                 rc = 0;
266         }
267
268 out:
269         free(dir);
270         return rc;
271 }
272
273 static int unmount_device(const char *dev_path)
274 {
275         int pid, status, rc;
276
277         pid = fork();
278
279         if (pid == -1) {
280                 log("%s: fork failed: %s\n", __FUNCTION__, strerror(errno));
281                 return -1;
282         }
283
284         if (pid == 0) {
285                 execl(umount_bin, umount_bin, dev_path, NULL);
286                 exit(EXIT_FAILURE);
287         }
288
289         if (waitpid(pid, &status, 0) == -1) {
290                 log("%s: waitpid failed: %s\n", __FUNCTION__, strerror(errno));
291                 return -1;
292         }
293
294         rc = !WIFEXITED(status) || WEXITSTATUS(status) != 0;
295
296         return rc;
297 }
298
299 const char *generic_icon_file(enum generic_icon_type type)
300 {
301         switch (type) {
302         case ICON_TYPE_DISK:
303                 return "artwork/hdd.png";
304         case ICON_TYPE_USB:
305                 return "artwork/usbpen.png";
306         case ICON_TYPE_OPTICAL:
307                 return "artwork/cdrom.png";
308         case ICON_TYPE_NETWORK:
309         case ICON_TYPE_UNKNOWN:
310                 break;
311         }
312         return "artwork/hdd.png";
313 }
314
315 static const struct device fake_boot_devices[] =
316 {
317         {
318                 .id             = "fakeDisk0",
319                 .name           = "Hard Disk",
320                 .icon_file      = "artwork/hdd.png",
321         },
322         {
323                 .id             = "fakeDisk1",
324                 .name           = "PinkCat Linux CD",
325                 .icon_file      = "artwork/cdrom.png",
326         }
327 };
328
329 static const struct boot_option fake_boot_options[] =
330 {
331         {
332                 .id             = "fakeBoot0",
333                 .name           = "Bloobuntu Linux",
334                 .description    = "Boot Bloobuntu Linux",
335                 .icon_file      = "artwork/hdd.png",
336         },
337         {
338                 .id             = "fakeBoot1",
339                 .name           = "Pendora Gore 6",
340                 .description    = "Boot Pendora Gora 6",
341                 .icon_file      = "artwork/hdd.png",
342         },
343         {
344                 .id             = "fakeBoot2",
345                 .name           = "Genfoo Minux",
346                 .description    = "Boot Genfoo Minux",
347                 .icon_file      = "artwork/hdd.png",
348         },
349         {
350                 .id             = "fakeBoot3",
351                 .name           = "PinkCat Linux",
352                 .description    = "Install PinkCat Linux - Graphical install",
353                 .icon_file      = "artwork/cdrom.png",
354         },
355 };
356
357 enum generic_icon_type guess_device_type(void)
358 {
359         const char *bus = getenv("ID_BUS");
360         if (streq(bus, "usb"))
361                 return ICON_TYPE_USB;
362         if (streq(bus, "ata") || streq(bus, "scsi"))
363                 return ICON_TYPE_DISK;
364         return ICON_TYPE_UNKNOWN;
365 }
366
367 int main(int argc, char **argv)
368 {
369         char mountpoint[PATH_MAX];
370         char *dev_path, *action;
371         int rc;
372
373         /*if (fork())
374                 return EXIT_SUCCESS;
375                 */
376         action = getenv("ACTION");
377
378         logf = stdout;
379         rc = EXIT_SUCCESS;
380
381         if (!action) {
382                 log("missing environment?\n");
383                 return EXIT_FAILURE;
384         }
385
386         if (connect_to_socket())
387                 return EXIT_FAILURE;
388
389         if (streq(action, "fake")) {
390                 log("fake mode");
391
392                 add_device(&fake_boot_devices[0]);
393                 add_boot_option(&fake_boot_options[0]);
394                 add_boot_option(&fake_boot_options[1]);
395                 add_boot_option(&fake_boot_options[2]);
396                 add_device(&fake_boot_devices[1]);
397                 add_boot_option(&fake_boot_options[3]);
398
399                 return EXIT_SUCCESS;
400         }
401
402         dev_path = getenv("DEVNAME");
403         if (!dev_path) {
404                 log("missing environment?\n");
405                 return EXIT_FAILURE;
406         }
407
408         if (streq(action, "add")) {
409                 if (mount_device(dev_path, mountpoint)) {
410                         log("failed to mount %s\n", dev_path);
411                         return EXIT_FAILURE;
412                 }
413
414                 log("mounted %s at %s\n", dev_path, mountpoint);
415
416                 iterate_parsers(dev_path, mountpoint);
417
418         } else if (streq(action, "remove")) {
419                 log("%s removed\n", dev_path);
420
421                 remove_device(dev_path);
422
423                 unmount_device(dev_path);
424
425         } else {
426                 log("invalid action '%s'\n", action);
427                 rc = EXIT_FAILURE;
428         }
429         return rc;
430 }