|
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 } |