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