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