2 Copyright (c) 2000-2002 Craig Condit, Stefan Hülswitt.
4 Redistribution and use in source and binary forms, with or without
5 modification, are permitted provided that the following conditions are met:
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.
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
26 #define _LARGEFILE64_SOURCE
37 #include <sys/ioctl.h>
38 #include <netinet/in.h>
39 #include <linux/cdrom.h>
46 /* #define DEBUGOUT */
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";
54 long cd_len =333000; /* blocks */
55 int padsize =15; /* blocks */
62 int cdrec_opt_count=0;
65 struct tm curtime; /* global, so multi-disks get all the same time */
67 /****************************************************************************/
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"
84 " -V prints version & exits\n"
85 " -- pass rest of commandline to cdrecord\n"
89 /****************************************************************************/
91 void parse_cmdline(char argc, char *argv[])
95 while ((i=getopt(argc,argv,"d:r:l:s:p:a:c:mvVX"))>0) {
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");
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 */
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");
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");
120 default: usage(); exit(0);
124 if(optind<argc) { /* save position/count of cdrecord options */
125 cdrec_opt_count=argc-optind;
126 cdrec_opt=&argv[optind];
129 if(!cdr_dev) serror("You must specify a device for cdrecord with -r\n");
132 /****************************************************************************/
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);\
139 void start_cdrecord()
144 if(!(p=args=calloc(cdrec_opt_count+8,sizeof(char *))))
145 serror("No memory for cdrecord args\n");
149 MARG(p,16,"speed=%d",cd_speed);
150 MARG(p,6+strlen(cdr_dev),"dev=%s",cdr_dev);
152 for(l=0 ; l<cdrec_opt_count ; l++) *p++=cdrec_opt[l];
154 MARG(p,20,"padsize=%ds",padsize);
155 if(xamode2) *p++="-xa2"; else *p++="-data";
160 fprintf(stderr,"%s: cdrecord command:",prg_name);
161 for(p=args ; *p ; p++) fprintf(stderr," %s",*p);
162 fprintf(stderr,"\n");
165 execvp("cdrecord",args);
166 error("Unable to launch cdrecord");
169 /****************************************************************************/
171 int backup(char disk_set)
176 long long grandTotal=0;
177 struct header_block header;
179 char buffer[CD_FRAMESIZE];
180 struct data_block *db=(struct data_block *)&buffer[0];
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);
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;
191 fprintf(stderr,"%s: Recording to device %s, multidisk %s, disk %d\n",prg_name,cdr_dev,multidisk?"enabled":"disabled",disk_set);
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 */
199 close(0); /* stdin */
201 start_cdrecord(); /* doesn't return */
204 close(fd[0]); outpipe=fd[1];
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");
211 cd_avail-=bytes; grandTotal+=bytes;
212 /* account for the padsize */
213 cd_avail-=padsize*CD_FRAMESIZE;
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); }
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);
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 */
237 bytes = write(outpipe, buffer, CD_FRAMESIZE);
238 if(bytes != CD_FRAMESIZE) error("Error writing data block");
240 grandTotal+=bytes; cd_avail-=bytes;
241 } while(db->status==0);
243 /* close pipe and wait for child termination */
245 while (wait(0) != childpid);
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);
251 if(db->status==2) return 1; /* disk was full */
255 /****************************************************************************/
257 int main(int argc, char *argv[])
260 int disknum, result, loop;
263 disknum=1; totalSize=0;
264 curtime_t=time(0); curtime=*localtime(&curtime_t);
266 parse_cmdline(argc,argv);
270 cdr=open_cdr(cd_dev); result=read_toc(cdr,0); close_cdr(cdr);
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");
278 else if(cd_avail<(padsize+MIN_BLOCKS)*CD_FRAMESIZE) {
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");
283 else serror("Not enough free space on disk");
288 result = backup(disknum);
290 if(multidisk == 0) serror("Disk full, multi-disk not enabled. Aborting");
293 if(start_diskchange(multicmd,cd_dev)) serror("Diskchange command failed");
295 } while (result != 0);