+
+static int is_removable_device(const char *sysfs_path)
+{
+ char full_path[PATH_MAX];
+ char buf[80];
+ int fd, buf_len;
+
+ sprintf(full_path, "/sys/%s/removable", sysfs_path);
+ fd = open(full_path, O_RDONLY);
+ pb_log(" -> removable check on %s, fd=%d\n", full_path, fd);
+ if (fd < 0)
+ return 0;
+ buf_len = read(fd, buf, 79);
+ close(fd);
+ if (buf_len < 0)
+ return 0;
+ buf[buf_len] = 0;
+ return strtol(buf, NULL, 10);
+}
+
+static int is_ignored_device(const char *devname)
+{
+ static const char *ignored_devices[] =
+ { "/dev/ram", "/dev/loop", NULL };
+ const char **dev;
+
+ for (dev = ignored_devices; *dev; dev++)
+ if (!strncmp(devname, *dev, strlen(*dev)))
+ return 1;
+
+ return 0;
+}
+
+static int found_new_device(const char *dev_path)
+{
+ const char *mountpoint = mountpoint_for_device(dev_path);
+
+ if (mount_device(dev_path)) {
+ pb_log("failed to mount %s\n", dev_path);
+ return EXIT_FAILURE;
+ }
+
+ pb_log("mounted %s at %s\n", dev_path, mountpoint);
+
+ iterate_parsers(dev_path, mountpoint);
+
+ return EXIT_SUCCESS;
+}
+
+static void detach_and_sleep(int sec)
+{
+ static int forked = 0;
+ int rc = 0;
+
+ if (sec <= 0)
+ return;
+
+ if (!forked) {
+ pb_log("running in background...");
+ rc = fork();
+ forked = 1;
+ }
+
+ if (rc == 0) {
+ sleep(sec);
+
+ } else if (rc == -1) {
+ perror("fork()");
+ exit(EXIT_FAILURE);
+ } else {
+ exit(EXIT_SUCCESS);
+ }
+}
+
+static int poll_device_plug(const char *dev_path,
+ int *optical)
+{
+ int rc, fd;
+
+ /* Polling loop for optical drive */
+ for (; (*optical) != 0; ) {
+ fd = open(dev_path, O_RDONLY|O_NONBLOCK);
+ if (fd < 0)
+ return EXIT_FAILURE;
+ rc = ioctl(fd, CDROM_DRIVE_STATUS, CDSL_CURRENT);
+ close(fd);
+ if (rc == -1)
+ break;
+
+ *optical = 1;
+ if (rc == CDS_DISC_OK)
+ return EXIT_SUCCESS;
+
+ detach_and_sleep(REMOVABLE_SLEEP_DELAY);
+ }
+
+ /* Fall back to bare open() */
+ *optical = 0;
+ for (;;) {
+ fd = open(dev_path, O_RDONLY);
+ if (fd < 0 && errno != ENOMEDIUM)
+ return EXIT_FAILURE;
+ close(fd);
+ if (fd >= 0)
+ return EXIT_SUCCESS;
+ detach_and_sleep(REMOVABLE_SLEEP_DELAY);
+ }
+}
+
+static int poll_device_unplug(const char *dev_path, int optical)
+{
+ int rc, fd;
+
+ for (;optical;) {
+ fd = open(dev_path, O_RDONLY|O_NONBLOCK);
+ if (fd < 0)
+ return EXIT_FAILURE;
+ rc = ioctl(fd, CDROM_DRIVE_STATUS, CDSL_CURRENT);
+ close(fd);
+ if (rc != CDS_DISC_OK)
+ return EXIT_SUCCESS;
+ detach_and_sleep(REMOVABLE_SLEEP_DELAY);
+ }
+
+ /* Fall back to bare open() */
+ for (;;) {
+ fd = open(dev_path, O_RDONLY);
+ if (fd < 0 && errno != ENOMEDIUM)
+ return EXIT_FAILURE;
+ close(fd);
+ if (fd < 0)
+ return EXIT_SUCCESS;
+ detach_and_sleep(REMOVABLE_SLEEP_DELAY);
+ }
+}
+
+static int poll_removable_device(const char *sysfs_path,
+ const char *dev_path)
+{
+ int rc, mounted, optical = -1;
+
+ for (;;) {
+ rc = poll_device_plug(dev_path, &optical);
+ if (rc == EXIT_FAILURE)
+ return rc;
+ rc = found_new_device(dev_path);
+ mounted = (rc == EXIT_SUCCESS);
+
+ poll_device_unplug(dev_path, optical);
+
+ remove_device(dev_path);
+
+ /* Unmount it repeatedly, if needs be */
+ while (mounted && !unmount_device(dev_path))
+ ;
+ detach_and_sleep(1);
+ }
+}
+