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