]> git.ozlabs.org Git - petitboot/blob - devices/udev-helper.c
Allow petitboot to run udevtrigger on start
[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 #include <linux/cdrom.h>
16 #include <sys/ioctl.h>
17
18 #include "udev-helper.h"
19 #include "petitboot-paths.h"
20
21 /* Define below to operate without the frontend */
22 #undef USE_FAKE_SOCKET
23
24 /* Delay in seconds between polling of removable devices */
25 #define REMOVABLE_SLEEP_DELAY   2
26
27 extern struct parser native_parser;
28 extern struct parser yaboot_parser;
29 static FILE *logf;
30 static int sock;
31
32 /* array of parsers, ordered by priority */
33 static struct parser *parsers[] = {
34         &native_parser,
35         &yaboot_parser,
36         NULL
37 };
38
39 #define log(...) fprintf(logf, __VA_ARGS__)
40
41 static void iterate_parsers(const char *devpath, const char *mountpoint)
42 {
43         int i;
44
45         log("trying parsers for %s@%s\n", devpath, mountpoint);
46
47         for (i = 0; parsers[i]; i++) {
48                 log("\ttrying parser '%s'\n", parsers[i]->name);
49                 /* just use a dummy device path for now */
50                 if (parsers[i]->parse(devpath, mountpoint))
51                         return;
52         }
53         log("\tno boot_options found\n");
54 }
55
56 static void print_boot_option(const struct boot_option *opt)
57 {
58         log("\tname: %s\n", opt->name);
59         log("\tdescription: %s\n", opt->description);
60         log("\tboot_image: %s\n", opt->boot_image_file);
61         log("\tinitrd: %s\n", opt->initrd_file);
62         log("\tboot_args: %s\n", opt->boot_args);
63
64 }
65
66 static void print_device(const struct device *dev)
67 {
68         log("\tid: %s\n", dev->id);
69         log("\tname: %s\n", dev->name);
70         log("\tdescription: %s\n", dev->description);
71         log("\tboot_image: %s\n", dev->icon_file);
72 }
73
74
75 void free_device(struct device *dev)
76 {
77         if (!dev)
78                 return;
79         if (dev->id)
80                 free(dev->id);
81         if (dev->name)
82                 free(dev->name);
83         if (dev->description)
84                 free(dev->description);
85         if (dev->icon_file)
86                 free(dev->icon_file);
87         free(dev);
88 }
89
90 void free_boot_option(struct boot_option *opt)
91 {
92         if (!opt)
93                 return;
94         if (opt->name)
95                 free(opt->name);
96         if (opt->description)
97                 free(opt->description);
98         if (opt->icon_file)
99                 free(opt->icon_file);
100         if (opt->boot_image_file)
101                 free(opt->boot_image_file);
102         if (opt->initrd_file)
103                 free(opt->initrd_file);
104         if (opt->boot_args)
105                 free(opt->boot_args);
106         free(opt);
107 }
108
109 static int write_action(int fd, enum device_action action)
110 {
111         uint8_t action_buf = action;
112         return write(fd, &action_buf, sizeof(action_buf)) != sizeof(action_buf);
113 }
114
115 static int write_string(int fd, const char *str)
116 {
117         int len, pos = 0;
118         uint32_t len_buf;
119
120         if (!str) {
121                 len_buf = 0;
122                 if (write(fd, &len_buf, sizeof(len_buf)) != sizeof(len_buf)) {
123                         log("write failed: %s\n", strerror(errno));
124                         return -1;
125                 }
126                 return 0;
127         }
128
129         len = strlen(str);
130         if (len > (1ull << (sizeof(len_buf) * 8 - 1))) {
131                 log("string too large\n");
132                 return -1;
133         }
134
135         len_buf = __cpu_to_be32(len);
136         if (write(fd, &len_buf, sizeof(len_buf)) != sizeof(len_buf)) {
137                 log("write failed: %s\n", strerror(errno));
138                 return -1;
139         }
140
141         while (pos < len) {
142                 int rc = write(fd, str, len - pos);
143                 if (rc <= 0) {
144                         log("write failed: %s\n", strerror(errno));
145                         return -1;
146                 }
147                 pos += rc;
148                 str += rc;
149         }
150
151         return 0;
152 }
153
154 int add_device(const struct device *dev)
155 {
156         int rc;
157
158         log("device added:\n");
159         print_device(dev);
160         rc = write_action(sock, DEV_ACTION_ADD_DEVICE) ||
161                 write_string(sock, dev->id) ||
162                 write_string(sock, dev->name) ||
163                 write_string(sock, dev->description) ||
164                 write_string(sock, dev->icon_file);
165
166         if (rc)
167                 log("error writing device %s to socket\n", dev->name);
168
169         return rc;
170 }
171
172 int add_boot_option(const struct boot_option *opt)
173 {
174         int rc;
175
176         log("boot option added:\n");
177         print_boot_option(opt);
178
179         rc = write_action(sock, DEV_ACTION_ADD_OPTION) ||
180                 write_string(sock, opt->id) ||
181                 write_string(sock, opt->name) ||
182                 write_string(sock, opt->description) ||
183                 write_string(sock, opt->icon_file) ||
184                 write_string(sock, opt->boot_image_file) ||
185                 write_string(sock, opt->initrd_file) ||
186                 write_string(sock, opt->boot_args);
187
188         if (rc)
189                 log("error writing boot option %s to socket\n", opt->name);
190
191         return rc;
192 }
193
194 int remove_device(const char *dev_path)
195 {
196         return write_action(sock, DEV_ACTION_REMOVE_DEVICE) ||
197                 write_string(sock, dev_path);
198 }
199
200 int connect_to_socket()
201 {
202 #ifndef USE_FAKE_SOCKET
203         int fd;
204         struct sockaddr_un addr;
205
206         fd = socket(PF_UNIX, SOCK_STREAM, 0);
207         if (fd == -1) {
208                 log("can't create socket: %s\n", strerror(errno));
209                 return -1;
210         }
211
212         addr.sun_family = AF_UNIX;
213         strcpy(addr.sun_path, PBOOT_DEVICE_SOCKET);
214
215         if (connect(fd, (struct sockaddr *)&addr, sizeof(addr))) {
216                 log("can't connect to %s: %s\n",
217                                 addr.sun_path, strerror(errno));
218                 return -1;
219         }
220         sock = fd;
221
222         return 0;
223 #else
224         int fd;
225         fd = open("./debug_socket", O_WRONLY | O_CREAT, 0640);
226         if (fd < 0) {
227                 log("can't create output file: %s\n", strerror(errno));
228                 return -1;
229         }
230         sock = fd;
231         return 0;
232 #endif
233 }
234
235 int mount_device(const char *dev_path, char *mount_path)
236 {
237         char *dir;
238         const char *basename;
239         int pid, status, rc = -1;
240
241         basename = strrchr(dev_path, '/');
242         if (basename)
243                 basename++;
244         else
245                 basename = dev_path;
246  
247         /* create a unique mountpoint */
248         dir = malloc(strlen(TMP_DIR) + 13 + strlen(basename));
249         sprintf(dir, "%s/mnt-%s-XXXXXX", TMP_DIR, basename);
250
251         if (!mkdtemp(dir)) {
252                 log("failed to create temporary directory in %s: %s",
253                                 TMP_DIR, strerror(errno));
254                 goto out;
255         }
256
257         pid = fork();
258         if (pid == -1) {
259                 log("%s: fork failed: %s\n", __FUNCTION__, strerror(errno));
260                 goto out;
261         }
262
263         if (pid == 0) {
264                 execl(MOUNT_BIN, MOUNT_BIN, dev_path, dir, "-o", "ro", NULL);
265                 exit(EXIT_FAILURE);
266         }
267
268         if (waitpid(pid, &status, 0) == -1) {
269                 log("%s: waitpid failed: %s\n", __FUNCTION__, strerror(errno));
270                 goto out;
271         }
272
273         if (WIFEXITED(status) && WEXITSTATUS(status) == 0) {
274                 strcpy(mount_path, dir);
275                 rc = 0;
276         }
277
278 out:
279         free(dir);
280         return rc;
281 }
282
283 static int unmount_device(const char *dev_path)
284 {
285         int pid, status, rc;
286
287         pid = fork();
288
289         if (pid == -1) {
290                 log("%s: fork failed: %s\n", __FUNCTION__, strerror(errno));
291                 return -1;
292         }
293
294         if (pid == 0) {
295                 execl(UMOUNT_BIN, UMOUNT_BIN, dev_path, NULL);
296                 exit(EXIT_FAILURE);
297         }
298
299         if (waitpid(pid, &status, 0) == -1) {
300                 log("%s: waitpid failed: %s\n", __FUNCTION__, strerror(errno));
301                 return -1;
302         }
303
304         rc = !WIFEXITED(status) || WEXITSTATUS(status) != 0;
305
306         return rc;
307 }
308
309 const char *generic_icon_file(enum generic_icon_type type)
310 {
311         switch (type) {
312         case ICON_TYPE_DISK:
313                 return artwork_pathname("hdd.png");
314         case ICON_TYPE_USB:
315                 return artwork_pathname("usbpen.png");
316         case ICON_TYPE_OPTICAL:
317                 return artwork_pathname("cdrom.png");
318         case ICON_TYPE_NETWORK:
319         case ICON_TYPE_UNKNOWN:
320                 break;
321         }
322         return artwork_pathname("hdd.png");
323 }
324
325 static const struct device fake_boot_devices[] =
326 {
327         {
328                 .id             = "fakeDisk0",
329                 .name           = "Hard Disk",
330                 .icon_file      = artwork_pathname("hdd.png"),
331         },
332         {
333                 .id             = "fakeDisk1",
334                 .name           = "PinkCat Linux CD",
335                 .icon_file      = artwork_pathname("cdrom.png"),
336         }
337 };
338
339 static const struct boot_option fake_boot_options[] =
340 {
341         {
342                 .id             = "fakeBoot0",
343                 .name           = "Bloobuntu Linux",
344                 .description    = "Boot Bloobuntu Linux",
345                 .icon_file      = artwork_pathname("hdd.png"),
346         },
347         {
348                 .id             = "fakeBoot1",
349                 .name           = "Pendora Gore 6",
350                 .description    = "Boot Pendora Gora 6",
351                 .icon_file      = artwork_pathname("hdd.png"),
352         },
353         {
354                 .id             = "fakeBoot2",
355                 .name           = "Genfoo Minux",
356                 .description    = "Boot Genfoo Minux",
357                 .icon_file      = artwork_pathname("hdd.png"),
358         },
359         {
360                 .id             = "fakeBoot3",
361                 .name           = "PinkCat Linux",
362                 .description    = "Install PinkCat Linux - Graphical install",
363                 .icon_file      = artwork_pathname("cdrom.png"),
364         },
365 };
366
367 enum generic_icon_type guess_device_type(void)
368 {
369         const char *type = getenv("ID_TYPE");
370         const char *bus = getenv("ID_BUS");
371         if (type && streq(type, "cd"))
372                 return ICON_TYPE_OPTICAL;
373         if (streq(bus, "usb"))
374                 return ICON_TYPE_USB;
375         if (streq(bus, "ata") || streq(bus, "scsi"))
376                 return ICON_TYPE_DISK;
377         return ICON_TYPE_UNKNOWN;
378 }
379
380
381 static int is_removable_device(const char *sysfs_path) 
382 {
383         char full_path[PATH_MAX];
384         char buf[80];
385         int fd, buf_len;
386
387         sprintf(full_path, "/sys/%s/removable", sysfs_path);    
388         fd = open(full_path, O_RDONLY);
389         printf(" -> removable check on %s, fd=%d\n", full_path, fd);
390         if (fd < 0)
391                 return 0;
392         buf_len = read(fd, buf, 79);
393         close(fd);
394         if (buf_len < 0)
395                 return 0;
396         buf[buf_len] = 0;
397         return strtol(buf, NULL, 10);
398 }
399
400 static int found_new_device(const char *dev_path)
401 {
402         char mountpoint[PATH_MAX];
403
404         if (mount_device(dev_path, mountpoint)) {
405                 log("failed to mount %s\n", dev_path);
406                 return EXIT_FAILURE;
407         }
408
409         log("mounted %s at %s\n", dev_path, mountpoint);
410
411         iterate_parsers(dev_path, mountpoint);
412
413         return EXIT_SUCCESS;
414 }
415
416 static int poll_device_plug(const char *dev_path,
417                             int *optical)
418 {
419         int rc, fd;
420
421         /* Polling loop for optical drive */
422         for (; (*optical) != 0; ) {
423                 printf("poll for optical drive insertion ...\n");
424                 fd = open(dev_path, O_RDONLY|O_NONBLOCK);
425                 if (fd < 0)
426                         return EXIT_FAILURE;
427                 rc = ioctl(fd, CDROM_DRIVE_STATUS, CDSL_CURRENT);
428                 close(fd);
429                 if (rc == -1) {
430                         printf("not an optical drive, fallback...\n");
431                         break;
432                 }
433                 *optical = 1;
434                 if (rc == CDS_DISC_OK)
435                         return EXIT_SUCCESS;
436
437                 printf("no... waiting\n");
438                 sleep(REMOVABLE_SLEEP_DELAY);
439         }
440
441         /* Fall back to bare open() */
442         *optical = 0;
443         for (;;) {
444                 printf("poll for non-optical drive insertion ...\n");
445                 fd = open(dev_path, O_RDONLY);
446                 if (fd < 0 && errno != ENOMEDIUM)
447                         return EXIT_FAILURE;
448                 close(fd);
449                 if (fd >= 0)
450                         return EXIT_SUCCESS;
451                 printf("no... waiting\n");
452                 sleep(REMOVABLE_SLEEP_DELAY);
453         }
454 }
455
456 static int poll_device_unplug(const char *dev_path, int optical)
457 {
458         int rc, fd;
459
460         for (;optical;) {
461                 printf("poll for optical drive removal ...\n");
462                 fd = open(dev_path, O_RDONLY|O_NONBLOCK);
463                 if (fd < 0)
464                         return EXIT_FAILURE;
465                 rc = ioctl(fd, CDROM_DRIVE_STATUS, CDSL_CURRENT);
466                 close(fd);
467                 if (rc != CDS_DISC_OK)
468                         return EXIT_SUCCESS;
469                 printf("no... waiting\n");
470                 sleep(REMOVABLE_SLEEP_DELAY);
471         }
472
473         /* Fall back to bare open() */
474         for (;;) {
475                 printf("poll for non-optical drive removal ...\n");
476                 fd = open(dev_path, O_RDONLY);
477                 if (fd < 0 && errno != ENOMEDIUM)
478                         return EXIT_FAILURE;
479                 close(fd);
480                 if (fd < 0)
481                         return EXIT_SUCCESS;
482                 printf("no... waiting\n");
483                 sleep(REMOVABLE_SLEEP_DELAY);
484         }
485 }
486
487 static int poll_removable_device(const char *sysfs_path,
488                                  const char *dev_path)
489 {
490         int rc, mounted, optical = -1;
491        
492         for (;;) {
493                 rc = poll_device_plug(dev_path, &optical);
494                 if (rc == EXIT_FAILURE)
495                         return rc;
496                 rc = found_new_device(dev_path);
497                 mounted = (rc == EXIT_SUCCESS);
498
499                 poll_device_unplug(dev_path, optical);
500
501                 remove_device(dev_path);
502
503                 /* Unmount it repeatedly, if needs be */
504                 while (mounted && !unmount_device(dev_path))
505                         ;
506                 sleep(1);
507         }
508 }
509
510 int main(int argc, char **argv)
511 {
512         char *dev_path, *action;
513         int rc;
514
515         /*if (fork())
516                 return EXIT_SUCCESS;
517                 */
518         action = getenv("ACTION");
519
520         logf = stdout;
521         rc = EXIT_SUCCESS;
522
523         if (!action) {
524                 log("missing environment?\n");
525                 return EXIT_FAILURE;
526         }
527
528         if (connect_to_socket())
529                 return EXIT_FAILURE;
530
531         if (streq(action, "fake")) {
532                 log("fake mode");
533
534                 add_device(&fake_boot_devices[0]);
535                 add_boot_option(&fake_boot_options[0]);
536                 add_boot_option(&fake_boot_options[1]);
537                 add_boot_option(&fake_boot_options[2]);
538                 add_device(&fake_boot_devices[1]);
539                 add_boot_option(&fake_boot_options[3]);
540
541                 return EXIT_SUCCESS;
542         }
543
544         dev_path = getenv("DEVNAME");
545         if (!dev_path) {
546                 log("missing environment?\n");
547                 return EXIT_FAILURE;
548         }
549
550         if (streq(action, "add")) {
551                 char *sysfs_path = getenv("DEVPATH");
552                 if (sysfs_path && is_removable_device(sysfs_path))
553                         rc = poll_removable_device(sysfs_path, dev_path);
554                 else
555                         rc = found_new_device(dev_path);
556         } else if (streq(action, "remove")) {
557                 log("%s removed\n", dev_path);
558
559                 remove_device(dev_path);
560
561                 /* Unmount it repeatedly, if needs be */
562                 while (!unmount_device(dev_path))
563                         ;
564
565         } else {
566                 log("invalid action '%s'\n", action);
567                 rc = EXIT_FAILURE;
568         }
569         return rc;
570 }