cdbackup.c
branchtrunk
changeset 0 d85c12073dea
child 2 6bcb44b9edb1
equal deleted inserted replaced
-1:000000000000 0:d85c12073dea
       
     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 }