cdbackup.c
author nathan
Sun, 23 May 2010 08:30:42 +0200
branchtrunk
changeset 12 6d3770b1a9e1
parent 10 79f97a86b31a
child 13 6ef9b2adca64
permissions -rw-r--r--
add option to specify cdrecord executable
     1 /* cdbackup.c
     2 Copyright (c) 2000-2010 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 #define _GNU_SOURCE
    28 
    29 #include <stdlib.h>
    30 #include <stdio.h>
    31 #include <stdarg.h>
    32 #include <unistd.h>
    33 #include <fcntl.h>
    34 #include <string.h>
    35 #include <time.h>
    36 #include <errno.h>
    37 #include <sys/wait.h>
    38 #include <sys/ioctl.h>
    39 #include <netinet/in.h>
    40 
    41 #ifndef sun
    42 #include <getopt.h>
    43 #endif
    44 
    45 #include "cdbackup.h"
    46 #include "cdrom.h"
    47 #include "virtual.h"
    48 #include "misc.h"
    49 #include "debug.h"
    50 #include "version.h"
    51 
    52 /* defaults */
    53 char *prg_name ="cdbackup";
    54 char *cd_dev   ="/dev/burner";
    55 char *cdr_dev  =0;                     /* no default here, too dangerous */
    56 char *cd_label ="CDBackup Track";
    57 int   cd_speed =4;
    58 long  cd_len   =-1;                    /* blocks */
    59 int   padsize  =15;                    /* blocks */
    60 int   multidisk=0;
    61 char *multicmd =0;
    62 int   verbose  =0;
    63 int   xamode2  =0;
    64 int   crc      =1;
    65 int   debug    =0;
    66 int   virtual  =0;
    67 char *virt_name=0;
    68 int   virt_dump=0;
    69 int   dvd      =0;
    70 char *exename  ="cdrecord";
    71 
    72 char **cdrec_opt=0;
    73 int    cdrec_opt_count=0;
    74 
    75 long long totalSize=0;
    76 int disknum=1;
    77 int secs;
    78 struct tm curtime;  /* global, so multi-disks get all the same time */
    79 int auto_size=0;
    80 
    81 /****************************************************************************/
    82 
    83 char *make_arg(const char *format, ...)
    84 {
    85   char *ptr;
    86   va_list ap;
    87   va_start(ap,format);
    88   if(vasprintf(&ptr,format,ap)<0) {
    89     serror("No memory for cdrecord args\n");
    90     ptr=0;
    91     }
    92   va_end(ap);
    93   return ptr;
    94 }
    95 
    96 void start_cdrecord(void)
    97 {
    98   char **args, **p;
    99   int l;
   100   
   101   if(!(p=args=calloc(cdrec_opt_count+32,sizeof(char *))))
   102     serror("No memory for cdrecord args\n");
   103 
   104   *p++=exename;
   105 
   106   if(virt_dump || dvd) {
   107     *p++="-dao";
   108     *p++=make_arg("tsize=%ds",secs);
   109     }
   110   else {
   111     *p++="-multi";
   112     *p++="-tao";
   113     *p++=make_arg("padsize=%ds",padsize);
   114     }
   115 
   116   *p++=make_arg("speed=%d",cd_speed);
   117   *p++=make_arg("dev=%s",cdr_dev);
   118 
   119   for(l=0 ; l<cdrec_opt_count ; l++) *p++=cdrec_opt[l];
   120 
   121   if(xamode2 && !dvd) *p++="-xa2"; else *p++="-data";
   122   *p++="-";
   123   *p++=0;
   124 
   125   if(debug) {
   126     fprintf(stderr,"%s: cdrecord command:",prg_name);
   127     for(p=args ; *p ; p++) fprintf(stderr," %s",*p);
   128     fprintf(stderr,"\n");
   129     }
   130 
   131   execvp(exename,args);
   132   error("Exec failed (cdrecord)");
   133 }
   134 
   135 long atip_cdrecord(void)
   136 {
   137   char *cmd;
   138   FILE *p;
   139   long size=-1;
   140   
   141   asprintf(&cmd,"%s 2>&1 dev=%s -atip",exename,cdr_dev);
   142   DEBUG("%s: cdrecord atip command: %s\n",prg_name,cmd);
   143 
   144   p=popen(cmd,"r");
   145   if(!p) fprintf(stderr,"%s: atip command failed\n",prg_name);
   146   else {
   147     char buff[256];
   148     while(fgets(buff,sizeof(buff),p)) {
   149       if(dvd) {
   150          /* DVD-R */
   151 	 if(!strncmp(buff,"rzone size:",11)) size=strtol(&buff[11],NULL,10);
   152 	 /* DVD+R */
   153 	 else if(!strncmp(buff,"phys size:...",13)) size=strtol(&buff[13],NULL,10);
   154 	 }
   155       else if(!strncmp(buff,"  ATIP start of lead out:",25)) size=strtol(&buff[25],NULL,10);
   156       }
   157     }
   158   pclose(p);
   159   free(cmd);
   160   if(size>0 && verbose) {
   161     char buff[16];
   162     fprintf(stderr,"%s: auto-detected media size %s (%ld blocks)\n",prg_name,FlexSize(buff,(long long)size*CD_FRAMESIZE),size);
   163     }
   164   return size;
   165 }
   166 
   167 /****************************************************************************/
   168 
   169 void parse_cmdline(char argc, char *argv[]) 
   170 {
   171   int i;
   172   char *val;
   173 
   174   /* get some default from the environment */
   175   val=getenv("CDR_DEVICE");
   176   if(val) {
   177     cdr_dev=strdup(val);
   178     DEBUG("cdbackup: using recording device %s from CDR_DEVICE\n",cdr_dev);
   179     }
   180   val=getenv("CDR_SPEED");
   181   if(val) {
   182     cd_speed=strtol(val,NULL,10);
   183     DEBUG("cdbackup: using speed %d from CDR_SPEED\n",cd_speed);
   184     }
   185   
   186   while ((i=getopt(argc,argv,"d:r:l:s:p:a:c:mvVXDCi:wRE:"))>0) {
   187     switch (i) {
   188        case 'V': fprintf(stderr,"cdbackup "VERSION" (compiled "__DATE__")\n"
   189 	                        "Copyright (C) 2000-2010\n"
   190 			        "This is free software; see the source for copying conditions.\n"
   191 			        "There is NO warranty; not even for MERCHANTABILITY or FITNESS FOR A\n"
   192 			        "PARTICULAR PURPOSE.\n");
   193                  exit(0);
   194        case 'v': verbose=1; break;
   195        case 'm': multidisk=1; break;
   196        case 'X': xamode2=1; break;
   197        case 'c': multicmd=optarg; break;
   198        case 'd': cd_dev=optarg; break;
   199        case 'r': cdr_dev=optarg; break;
   200        case 'a': cd_label=optarg; break;
   201        case 'C': crc=0; break;
   202        case 'i': virt_name=optarg; virtual=1; break;
   203        case 'w': virt_dump=1; break;
   204        case 'E': exename=optarg; break;
   205        case 'R': dvd=1;
   206                  DEBUG("cdbackup: DVD mode enabled\n");
   207                  break;
   208        case 'D': verbose=1; debug=1; 
   209                  DEBUG("cdbackup: DEBUG output enabled ("VERSION")\n");
   210                  break;
   211        case 'l': cd_len=(long)(FlexLen(optarg)/CD_FRAMESIZE);
   212                  break;
   213        case 's': errno=0; cd_speed=strtol(optarg,NULL,10);
   214                  if(errno==ERANGE || cd_speed<1) serror("Option -s: speed out of range (must be >=1)\n");
   215 	         break;
   216        case 'p': errno=0; padsize=strtol(optarg,NULL,10);
   217                  if(errno==ERANGE || padsize<15) serror("Option -p: padsize out of range (must be >=15)\n");
   218 	         break;
   219        default:  fprintf(stderr,
   220                          "Usage: %s [options ...] [-- cdrecord-options ...]\n"
   221                          "Reads from standard input, block formats and writes to CD-R(W).\n\n"
   222                          "  -d DEVICE      DEVICE for CD queries (default /dev/burner)\n"
   223                          "  -l N           set media size, disable auto-detect\n"
   224                          "  -r DEVICE      DEVICE for CD recording (e.g. 0,4,0)\n"
   225                          "  -s N           record CD at speed N (default 4)\n"
   226                          "  -X             enable CDROM XA2 mode in cdrecord\n"
   227                          "  -a LABEL       use LABEL as CD session title\n"
   228                          "  -p N           use a padsize of N sectors for the session (default 15)\n"
   229                          "  -m             enable multi-disk mode\n"
   230                          "  -c COMMAND     call COMMAND on disk change in multi-disk mode\n"
   231                          "  -C             disable checksum creation for datablocks\n"
   232                          "  -i IMAGE       use virtual image IMAGE for recording\n"
   233                          "  -w             dump virtual image to media\n"
   234                          "  -R             enables DVD mode\n"
   235                          "  -E EXE         set alternative cdrecord executable\n"
   236                          "  -v             be verbose\n"
   237                          "  -D             enable DEBUG output\n"
   238                          "  -V             prints version & exits\n"
   239                          "  --             pass rest of commandline to cdrecord\n"
   240                          "\n", prg_name);
   241                  exit(0);
   242        }
   243     }
   244 
   245   if(optind<argc) { /* save position/count of cdrecord options */
   246     cdrec_opt_count=argc-optind;
   247     cdrec_opt=&argv[optind];
   248     }
   249     
   250   if(cd_len<0) {
   251     auto_size=1;
   252     if(virtual && !virt_dump) serror("Can't auto-detect media size in virtual mode. Use option -l to set media size\n");
   253     }
   254   if(virtual && dvd && !virt_dump) {
   255      fprintf(stderr,"Option -R ignored in virtual mode\n");
   256      dvd=0;
   257      }
   258   if(dvd) {
   259     if(xamode2) fprintf(stderr,"Option -X ignored in DVD mode\n");
   260     padsize=0;
   261     }
   262   if(virt_dump && !virtual) serror("To dump an image you must supply the image name with -i\n");
   263   if(!cdr_dev && (!virtual || virt_dump)) serror("You must specify a device for cdrecord with -r\n");
   264 }
   265 
   266 /****************************************************************************/
   267 
   268 void autosize(void)
   269 {
   270   if(auto_size) {
   271     cd_len=atip_cdrecord();
   272     if(cd_len<0) serror("Media size detection failed. Use option -l to set media size\n");
   273     }
   274 }
   275 
   276 /****************************************************************************/
   277 
   278 void dump(void)
   279 {
   280   int n, cont;
   281   char buffer[CD_FRAMESIZE];
   282   long long grandTotal;
   283 
   284   do {
   285     int change;
   286     do {
   287       autosize();
   288       virtual=1;
   289       Vopen(1); n=VreadToc(0);
   290       if(n<1) serror("It's not usefull to dump an empty image");
   291       secs=Vsize(); cont=VhasCont();
   292       if(cd_len<secs) serror("Image doesn't fit to this media");
   293       Vseek(-1); VprepareDump();
   294 
   295       virtual=0; change=0;
   296       Vopen(0); n=VreadToc(0); VprintSpace();
   297       if(n!=0) {
   298         fprintf(stderr,"Can't dump to non-empty disk! Try another disk\n");
   299         change=1;
   300         }
   301 
   302       if(change) {
   303         Vclose();
   304         diskchange(multicmd,cd_dev);
   305         }
   306       } while(change);
   307 
   308     if(verbose)
   309       fprintf(stderr,"%s: Dumping image (%d blocks) to %s\n",prg_name,secs,VdevName());
   310     VnewTrack();
   311 
   312     grandTotal=0;
   313     while(secs>0) {
   314       VvirtRead(buffer);
   315       Vwrite(buffer); grandTotal+=CD_FRAMESIZE;
   316       secs--;
   317       }
   318 
   319     VcloseTrack(0);
   320 
   321     totalSize+=grandTotal;
   322     if(verbose) {
   323       char str1[16], str2[16];
   324       fprintf(stderr,"%s: Dumping finished. %s written (%s on this disk)\n",
   325               prg_name,FlexSize(str1,totalSize),FlexSize(str2,grandTotal));
   326       }
   327 
   328     if(multidisk==0) {
   329       if(cont) fprintf(stderr,"Multi-disk not enabled, ignoring continuation image(s)!\n");
   330       cont=0;
   331       }
   332     else if(cont) {
   333       disknum++;
   334       diskchange(multicmd,cd_dev);
   335       }
   336     } while(cont);
   337 }
   338 
   339 /****************************************************************************/
   340 
   341 int backup(void)
   342 {
   343   long long grandTotal=0;
   344   struct header_block header;
   345   int flags, datasize, result=0;
   346 
   347   char buffer[CD_FRAMESIZE];
   348   struct data_block *db=(struct data_block *)&buffer[0];
   349 
   350   flags=F_NONE;
   351   datasize=DATASIZE;
   352   if(crc) { flags|=F_CRC; datasize-=4; }
   353 
   354   sprintf(buffer,"%04d%02d%02d%02d%02d",curtime.tm_year+1900,
   355     curtime.tm_mon+1,curtime.tm_mday,curtime.tm_hour,curtime.tm_min);
   356   
   357   strncpy(header.id_str,HDR_STRING,32); header.id_str[32]=0;
   358   strncpy(header.vol_id,cd_label,32); header.vol_id[32]=0;
   359   strncpy(header.t_stamp,buffer,12); header.t_stamp[12]=0;
   360   header.disk_set = disknum;
   361   header.flags = flags;
   362 
   363   if(verbose)
   364     fprintf(stderr,"%s: Recording to %s, multidisk %s, CRC %s, disk %d\n",
   365             prg_name,VdevName(),
   366             multidisk?"enabled":"disabled",
   367             crc?"enabled":"disabled",
   368             disknum); 
   369   secs=cd_len;
   370   VnewTrack();
   371 
   372   memset(buffer,0,CD_FRAMESIZE);
   373   memcpy(buffer,&header,sizeof(struct header_block));
   374   Vwrite(buffer); grandTotal+=CD_FRAMESIZE;
   375 
   376   do {
   377     int bytes;
   378 
   379     db->flags=flags;
   380     db->status=0;		      /* this isn't the last block (for now) */
   381     bytes=full_read(0,&buffer[DBSIZE],datasize);
   382     if(bytes!=datasize) db->status=1; /* EOF, this is the last block */
   383     db->datasize=htons(bytes);
   384 
   385     if(cd_avail<(CD_FRAMESIZE*2)) {   /* less than 2 block free */
   386       if(db->status==0) {             /* if not last block, mark disk as full */
   387         db->status=2;
   388         result=1;
   389         }
   390       }
   391     if(crc) {
   392       int l=crc32(buffer,bytes+DBSIZE);
   393       *((unsigned long *)(&buffer[CD_FRAMESIZE-4]))=l;
   394       }
   395     Vwrite(buffer); grandTotal+=CD_FRAMESIZE;
   396     } while(db->status==0);
   397 
   398   if(dvd && cd_avail>=CD_FRAMESIZE) { /* pad up the track with zeros */
   399     memset(buffer,0,CD_FRAMESIZE);
   400     if(verbose) fprintf(stderr,"%s: padding up the track\n",prg_name);
   401     while(cd_avail>=CD_FRAMESIZE) Vwrite(buffer);
   402     }
   403 
   404   VcloseTrack(result);
   405 
   406   totalSize+=grandTotal;
   407   if(verbose) {
   408     char str1[16], str2[16];
   409     fprintf(stderr,"%s: Recording finished. %s written (%s on this disk)\n",
   410             prg_name,FlexSize(str1,totalSize),FlexSize(str2,grandTotal));
   411     }
   412   return result;
   413 }
   414 
   415 /****************************************************************************/
   416 
   417 int main(int argc, char *argv[]) 
   418 {
   419   int result, loop;
   420   time_t curtime_t;
   421 
   422   curtime_t=time(0); curtime=*localtime(&curtime_t);
   423   parse_cmdline(argc,argv);
   424 
   425   if(virt_dump) {
   426     dump();
   427     }
   428   else {
   429     do {
   430       do {
   431         autosize();
   432         Vopen(0); result=VreadToc(0); VprintSpace();
   433         loop=1;
   434 
   435         if(disknum>1 && result!=0) {
   436           Vclose();
   437           fprintf(stderr,"%s: Can't do multidisk continuation on non-empty disk! Try another disk\n", prg_name);
   438           diskchange(multicmd,cd_dev);
   439           }
   440         else if(cd_avail<(padsize+MIN_BLOCKS)*CD_FRAMESIZE) {
   441           Vclose();
   442           if(multidisk) {
   443             fprintf(stderr,"%s: Not enough free space on disk! Try another disk\n", prg_name);
   444             diskchange(multicmd,cd_dev);
   445             }
   446           else serror("Not enough free space on disk");
   447           }
   448         else loop=0;
   449         } while(loop);
   450 
   451       result=backup();
   452       if(result==1) {
   453         if(multidisk==0) serror("Disk full, multi-disk not enabled. Aborting");
   454 
   455         disknum++;
   456         if(!VisRegular()) diskchange(multicmd,cd_dev);
   457         }
   458       } while(result!=0);
   459     }
   460   return 0;
   461 }