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>
47 /* #define DEBUGOUT */
50 char * prg_name ="cdbackup";
51 char * cd_dev ="/dev/burner";
52 char * cdr_dev =0; /* no default here, too dangerous */
53 char * cd_label ="CDBackup Track";
55 long cd_len =333000; /* blocks */
56 int padsize =15; /* blocks */
64 int cdrec_opt_count=0;
67 struct tm curtime; /* global, so multi-disks get all the same time */
69 /****************************************************************************/
74 "Usage: %s [options ...] [-- cdrecord-options ...]\n"
75 "Reads from standard input, block formats and writes to CD-R(W).\n\n"
76 " -d DEVICE DEVICE for CD queries (default /dev/burner)\n"
77 " -l N CD-R has a size of N MB (default 650)\n"
78 " -r DEVICE DEVICE for CD recording (e.g. 0,4,0)\n"
79 " -s N record CD at speed N (default 4)\n"
80 " -X enable CDROM XA2 mode in cdrecord\n"
81 " -a LABEL use LABEL as CD session title\n"
82 " -p N use a padsize of N sectors for the session (default 15)\n"
83 " -m enable multi-disk mode\n"
84 " -c COMMAND call COMMAND on disk change in multi-disk mode\n"
86 " -D enable DEBUG output\n"
87 " -V prints version & exits\n"
88 " -- pass rest of commandline to cdrecord\n"
92 /****************************************************************************/
94 void parse_cmdline(char argc, char *argv[])
98 while ((i=getopt(argc,argv,"d:r:l:s:p:a:c:mvVXD"))>0) {
100 case 'V': fprintf(stderr,"cdbackup "VERSION" (compiled "__DATE__")\n"
101 "Copyright (C) 2000-2002\n"
102 "This is free software; see the source for copying conditions.\n"
103 "There is NO warranty; not even for MERCHANTABILITY or FITNESS FOR A\n"
104 "PARTICULAR PURPOSE.\n");
106 case 'v': verbose=1; break;
107 case 'm': multidisk=1; break;
108 case 'X': xamode2=1; break;
109 case 'c': multicmd=optarg; break;
110 case 'd': cd_dev=optarg; break;
111 case 'r': cdr_dev=optarg; break;
112 case 'a': cd_label=optarg; break;
113 case 'D': verbose=1; debug=1;
114 DEBUG("cdbackup: DEBUG output enabled ("VERSION")\n");
116 case 'l': errno=0; cd_len=strtol(optarg,NULL,10);
117 if(errno==ERANGE || cd_len<1) serror("Option -l: length out of range (must be >=1)\n");
118 cd_len = (long long)cd_len * (1024*1024) / CD_FRAMESIZE; /* convert to blocks */
120 case 's': errno=0; cd_speed=strtol(optarg,NULL,10);
121 if(errno==ERANGE || cd_speed<1) serror("Option -s: speed out of range (must be >=1)\n");
123 case 'p': errno=0; padsize=strtol(optarg,NULL,10);
124 if(errno==ERANGE || padsize<15) serror("Option -p: padsize out of range (must be >=15)\n");
126 default: usage(); exit(0);
130 if(optind<argc) { /* save position/count of cdrecord options */
131 cdrec_opt_count=argc-optind;
132 cdrec_opt=&argv[optind];
135 if(!cdr_dev) serror("You must specify a device for cdrecord with -r\n");
138 /****************************************************************************/
140 #define MARG(ptr,len,form,arg) { int l=(len);\
141 if(!(*ptr=(char *)malloc(l+1))) serror("No memory for cdrecord args\n");\
142 snprintf(*ptr++,l,form,arg);\
145 void start_cdrecord()
150 if(!(p=args=calloc(cdrec_opt_count+8,sizeof(char *))))
151 serror("No memory for cdrecord args\n");
155 MARG(p,16,"speed=%d",cd_speed);
156 MARG(p,6+strlen(cdr_dev),"dev=%s",cdr_dev);
158 for(l=0 ; l<cdrec_opt_count ; l++) *p++=cdrec_opt[l];
160 MARG(p,20,"padsize=%ds",padsize);
161 if(xamode2) *p++="-xa2"; else *p++="-data";
166 fprintf(stderr,"%s: cdrecord command:",prg_name);
167 for(p=args ; *p ; p++) fprintf(stderr," %s",*p);
168 fprintf(stderr,"\n");
171 execvp("cdrecord",args);
172 error("Unable to launch cdrecord");
175 /****************************************************************************/
177 int backup(char disk_set)
182 long long grandTotal=0;
183 struct header_block header;
185 char buffer[CD_FRAMESIZE];
186 struct data_block *db=(struct data_block *)&buffer[0];
188 sprintf(buffer, "%04d%02d%02d%02d%02d", curtime.tm_year + 1900,
189 curtime.tm_mon + 1, curtime.tm_mday, curtime.tm_hour, curtime.tm_min);
191 strncpy(header.id_str,HDR_STRING,32); header.id_str[32] = 0;
192 strncpy(header.vol_id,cd_label,32); header.vol_id[32] = 0;
193 strncpy(header.t_stamp,buffer,12); header.t_stamp[12] = 0;
194 header.disk_set = disk_set;
197 fprintf(stderr,"%s: Recording to device %s, multidisk %s, disk %d\n",prg_name,cdr_dev,multidisk?"enabled":"disabled",disk_set);
199 #ifndef DEBUGOUT /* the "real" code */
200 /* launch cdrecord */
201 if(pipe(fd) == -1) error("Unable to create pipe handles");
202 if((childpid=fork()) == -1) error("Fork failed");
203 if(childpid == 0) { /* child */
205 close(0); /* stdin */
207 start_cdrecord(); /* doesn't return */
210 close(fd[0]); outpipe=fd[1];
212 /* output the header block */
213 memset(buffer,0,CD_FRAMESIZE);
214 memcpy(buffer,&header,sizeof(struct header_block));
215 if((bytes=write(outpipe, buffer, CD_FRAMESIZE)) != CD_FRAMESIZE) error("Error writing header block");
217 cd_avail-=bytes; grandTotal+=bytes;
218 /* account for the padsize */
219 cd_avail-=padsize*CD_FRAMESIZE;
221 /* debug code; send data to /dev/null. Don't need the pipe. */
222 fprintf(stderr, "DEBUG CODE: sending data to /dev/null!\n");
223 outpipe = open("/dev/null", O_WRONLY);
224 if (outpipe < 0) { perror("/dev/null"); exit(1); }
231 db->status = 0; /* this isn't the last block (for now) */
232 bytes=full_read(0,&buffer[DBSIZE],DATASIZE);
233 if (bytes < 0) error("Error reading input");
234 if (bytes != DATASIZE) db->status=1; /* EOF, this is the last block */
235 db->datasize = htons(bytes);
237 /* check for free space */
238 if(cd_avail < (CD_FRAMESIZE*2)) { /* less than 2 block free */
239 if(db->status==0) db->status=2; /* if not last block, mark disk as full */
243 bytes = write(outpipe, buffer, CD_FRAMESIZE);
244 if(bytes != CD_FRAMESIZE) error("Error writing data block");
246 grandTotal+=bytes; cd_avail-=bytes;
247 } while(db->status==0);
249 /* close pipe and wait for child termination */
251 while (wait(0) != childpid);
253 totalSize+=grandTotal;
254 if(verbose) fprintf(stderr,"%s: Recording finished. %lld kB written (%lld kB on this disk)\n",
255 prg_name,totalSize/1024,grandTotal/1024);
257 if(db->status==2) return 1; /* disk was full */
261 /****************************************************************************/
263 int main(int argc, char *argv[])
266 int disknum, result, loop;
269 disknum=1; totalSize=0;
270 curtime_t=time(0); curtime=*localtime(&curtime_t);
272 parse_cmdline(argc,argv);
276 cdr=open_cdr(cd_dev); result=read_toc(cdr,0); close_cdr(cdr);
280 if(disknum>1 && result!=0) {
281 fprintf(stderr,"%s: Can't do multidisk continuation on non-empty disk! Try another disk\n", prg_name);
282 if(start_diskchange(multicmd,cd_dev)) serror("Diskchange command failed");
284 else if(cd_avail<(padsize+MIN_BLOCKS)*CD_FRAMESIZE) {
286 fprintf(stderr,"%s: Not enough free space on disk! Try another disk\n", prg_name);
287 if(start_diskchange(multicmd,cd_dev)) serror("Diskchange command failed");
289 else serror("Not enough free space on disk");
294 result = backup(disknum);
296 if(multidisk == 0) serror("Disk full, multi-disk not enabled. Aborting");
299 if(start_diskchange(multicmd,cd_dev)) serror("Diskchange command failed");
301 } while (result != 0);