2

I'm putting together a quick-and-dirty script to read 100s of CD/DVDs onto a NAS. It takes care of ejecting and loading the tray, but I haven't figured out a simple way to wait/block for the medium to become available to the OS after inserting the tray.

  • eject -t exits as soon as the tray closes
  • mount will fail with No medium found if it's run immediately after eject, but also when it's run 15 seconds later.

The discs are fairly old and some take considerable time to become available, so I'd like to avoid the "sleep for 2 minutes" non-solution. Any ideas? Using ubuntu 18.04.

Rui F Ribeiro
  • 55,929
  • 26
  • 146
  • 227
myxal
  • 135
  • 1
  • 8
  • On most distros, there's a fairly complicated system of udev rules which try to identify a CD as soon as it becomes available. Try to hook into those rules, or see if they emit DBUS events. – dirkt Oct 25 '18 at 06:16

2 Answers2

3

Will this do?

while ! dd if=/dev/sr2 bs=2048 count=1 of=/dev/null 2>/dev/null; do sleep 1; done

Replace /dev/sr2 with your actual cd/dvd device.

You can make it more robust by checking for errors other than No medium found; untested, since I don't have any broken DVD at hand:

dev=/dev/sr2
while :; do
        err=$(dd if=$dev of=/dev/null bs=2048 status=none count=1 2>&1)
        case $err in
        "dd: failed to open '$dev': No medium found")
                sleep 1 ;;
        '')
                # successfully opened
                break ;;
        *)
                # unexpected error
                # play some SOUND in the speakers
                # and wait for user input to continue
                read wtf ;;
        esac
done

Update:

On drives that support it, linux will automatically close the tray on trying to open the device. While this could probably be disabled by ioctl(CDROM_CLEAR_OPTIONS, CDO_AUTO_CLOSE), I wasn't able to find any command line utility implementing it. Since that forces one to use C, better use C for the whole thing.

So, here's a small application that can check and poll for the cd/dvd drive status.

If called with a single argument:

cdstatus /dev/sr1

it will print the status of /dev/sr1: one of no_disc, tray_open, drive_not_ready or disc_ok.

If called with two arguments:

cdstatus /dev/sr1 1.3

it will poll /dev/sr1 every 1.3 seconds until its status is disk_ok.

It could be easily built with cc -Os -Wall cdstatus.c -s -o cdstatus, provided that the gcc and libc6-dev packages are installed.

cdstatus.c

#include <sys/ioctl.h>
#include <linux/cdrom.h>
#include <fcntl.h>
#include <err.h>
#include <stdio.h>
#include <stdlib.h>
#include <poll.h>
int main(int argc, char **argv){
        int fd, s; int pt = -1;
        if(argc < 2)
                errx(1, "usage: %s /dev/srX [poll_secs] [verbose]", argv[0]);
        if((fd = open(argv[1], O_RDONLY|O_NONBLOCK)) == -1)
                err(1, "open %s", argv[1]);
        if(argc > 2 && ((pt = strtod(argv[2], 0) * 1000) < 1 || pt > 3600000))
                errx(1, "bad timeout '%s'", argv[2]);
redo:
        switch(s = ioctl(fd, CDROM_DRIVE_STATUS, 0)){
        case -1: err(1, "ioctl(CDROM_DRIVE_STATUS)");
        case CDS_NO_INFO: errx(1, "ioctl(CDROM_DRIVE_STATUS) not implemented");
        }
        if(pt < 0 || argc > 3)
                switch(s){
                case CDS_NO_DISC: printf("no_disc\n"); break;
                case CDS_TRAY_OPEN: printf("tray_open\n"); break;
                case CDS_DRIVE_NOT_READY: printf("drive_not_ready\n"); break;
                case CDS_DISC_OK: printf("disc_ok\n"); break;
                default: printf("status=%d\n", s); break;
                }
        if(pt > 0 && s != CDS_DISC_OK){
                if(poll(0, 0, pt) < 0) err(1, "poll");
                goto redo;
        }
        return s != CDS_DISC_OK;
}
  • ... almost. However in my case it also closes an open DVD/CD drive platter. I was looking for a way to detect a DVD/CD after a User has closed the drive. – will Nov 05 '18 at 10:51
  • 1
    I have updated the answer -- if that's not an option for you, it may at least give you some hints on what to google for. –  Nov 06 '18 at 10:24
1

In the end, a post in this vaguely-related thread on linuxquestions suggested to use wodim -atip (among other things) which turns out to work great to block until the medium is available.

#!/bin/bash

set -e

DRIVE=${DRIVE:-"sr0"}
DESTROOT=${DESTROOT:-"/mnt/newshare"}
DRV_FULLNAME="$(</sys/class/block/$DRIVE/device/vendor) $(</sys/class/block/$DRIVE/device/model)"

[ -d /tmp/$DRIVE ] || mkdir /tmp/$DRIVE
while true; do
  eject /dev/$DRIVE
  echo "$DRV_FULLNAME is ready."
  read -p "Enter next disc code: " DISC
  if [ -z "$DISC" ]; then
    echo "No disc code = we're done, exiting"
    break
  fi
  eject -t /dev/$DRIVE || {
    read -n1 -r -p "Unable to load tray. Push it in and hit Any key..."
    echo
  }
  wodim dev=/dev/$DRIVE -atip > /dev/null || {
    echo "Medium not detected."
    break
  }
  mount /dev/$DRIVE /tmp/$DRIVE -o ro ||  {
    echo "Couldn't mount medium."
    break
  }
  cp -ia /tmp/$DRIVE/. $DESTROOT/$DISC/ || {
    echo "COPY FAILED. Put it on the read-error pile and continue with next one."
  }
done
myxal
  • 135
  • 1
  • 8
  • 1
    I wouldn't use that. There's no guarantee that `wodim` command won't simply return with an error. It's a race game. Please also consider my [answer](https://unix.stackexchange.com/a/477629/314839). And if I was you, I wouldn't try to mount the cd/dvds on the device-- I would rather `dd` all of them whole into `.iso` images in some dir, and then process them leasurely (mount them with `-o loop`, etc). –  Oct 25 '18 at 12:04
  • Thanks for the feedback. Re: mounting and copying - no worries. The discs are well sorted and the largest group are things where copying files is the right thing to do. ;-) Re: `wodim` race game - oops, I seem to have put the error handler in the wrong place. Will edit that. I have seen your answer and thanks for the case-based error handler, but I was looking for a one-liner that would avoid looping. – myxal Oct 25 '18 at 13:17