cdbackup.c
author nathan
Sat, 29 Dec 2007 15:23:49 +0100
branchtrunk
changeset 2 6bcb44b9edb1
parent 0 d85c12073dea
child 4 79da91042fcc
permissions -rw-r--r--
release 0.6.3
     1 /* cdbackup.c
     2 Copyright (c) 2000-2002 Craig Condit, Stefan Hülswitt.
     3 
     4 Redistribution and use in source and binary forms, with or without
     5 modification, are permitted provided that the following conditions are met: 
     6 
     7 1. Redistributions of source code must retain the above copyright notice,
     8    this list of conditions and the following disclaimer. 
     9 2. Redistributions in binary form must reproduce the above copyright notice,
    10    this list of conditions and the following disclaimer in the documentation
    11    and/or other materials provided with the distribution. 
    12 
    13 THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS
    14 OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
    15 WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
    16 DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR
    17 ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
    18 DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
    19 SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
    20 CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
    21 LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
    22 OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
    23 SUCH DAMAGE.
    24 */
    25 
    26 #define _LARGEFILE64_SOURCE
    27 
    28 #include <stdlib.h>
    29 #include <stdio.h>
    30 #include <unistd.h>
    31 #include <fcntl.h>
    32 #include <string.h>
    33 #include <time.h>
    34 #include <errno.h>
    35 #include <getopt.h>
    36 #include <sys/wait.h>
    37 #include <sys/ioctl.h>
    38 #include <netinet/in.h>
    39 #include <linux/cdrom.h>
    40 
    41 #include "cdbackup.h"
    42 #include "cdrom.h"
    43 #include "misc.h"
    44 #include "debug.h"
    45 #include "version.h"
    46 
    47 /* #define DEBUGOUT */
    48 
    49 /* defaults */
    50 char * prg_name ="cdbackup";
    51 char * cd_dev   ="/dev/burner";
    52 char * cdr_dev  =0;			/* no default here, too dangerous */
    53 char * cd_label ="CDBackup Track";
    54 int    cd_speed =4;
    55 long   cd_len   =333000;		/* blocks */
    56 int    padsize  =15;			/* blocks */
    57 int    multidisk=0;
    58 char * multicmd =0;
    59 int    verbose  =0;
    60 int    xamode2  =0;
    61 int    debug    =0;
    62 
    63 char **cdrec_opt=0;
    64 int    cdrec_opt_count=0;
    65 
    66 long long totalSize;
    67 struct tm curtime;  /* global, so multi-disks get all the same time */
    68 
    69 /****************************************************************************/
    70 
    71 void usage()
    72 {
    73   fprintf(stderr,
    74     "Usage: %s [options ...] [-- cdrecord-options ...]\n"
    75     "Reads from standard input, block formats and writes to CD-R(W).\n\n"
    76     "  -d DEVICE      DEVICE for CD queries (default /dev/burner)\n"
    77     "  -l N           CD-R has a size of N MB (default 650)\n"
    78     "  -r DEVICE      DEVICE for CD recording (e.g. 0,4,0)\n"
    79     "  -s N           record CD at speed N (default 4)\n"
    80     "  -X             enable CDROM XA2 mode in cdrecord\n"
    81     "  -a LABEL       use LABEL as CD session title\n"
    82     "  -p N           use a padsize of N sectors for the session (default 15)\n"
    83     "  -m             enable multi-disk mode\n"
    84     "  -c COMMAND     call COMMAND on disk change in multi-disk mode\n"
    85     "  -v             be verbose\n"
    86     "  -D             enable DEBUG output\n"
    87     "  -V             prints version & exits\n"
    88     "  --             pass rest of commandline to cdrecord\n"
    89     "\n", prg_name);
    90 }
    91 
    92 /****************************************************************************/
    93 
    94 void parse_cmdline(char argc, char *argv[]) 
    95 {
    96   int i;
    97 
    98   while ((i=getopt(argc,argv,"d:r:l:s:p:a:c:mvVXD"))>0) {
    99     switch (i) {
   100        case 'V': fprintf(stderr,"cdbackup "VERSION" (compiled "__DATE__")\n"
   101 	                        "Copyright (C) 2000-2002\n"
   102 			        "This is free software; see the source for copying conditions.\n"
   103 			        "There is NO warranty; not even for MERCHANTABILITY or FITNESS FOR A\n"
   104 			        "PARTICULAR PURPOSE.\n");
   105                  exit(0);
   106        case 'v': verbose=1; break;
   107        case 'm': multidisk=1; break;
   108        case 'X': xamode2=1; break;
   109        case 'c': multicmd=optarg; break;
   110        case 'd': cd_dev=optarg; break;
   111        case 'r': cdr_dev=optarg; break;
   112        case 'a': cd_label=optarg; break;
   113        case 'D': verbose=1; debug=1; 
   114                  DEBUG("cdbackup: DEBUG output enabled ("VERSION")\n");
   115                  break;
   116        case 'l': errno=0; cd_len=strtol(optarg,NULL,10);
   117                  if(errno==ERANGE || cd_len<1) serror("Option -l: length out of range (must be >=1)\n");
   118 	         cd_len = (long long)cd_len * (1024*1024) / CD_FRAMESIZE; /* convert to blocks */
   119 	         break;
   120        case 's': errno=0; cd_speed=strtol(optarg,NULL,10);
   121                  if(errno==ERANGE || cd_speed<1) serror("Option -s: speed out of range (must be >=1)\n");
   122 	         break;
   123        case 'p': errno=0; padsize=strtol(optarg,NULL,10);
   124                  if(errno==ERANGE || padsize<15) serror("Option -p: padsize out of range (must be >=15)\n");
   125 	         break;
   126        default:  usage(); exit(0);
   127        }
   128     }
   129 
   130   if(optind<argc) { /* save position/count of cdrecord options */
   131     cdrec_opt_count=argc-optind;
   132     cdrec_opt=&argv[optind];
   133     }
   134     
   135   if(!cdr_dev) serror("You must specify a device for cdrecord with -r\n");
   136 }
   137 
   138 /****************************************************************************/
   139 
   140 #define MARG(ptr,len,form,arg) { int l=(len);\
   141                                if(!(*ptr=(char *)malloc(l+1))) serror("No memory for cdrecord args\n");\
   142                                snprintf(*ptr++,l,form,arg);\
   143                              }
   144 
   145 void start_cdrecord() 
   146 {
   147   char **args, **p;
   148   int l;
   149   
   150   if(!(p=args=calloc(cdrec_opt_count+8,sizeof(char *))))
   151     serror("No memory for cdrecord args\n");
   152 
   153   *p++="cdrecord";
   154   *p++="-multi";
   155   MARG(p,16,"speed=%d",cd_speed);
   156   MARG(p,6+strlen(cdr_dev),"dev=%s",cdr_dev);
   157 
   158   for(l=0 ; l<cdrec_opt_count ; l++) *p++=cdrec_opt[l];
   159 
   160   MARG(p,20,"padsize=%ds",padsize);
   161   if(xamode2) *p++="-xa2"; else *p++="-data";
   162   *p++="-";
   163   *p++=0;
   164 
   165   if(debug) {
   166     fprintf(stderr,"%s: cdrecord command:",prg_name);
   167     for(p=args ; *p ; p++) fprintf(stderr," %s",*p);
   168     fprintf(stderr,"\n");
   169     }
   170 
   171   execvp("cdrecord",args);
   172   error("Unable to launch cdrecord");
   173 }
   174 
   175 /****************************************************************************/
   176 
   177 int backup(char disk_set)
   178 {
   179   pid_t childpid;
   180   int fd[2];
   181   int outpipe, bytes;
   182   long long grandTotal=0;
   183   struct header_block header;
   184 
   185   char buffer[CD_FRAMESIZE];
   186   struct data_block *db=(struct data_block *)&buffer[0];
   187 
   188   sprintf(buffer, "%04d%02d%02d%02d%02d", curtime.tm_year + 1900,
   189     curtime.tm_mon + 1, curtime.tm_mday, curtime.tm_hour, curtime.tm_min);
   190   
   191   strncpy(header.id_str,HDR_STRING,32); header.id_str[32] = 0;
   192   strncpy(header.vol_id,cd_label,32); header.vol_id[32] = 0;
   193   strncpy(header.t_stamp,buffer,12); header.t_stamp[12] = 0;
   194   header.disk_set = disk_set;
   195 
   196   if(verbose)
   197     fprintf(stderr,"%s: Recording to device %s, multidisk %s, disk %d\n",prg_name,cdr_dev,multidisk?"enabled":"disabled",disk_set); 
   198 
   199 #ifndef DEBUGOUT /* the "real" code */
   200   /* launch cdrecord */
   201   if(pipe(fd) == -1) error("Unable to create pipe handles");
   202   if((childpid=fork()) == -1) error("Fork failed");
   203   if(childpid == 0) {        /* child */
   204     close(fd[1]);
   205     close(0);		     /* stdin */
   206     dup2(fd[0], 0);
   207     start_cdrecord();        /* doesn't return */
   208     }
   209 
   210   close(fd[0]); outpipe=fd[1];
   211   
   212   /* output the header block */
   213   memset(buffer,0,CD_FRAMESIZE);
   214   memcpy(buffer,&header,sizeof(struct header_block));
   215   if((bytes=write(outpipe, buffer, CD_FRAMESIZE)) != CD_FRAMESIZE) error("Error writing header block");
   216 
   217   cd_avail-=bytes; grandTotal+=bytes;
   218   /* account for the padsize */
   219   cd_avail-=padsize*CD_FRAMESIZE;
   220 #else
   221   /* debug code; send data to /dev/null.  Don't need the pipe. */
   222   fprintf(stderr, "DEBUG CODE: sending data to /dev/null!\n");
   223   outpipe = open("/dev/null", O_WRONLY);
   224   if (outpipe < 0) { perror("/dev/null"); exit(1); }
   225 #endif
   226 
   227   db->reserved = 0;
   228 
   229   do {
   230     /* read a block */
   231     db->status = 0;		         /* this isn't the last block (for now) */
   232     bytes=full_read(0,&buffer[DBSIZE],DATASIZE);
   233     if (bytes < 0) error("Error reading input");
   234     if (bytes != DATASIZE) db->status=1; /* EOF, this is the last block */
   235     db->datasize = htons(bytes);
   236 
   237     /* check for free space */
   238     if(cd_avail < (CD_FRAMESIZE*2)) {	/* less than 2 block free */
   239       if(db->status==0) db->status=2;   /* if not last block, mark disk as full */
   240       }
   241 
   242     /* write a block */
   243     bytes = write(outpipe, buffer, CD_FRAMESIZE);
   244     if(bytes != CD_FRAMESIZE) error("Error writing data block");
   245 
   246     grandTotal+=bytes; cd_avail-=bytes;
   247     } while(db->status==0);
   248 
   249   /* close pipe and wait for child termination */
   250   close(outpipe);
   251   while (wait(0) != childpid);
   252 
   253   totalSize+=grandTotal;
   254   if(verbose) fprintf(stderr,"%s: Recording finished. %lld kB written (%lld kB on this disk)\n",
   255                       prg_name,totalSize/1024,grandTotal/1024);
   256 
   257   if(db->status==2) return 1; /* disk was full */
   258   return 0;
   259 }
   260 
   261 /****************************************************************************/
   262 
   263 int main(int argc, char *argv[]) 
   264 {
   265   int cdr;
   266   int disknum, result, loop;
   267   time_t curtime_t;
   268 
   269   disknum=1; totalSize=0;
   270   curtime_t=time(0); curtime=*localtime(&curtime_t);
   271 
   272   parse_cmdline(argc,argv);
   273 
   274   do {
   275     do {
   276       cdr=open_cdr(cd_dev); result=read_toc(cdr,0); close_cdr(cdr);
   277       print_space();
   278       loop=1;
   279   
   280       if(disknum>1 && result!=0) {
   281         fprintf(stderr,"%s: Can't do multidisk continuation on non-empty disk! Try another disk\n", prg_name);
   282         if(start_diskchange(multicmd,cd_dev)) serror("Diskchange command failed");
   283         }
   284       else if(cd_avail<(padsize+MIN_BLOCKS)*CD_FRAMESIZE) {
   285         if(multidisk) {
   286           fprintf(stderr,"%s: Not enough free space on disk! Try another disk\n", prg_name);
   287           if(start_diskchange(multicmd,cd_dev)) serror("Diskchange command failed");
   288           }
   289         else serror("Not enough free space on disk");
   290         }
   291       else loop=0;
   292       } while(loop);
   293 
   294     result = backup(disknum);
   295     if(result == 1) {
   296       if(multidisk == 0) serror("Disk full, multi-disk not enabled. Aborting");
   297       
   298       disknum++;
   299       if(start_diskchange(multicmd,cd_dev)) serror("Diskchange command failed");
   300       }
   301     } while (result != 0);
   302 
   303   return 0;
   304 }