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