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