libburner.c

Go to the documentation of this file.
00001 
00002 /* test/libburner.c , API illustration of burning data or audio tracks to CD */
00003 /* Copyright (C) 2005 - 2016 Thomas Schmitt <scdbackup@gmx.net> */
00004 /* Provided under GPL, see also "License and copyright aspects" at file end */
00005 
00006 
00007 /**                               Overview 
00008   
00009   libburner is a minimal demo application for the library libburn as provided
00010   on  http://libburnia-project.org . It can list the available devices, can
00011   blank a CD-RW or DVD-RW, can format DVD-RW and BD, can burn to CD-R,
00012   CD-RW, DVD-R, DVD+R, DVD+R/DL, DVD+RW, DVD-RW, DVD-RAM, BD-R, BD-RE. 
00013   Not supported yet: DVD-R/DL.
00014 
00015   It's main purpose, nevertheless, is to show you how to use libburn and also
00016   to serve the libburnia team as reference application. libburner.c does indeed
00017   define the standard way how above three gestures can be implemented and
00018   stay upward compatible for a good while.
00019   There is another demo program, test/telltoc.c, which inspects drive, media
00020   state, and media contents.
00021   
00022   Before you can do anything, you have to initialize libburn by
00023      burn_initialize()
00024   and provide some signal and abort handling, e.g. by the builtin handler, by
00025      burn_set_signal_handling("libburner : ", NULL, 0x0) 
00026   as it is done in main() at the end of this file.
00027   Then you acquire a drive in an appropriate way conforming to the API. The two
00028   main approaches are shown here in application functions:
00029      libburner_aquire_by_adr()     demonstrates usage as of cdrecord traditions
00030      libburner_aquire_by_driveno()      demonstrates a scan-and-choose approach
00031 
00032   With that acquired drive you can blank a CD-RW or DVD-RW as shown in
00033      libburner_blank_disc()
00034   or you can format a DVD-RW to profile "Restricted Overwrite" (needed once)
00035   or an unused BD to default size with spare blocks
00036      libburner_format()
00037   With the acquired drive you can burn to CD, DVD, BD. See
00038      libburner_payload()
00039 
00040   These three functions switch temporarily to a non-fatal signal handler
00041   while they are waiting for the drive to become idle again:
00042      burn_set_signal_handling("libburner : ", NULL, 0x30)
00043   After the waiting loop ended, they check for eventual abort events by
00044      burn_is_aborting(0)
00045   The 0x30 handler will eventually execute
00046      burn_abort()
00047   but not wait for the drive to become idle and not call exit().
00048   This is needed because the worker threads might block as long as the signal
00049   handler has not returned. The 0x0 handler would wait for them to finish.
00050   Take this into respect when implementing own signal handlers.
00051 
00052   When everything is done, main() releases the drive and shuts down libburn:
00053      burn_drive_release();
00054      burn_finish()
00055 
00056   Applications must use 64 bit off_t. E.g. by defining
00057     #define _LARGEFILE_SOURCE
00058     #define _FILE_OFFSET_BITS 64
00059   or take special precautions to interface with the library by 64 bit integers
00060   where libburn/libburn.h prescribes off_t.
00061   This program gets fed with appropriate settings externally by libburn's
00062   autotools generated build system.
00063 */
00064 
00065 
00066 /** See this for the decisive API specs . libburn.h is The Original */
00067 /*  For using the installed header file :  #include <libburn/libburn.h> */
00068 /*  This program insists in the own headerfile. */
00069 #include "../libburn/libburn.h"
00070 
00071 /* libburn works on Linux systems with kernel 2.4 or 2.6, FreeBSD, Solaris */
00072 #include <stdio.h>
00073 #include <ctype.h>
00074 #include <sys/types.h>
00075 #include <unistd.h>
00076 #include <string.h>
00077 #include <stdlib.h>
00078 #include <time.h>
00079 #include <errno.h>
00080 #include <sys/stat.h>
00081 #include <fcntl.h>
00082 
00083 
00084 /** For simplicity i use global variables to represent the drives.
00085     Drives are systemwide global, so we do not give away much of good style.
00086 */
00087 
00088 /** This list will hold the drives known to libburn. This might be all CD
00089     drives of the system and thus might impose severe impact on the system.
00090 */
00091 static struct burn_drive_info *drive_list;
00092 
00093 /** If you start a long lasting operation with drive_count > 1 then you are
00094     not friendly to the users of other drives on those systems. Beware. */
00095 static unsigned int drive_count;
00096 
00097 /** This variable indicates wether the drive is grabbed and must be
00098     finally released */
00099 static int drive_is_grabbed = 0;
00100 
00101 /** A number and a text describing the type of media in acquired drive */
00102 static int current_profile= -1;
00103 static char current_profile_name[80]= {""};
00104 
00105 
00106 /* Some in-advance definitions make possible a more comprehensive ordering
00107    of the functions and their explanations in here */
00108 int libburner_aquire_by_adr(char *drive_adr);
00109 int libburner_aquire_by_driveno(int *drive_no);
00110 
00111 
00112 /* ------------------------------- API gestures ---------------------------- */
00113 
00114 /** You need to acquire a drive before burning. The API offers this as one
00115     compact call and alternatively as application controllable gestures of
00116     whitelisting, scanning for drives and finally grabbing one of them.
00117 
00118     If you have a persistent address of the drive, then the compact call is
00119     to prefer because it only touches one drive. On modern Linux kernels,
00120     there should be no fatal disturbance of ongoing burns of other libburn
00121     instances with any of our approaches. We use open(O_EXCL) by default.
00122     On /dev/hdX it should cooperate with growisofs and some cdrecord variants.
00123     On /dev/sgN versus /dev/scdM expect it not to respect other programs.
00124 */
00125 int libburner_aquire_drive(char *drive_adr, int *driveno)
00126 {
00127     int ret;
00128 
00129     if(drive_adr != NULL && drive_adr[0] != 0)
00130         ret = libburner_aquire_by_adr(drive_adr);
00131     else
00132         ret = libburner_aquire_by_driveno(driveno);
00133     if (ret <= 0 || *driveno <= 0)
00134         return ret;
00135     burn_disc_get_profile(drive_list[0].drive, &current_profile,
00136                  current_profile_name);
00137     if (current_profile_name[0])
00138         printf("Detected media type: %s\n", current_profile_name);
00139     return 1;
00140 }
00141 
00142 
00143 /** If the persistent drive address is known, then this approach is much
00144     more un-obtrusive to the systemwide livestock of drives. Only the
00145     given drive device will be opened during this procedure.
00146 */
00147 int libburner_aquire_by_adr(char *drive_adr)
00148 {
00149     int ret;
00150     char libburn_drive_adr[BURN_DRIVE_ADR_LEN];
00151 
00152     /* Some not-so-harmless drive addresses get blocked in this demo */
00153     if (strncmp(drive_adr, "stdio:/dev/fd/", 14) == 0 ||
00154         strcmp(drive_adr, "stdio:-") == 0) {
00155         fprintf(stderr, "Will not work with pseudo-drive '%s'\n",
00156             drive_adr);
00157         return 0;
00158     }
00159 
00160     /* This tries to resolve links or alternative device files */
00161     ret = burn_drive_convert_fs_adr(drive_adr, libburn_drive_adr);  
00162     if (ret<=0) {
00163         fprintf(stderr, "Address does not lead to a CD burner: '%s'\n",
00164                  drive_adr);
00165         return 0;
00166     }
00167     fprintf(stderr,"Aquiring drive '%s' ...\n", libburn_drive_adr);
00168     ret = burn_drive_scan_and_grab(&drive_list, libburn_drive_adr, 1);
00169     if (ret <= 0) {
00170         fprintf(stderr,"FAILURE with persistent drive address  '%s'\n",
00171             libburn_drive_adr);
00172     } else {
00173         fprintf(stderr,"Done\n");
00174         drive_is_grabbed = 1;
00175     }
00176     return ret;
00177 }
00178 
00179 
00180 /** This method demonstrates how to use libburn without knowing a persistent
00181     drive address in advance. It has to make sure that after assessing the list
00182     of available drives, all unwanted drives get closed again. As long as they
00183     are open, no other libburn instance can see them. This is an intended
00184     locking feature. The application is responsible for giving up the locks
00185     by either burn_drive_release() (only after burn_drive_grab() !),
00186     burn_drive_info_forget(), burn_drive_info_free(), or burn_finish().
00187     @param driveno the index number in libburn's drive list. This will get
00188                    set to 0 on success and will then be the drive index to
00189                    use in the further dourse of processing.
00190     @return 1 success , <= 0 failure
00191 */
00192 int libburner_aquire_by_driveno(int *driveno)
00193 {
00194     char adr[BURN_DRIVE_ADR_LEN];
00195     int ret, i;
00196 
00197     printf("Beginning to scan for devices ...\n");
00198     while (!burn_drive_scan(&drive_list, &drive_count))
00199         usleep(100002);
00200     if (drive_count <= 0 && *driveno >= 0) {
00201         printf("FAILED (no drives found)\n");
00202         return 0;
00203     }
00204     printf("Done\n");
00205 
00206     /*
00207     Interactive programs may choose the drive number at this moment.
00208 
00209     drive[0] to drive[drive_count-1] are struct burn_drive_info
00210     as defined in  libburn/libburn.h  . This structure is part of API
00211     and thus will strive for future compatibility on source level.
00212     Have a look at the info offered.
00213     Caution: do not take .location for drive address. Always use
00214         burn_drive_get_adr() or you might become incompatible
00215         in future.
00216     Note: bugs with struct burn_drive_info - if any - will not be
00217         easy to fix. Please report them but also strive for
00218         workarounds on application level.
00219     */
00220     printf("\nOverview of accessible drives (%d found) :\n",
00221         drive_count);
00222     printf("-----------------------------------------------------------------------------\n");
00223     for (i = 0; i < (int) drive_count; i++) {
00224         if (burn_drive_get_adr(&(drive_list[i]), adr) <=0)
00225             strcpy(adr, "-get_adr_failed-");
00226         printf("%d  --drive '%s'  :  '%s'  '%s'\n",
00227             i,adr,drive_list[i].vendor,drive_list[i].product);
00228     }
00229     printf("-----------------------------------------------------------------------------\n\n");
00230 
00231     /*
00232     On multi-drive systems save yourself from sysadmins' revenge.
00233 
00234     Be aware that you hold reserved all available drives at this point.
00235     So either make your choice quick enough not to annoy other system
00236     users, or set free the drives for a while.
00237 
00238     The tested way of setting free all drives is to shutdown the library
00239     and to restart when the choice has been made. The list of selectable
00240     drives should also hold persistent drive addresses as obtained
00241     above by burn_drive_get_adr(). By such an address one may use
00242     burn_drive_scan_and_grab() to finally acquire exactly one drive.
00243 
00244     A not yet tested shortcut should be to call burn_drive_info_free()
00245     and to call either burn_drive_scan() or burn_drive_scan_and_grab()
00246     before accessing any drives again.
00247 
00248     In both cases you have to be aware that the desired drive might get
00249     acquired in the meantime by another user or libburn process.
00250     */
00251 
00252     /* We already made our choice via command line. (default is 0)
00253        So we just have to keep our desired drive and drop all others.
00254        No other libburn instance will have a chance to steal our drive.
00255      */
00256     if (*driveno < 0) {
00257         printf("Pseudo-drive \"-\" given : bus scanning done.\n");
00258         return 2; /* the program will end after this */
00259     }
00260     if ((int) drive_count <= *driveno) {
00261         fprintf(stderr,
00262             "Found only %d drives. Number %d not available.\n",
00263             drive_count, *driveno);
00264         return 0; /* the program will end after this */
00265     }
00266 
00267     /* Drop all drives which we do not want to use */
00268     for (i = 0; i < (int) drive_count; i++) {
00269         if (i == *driveno) /* the one drive we want to keep */
00270     continue;
00271         ret = burn_drive_info_forget(&(drive_list[i]),0);
00272         if (ret != 1)
00273             fprintf(stderr, "Cannot drop drive %d. Please report \"ret=%d\" to libburn-hackers@pykix.org\n",
00274                 i, ret);
00275         else
00276             printf("Dropped unwanted drive %d\n",i);
00277     }
00278     /* Make the one we want ready for blanking or burning */
00279     ret= burn_drive_grab(drive_list[*driveno].drive, 1);
00280     if (ret != 1)
00281         return 0;
00282     drive_is_grabbed = 1;
00283     return 1;
00284 }
00285 
00286 
00287 /** Makes a previously used CD-RW or unformatted DVD-RW ready for thorough
00288     re-usal.
00289 
00290     To our knowledge it is hardly possible to abort an ongoing blank operation
00291     because after start it is entirely handled by the drive.
00292     So expect signal handling to wait the normal blanking timespan until it
00293     can allow the process to end. External kill -9 will not help the drive.
00294 */
00295 int libburner_blank_disc(struct burn_drive *drive, int blank_fast)
00296 {
00297     enum burn_disc_status disc_state;
00298     struct burn_progress p;
00299     double percent = 1.0;
00300 
00301     disc_state = burn_disc_get_status(drive);
00302     printf(
00303         "Drive media status:  %d  (see  libburn/libburn.h  BURN_DISC_*)\n",
00304         disc_state);
00305     if (current_profile == 0x13) {
00306         ; /* formatted DVD-RW will get blanked to sequential state */
00307     } else if (disc_state == BURN_DISC_BLANK) {
00308         fprintf(stderr,
00309           "IDLE: Blank media detected. Will leave it untouched\n");
00310         return 2;
00311     } else if (disc_state == BURN_DISC_FULL ||
00312            disc_state == BURN_DISC_APPENDABLE) {
00313         ; /* this is what libburner is willing to blank */
00314     } else if (disc_state == BURN_DISC_EMPTY) {
00315         fprintf(stderr,"FATAL: No media detected in drive\n");
00316         return 0;
00317     } else {
00318         fprintf(stderr,
00319             "FATAL: Unsuitable drive and media state\n");
00320         return 0;
00321     }
00322     if(!burn_disc_erasable(drive)) {
00323         fprintf(stderr,
00324             "FATAL : Media is not of erasable type\n");
00325         return 0;
00326     }
00327     /* Switch to asynchronous signal handling for the time of waiting */
00328     burn_set_signal_handling("libburner : ", NULL, 0x30);
00329 
00330     printf("Beginning to %s-blank media.\n", (blank_fast?"fast":"full"));
00331     burn_disc_erase(drive, blank_fast);
00332 
00333     sleep(1);
00334     while (burn_drive_get_status(drive, &p) != BURN_DRIVE_IDLE) {
00335         if(p.sectors>0 && p.sector>=0) /* display 1 to 99 percent */
00336             percent = 1.0 + ((double) p.sector+1.0)
00337                      / ((double) p.sectors) * 98.0;
00338         printf("Blanking  ( %.1f%% done )\n", percent);
00339         sleep(1);
00340     }
00341     if (burn_is_aborting(0) > 0)
00342         return -1;
00343     /* Back to synchronous handling */
00344     burn_set_signal_handling("libburner : ", NULL, 0x0);
00345     printf("Done\n");
00346     return 1;
00347 }
00348 
00349 
00350 /** Formats unformatted DVD-RW to profile 0013h "Restricted Overwrite"
00351     which needs no blanking for re-use but is not capable of multi-session.
00352     Expect a behavior similar to blanking with unusual noises from the drive.
00353 
00354     Formats unformatted BD-RE to default size. This will allocate some
00355     reserve space, test for bad blocks and make the media ready for writing.
00356     Expect a very long run time.
00357 
00358     Formats unformatted blank BD-R to hold a default amount of spare blocks
00359     for eventual mishaps during writing. If BD-R get written without being
00360     formatted, then they get no such reserve and will burn at full speed.
00361 */
00362 int libburner_format(struct burn_drive *drive)
00363 {
00364     struct burn_progress p;
00365     double percent = 1.0;
00366     int ret, status, num_formats, format_flag= 0;
00367     off_t size = 0;
00368     unsigned dummy;
00369     enum burn_disc_status disc_state;
00370 
00371     if (current_profile == 0x13) {
00372         fprintf(stderr, "IDLE: DVD-RW media is already formatted\n");
00373         return 2;
00374     } else if (current_profile == 0x41 || current_profile == 0x43) {
00375         disc_state = burn_disc_get_status(drive);
00376         if (disc_state != BURN_DISC_BLANK && current_profile == 0x41) {
00377             fprintf(stderr,
00378                 "FATAL: BD-R is not blank. Cannot format.\n");
00379             return 0;
00380         }
00381         ret = burn_disc_get_formats(drive, &status, &size, &dummy,
00382                                 &num_formats);
00383         if (ret > 0 && status != BURN_FORMAT_IS_UNFORMATTED) {
00384             fprintf(stderr,
00385                 "IDLE: BD media is already formatted\n");
00386             return 2;
00387         }
00388         size = 0;           /* does not really matter */
00389         format_flag = 3<<1; /* format to default size, no quick */
00390     } else if (current_profile == 0x14) { /* sequential DVD-RW */
00391         size = 128 * 1024 * 1024;
00392         format_flag = 1; /* write initial 128 MiB */
00393     } else {
00394         fprintf(stderr, "FATAL: Can only format DVD-RW or BD\n");
00395         return 0;
00396     }
00397     burn_set_signal_handling("libburner : ", NULL, 0x30);
00398 
00399     printf("Beginning to format media.\n");
00400     burn_disc_format(drive, size, format_flag);
00401 
00402     sleep(1);
00403     while (burn_drive_get_status(drive, &p) != BURN_DRIVE_IDLE) {
00404         if(p.sectors>0 && p.sector>=0) /* display 1 to 99 percent */
00405             percent = 1.0 + ((double) p.sector+1.0)
00406                      / ((double) p.sectors) * 98.0;
00407         printf("Formatting  ( %.1f%% done )\n", percent);
00408         sleep(1);
00409     }
00410     if (burn_is_aborting(0) > 0)
00411         return -1;
00412     burn_set_signal_handling("libburner : ", NULL, 0x0);
00413     burn_disc_get_profile(drive_list[0].drive, &current_profile,
00414                  current_profile_name);
00415     if (current_profile == 0x14 || current_profile == 0x13)
00416         printf("Media type now: %4.4xh  \"%s\"\n",
00417                  current_profile, current_profile_name);
00418     if (current_profile == 0x14) {
00419         fprintf(stderr,
00420           "FATAL: Failed to change media profile to desired value\n");
00421         return 0;
00422     }
00423     return 1;
00424 }
00425 
00426 
00427 /** Brings preformatted track images (ISO 9660, audio, ...) onto media.
00428     To make sure a data image is fully readable on any Linux machine, this
00429     function adds 300 kiB of padding to the (usualy single) track.
00430     Audio tracks get padded to complete their last sector.
00431     A fifo of 4 MB is installed between each track and its data source.
00432     Each of the 4 MB buffers gets allocated automatically as soon as a track
00433     begins to be processed and it gets freed as soon as the track is done.
00434     The fifos do not wait for buffer fill but writing starts immediately.
00435 
00436     In case of external signals expect abort handling of an ongoing burn to
00437     last up to a minute. Wait the normal burning timespan before any kill -9.
00438 */
00439 int libburner_payload(struct burn_drive *drive, 
00440               char source_adr[][4096], int source_adr_count,
00441               int multi, int simulate_burn, int all_tracks_type)
00442 {
00443     struct burn_source *data_src = NULL, *fifo_src[99];
00444     struct burn_disc *target_disc = NULL;
00445     struct burn_session *session = NULL;
00446     struct burn_write_opts *burn_options = NULL;
00447     enum burn_disc_status disc_state;
00448     struct burn_track *track, *tracklist[99];
00449     struct burn_progress progress;
00450     time_t start_time;
00451     int last_sector = 0, padding = 0, trackno, unpredicted_size = 0, fd;
00452     int fifo_chunksize = 2352, fifo_chunks = 1783; /* ~ 4 MB fifo */
00453     int ret;
00454     off_t fixed_size;
00455     char *adr, reasons[BURN_REASONS_LEN];
00456     struct stat stbuf;
00457 
00458     for (trackno = 0 ; trackno < source_adr_count; trackno++) {
00459         fifo_src[trackno] = NULL;
00460         tracklist[trackno] = NULL;
00461     }
00462 
00463     if (all_tracks_type != BURN_AUDIO) {
00464         all_tracks_type = BURN_MODE1;
00465         /* a padding of 300 kiB helps to avoid the read-ahead bug */
00466         padding = 300*1024;
00467         fifo_chunksize = 2048;
00468         fifo_chunks = 2048; /* 4 MB fifo */
00469     }
00470 
00471     target_disc = burn_disc_create();
00472     session = burn_session_create();
00473     burn_disc_add_session(target_disc, session, BURN_POS_END);
00474 
00475     for (trackno = 0 ; trackno < source_adr_count; trackno++) {
00476       tracklist[trackno] = track = burn_track_create();
00477       burn_track_define_data(track, 0, padding, 1, all_tracks_type);
00478 
00479       /* Open file descriptor to source of track data */
00480       adr = source_adr[trackno];
00481       fixed_size = 0;
00482       if (adr[0] == '-' && adr[1] == 0) {
00483         fd = 0;
00484       } else {
00485         fd = open(adr, O_RDONLY);
00486         if (fd>=0)
00487             if (fstat(fd,&stbuf)!=-1)
00488                 if((stbuf.st_mode&S_IFMT)==S_IFREG)
00489                     fixed_size = stbuf.st_size;
00490       }
00491       if (fixed_size==0)
00492         unpredicted_size = 1;
00493 
00494       /* Convert this filedescriptor into a burn_source object */
00495       data_src = NULL;
00496       if (fd >= 0)
00497         data_src = burn_fd_source_new(fd, -1, fixed_size);
00498       if (data_src == NULL) {
00499         fprintf(stderr,
00500                "FATAL: Could not open data source '%s'.\n",adr);
00501         if(errno!=0)
00502             fprintf(stderr,"(Most recent system error: %s )\n",
00503                 strerror(errno));
00504         {ret = 0; goto ex;}
00505       }
00506       /* Install a fifo object on top of that data source object */
00507       fifo_src[trackno] = burn_fifo_source_new(data_src,
00508                     fifo_chunksize, fifo_chunks, 0);
00509       if (fifo_src[trackno] == NULL) {
00510         fprintf(stderr,
00511             "FATAL: Could not create fifo object of 4 MB\n");
00512         {ret = 0; goto ex;}
00513       }
00514 
00515       /* Use the fifo object as data source for the track */
00516       if (burn_track_set_source(track, fifo_src[trackno])
00517                              != BURN_SOURCE_OK) {
00518         fprintf(stderr,
00519                "FATAL: Cannot attach source object to track object\n");
00520         {ret = 0; goto ex;}
00521       }
00522 
00523       burn_session_add_track(session, track, BURN_POS_END);
00524       printf("Track %d : source is '%s'\n", trackno+1, adr);
00525 
00526       /* Give up local reference to the data burn_source object */
00527       burn_source_free(data_src);
00528       data_src = NULL;
00529       
00530     } /* trackno loop end */
00531 
00532     /* Evaluate drive and media */
00533     disc_state = burn_disc_get_status(drive);
00534     if (disc_state != BURN_DISC_BLANK &&
00535         disc_state != BURN_DISC_APPENDABLE) {
00536         if (disc_state == BURN_DISC_FULL) {
00537             fprintf(stderr, "FATAL: Closed media with data detected. Need blank or appendable media.\n");
00538             if (burn_disc_erasable(drive))
00539                 fprintf(stderr, "HINT: Try --blank_fast\n\n");
00540         } else if (disc_state == BURN_DISC_EMPTY) 
00541             fprintf(stderr,"FATAL: No media detected in drive\n");
00542         else
00543             fprintf(stderr,
00544              "FATAL: Cannot recognize state of drive and media\n");
00545         {ret = 0; goto ex;}
00546     }
00547 
00548     burn_options = burn_write_opts_new(drive);
00549     burn_write_opts_set_perform_opc(burn_options, 0);
00550     burn_write_opts_set_multi(burn_options, !!multi);
00551     if(simulate_burn)
00552         printf("\n*** Will TRY to SIMULATE burning ***\n\n");
00553     burn_write_opts_set_simulate(burn_options, simulate_burn);
00554     burn_drive_set_speed(drive, 0, 0);
00555     burn_write_opts_set_underrun_proof(burn_options, 1);
00556     if (burn_write_opts_auto_write_type(burn_options, target_disc,
00557                     reasons, 0) == BURN_WRITE_NONE) {
00558         fprintf(stderr, "FATAL: Failed to find a suitable write mode with this media.\n");
00559         fprintf(stderr, "Reasons given:\n%s\n", reasons);
00560         {ret = 0; goto ex;}
00561     }
00562     burn_set_signal_handling("libburner : ", NULL, 0x30);
00563 
00564     printf("Burning starts. With e.g. 4x media expect up to a minute of zero progress.\n");
00565     start_time = time(0);
00566     burn_disc_write(burn_options, target_disc);
00567 
00568     while (burn_drive_get_status(drive, NULL) == BURN_DRIVE_SPAWNING)
00569         usleep(100002);
00570     while (burn_drive_get_status(drive, &progress) != BURN_DRIVE_IDLE) {
00571         if (progress.sectors <= 0 ||
00572             (progress.sector >= progress.sectors - 1 &&
00573                  !unpredicted_size) ||
00574             (unpredicted_size && progress.sector == last_sector))
00575             printf(
00576                  "Thank you for being patient since %d seconds.",
00577                  (int) (time(0) - start_time));
00578         else if(unpredicted_size)
00579             printf("Track %d : sector %d", progress.track+1,
00580                 progress.sector);
00581         else
00582             printf("Track %d : sector %d of %d",progress.track+1,
00583                 progress.sector, progress.sectors);
00584         last_sector = progress.sector;
00585         if (progress.track >= 0 && progress.track < source_adr_count) {
00586             int size, free_bytes, ret;
00587             char *status_text;
00588     
00589             ret = burn_fifo_inquire_status(
00590                 fifo_src[progress.track], &size, &free_bytes,
00591                 &status_text);
00592             if (ret >= 0 ) 
00593                 printf("  [fifo %s, %2d%% fill]", status_text,
00594                     (int) (100.0 - 100.0 *
00595                         ((double) free_bytes) /
00596                         (double) size));
00597         } 
00598         printf("\n");
00599         sleep(1);
00600     }
00601     printf("\n");
00602 
00603     if (burn_is_aborting(0) > 0)
00604         {ret = -1; goto ex;}
00605     if (multi && current_profile != 0x1a && current_profile != 0x13 &&
00606         current_profile != 0x12 && current_profile != 0x43) 
00607             /* not with DVD+RW, formatted DVD-RW, DVD-RAM, BD-RE */
00608         printf("NOTE: Media left appendable.\n");
00609     if (simulate_burn)
00610         printf("\n*** Did TRY to SIMULATE burning ***\n\n");
00611     ret = 1;
00612 ex:;
00613     /* Dispose objects */
00614     if (burn_options != NULL)
00615         burn_write_opts_free(burn_options);
00616     for (trackno = 0 ; trackno < source_adr_count; trackno++) {
00617         if (fifo_src[trackno] != NULL)
00618             burn_source_free(fifo_src[trackno]);
00619         if (tracklist[trackno])
00620             burn_track_free(tracklist[trackno]);
00621     }
00622     if (data_src != NULL)
00623         burn_source_free(data_src);
00624     if (session != NULL)
00625         burn_session_free(session);
00626     if (target_disc != NULL)
00627         burn_disc_free(target_disc);
00628     return ret;
00629 }
00630 
00631 
00632 /** The setup parameters of libburner */
00633 static char drive_adr[BURN_DRIVE_ADR_LEN] = {""};
00634 static int driveno = 0;
00635 static int do_blank = 0;
00636 static char source_adr[99][4096];
00637 static int source_adr_count = 0;
00638 static int do_multi = 0;
00639 static int simulate_burn = 0;
00640 static int all_tracks_type = BURN_MODE1;
00641 
00642 
00643 /** Converts command line arguments into above setup parameters.
00644 */
00645 int libburner_setup(int argc, char **argv)
00646 {
00647     int i, insuffient_parameters = 0, print_help = 0;
00648 
00649     for (i = 1; i < argc; ++i) {
00650         if (!strcmp(argv[i], "--audio")) {
00651             all_tracks_type = BURN_AUDIO;
00652 
00653         } else if (!strcmp(argv[i], "--blank_fast")) {
00654             do_blank = 1;
00655 
00656         } else if (!strcmp(argv[i], "--blank_full")) {
00657             do_blank = 2;
00658 
00659         } else if (!strcmp(argv[i], "--burn_for_real")) {
00660             simulate_burn = 0;
00661 
00662         } else if (!strcmp(argv[i], "--drive")) {
00663             ++i;
00664             if (i >= argc) {
00665                 fprintf(stderr,"--drive requires an argument\n");
00666                 return 1;
00667             } else if (strcmp(argv[i], "-") == 0) {
00668                 drive_adr[0] = 0;
00669                 driveno = -1;
00670             } else if (isdigit(argv[i][0])) {
00671                 drive_adr[0] = 0;
00672                 driveno = atoi(argv[i]);
00673             } else {
00674                 if(strlen(argv[i]) >= BURN_DRIVE_ADR_LEN) {
00675                     fprintf(stderr,"--drive address too long (max. %d)\n",
00676                             BURN_DRIVE_ADR_LEN-1);
00677                     return 2;
00678                 }
00679                 strcpy(drive_adr, argv[i]);
00680             }
00681         } else if ((!strcmp(argv[i], "--format_overwrite")) ||
00682            (!strcmp(argv[i], "--format"))) {
00683             do_blank = 101;
00684 
00685         } else if (!strcmp(argv[i], "--multi")) {
00686         do_multi = 1;
00687 
00688     } else if (!strcmp(argv[i], "--stdin_size")) { /* obsoleted */
00689         i++;
00690 
00691         } else if (!strcmp(argv[i], "--try_to_simulate")) {
00692             simulate_burn = 1;
00693 
00694         } else if (!strcmp(argv[i], "--help")) {
00695             print_help = 1;
00696 
00697         } else if (!strncmp(argv[i], "--",2)) {
00698             fprintf(stderr, "Unidentified option: %s\n", argv[i]);
00699             return 7;
00700         } else {
00701             if(strlen(argv[i]) >= 4096) {
00702                 fprintf(stderr, "Source address too long (max. %d)\n", 4096-1);
00703                 return 5;
00704             }
00705             if(source_adr_count >= 99) {
00706                 fprintf(stderr, "Too many tracks (max. 99)\n");
00707                 return 6;
00708             }
00709             strcpy(source_adr[source_adr_count], argv[i]);
00710             source_adr_count++;
00711         }
00712     }
00713     insuffient_parameters = 1;
00714     if (driveno < 0)
00715         insuffient_parameters = 0;
00716     if (source_adr_count > 0)
00717         insuffient_parameters = 0; 
00718     if (do_blank)
00719         insuffient_parameters = 0;
00720     if (print_help || insuffient_parameters ) {
00721         printf("Usage: %s\n", argv[0]);
00722         printf("       [--drive <address>|<driveno>|\"-\"]  [--audio]\n");
00723         printf("       [--blank_fast|--blank_full|--format]  [--try_to_simulate]\n");
00724         printf("       [--multi]  [<one or more imagefiles>|\"-\"]\n");
00725         printf("Examples\n");
00726         printf("A bus scan (needs rw-permissions to see a drive):\n");
00727         printf("  %s --drive -\n",argv[0]);
00728         printf("Burn a file to drive chosen by number, leave appendable:\n");
00729         printf("  %s --drive 0 --multi my_image_file\n", argv[0]);
00730         printf("Burn a file to drive chosen by persistent address, close:\n");
00731         printf("  %s --drive /dev/hdc my_image_file\n", argv[0]);
00732         printf("Blank a used CD-RW (is combinable with burning in one run):\n");
00733         printf("  %s --drive /dev/hdc --blank_fast\n",argv[0]);
00734         printf("Blank a used DVD-RW (is combinable with burning in one run):\n");
00735         printf("  %s --drive /dev/hdc --blank_full\n",argv[0]);
00736         printf("Format a DVD-RW, BD-RE or BD-R:\n");
00737         printf("  %s --drive /dev/hdc --format\n", argv[0]);
00738         printf("Burn two audio tracks (to CD only):\n");
00739         printf("  lame --decode -t /path/to/track1.mp3 track1.cd\n");
00740         printf("  test/dewav /path/to/track2.wav -o track2.cd\n");
00741         printf("  %s --drive /dev/hdc --audio track1.cd track2.cd\n", argv[0]);
00742         printf("Burn a compressed afio archive on-the-fly:\n");
00743         printf("  ( cd my_directory ; find . -print | afio -oZ - ) | \\\n");
00744         printf("  %s --drive /dev/hdc -\n", argv[0]);
00745         printf("To be read from *not mounted* media via: afio -tvZ /dev/hdc\n");
00746         if (insuffient_parameters)
00747             return 6;
00748     }
00749     return 0;
00750 }
00751 
00752 
00753 int main(int argc, char **argv)
00754 {
00755     int ret;
00756 
00757     /* A warning to programmers who start their own projekt from here. */
00758     if (sizeof(off_t) != 8) {
00759          fprintf(stderr,
00760        "\nFATAL: Compile time misconfiguration. off_t is not 64 bit.\n\n");
00761          exit(39);
00762     }
00763 
00764     ret = libburner_setup(argc, argv);
00765     if (ret)
00766         exit(ret);
00767 
00768     printf("Initializing libburnia-project.org ...\n");
00769     if (burn_initialize())
00770         printf("Done\n");
00771     else {
00772         printf("FAILED\n");
00773         fprintf(stderr,"\nFATAL: Failed to initialize.\n");
00774         exit(33);
00775     }
00776 
00777     /* Print messages of severity SORRY or more directly to stderr */
00778     burn_msgs_set_severities("NEVER", "SORRY", "libburner : ");
00779 
00780     /* Activate the synchronous signal handler which eventually will try to
00781        properly shutdown drive and library on aborting events. */
00782     burn_set_signal_handling("libburner : ", NULL, 0x0);
00783 
00784     /** Note: driveno might change its value in this call */
00785     ret = libburner_aquire_drive(drive_adr, &driveno);
00786     if (ret<=0) {
00787         fprintf(stderr,"\nFATAL: Failed to acquire drive.\n");
00788         { ret = 34; goto finish_libburn; }
00789     }
00790     if (ret == 2)
00791         { ret = 0; goto release_drive; }
00792     if (do_blank) {
00793         if (do_blank > 100)
00794             ret = libburner_format(drive_list[driveno].drive);
00795         else
00796             ret = libburner_blank_disc(drive_list[driveno].drive,
00797                             do_blank == 1);
00798         if (ret<=0)
00799             { ret = 36; goto release_drive; }
00800     }
00801     if (source_adr_count > 0) {
00802         ret = libburner_payload(drive_list[driveno].drive,
00803                 source_adr, source_adr_count,
00804                 do_multi, simulate_burn, all_tracks_type);
00805         if (ret<=0)
00806             { ret = 38; goto release_drive; }
00807     }
00808     ret = 0;
00809 release_drive:;
00810     if (drive_is_grabbed)
00811         burn_drive_release(drive_list[driveno].drive, 0);
00812 
00813 finish_libburn:;
00814     if (burn_is_aborting(0) > 0) {
00815         burn_abort(4400, burn_abort_pacifier, "libburner : ");
00816         fprintf(stderr,"\nlibburner run aborted\n");
00817         exit(1);
00818     } 
00819     /* This app does not bother to know about exact scan state. 
00820        Better to accept a memory leak here. We are done anyway. */
00821     /* burn_drive_info_free(drive_list); */
00822     burn_finish();
00823     exit(ret);
00824 }
00825 
00826 
00827 /*  License and copyright aspects:
00828 
00829 This all is provided under GPL.
00830 Read. Try. Think. Play. Write yourself some code. Be free of my copyright.
00831 
00832 Be also invited to study the code of cdrskin/cdrskin.c et al.
00833 
00834 History:
00835 libburner is a compilation of my own contributions to test/burniso.c and
00836 fresh code which replaced the remaining parts under copyright of
00837 Derek Foreman.
00838 My respect and my thanks to Derek for providing me a start back in 2005.
00839 
00840 */
00841 

Generated for libburn by  doxygen 1.4.7