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