cdbackup.c
author nathan
Sun, 23 May 2010 08:39:19 +0200
branchtrunk
changeset 13 6ef9b2adca64
parent 12 6d3770b1a9e1
child 14 ae01b419e8f9
permissions -rw-r--r--
fix gcc warnings (asprintf)
     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   if(asprintf(&cmd,"%s 2>&1 dev=%s -atip",exename,cdr_dev)<0) {
   142     fprintf(stderr,"%s: error making atip command: %s\n",prg_name,strerror(errno));
   143     return -1;
   144     }
   145   DEBUG("%s: cdrecord atip command: %s\n",prg_name,cmd);
   146 
   147   p=popen(cmd,"r");
   148   if(!p) fprintf(stderr,"%s: atip command failed\n",prg_name);
   149   else {
   150     char buff[256];
   151     while(fgets(buff,sizeof(buff),p)) {
   152       if(dvd) {
   153          /* DVD-R */
   154 	 if(!strncmp(buff,"rzone size:",11)) size=strtol(&buff[11],NULL,10);
   155 	 /* DVD+R */
   156 	 else if(!strncmp(buff,"phys size:...",13)) size=strtol(&buff[13],NULL,10);
   157 	 }
   158       else if(!strncmp(buff,"  ATIP start of lead out:",25)) size=strtol(&buff[25],NULL,10);
   159       }
   160     }
   161   pclose(p);
   162   free(cmd);
   163   if(size>0 && verbose) {
   164     char buff[16];
   165     fprintf(stderr,"%s: auto-detected media size %s (%ld blocks)\n",prg_name,FlexSize(buff,(long long)size*CD_FRAMESIZE),size);
   166     }
   167   return size;
   168 }
   169 
   170 /****************************************************************************/
   171 
   172 void parse_cmdline(char argc, char *argv[]) 
   173 {
   174   int i;
   175   char *val;
   176 
   177   /* get some default from the environment */
   178   val=getenv("CDR_DEVICE");
   179   if(val) {
   180     cdr_dev=strdup(val);
   181     DEBUG("cdbackup: using recording device %s from CDR_DEVICE\n",cdr_dev);
   182     }
   183   val=getenv("CDR_SPEED");
   184   if(val) {
   185     cd_speed=strtol(val,NULL,10);
   186     DEBUG("cdbackup: using speed %d from CDR_SPEED\n",cd_speed);
   187     }
   188   
   189   while ((i=getopt(argc,argv,"d:r:l:s:p:a:c:mvVXDCi:wRE:"))>0) {
   190     switch (i) {
   191        case 'V': fprintf(stderr,"cdbackup "VERSION" (compiled "__DATE__")\n"
   192 	                        "Copyright (C) 2000-2010\n"
   193 			        "This is free software; see the source for copying conditions.\n"
   194 			        "There is NO warranty; not even for MERCHANTABILITY or FITNESS FOR A\n"
   195 			        "PARTICULAR PURPOSE.\n");
   196                  exit(0);
   197        case 'v': verbose=1; break;
   198        case 'm': multidisk=1; break;
   199        case 'X': xamode2=1; break;
   200        case 'c': multicmd=optarg; break;
   201        case 'd': cd_dev=optarg; break;
   202        case 'r': cdr_dev=optarg; break;
   203        case 'a': cd_label=optarg; break;
   204        case 'C': crc=0; break;
   205        case 'i': virt_name=optarg; virtual=1; break;
   206        case 'w': virt_dump=1; break;
   207        case 'E': exename=optarg; break;
   208        case 'R': dvd=1;
   209                  DEBUG("cdbackup: DVD mode enabled\n");
   210                  break;
   211        case 'D': verbose=1; debug=1; 
   212                  DEBUG("cdbackup: DEBUG output enabled ("VERSION")\n");
   213                  break;
   214        case 'l': cd_len=(long)(FlexLen(optarg)/CD_FRAMESIZE);
   215                  break;
   216        case 's': errno=0; cd_speed=strtol(optarg,NULL,10);
   217                  if(errno==ERANGE || cd_speed<1) serror("Option -s: speed out of range (must be >=1)\n");
   218 	         break;
   219        case 'p': errno=0; padsize=strtol(optarg,NULL,10);
   220                  if(errno==ERANGE || padsize<15) serror("Option -p: padsize out of range (must be >=15)\n");
   221 	         break;
   222        default:  fprintf(stderr,
   223                          "Usage: %s [options ...] [-- cdrecord-options ...]\n"
   224                          "Reads from standard input, block formats and writes to CD-R(W).\n\n"
   225                          "  -d DEVICE      DEVICE for CD queries (default /dev/burner)\n"
   226                          "  -l N           set media size, disable auto-detect\n"
   227                          "  -r DEVICE      DEVICE for CD recording (e.g. 0,4,0)\n"
   228                          "  -s N           record CD at speed N (default 4)\n"
   229                          "  -X             enable CDROM XA2 mode in cdrecord\n"
   230                          "  -a LABEL       use LABEL as CD session title\n"
   231                          "  -p N           use a padsize of N sectors for the session (default 15)\n"
   232                          "  -m             enable multi-disk mode\n"
   233                          "  -c COMMAND     call COMMAND on disk change in multi-disk mode\n"
   234                          "  -C             disable checksum creation for datablocks\n"
   235                          "  -i IMAGE       use virtual image IMAGE for recording\n"
   236                          "  -w             dump virtual image to media\n"
   237                          "  -R             enables DVD mode\n"
   238                          "  -E EXE         set alternative cdrecord executable\n"
   239                          "  -v             be verbose\n"
   240                          "  -D             enable DEBUG output\n"
   241                          "  -V             prints version & exits\n"
   242                          "  --             pass rest of commandline to cdrecord\n"
   243                          "\n", prg_name);
   244                  exit(0);
   245        }
   246     }
   247 
   248   if(optind<argc) { /* save position/count of cdrecord options */
   249     cdrec_opt_count=argc-optind;
   250     cdrec_opt=&argv[optind];
   251     }
   252     
   253   if(cd_len<0) {
   254     auto_size=1;
   255     if(virtual && !virt_dump) serror("Can't auto-detect media size in virtual mode. Use option -l to set media size\n");
   256     }
   257   if(virtual && dvd && !virt_dump) {
   258      fprintf(stderr,"Option -R ignored in virtual mode\n");
   259      dvd=0;
   260      }
   261   if(dvd) {
   262     if(xamode2) fprintf(stderr,"Option -X ignored in DVD mode\n");
   263     padsize=0;
   264     }
   265   if(virt_dump && !virtual) serror("To dump an image you must supply the image name with -i\n");
   266   if(!cdr_dev && (!virtual || virt_dump)) serror("You must specify a device for cdrecord with -r\n");
   267 }
   268 
   269 /****************************************************************************/
   270 
   271 void autosize(void)
   272 {
   273   if(auto_size) {
   274     cd_len=atip_cdrecord();
   275     if(cd_len<0) serror("Media size detection failed. Use option -l to set media size\n");
   276     }
   277 }
   278 
   279 /****************************************************************************/
   280 
   281 void dump(void)
   282 {
   283   int n, cont;
   284   char buffer[CD_FRAMESIZE];
   285   long long grandTotal;
   286 
   287   do {
   288     int change;
   289     do {
   290       autosize();
   291       virtual=1;
   292       Vopen(1); n=VreadToc(0);
   293       if(n<1) serror("It's not usefull to dump an empty image");
   294       secs=Vsize(); cont=VhasCont();
   295       if(cd_len<secs) serror("Image doesn't fit to this media");
   296       Vseek(-1); VprepareDump();
   297 
   298       virtual=0; change=0;
   299       Vopen(0); n=VreadToc(0); VprintSpace();
   300       if(n!=0) {
   301         fprintf(stderr,"Can't dump to non-empty disk! Try another disk\n");
   302         change=1;
   303         }
   304 
   305       if(change) {
   306         Vclose();
   307         diskchange(multicmd,cd_dev);
   308         }
   309       } while(change);
   310 
   311     if(verbose)
   312       fprintf(stderr,"%s: Dumping image (%d blocks) to %s\n",prg_name,secs,VdevName());
   313     VnewTrack();
   314 
   315     grandTotal=0;
   316     while(secs>0) {
   317       VvirtRead(buffer);
   318       Vwrite(buffer); grandTotal+=CD_FRAMESIZE;
   319       secs--;
   320       }
   321 
   322     VcloseTrack(0);
   323 
   324     totalSize+=grandTotal;
   325     if(verbose) {
   326       char str1[16], str2[16];
   327       fprintf(stderr,"%s: Dumping finished. %s written (%s on this disk)\n",
   328               prg_name,FlexSize(str1,totalSize),FlexSize(str2,grandTotal));
   329       }
   330 
   331     if(multidisk==0) {
   332       if(cont) fprintf(stderr,"Multi-disk not enabled, ignoring continuation image(s)!\n");
   333       cont=0;
   334       }
   335     else if(cont) {
   336       disknum++;
   337       diskchange(multicmd,cd_dev);
   338       }
   339     } while(cont);
   340 }
   341 
   342 /****************************************************************************/
   343 
   344 int backup(void)
   345 {
   346   long long grandTotal=0;
   347   struct header_block header;
   348   int flags, datasize, result=0;
   349 
   350   char buffer[CD_FRAMESIZE];
   351   struct data_block *db=(struct data_block *)&buffer[0];
   352 
   353   flags=F_NONE;
   354   datasize=DATASIZE;
   355   if(crc) { flags|=F_CRC; datasize-=4; }
   356 
   357   sprintf(buffer,"%04d%02d%02d%02d%02d",curtime.tm_year+1900,
   358     curtime.tm_mon+1,curtime.tm_mday,curtime.tm_hour,curtime.tm_min);
   359   
   360   strncpy(header.id_str,HDR_STRING,32); header.id_str[32]=0;
   361   strncpy(header.vol_id,cd_label,32); header.vol_id[32]=0;
   362   strncpy(header.t_stamp,buffer,12); header.t_stamp[12]=0;
   363   header.disk_set = disknum;
   364   header.flags = flags;
   365 
   366   if(verbose)
   367     fprintf(stderr,"%s: Recording to %s, multidisk %s, CRC %s, disk %d\n",
   368             prg_name,VdevName(),
   369             multidisk?"enabled":"disabled",
   370             crc?"enabled":"disabled",
   371             disknum); 
   372   secs=cd_len;
   373   VnewTrack();
   374 
   375   memset(buffer,0,CD_FRAMESIZE);
   376   memcpy(buffer,&header,sizeof(struct header_block));
   377   Vwrite(buffer); grandTotal+=CD_FRAMESIZE;
   378 
   379   do {
   380     int bytes;
   381 
   382     db->flags=flags;
   383     db->status=0;		      /* this isn't the last block (for now) */
   384     bytes=full_read(0,&buffer[DBSIZE],datasize);
   385     if(bytes!=datasize) db->status=1; /* EOF, this is the last block */
   386     db->datasize=htons(bytes);
   387 
   388     if(cd_avail<(CD_FRAMESIZE*2)) {   /* less than 2 block free */
   389       if(db->status==0) {             /* if not last block, mark disk as full */
   390         db->status=2;
   391         result=1;
   392         }
   393       }
   394     if(crc) {
   395       int l=crc32(buffer,bytes+DBSIZE);
   396       *((unsigned long *)(&buffer[CD_FRAMESIZE-4]))=l;
   397       }
   398     Vwrite(buffer); grandTotal+=CD_FRAMESIZE;
   399     } while(db->status==0);
   400 
   401   if(dvd && cd_avail>=CD_FRAMESIZE) { /* pad up the track with zeros */
   402     memset(buffer,0,CD_FRAMESIZE);
   403     if(verbose) fprintf(stderr,"%s: padding up the track\n",prg_name);
   404     while(cd_avail>=CD_FRAMESIZE) Vwrite(buffer);
   405     }
   406 
   407   VcloseTrack(result);
   408 
   409   totalSize+=grandTotal;
   410   if(verbose) {
   411     char str1[16], str2[16];
   412     fprintf(stderr,"%s: Recording finished. %s written (%s on this disk)\n",
   413             prg_name,FlexSize(str1,totalSize),FlexSize(str2,grandTotal));
   414     }
   415   return result;
   416 }
   417 
   418 /****************************************************************************/
   419 
   420 int main(int argc, char *argv[]) 
   421 {
   422   int result, loop;
   423   time_t curtime_t;
   424 
   425   curtime_t=time(0); curtime=*localtime(&curtime_t);
   426   parse_cmdline(argc,argv);
   427 
   428   if(virt_dump) {
   429     dump();
   430     }
   431   else {
   432     do {
   433       do {
   434         autosize();
   435         Vopen(0); result=VreadToc(0); VprintSpace();
   436         loop=1;
   437 
   438         if(disknum>1 && result!=0) {
   439           Vclose();
   440           fprintf(stderr,"%s: Can't do multidisk continuation on non-empty disk! Try another disk\n", prg_name);
   441           diskchange(multicmd,cd_dev);
   442           }
   443         else if(cd_avail<(padsize+MIN_BLOCKS)*CD_FRAMESIZE) {
   444           Vclose();
   445           if(multidisk) {
   446             fprintf(stderr,"%s: Not enough free space on disk! Try another disk\n", prg_name);
   447             diskchange(multicmd,cd_dev);
   448             }
   449           else serror("Not enough free space on disk");
   450           }
   451         else loop=0;
   452         } while(loop);
   453 
   454       result=backup();
   455       if(result==1) {
   456         if(multidisk==0) serror("Disk full, multi-disk not enabled. Aborting");
   457 
   458         disknum++;
   459         if(!VisRegular()) diskchange(multicmd,cd_dev);
   460         }
   461       } while(result!=0);
   462     }
   463   return 0;
   464 }