1 
2 /* test/libburner.c , API illustration of burning data or audio tracks to CD */
3 /* Copyright (C) 2005 - 2016 Thomas Schmitt <scdbackup@gmx.net> */
4 /* Provided under GPL, see also "License and copyright aspects" at file end */
5 
6 
7 /**                               Overview
8 
9   libburner is a minimal demo application for the library libburn as provided
10   on  http://libburnia-project.org . It can list the available devices, can
11   blank a CD-RW or DVD-RW, can format DVD-RW and BD, can burn to CD-R,
12   CD-RW, DVD-R, DVD+R, DVD+R/DL, DVD+RW, DVD-RW, DVD-RAM, BD-R, BD-RE.
13   Not supported yet: DVD-R/DL.
14 
15   It's main purpose, nevertheless, is to show you how to use libburn and also
16   to serve the libburnia team as reference application. libburner.c does indeed
17   define the standard way how above three gestures can be implemented and
18   stay upward compatible for a good while.
19   There is another demo program, test/telltoc.c, which inspects drive, media
20   state, and media contents.
21 
22   Before you can do anything, you have to initialize libburn by
23      burn_initialize()
24   and provide some signal and abort handling, e.g. by the builtin handler, by
25      burn_set_signal_handling("libburner : ", NULL, 0x0)
26   as it is done in main() at the end of this file.
27   Then you acquire a drive in an appropriate way conforming to the API. The two
28   main approaches are shown here in application functions:
29      libburner_aquire_by_adr()     demonstrates usage as of cdrecord traditions
30      libburner_aquire_by_driveno()      demonstrates a scan-and-choose approach
31 
32   With that acquired drive you can blank a CD-RW or DVD-RW as shown in
33      libburner_blank_disc()
34   or you can format a DVD-RW to profile "Restricted Overwrite" (needed once)
35   or an unused BD to default size with spare blocks
36      libburner_format()
37   With the acquired drive you can burn to CD, DVD, BD. See
38      libburner_payload()
39 
40   These three functions switch temporarily to a non-fatal signal handler
41   while they are waiting for the drive to become idle again:
42      burn_set_signal_handling("libburner : ", NULL, 0x30)
43   After the waiting loop ended, they check for eventual abort events by
44      burn_is_aborting(0)
45   The 0x30 handler will eventually execute
46      burn_abort()
47   but not wait for the drive to become idle and not call exit().
48   This is needed because the worker threads might block as long as the signal
49   handler has not returned. The 0x0 handler would wait for them to finish.
50   Take this into respect when implementing own signal handlers.
51 
52   When everything is done, main() releases the drive and shuts down libburn:
53      burn_drive_release();
54      burn_finish()
55 
56   Applications must use 64 bit off_t. E.g. by defining
57     #define _LARGEFILE_SOURCE
58     #define _FILE_OFFSET_BITS 64
59   or take special precautions to interface with the library by 64 bit integers
60   where libburn/libburn.h prescribes off_t.
61   This program gets fed with appropriate settings externally by libburn's
62   autotools generated build system.
63 */
64 
65 
66 /** See this for the decisive API specs . libburn.h is The Original */
67 /*  For using the installed header file :  #include <libburn/libburn.h> */
68 /*  This program insists in the own headerfile. */
69 #include "../libburn/libburn.h"
70 
71 /* libburn works on Linux systems with kernel 2.4 or 2.6, FreeBSD, Solaris */
72 #include <stdio.h>
73 #include <ctype.h>
74 #include <sys/types.h>
75 #include <unistd.h>
76 #include <string.h>
77 #include <stdlib.h>
78 #include <time.h>
79 #include <errno.h>
80 #include <sys/stat.h>
81 #include <fcntl.h>
82 
83 
84 /** For simplicity i use global variables to represent the drives.
85     Drives are systemwide global, so we do not give away much of good style.
86 */
87 
88 /** This list will hold the drives known to libburn. This might be all CD
89     drives of the system and thus might impose severe impact on the system.
90 */
91 static struct burn_drive_info *drive_list;
92 
93 /** If you start a long lasting operation with drive_count > 1 then you are
94     not friendly to the users of other drives on those systems. Beware. */
95 static unsigned int drive_count;
96 
97 /** This variable indicates wether the drive is grabbed and must be
98     finally released */
99 static int drive_is_grabbed = 0;
100 
101 /** A number and a text describing the type of media in acquired drive */
102 static int current_profile= -1;
103 static char current_profile_name[80]= {""};
104 
105 
106 /* Some in-advance definitions make possible a more comprehensive ordering
107    of the functions and their explanations in here */
108 int libburner_aquire_by_adr(char *drive_adr);
109 int libburner_aquire_by_driveno(int *drive_no);
110 
111 
112 /* ------------------------------- API gestures ---------------------------- */
113 
114 /** You need to acquire a drive before burning. The API offers this as one
115     compact call and alternatively as application controllable gestures of
116     whitelisting, scanning for drives and finally grabbing one of them.
117 
118     If you have a persistent address of the drive, then the compact call is
119     to prefer because it only touches one drive. On modern Linux kernels,
120     there should be no fatal disturbance of ongoing burns of other libburn
121     instances with any of our approaches. We use open(O_EXCL) by default.
122     On /dev/hdX it should cooperate with growisofs and some cdrecord variants.
123     On /dev/sgN versus /dev/scdM expect it not to respect other programs.
124 */
libburner_aquire_drive(char * drive_adr,int * driveno)125 int libburner_aquire_drive(char *drive_adr, int *driveno)
126 {
127 	int ret;
128 
129 	if(drive_adr != NULL && drive_adr[0] != 0)
130 		ret = libburner_aquire_by_adr(drive_adr);
131 	else
132 		ret = libburner_aquire_by_driveno(driveno);
133 	if (ret <= 0 || *driveno <= 0)
134 		return ret;
135 	burn_disc_get_profile(drive_list[0].drive, &current_profile,
136 				 current_profile_name);
137 	if (current_profile_name[0])
138 		printf("Detected media type: %s\n", current_profile_name);
139 	return 1;
140 }
141 
142 
143 /** If the persistent drive address is known, then this approach is much
144     more un-obtrusive to the systemwide livestock of drives. Only the
145     given drive device will be opened during this procedure.
146 */
libburner_aquire_by_adr(char * drive_adr)147 int libburner_aquire_by_adr(char *drive_adr)
148 {
149 	int ret;
150 	char libburn_drive_adr[BURN_DRIVE_ADR_LEN];
151 
152 	/* Some not-so-harmless drive addresses get blocked in this demo */
153 	if (strncmp(drive_adr, "stdio:/dev/fd/", 14) == 0 ||
154 		strcmp(drive_adr, "stdio:-") == 0) {
155 		fprintf(stderr, "Will not work with pseudo-drive '%s'\n",
156 			drive_adr);
157 		return 0;
158 	}
159 
160 	/* This tries to resolve links or alternative device files */
161 	ret = burn_drive_convert_fs_adr(drive_adr, libburn_drive_adr);
162 	if (ret<=0) {
163 		fprintf(stderr, "Address does not lead to a CD burner: '%s'\n",
164 				 drive_adr);
165 		return 0;
166 	}
167 	fprintf(stderr,"Aquiring drive '%s' ...\n", libburn_drive_adr);
168 	ret = burn_drive_scan_and_grab(&drive_list, libburn_drive_adr, 1);
169 	if (ret <= 0) {
170 		fprintf(stderr,"FAILURE with persistent drive address  '%s'\n",
171 			libburn_drive_adr);
172 	} else {
173 		fprintf(stderr,"Done\n");
174 		drive_is_grabbed = 1;
175 	}
176 	return ret;
177 }
178 
179 
180 /** This method demonstrates how to use libburn without knowing a persistent
181     drive address in advance. It has to make sure that after assessing the list
182     of available drives, all unwanted drives get closed again. As long as they
183     are open, no other libburn instance can see them. This is an intended
184     locking feature. The application is responsible for giving up the locks
185     by either burn_drive_release() (only after burn_drive_grab() !),
186     burn_drive_info_forget(), burn_drive_info_free(), or burn_finish().
187     @param driveno the index number in libburn's drive list. This will get
188                    set to 0 on success and will then be the drive index to
189                    use in the further dourse of processing.
190     @return 1 success , <= 0 failure
191 */
libburner_aquire_by_driveno(int * driveno)192 int libburner_aquire_by_driveno(int *driveno)
193 {
194 	char adr[BURN_DRIVE_ADR_LEN];
195 	int ret, i;
196 
197 	printf("Beginning to scan for devices ...\n");
198 	while (!burn_drive_scan(&drive_list, &drive_count))
199 		usleep(100002);
200 	if (drive_count <= 0 && *driveno >= 0) {
201 		printf("FAILED (no drives found)\n");
202 		return 0;
203 	}
204 	printf("Done\n");
205 
206 	/*
207 	Interactive programs may choose the drive number at this moment.
208 
209 	drive[0] to drive[drive_count-1] are struct burn_drive_info
210 	as defined in  libburn/libburn.h  . This structure is part of API
211 	and thus will strive for future compatibility on source level.
212 	Have a look at the info offered.
213 	Caution: do not take .location for drive address. Always use
214 		burn_drive_get_adr() or you might become incompatible
215 		in future.
216 	Note: bugs with struct burn_drive_info - if any - will not be
217 		easy to fix. Please report them but also strive for
218 		workarounds on application level.
219 	*/
220 	printf("\nOverview of accessible drives (%d found) :\n",
221 		drive_count);
222 	printf("-----------------------------------------------------------------------------\n");
223 	for (i = 0; i < (int) drive_count; i++) {
224 		if (burn_drive_get_adr(&(drive_list[i]), adr) <=0)
225 			strcpy(adr, "-get_adr_failed-");
226 		printf("%d  --drive '%s'  :  '%s'  '%s'\n",
227 			i,adr,drive_list[i].vendor,drive_list[i].product);
228 	}
229 	printf("-----------------------------------------------------------------------------\n\n");
230 
231 	/*
232 	On multi-drive systems save yourself from sysadmins' revenge.
233 
234 	Be aware that you hold reserved all available drives at this point.
235 	So either make your choice quick enough not to annoy other system
236 	users, or set free the drives for a while.
237 
238 	The tested way of setting free all drives is to shutdown the library
239 	and to restart when the choice has been made. The list of selectable
240 	drives should also hold persistent drive addresses as obtained
241 	above by burn_drive_get_adr(). By such an address one may use
242 	burn_drive_scan_and_grab() to finally acquire exactly one drive.
243 
244 	A not yet tested shortcut should be to call burn_drive_info_free()
245 	and to call either burn_drive_scan() or burn_drive_scan_and_grab()
246 	before accessing any drives again.
247 
248 	In both cases you have to be aware that the desired drive might get
249 	acquired in the meantime by another user or libburn process.
250 	*/
251 
252 	/* We already made our choice via command line. (default is 0)
253 	   So we just have to keep our desired drive and drop all others.
254 	   No other libburn instance will have a chance to steal our drive.
255 	 */
256 	if (*driveno < 0) {
257 		printf("Pseudo-drive \"-\" given : bus scanning done.\n");
258 		return 2; /* the program will end after this */
259 	}
260 	if ((int) drive_count <= *driveno) {
261 		fprintf(stderr,
262 			"Found only %d drives. Number %d not available.\n",
263 			drive_count, *driveno);
264 		return 0; /* the program will end after this */
265 	}
266 
267 	/* Drop all drives which we do not want to use */
268 	for (i = 0; i < (int) drive_count; i++) {
269 		if (i == *driveno) /* the one drive we want to keep */
270 	continue;
271 		ret = burn_drive_info_forget(&(drive_list[i]),0);
272 		if (ret != 1)
273 			fprintf(stderr, "Cannot drop drive %d. Please report \"ret=%d\" to libburn-hackers@pykix.org\n",
274 				i, ret);
275 		else
276 			printf("Dropped unwanted drive %d\n",i);
277 	}
278 	/* Make the one we want ready for blanking or burning */
279 	ret= burn_drive_grab(drive_list[*driveno].drive, 1);
280 	if (ret != 1)
281 		return 0;
282 	drive_is_grabbed = 1;
283 	return 1;
284 }
285 
286 
287 /** Makes a previously used CD-RW or unformatted DVD-RW ready for thorough
288     re-usal.
289 
290     To our knowledge it is hardly possible to abort an ongoing blank operation
291     because after start it is entirely handled by the drive.
292     So expect signal handling to wait the normal blanking timespan until it
293     can allow the process to end. External kill -9 will not help the drive.
294 */
libburner_blank_disc(struct burn_drive * drive,int blank_fast)295 int libburner_blank_disc(struct burn_drive *drive, int blank_fast)
296 {
297 	enum burn_disc_status disc_state;
298 	struct burn_progress p;
299 	double percent = 1.0;
300 
301 	disc_state = burn_disc_get_status(drive);
302 	printf(
303 	    "Drive media status:  %d  (see  libburn/libburn.h  BURN_DISC_*)\n",
304 	    disc_state);
305 	if (current_profile == 0x13) {
306 		; /* formatted DVD-RW will get blanked to sequential state */
307 	} else if (disc_state == BURN_DISC_BLANK) {
308 		fprintf(stderr,
309 		  "IDLE: Blank media detected. Will leave it untouched\n");
310 		return 2;
311 	} else if (disc_state == BURN_DISC_FULL ||
312 		   disc_state == BURN_DISC_APPENDABLE) {
313 		; /* this is what libburner is willing to blank */
314 	} else if (disc_state == BURN_DISC_EMPTY) {
315 		fprintf(stderr,"FATAL: No media detected in drive\n");
316 		return 0;
317 	} else {
318 		fprintf(stderr,
319 			"FATAL: Unsuitable drive and media state\n");
320 		return 0;
321 	}
322 	if(!burn_disc_erasable(drive)) {
323 		fprintf(stderr,
324 			"FATAL : Media is not of erasable type\n");
325 		return 0;
326 	}
327 	/* Switch to asynchronous signal handling for the time of waiting */
328 	burn_set_signal_handling("libburner : ", NULL, 0x30);
329 
330 	printf("Beginning to %s-blank media.\n", (blank_fast?"fast":"full"));
331 	burn_disc_erase(drive, blank_fast);
332 
333 	sleep(1);
334 	while (burn_drive_get_status(drive, &p) != BURN_DRIVE_IDLE) {
335 		if(p.sectors>0 && p.sector>=0) /* display 1 to 99 percent */
336 			percent = 1.0 + ((double) p.sector+1.0)
337 					 / ((double) p.sectors) * 98.0;
338 		printf("Blanking  ( %.1f%% done )\n", percent);
339 		sleep(1);
340 	}
341 	if (burn_is_aborting(0) > 0)
342 		return -1;
343 	/* Back to synchronous handling */
344 	burn_set_signal_handling("libburner : ", NULL, 0x0);
345 	printf("Done\n");
346 	return 1;
347 }
348 
349 
350 /** Formats unformatted DVD-RW to profile 0013h "Restricted Overwrite"
351     which needs no blanking for re-use but is not capable of multi-session.
352     Expect a behavior similar to blanking with unusual noises from the drive.
353 
354     Formats unformatted BD-RE to default size. This will allocate some
355     reserve space, test for bad blocks and make the media ready for writing.
356     Expect a very long run time.
357 
358     Formats unformatted blank BD-R to hold a default amount of spare blocks
359     for eventual mishaps during writing. If BD-R get written without being
360     formatted, then they get no such reserve and will burn at full speed.
361 */
libburner_format(struct burn_drive * drive)362 int libburner_format(struct burn_drive *drive)
363 {
364 	struct burn_progress p;
365 	double percent = 1.0;
366 	int ret, status, num_formats, format_flag= 0;
367 	off_t size = 0;
368 	unsigned dummy;
369 	enum burn_disc_status disc_state;
370 
371 	if (current_profile == 0x13) {
372 		fprintf(stderr, "IDLE: DVD-RW media is already formatted\n");
373 		return 2;
374 	} else if (current_profile == 0x41 || current_profile == 0x43) {
375 		disc_state = burn_disc_get_status(drive);
376 		if (disc_state != BURN_DISC_BLANK && current_profile == 0x41) {
377 			fprintf(stderr,
378 				"FATAL: BD-R is not blank. Cannot format.\n");
379 			return 0;
380 		}
381 		ret = burn_disc_get_formats(drive, &status, &size, &dummy,
382 								&num_formats);
383 		if (ret > 0 && status != BURN_FORMAT_IS_UNFORMATTED) {
384 			fprintf(stderr,
385 				"IDLE: BD media is already formatted\n");
386 			return 2;
387 		}
388 		size = 0;           /* does not really matter */
389 		format_flag = 3<<1; /* format to default size, no quick */
390 	} else if (current_profile == 0x14) { /* sequential DVD-RW */
391 		size = 128 * 1024 * 1024;
392 		format_flag = 1; /* write initial 128 MiB */
393 	} else {
394 		fprintf(stderr, "FATAL: Can only format DVD-RW or BD\n");
395 		return 0;
396 	}
397 	burn_set_signal_handling("libburner : ", NULL, 0x30);
398 
399 	printf("Beginning to format media.\n");
400 	burn_disc_format(drive, size, format_flag);
401 
402 	sleep(1);
403 	while (burn_drive_get_status(drive, &p) != BURN_DRIVE_IDLE) {
404 		if(p.sectors>0 && p.sector>=0) /* display 1 to 99 percent */
405 			percent = 1.0 + ((double) p.sector+1.0)
406 					 / ((double) p.sectors) * 98.0;
407 		printf("Formatting  ( %.1f%% done )\n", percent);
408 		sleep(1);
409 	}
410 	if (burn_is_aborting(0) > 0)
411 		return -1;
412 	burn_set_signal_handling("libburner : ", NULL, 0x0);
413 	burn_disc_get_profile(drive_list[0].drive, &current_profile,
414 				 current_profile_name);
415 	if (current_profile == 0x14 || current_profile == 0x13)
416 		printf("Media type now: %4.4xh  \"%s\"\n",
417 				 current_profile, current_profile_name);
418 	if (current_profile == 0x14) {
419 		fprintf(stderr,
420 		  "FATAL: Failed to change media profile to desired value\n");
421 		return 0;
422 	}
423 	return 1;
424 }
425 
426 
427 /** Brings preformatted track images (ISO 9660, audio, ...) onto media.
428     To make sure a data image is fully readable on any Linux machine, this
429     function adds 300 kiB of padding to the (usualy single) track.
430     Audio tracks get padded to complete their last sector.
431     A fifo of 4 MB is installed between each track and its data source.
432     Each of the 4 MB buffers gets allocated automatically as soon as a track
433     begins to be processed and it gets freed as soon as the track is done.
434     The fifos do not wait for buffer fill but writing starts immediately.
435 
436     In case of external signals expect abort handling of an ongoing burn to
437     last up to a minute. Wait the normal burning timespan before any kill -9.
438 */
libburner_payload(struct burn_drive * drive,char source_adr[][4096],int source_adr_count,int multi,int simulate_burn,int all_tracks_type)439 int libburner_payload(struct burn_drive *drive,
440 		      char source_adr[][4096], int source_adr_count,
441 		      int multi, int simulate_burn, int all_tracks_type)
442 {
443 	struct burn_source *data_src = NULL, *fifo_src[99];
444 	struct burn_disc *target_disc = NULL;
445 	struct burn_session *session = NULL;
446 	struct burn_write_opts *burn_options = NULL;
447 	enum burn_disc_status disc_state;
448 	struct burn_track *track, *tracklist[99];
449 	struct burn_progress progress;
450 	time_t start_time;
451 	int last_sector = 0, padding = 0, trackno, unpredicted_size = 0, fd;
452 	int fifo_chunksize = 2352, fifo_chunks = 1783; /* ~ 4 MB fifo */
453 	int ret;
454 	off_t fixed_size;
455 	char *adr, reasons[BURN_REASONS_LEN];
456 	struct stat stbuf;
457 
458 	for (trackno = 0 ; trackno < source_adr_count; trackno++) {
459 		fifo_src[trackno] = NULL;
460 		tracklist[trackno] = NULL;
461 	}
462 
463 	if (all_tracks_type != BURN_AUDIO) {
464 		all_tracks_type = BURN_MODE1;
465 		/* a padding of 300 kiB helps to avoid the read-ahead bug */
466 		padding = 300*1024;
467 		fifo_chunksize = 2048;
468 		fifo_chunks = 2048; /* 4 MB fifo */
469 	}
470 
471 	target_disc = burn_disc_create();
472 	session = burn_session_create();
473 	burn_disc_add_session(target_disc, session, BURN_POS_END);
474 
475 	for (trackno = 0 ; trackno < source_adr_count; trackno++) {
476 	  tracklist[trackno] = track = burn_track_create();
477 	  burn_track_define_data(track, 0, padding, 1, all_tracks_type);
478 
479 	  /* Open file descriptor to source of track data */
480 	  adr = source_adr[trackno];
481 	  fixed_size = 0;
482 	  if (adr[0] == '-' && adr[1] == 0) {
483 		fd = 0;
484 	  } else {
485 		fd = open(adr, O_RDONLY);
486 		if (fd>=0)
487 			if (fstat(fd,&stbuf)!=-1)
488 				if((stbuf.st_mode&S_IFMT)==S_IFREG)
489 					fixed_size = stbuf.st_size;
490 	  }
491 	  if (fixed_size==0)
492 		unpredicted_size = 1;
493 
494 	  /* Convert this filedescriptor into a burn_source object */
495 	  data_src = NULL;
496 	  if (fd >= 0)
497 	  	data_src = burn_fd_source_new(fd, -1, fixed_size);
498 	  if (data_src == NULL) {
499 		fprintf(stderr,
500 		       "FATAL: Could not open data source '%s'.\n",adr);
501 		if(errno!=0)
502 			fprintf(stderr,"(Most recent system error: %s )\n",
503 				strerror(errno));
504 		{ret = 0; goto ex;}
505 	  }
506 	  /* Install a fifo object on top of that data source object */
507 	  fifo_src[trackno] = burn_fifo_source_new(data_src,
508 					fifo_chunksize, fifo_chunks, 0);
509 	  if (fifo_src[trackno] == NULL) {
510 		fprintf(stderr,
511 			"FATAL: Could not create fifo object of 4 MB\n");
512 		{ret = 0; goto ex;}
513 	  }
514 
515 	  /* Use the fifo object as data source for the track */
516 	  if (burn_track_set_source(track, fifo_src[trackno])
517 							 != BURN_SOURCE_OK) {
518 		fprintf(stderr,
519 		       "FATAL: Cannot attach source object to track object\n");
520 		{ret = 0; goto ex;}
521 	  }
522 
523 	  burn_session_add_track(session, track, BURN_POS_END);
524 	  printf("Track %d : source is '%s'\n", trackno+1, adr);
525 
526 	  /* Give up local reference to the data burn_source object */
527 	  burn_source_free(data_src);
528 	  data_src = NULL;
529 
530 	} /* trackno loop end */
531 
532 	/* Evaluate drive and media */
533 	disc_state = burn_disc_get_status(drive);
534 	if (disc_state != BURN_DISC_BLANK &&
535 	    disc_state != BURN_DISC_APPENDABLE) {
536 		if (disc_state == BURN_DISC_FULL) {
537 			fprintf(stderr, "FATAL: Closed media with data detected. Need blank or appendable media.\n");
538 			if (burn_disc_erasable(drive))
539 				fprintf(stderr, "HINT: Try --blank_fast\n\n");
540 		} else if (disc_state == BURN_DISC_EMPTY)
541 			fprintf(stderr,"FATAL: No media detected in drive\n");
542 		else
543 			fprintf(stderr,
544 			 "FATAL: Cannot recognize state of drive and media\n");
545 		{ret = 0; goto ex;}
546 	}
547 
548 	burn_options = burn_write_opts_new(drive);
549 	burn_write_opts_set_perform_opc(burn_options, 0);
550 	burn_write_opts_set_multi(burn_options, !!multi);
551 	if(simulate_burn)
552 		printf("\n*** Will TRY to SIMULATE burning ***\n\n");
553 	burn_write_opts_set_simulate(burn_options, simulate_burn);
554 	burn_drive_set_speed(drive, 0, 0);
555 	burn_write_opts_set_underrun_proof(burn_options, 1);
556 	if (burn_write_opts_auto_write_type(burn_options, target_disc,
557 					reasons, 0) == BURN_WRITE_NONE) {
558 		fprintf(stderr, "FATAL: Failed to find a suitable write mode with this media.\n");
559 		fprintf(stderr, "Reasons given:\n%s\n", reasons);
560 		{ret = 0; goto ex;}
561 	}
562 	burn_set_signal_handling("libburner : ", NULL, 0x30);
563 
564 	printf("Burning starts. With e.g. 4x media expect up to a minute of zero progress.\n");
565 	start_time = time(0);
566 	burn_disc_write(burn_options, target_disc);
567 
568 	while (burn_drive_get_status(drive, NULL) == BURN_DRIVE_SPAWNING)
569 		usleep(100002);
570 	while (burn_drive_get_status(drive, &progress) != BURN_DRIVE_IDLE) {
571 		if (progress.sectors <= 0 ||
572 		    (progress.sector >= progress.sectors - 1 &&
573 	             !unpredicted_size) ||
574 		    (unpredicted_size && progress.sector == last_sector))
575 			printf(
576 			     "Thank you for being patient since %d seconds.",
577 			     (int) (time(0) - start_time));
578 		else if(unpredicted_size)
579 			printf("Track %d : sector %d", progress.track+1,
580 				progress.sector);
581 		else
582 			printf("Track %d : sector %d of %d",progress.track+1,
583 				progress.sector, progress.sectors);
584 		last_sector = progress.sector;
585 		if (progress.track >= 0 && progress.track < source_adr_count) {
586 			int size, free_bytes, ret;
587 			char *status_text;
588 
589 			ret = burn_fifo_inquire_status(
590 				fifo_src[progress.track], &size, &free_bytes,
591 				&status_text);
592 			if (ret >= 0 )
593 				printf("  [fifo %s, %2d%% fill]", status_text,
594 					(int) (100.0 - 100.0 *
595 						((double) free_bytes) /
596 						(double) size));
597 		}
598 		printf("\n");
599 		sleep(1);
600 	}
601 	printf("\n");
602 
603 	if (burn_is_aborting(0) > 0)
604 		{ret = -1; goto ex;}
605 	if (multi && current_profile != 0x1a && current_profile != 0x13 &&
606 		current_profile != 0x12 && current_profile != 0x43)
607 			/* not with DVD+RW, formatted DVD-RW, DVD-RAM, BD-RE */
608 		printf("NOTE: Media left appendable.\n");
609 	if (simulate_burn)
610 		printf("\n*** Did TRY to SIMULATE burning ***\n\n");
611 	ret = 1;
612 ex:;
613 	/* Dispose objects */
614 	if (burn_options != NULL)
615 		burn_write_opts_free(burn_options);
616 	for (trackno = 0 ; trackno < source_adr_count; trackno++) {
617 		if (fifo_src[trackno] != NULL)
618 	  		burn_source_free(fifo_src[trackno]);
619 		if (tracklist[trackno])
620 			burn_track_free(tracklist[trackno]);
621 	}
622 	if (data_src != NULL)
623 		burn_source_free(data_src);
624 	if (session != NULL)
625 		burn_session_free(session);
626 	if (target_disc != NULL)
627 		burn_disc_free(target_disc);
628 	return ret;
629 }
630 
631 
632 /** The setup parameters of libburner */
633 static char drive_adr[BURN_DRIVE_ADR_LEN] = {""};
634 static int driveno = 0;
635 static int do_blank = 0;
636 static char source_adr[99][4096];
637 static int source_adr_count = 0;
638 static int do_multi = 0;
639 static int simulate_burn = 0;
640 static int all_tracks_type = BURN_MODE1;
641 
642 
643 /** Converts command line arguments into above setup parameters.
644 */
libburner_setup(int argc,char ** argv)645 int libburner_setup(int argc, char **argv)
646 {
647     int i, insuffient_parameters = 0, print_help = 0;
648 
649     for (i = 1; i < argc; ++i) {
650         if (!strcmp(argv[i], "--audio")) {
651             all_tracks_type = BURN_AUDIO;
652 
653         } else if (!strcmp(argv[i], "--blank_fast")) {
654             do_blank = 1;
655 
656         } else if (!strcmp(argv[i], "--blank_full")) {
657             do_blank = 2;
658 
659         } else if (!strcmp(argv[i], "--burn_for_real")) {
660             simulate_burn = 0;
661 
662         } else if (!strcmp(argv[i], "--drive")) {
663             ++i;
664             if (i >= argc) {
665                 fprintf(stderr,"--drive requires an argument\n");
666                 return 1;
667             } else if (strcmp(argv[i], "-") == 0) {
668                 drive_adr[0] = 0;
669                 driveno = -1;
670             } else if (isdigit(argv[i][0])) {
671                 drive_adr[0] = 0;
672                 driveno = atoi(argv[i]);
673             } else {
674                 if(strlen(argv[i]) >= BURN_DRIVE_ADR_LEN) {
675                     fprintf(stderr,"--drive address too long (max. %d)\n",
676                             BURN_DRIVE_ADR_LEN-1);
677                     return 2;
678                 }
679                 strcpy(drive_adr, argv[i]);
680             }
681         } else if ((!strcmp(argv[i], "--format_overwrite")) ||
682 		   (!strcmp(argv[i], "--format"))) {
683             do_blank = 101;
684 
685         } else if (!strcmp(argv[i], "--multi")) {
686 	    do_multi = 1;
687 
688 	} else if (!strcmp(argv[i], "--stdin_size")) { /* obsoleted */
689 	    i++;
690 
691         } else if (!strcmp(argv[i], "--try_to_simulate")) {
692             simulate_burn = 1;
693 
694         } else if (!strcmp(argv[i], "--help")) {
695             print_help = 1;
696 
697         } else if (!strncmp(argv[i], "--",2)) {
698             fprintf(stderr, "Unidentified option: %s\n", argv[i]);
699             return 7;
700         } else {
701             if(strlen(argv[i]) >= 4096) {
702                 fprintf(stderr, "Source address too long (max. %d)\n", 4096-1);
703                 return 5;
704             }
705             if(source_adr_count >= 99) {
706                 fprintf(stderr, "Too many tracks (max. 99)\n");
707                 return 6;
708             }
709             strcpy(source_adr[source_adr_count], argv[i]);
710             source_adr_count++;
711         }
712     }
713     insuffient_parameters = 1;
714     if (driveno < 0)
715         insuffient_parameters = 0;
716     if (source_adr_count > 0)
717         insuffient_parameters = 0;
718     if (do_blank)
719         insuffient_parameters = 0;
720     if (print_help || insuffient_parameters ) {
721         printf("Usage: %s\n", argv[0]);
722         printf("       [--drive <address>|<driveno>|\"-\"]  [--audio]\n");
723         printf("       [--blank_fast|--blank_full|--format]  [--try_to_simulate]\n");
724         printf("       [--multi]  [<one or more imagefiles>|\"-\"]\n");
725         printf("Examples\n");
726         printf("A bus scan (needs rw-permissions to see a drive):\n");
727         printf("  %s --drive -\n",argv[0]);
728         printf("Burn a file to drive chosen by number, leave appendable:\n");
729         printf("  %s --drive 0 --multi my_image_file\n", argv[0]);
730         printf("Burn a file to drive chosen by persistent address, close:\n");
731         printf("  %s --drive /dev/hdc my_image_file\n", argv[0]);
732         printf("Blank a used CD-RW (is combinable with burning in one run):\n");
733         printf("  %s --drive /dev/hdc --blank_fast\n",argv[0]);
734         printf("Blank a used DVD-RW (is combinable with burning in one run):\n");
735         printf("  %s --drive /dev/hdc --blank_full\n",argv[0]);
736         printf("Format a DVD-RW, BD-RE or BD-R:\n");
737         printf("  %s --drive /dev/hdc --format\n", argv[0]);
738         printf("Burn two audio tracks (to CD only):\n");
739         printf("  lame --decode -t /path/to/track1.mp3 track1.cd\n");
740         printf("  test/dewav /path/to/track2.wav -o track2.cd\n");
741         printf("  %s --drive /dev/hdc --audio track1.cd track2.cd\n", argv[0]);
742         printf("Burn a compressed afio archive on-the-fly:\n");
743         printf("  ( cd my_directory ; find . -print | afio -oZ - ) | \\\n");
744         printf("  %s --drive /dev/hdc -\n", argv[0]);
745         printf("To be read from *not mounted* media via: afio -tvZ /dev/hdc\n");
746         if (insuffient_parameters)
747             return 6;
748     }
749     return 0;
750 }
751 
752 
main(int argc,char ** argv)753 int main(int argc, char **argv)
754 {
755 	int ret;
756 
757 	/* A warning to programmers who start their own projekt from here. */
758 	if (sizeof(off_t) != 8) {
759 		 fprintf(stderr,
760 	   "\nFATAL: Compile time misconfiguration. off_t is not 64 bit.\n\n");
761 		 exit(39);
762 	}
763 
764 	ret = libburner_setup(argc, argv);
765 	if (ret)
766 		exit(ret);
767 
768 	printf("Initializing libburnia-project.org ...\n");
769 	if (burn_initialize())
770 		printf("Done\n");
771 	else {
772 		printf("FAILED\n");
773 		fprintf(stderr,"\nFATAL: Failed to initialize.\n");
774 		exit(33);
775 	}
776 
777 	/* Print messages of severity SORRY or more directly to stderr */
778 	burn_msgs_set_severities("NEVER", "SORRY", "libburner : ");
779 
780 	/* Activate the synchronous signal handler which eventually will try to
781 	   properly shutdown drive and library on aborting events. */
782 	burn_set_signal_handling("libburner : ", NULL, 0x0);
783 
784 	/** Note: driveno might change its value in this call */
785 	ret = libburner_aquire_drive(drive_adr, &driveno);
786 	if (ret<=0) {
787 		fprintf(stderr,"\nFATAL: Failed to acquire drive.\n");
788 		{ ret = 34; goto finish_libburn; }
789 	}
790 	if (ret == 2)
791 		{ ret = 0; goto release_drive; }
792 	if (do_blank) {
793 		if (do_blank > 100)
794 			ret = libburner_format(drive_list[driveno].drive);
795 		else
796 			ret = libburner_blank_disc(drive_list[driveno].drive,
797 							do_blank == 1);
798 		if (ret<=0)
799 			{ ret = 36; goto release_drive; }
800 	}
801 	if (source_adr_count > 0) {
802 		ret = libburner_payload(drive_list[driveno].drive,
803 				source_adr, source_adr_count,
804 				do_multi, simulate_burn, all_tracks_type);
805 		if (ret<=0)
806 			{ ret = 38; goto release_drive; }
807 	}
808 	ret = 0;
809 release_drive:;
810 	if (drive_is_grabbed)
811 		burn_drive_release(drive_list[driveno].drive, 0);
812 
813 finish_libburn:;
814 	if (burn_is_aborting(0) > 0) {
815 		burn_abort(4400, burn_abort_pacifier, "libburner : ");
816 		fprintf(stderr,"\nlibburner run aborted\n");
817 		exit(1);
818 	}
819 	/* This app does not bother to know about exact scan state.
820 	   Better to accept a memory leak here. We are done anyway. */
821 	/* burn_drive_info_free(drive_list); */
822 	burn_finish();
823 	exit(ret);
824 }
825 
826 
827 /*  License and copyright aspects:
828 
829 This all is provided under GPL.
830 Read. Try. Think. Play. Write yourself some code. Be free of my copyright.
831 
832 Be also invited to study the code of cdrskin/cdrskin.c et al.
833 
834 History:
835 libburner is a compilation of my own contributions to test/burniso.c and
836 fresh code which replaced the remaining parts under copyright of
837 Derek Foreman.
838 My respect and my thanks to Derek for providing me a start back in 2005.
839 
840 */
841 
842