#!/usr/bin/env perl # # spcd (Server side PIRL CD ) # this runs on grendel # # Written by Moses Milazzo, Slacker Extraordinaire, May 2000 LPL/PIRL # # Modified Oct 2001 JHP # Added routine for copying CD # Modified 20 Dec 2001 MPM # Added ability to get username, removed "cdr" alias on PIRL systems, # and wrote an expect script to send spcd the calling user's username. # We now inform people of who to contact if they can't run the cdr # program. # Modified 28 Jan 2002 MPM # Added back the ability to check a newly burned CD, and changed # the write speed from 4x to 8x. # Modified 17 Oct 2002 MPM # Added to the signals which are handled cleanly # There was an incident in which a user was unable to run the program # because it didn't cleanly delete the lock file. I'm not sure what # caused this, probably some strange crash or maybe he logged off w/o # exiting cdr. I'll have to look into the expect script and see if # THAT can be made to exit cleanly. # Modified 10 Dec 2002 MPM # Added the DVD reader/writer, but have yet to implement recording. # # Modified 2 Jan 2003 MPM # Added ability to burn DVDs, erase rewritable disks, and fixed a bug # in the copy CD routine. It would return from the dd command before # it was finished, which meant the user could write bad data to disk. # # Improvements: # Needs a cdrecord subroutine that handles recording, I/O, etc. # Argument is location of image. # Need to get cdrecord to give information about time to burn, etc. # $VERSION="0.0.10 Alpha"; use FileHandle; use IPC::Open2; use Shell; use Env; use sigtrap 'handler' => \&catch_zap, qw(HUP BUS SEGV PIPE ABRT QUIT TERM INT SYS TRAP); # Mount points $IMAGE_MNT = "/test_image"; $RECORDER_MNT = "/cdrom"; $READER_MNT = "/cdrom1"; # special variables $RECORDER="/dev/sr0"; $CDROM="/dev/sr1"; #$CACHE="/dev/hdb2"; $ISOIMAGE="/image/image.iso"; # CDRECORD special variables # what is the program name used to record the CDs? $CDRECORD="cdrecord"; # What speed do we burn? $SPEED="8"; # What is the SCSI device number (needed by cdrecord)? $DEVICE="0,0,0"; # this is for clearing the screen when we print menus, etc. $cls=clear(); # lock file to disallow concurrent access. $lock = "/tmp/lock_pcdc"; print $cls; # Get the user's username. The name is sent to us via an expect # script, if the user has called this with "cdr". # This subroutine is called if the lock file doesn't exist. # Otherwise, jump to the next subroutine, which exits with a # message informing the user that spcd (cdr) is in use. sub getname { print "Please enter your username: "; chop($usern = ); return $usern; } if ( !-e $lock ) { getname(); } # If the lock file exists, then refuse to allow access to the rest # of the program. # Get the username from the lock file so we can tell the person to whom we # are refusing access who to whine to. if ( -e $lock ) { open LOCKFILE, "<$lock"; $_ = ; close LOCKFILE; $usern = $_; print $cls; print " " .$usern . " is using the CD/DVD-burner, I would suggest you wait about 15-45 minutes and try again. If you keep getting this, please inform the system administrator, or send " . $usern . " an email, perhaps they forgot to quit the program.\n"; sleep 2; exit; } # OK, no problems with the lock file, and we have the username, so # now we'll start the program in earnest. First we need to lock # access to the burner. . . # Spit the username out to the lock file. open LOCKFILE, ">$lock"; print LOCKFILE "$usern"; close LOCKFILE; # catch ^C , ^Z and other signals that would otherwise # cause us to sit in limbo. We also need to catch the # various kill signals. . . # This is done with the module sigtrap used above, which # calls the catch_zap subroutine. This way, one should be # able to easily add signals that I didn't think were important, # but happen to be. . . # sub catch_zap { my $signame = shift; print "\nGoodbye $usern!\n"; if (-e $lock) { rm($lock); } exit; } # This is the old way of handling signals. . . #$SIG{INT} = \&catch_zap; #$SIG{TERM}= \&catch_zap; #$SIG{QUIT}= \&catch_zap; #$SIG{KILL}= \&catch_zap; # define the terminal environment so we know what we are # dealing with. $ENV{TERM}="vt100"; sub prompt { print $cls; print "\n"; print "\n"; print " Welcome to the PIRL CD/DVD burner version $VERSION\n"; print "\n"; print " If you are just starting, you may want to do the first three\n"; print " steps in order, premaster, test, and burn. If you already\n"; print " have a disk image that you would like to burn, you want to \n"; print " use option [i], then option [e].\n"; print "\n"; print " Please choose:\n"; print "\n"; print "\n"; print " Premaster (make ISO9660 image of data).......................[p]\n"; print " Test your new Image..........................................[t]\n"; print " Burn yourself a new CD.......................................[b]\n"; print " Test a pre-made image........................................[i]\n"; print " Burn a pre-made image (to CD)................................[e]\n"; print " Copy a CD/DVD to the premaster partition.....................[c]\n"; print " Blank a rewritable CD........................................[w]\n"; print " Mount your new CD/DVD (test to see if the burn worked).......[m]\n"; print " Burn yourself a new DVD......................................[d]\n"; print "\n"; print " Quit CDR.....................................................[q]\n"; print "\n"; print " Your choice? ==> "; } # Some day, I should change this so there are a couple of subroutines that # the prompt loop calls with specific arguments. This would be much cleaner # and easier to keep track of. . . # prompt for an option until the user gives us something we can use. RUN: for (prompt(); $choice = ; prompt()) { $_=$choice; SWITCH: { /^[Pp]$/ && do { $pready = 0; PREMASTER: while ( !$pready ) { print "\n"; print "Please enter the name of your volume: \n"; print "That is, what do you want your CD to be called?\n"; print "This must be one word; no spaces allowed!\n"; print " > "; chop($volume = ); print "Please enter the directory in which your data resides: \n"; print "####NOTE: You MUST use a full pathname.####\n"; print " > "; chop($dir = ); unless ( $volume && $dir ) { print "\n"; print "ERROR: \n"; print "\n"; print "ERROR: \n"; print "One or the both of your selections was NULL, please re-enter.\n"; print "I'll just return you back to the main menu.\n"; print "Please hit ENTER to continue\n"; while (!) { } last SWITCH; } print "This is what you entered for your volume name:\n"; print "\n"; print $volume . "\n"; print "\n"; print "This is what you entered for your data directory:\n"; print "\n"; print $dir . "\n"; print "\n"; print "\n"; print "Are you happy with your decision? \n [Y(es)/N(o)/Q(uit to main)] > "; chop($change = ); $_ = $change; DECISION: { /^[yY]$/&& do { $pready = 1; last DECISION; }; /^[qQ]$/&& do { last SWITCH; }; print "I will take that ( ".$change." ) as a no.\n"; } } # endwhile (on $pready) print "You'll probably receive some errors about this being a \n"; print "non-ISO 9660 compliant filesystem. These can be safely \n"; print "ignored.\n"; unlink($ISOIMAGE); open PRESTAT, "mkisofs -R -U -D -r -V $volume -o $ISOIMAGE $dir|"; while () { print; } close(PRESTAT); print "Premaster step complete.\n"; print "Please hit ENTER to continue. . ."; while (!) {} last SWITCH; }; #end of "premaster" /^[Cc]$/ && do { # This option now uses the spare DVD drive to directly copy a CD/DVD # to the premaster partition. Unfortunately, there doesn't seem to # be enough buffering going on to directly send the image to the # recorder. Hence this intermediate step. # JHP LPL/PIRL Oct 2001 # /dev/sr1 is where the CD/DVD reader is located. This is $CDROM # perhaps we should put in a faster drive, but I guess it doesn't # matter too much, since if we want to copy directly, we can only # use 8x. . . # MPM LPL/PIRL Jan 2002 print "Copying CD/DVD. . .\n"; print "Make sure that you have the disk you want to copy in the\n"; print "lower drive and then hit ENTER to continue. . ."; while (!) {} my($cdin, $cdout); # Why do we unlink the ISOIMAGE here? Because we just want a clean # slate. We'll copy in 1MB increments so dd will report the approximate # size of the disk in a manner that the user can understand. unlink($ISOIMAGE); $pid = open2($copyin,$copyout,"dd if=$CDROM of=$ISOIMAGE bs=1024k;echo"); # tell the user that dd's output should tell them the number of MB # copied. print "\nYou should be told the number of records in and out. This\n"; print "should be approximately the number of MB on your disk\n"; while (<$copyin>) { print; } close($copyin); close($copyout); print "Please test your image before recording!\n"; print "Please hit ENTER to continue. . ."; while (!) { } last SWITCH; }; # end "copy" /^[Tt]$/ && do { print "Testing image. . . . .\n"; print "Here is a listing of everything in the root directory of\n"; print "your CD image:\n"; print "\n"; mount("$IMAGE_MNT"); open TESTSTAT, " ls $IMAGE_MNT |"; while() { print; } close(TESTSTAT); print "\n"; print "Here is the amount of disk space (KB) your data uses:\n"; open TESTSTAT, " du -s $IMAGE_MNT | cut -f1 |"; while() { print; } $ifmnt = mount("-l -t iso9660"); if ($ifmnt) { umount("$IMAGE_MNT"); } print "\nPlease hit ENTER to continue. . ."; while (!) { } last SWITCH; }; # end "T" /^[Ii]$/ && do { $iready = 0; IMAGE: while (!$iready) { print "\n"; print "Please enter the full path to your ISO9660 image:\n"; print " > "; chop($iso = ); unless ($iso) { print "\n"; print "ERROR: \n"; print "You entered a NULL for the image. I can't mount that.\n"; print "I'll just return you back to the main menu.\n"; print "Please hit ENTER to continue\n"; while (!) { } last SWITCH; } $iready = 1; } print "Mounting the image. . .\n"; print "Here is a listing of everything in the root directory of\n"; print "your CD image:\n"; print "\n"; #open2($rfoo, $cbar, "sudo mount $iso -o loop $IMAGE_MNT\n"); $mnt_cmd = "sudo mount $iso -o loop ".$IMAGE_MNT; system($mnt_cmd); open TESTIMAGE, "ls $IMAGE_MNT |"; while() { print; } close(TESTIMAGE); print "\n"; print "Here is the amount of disk space (KB) your data uses:\n"; open TESTSTAT, " du -s $IMAGE_MNT | cut -f1 |"; while() { print; } $ifmnt = mount("-l -t iso9660"); if ($ifmnt) { open2($rfoo, $cbar, "sudo umount $IMAGE_MNT");; close ($rfoo); close ($cbar); } print "\nPlease hit ENTER to continue. . ."; while (!) { } last SWITCH; }; # end "I" /^[Ee]$/ && do { $pmade = 0; PREMADE: while (!$pmade) { print "\n"; print "Please enter the full path to your ISO9660 image:\n"; print " > "; chop($iso = ); unless($iso) { print "\n"; print "ERROR: \n"; print "You entered a NULL! I can't deal with that.\n"; print "I'll just return you back to the main menu.\n"; print "Please hit ENTER to continue\n"; while (!) { } last SWITCH; } $pmade = 1; } print "burning the image. . .\n"; my($bcdin, $bcdout); $pid = open2($bcin, $bcout, "sudo $CDRECORD -v speed=$SPEED dev=$DEVICE -data $iso"); while(<$bcin>) { print; } close($bcin); close($bcout); $ifmnt = mount("-l -t iso9660"); if ($ifmnt) { $plid = open2($rfoo, $cbar, "sudo umount $IMAGE_MNT"); close ($rfoo); close ($cbar); } print "\n Please hit ENTER to continue. . ."; while (!) {} last SWITCH; }; # end of "e" /^[Dd]$/ && do { print "Burning the DVD. . .\n"; my($cdrin, $cdrout); $pid = open2($cdrin, $cdrout, "sudo $CDRECORD -packet -v speed=$SPEED dev=$DEVICE -data $ISOIMAGE" ); while (<$cdrin>) { print; } close($cdrin); close($cdrout); my($cdrin, $cdrout); $pid = open2($cdrin, $cdrout, "sudo $CDRECORD -inq dev=$DEVICE" ); while (<$cdrin>) { print; } close($cdrin); close($cdrout); print "Please hit ENTER to continue. . ."; while (!) { } last SWITCH; }; # end of "D" /^[Bb]$/ && do { print "Burning the CD. . .\n"; my($cdrin, $cdrout); $pid = open2($cdrin, $cdrout, "sudo $CDRECORD -v speed=$SPEED dev=$DEVICE -data $ISOIMAGE" ); while (<$cdrin>) { print; } close($cdrin); close($cdrout); my($cdrin, $cdrout); $pid = open2($cdrin, $cdrout, "sudo $CDRECORD -inq dev=$DEVICE" ); while (<$cdrin>) { print; } close($cdrin); close($cdrout); print "Please hit ENTER to continue. . ."; while (!) { } last SWITCH; }; # end of "B" /^[Ww]$/ && do { $erase=0; NOERASE: while (!$erase) { print "\n"; print "Do you really want to erase your entire disk? All data will be destroyed.\n"; print "[Y(es)/N(o)] > "; chop($change = ); $_ = $change; DECISION: { /^[yY]$/&& do { $erase = 1; last DECISION; }; /^[qQ]$/&& do { last SWITCH; }; /^[nN]$/&& do { last SWITCH; }; print "I will take that ( ".$change." ) as a no.\n"; last SWITCH; } } print "Erasing the CD, all of your data will be destroyed. . .\n"; my($erasein, $eraseout); $pid = open2($erasein, $eraseout, "sudo $CDRECORD -v blank=fast -force dev=$DEVICE"); while (<$erasein>) { print; } print "Please hit ENTER to continue. . ."; while (!) { } last SWITCH; }; # end of blanking ("W") /^[Mm]$/ && do { print "I would suggest you just also test the CD on other systems, \n"; print "but you can do what you want. . . \n"; print "\n"; print "Making sure the CD-ROM is not already mounted for some reason.\n"; print "\n"; $plid = open2($rmnt, $cmnt, "umount $RECORDER_MNT"); close ($rmnt); close ($cmnt); $plid = open2($rmnt, $cmnt, "sudo mount $RECORDER_MNT"); while (<$rmnt>) { print; } close ($rmnt); close ($cmnt); print "\n"; print "Here's the standard directory listing:\n\n"; $mid = open2($ls, $uls, "ls /cdrom"); while (<$ls>) { print; } close ($ls); close ($uls); print "\n Here's the size your CD takes up:\n"; $did = open2($idu, $odu, " du -s /cdrom"); while (<$idu>) { print; } close ($idu); close ($odu); $plid = open2($rmnt, $cmnt, "umount $RECORDER_MNT"); close ($rmnt); close ($cmnt); #umount("$RECORDER_MNT"); print "Please hit ENTER to continue. . ."; while (!) { } last SWITCH; }; # end of "M" /^[qQ]$/ && do { print "That's all " . $usern . ", thanks for playing.\n"; # clean up the lock file so someone else can get in. . . rm($lock); # Let's leave with a clean slate. Shouldn't have to worry about # this, but I think we're better off with removing the disk images # when we quit than if we were to leave them on the disk. unlink($ISOIMAGE); last RUN; }; # end of "q" } # end SWITCH } #end RUN if (-e $lock) { rm ($lock); }