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