libburn/cdrskin/cdrskin.c

4231 lines
126 KiB
C

/*
cdrskin.c , Copyright 2006 Thomas Schmitt <scdbackup@gmx.net>
A cdrecord compatible command line interface for libburn.
Originally inspired by libburn-0.2/test/burniso.c
(c) Derek Foreman <derek@signalmarketing.com> and Ben Jansens <xor@orodu.net>
Provided under GPL.
This project is neither directed against original cdrecord nor does it exploit
any source code of said program. It rather tries to be an alternative method
to burn CD which is not based on the same code as cdrecord.
See also : http://scdbackup.sourceforge.net/cdrskin_eng.html
Interested users of cdrecord are encouraged to contribute further option
implementations as they need them. Contributions will get published under GPL
but it is essential that the authors allow a future release under LGPL and/or
BSD license.
There is a script test/cdrecord_spy.sh which may be installed between
the cdrecord command and real cdrecord in order to learn about the options
used by your favorite cdrecord frontend. Edit said script and install it
according to the instructions given inside.
The implementation of an option would probably consist of
- necessary structure members for structs CdrpreskiN and/or CdrskiN
- code in Cdrpreskin_setup() and Cdrskin_setup() which converts
argv[i] into CdrpreskiN/CdrskiN members (or into direct actions)
- removal of option from ignore list "ignored_partial_options" resp.
"ignored_full_options" in Cdrskin_setup()
- functions which implement the option's run time functionality
- eventually calls of those functions in Cdrskin_run()
- changes to be made within Cdrskin_burn() or Cdrskin_blank() or other
existing methods
See option blank= for an example.
------------------------------------------------------------------------------
About compliance with *strong urge* of API towards burn_drive_scan_and_grab()
For a more comprehensive example of the advised way to behave with libburn
see test/burniso.c test/blank.c test/devices.c .
cdrskin was the initiator of the whitelist functionality within libburn.
Now it has problems to obviously comply with the new API best practice
presciptions literally. Therefore this explanation:
On start it restricts the library to a single drive if it already knows the
persistent address by option dev= . This is done with a combination of
burn_drive_add_whitelist() and burn_drive_scan(). Not compliant to the
literal strong urge but in fact exactly fulfilling the reason for that
urge in the API: any scanned drive might be opened exclusively after
burn_drive_scan(). It is kernel dependent wether this behavior is on, off
or switchable. The sysdamin will want it on - but only for one drive.
So with dev=... cdrskin complies to the spirit of the strong urge.
Without dev=... it has to leave out the whitelist in order to enable bus
scanning and implicit drive address 0. A tradition of 9 months shall not
be broken. So burns without dev= will stay possible - and are now harmless.
*
Only if the new API compliance is enabled by macro Cdrskin_new_api_tesT .
There is a flaw to be solved in libburn until new API compliance is in all
aspects as good as the old handcrafted attempt to be sysadmin friendly.
In many aspects it is already superior, note well.
As soon as the flaw http://libburn.pykix.org/ticket/10 is removed,
cdrskin will switch to its new way of API best practice compliance.
*
This is because Cdrskin_grab_drive() enforces a restart of the library
with the desired drive's persistent address. This restart then really uses
the strongly urged function burn_drive_scan_and_grab().
Thus, cdrskin complies with the new spirit of API by closing down libburn
as soon as the persistent drive address is known and the drive is to be
used with a long running operation. To my knowlege all long running
operations in cdrskin need a grabbed drive.
This spaghetti approach seems necessary to keep small the impact of new API
urge on cdrskin's stability. cdrskin suffers from having donated the body
parts which have been transplanted to libburn in order to create
burn_drive_scan_and_grab() . The desired sysadmin friendlyness was already
achieved by most cdrskin runs. The remaining problem situations should now
be defused by releasing any short time grabbed flocks of drives during the
restart of libburn.
------------------------------------------------------------------------------
Compilation within cdrskin-* :
cd cdrskin
cc -g -I.. -DCdrskin_build_timestamP='...' \
-o cdrskin cdrskin.c cdrfifo.c cleanup.c \
-L../libburn/.libs -lburn -lpthread
or
cd ..
cc -g -I. -DCdrskin_build_timestamP='...' \
-o cdrskin/cdrskin cdrskin/cdrskin.c cdrskin/cdrfifo.c cdrskin/cleanup.c \
libburn/async.o libburn/crc.o libburn/debug.o libburn/drive.o \
libburn/file.o libburn/init.o libburn/lec.o libburn/message.o \
libburn/mmc.o libburn/options.o libburn/sbc.o libburn/sector.o \
libburn/sg.o libburn/spc.o libburn/source.o libburn/structure.o \
libburn/toc.o libburn/util.o libburn/write.o \
-lpthread
*/
/** The official program version */
#define Cdrskin_prog_versioN "0.1.5"
/** The source code release timestamp */
#include "cdrskin_timestamp.h"
#ifndef Cdrskin_timestamP
#define Cdrskin_timestamP "-none-given-"
#endif
/** The binary build timestamp is to be set externally by the compiler */
#ifndef Cdrskin_build_timestamP
#define Cdrskin_build_timestamP "-none-given-"
#endif
/** use this to accomodate to the CVS version as of Dec 8, 2005
#define Cdrskin_libburn_cvs_A51208_tS 1
*/
#ifdef Cdrskin_libburn_cvs_A51208_tS
#define Cdrskin_libburn_versioN "0.2.tsA51208"
#define Cdrskin_libburn_p_sectoR 1
/* forever: */
#define Cdrskin_libburn_no_burn_preset_device_opeN 1
#endif
/** use this to accomodate to the CVS version as of Feb 20, 2006
#define Cdrskin_libburn_cvs_A60220_tS 1
*/
#ifdef Cdrskin_libburn_cvs_A60220_tS
#define Cdrskin_libburn_versioN "0.2.tsA60220"
#define Cdrskin_libburn_p_sectoR 1
#define Cdrskin_libburn_with_fd_sourcE 1
#define Cdrskin_libburn_largefilE 1
#define Cdrskin_libburn_padding_does_worK 1
#define Cdrskin_libburn_no_burn_preset_device_opeN 1
#endif /* Cdrskin_libburn_cvs_A60220_tS */
#ifdef Cdrskin_libburn_0_2_1
#define Cdrskin_libburn_versioN "0.2.1"
#define Cdrskin_libburn_p_sectoR 1
#define Cdrskin_libburn_with_fd_sourcE 1
#define Cdrskin_libburn_largefilE 1
#define Cdrskin_libburn_padding_does_worK 1
#ifdef Cdrskin_new_api_tesT
#define Cdrskin_libburn_has_drive_get_adR 1
#endif
#endif /* Cdrskin_libburn_0_2_1 */
#ifndef Cdrskin_libburn_versioN
#define Cdrskin_libburn_versioN "0.2.1"
#endif
#ifdef Cdrskin_libburn_largefilE
#ifndef _LARGEFILE_SOURCE
#define _LARGEFILE_SOURCE 1
#endif
#ifndef _FILE_OFFSET_BITS
#define _FILE_OFFSET_BITS 64
#endif
#endif /* Cdrskin_libburn_largefilE */
/* These macros activate cdrskin workarounds for deficiencies resp.
problematic features of libburn which hopefully will change in
future. */
/** Work around the fact that padding is not performed by libburn */
#ifndef Cdrskin_libburn_padding_does_worK
#define Cdrskin_burn_track_padding_brokeN 1
#endif
/** Work around the fact that neither /dev/sg0 (kernel 2.4 + ide-scsi) nor
/dev/hdc (kernel 2.6) get ejected by libburn */
#define Cdrskin_burn_drive_eject_brokeN 1
/** Work around the fact that after loading media speed report is wrong */
#define Cdrskin_atip_speed_brokeN 1
/** Work around the fact that burn_drive_get_status() always reports to do
track 0 */
#define Cdrskin_progress_track_brokeN 1
/** Work around the fact that a drive interrupted at burn_drive_grab() never
leaves status BURN_DRIVE_GRABBING */
#define Cdrskin_grab_abort_brokeN 1
/** A macro which is able to eat up a function call like printf() */
#ifdef Cdrskin_extra_leaN
#define ClN(x)
#else
#define ClN(x) x
#endif
#include <stdio.h>
#include <assert.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <string.h>
#include <stdlib.h>
#include <errno.h>
#include <sys/time.h>
#include <libburn/libburn.h>
#include "cleanup.h"
/** The size of a string buffer for pathnames and similar texts */
#define Cdrskin_strleN 4096
/** The maximum length +1 of a drive address */
#ifdef Cdrskin_new_api_tesT
#define Cdrskin_adrleN BURN_DRIVE_ADR_LEN
#else
#define Cdrskin_adrleN 80
#endif
/* --------------------------------------------------------------------- */
/* Imported from scdbackup-0.8.5/src/cd_backup_planer.c */
/** Macro for creation of arrays of objects (or single objects) */
#define TSOB_FELD(typ,anz) (typ *) malloc((anz)*sizeof(typ));
/** Convert a text so that eventual characters special to the shell are
made literal. Note: this does not make a text terminal-safe !
@param in_text The text to be converted
@param out_text The buffer for the result.
It should have size >= strlen(in_text)*5+2
@param flag Unused yet
@return For convenience out_text is returned
*/
char *Text_shellsafe(char *in_text, char *out_text, int flag)
{
int l,i,w=0;
/* enclose everything by hard quotes */
l= strlen(in_text);
out_text[w++]= '\'';
for(i=0;i<l;i++){
if(in_text[i]=='\''){
/* escape hard quote within the text */
out_text[w++]= '\'';
out_text[w++]= '"';
out_text[w++]= '\'';
out_text[w++]= '"';
out_text[w++]= '\'';
} else {
out_text[w++]= in_text[i];
}
}
out_text[w++]= '\'';
out_text[w++]= 0;
return(out_text);
}
/** Convert a text into a number of type double and multiply it by unit code
[kmgtpe] (2^10 to 2^60) or [s] (2048). (Also accepts capital letters.)
@param text Input like "42", "2k", "3.14m" or "-1g"
@param flag Bitfield for control purposes:
bit0= return -1 rathern than 0 on failure
@return The derived double value
*/
double Scanf_io_size(char *text, int flag)
/*
bit0= default value -1 rather than 0
*/
{
int c;
double ret= 0.0;
if(flag&1)
ret= -1.0;
if(text[0]==0)
return(ret);
sscanf(text,"%lf",&ret);
c= text[strlen(text)-1];
if(c=='k' || c=='K') ret*= 1024.0;
if(c=='m' || c=='M') ret*= 1024.0*1024.0;
if(c=='g' || c=='G') ret*= 1024.0*1024.0*1024.0;
if(c=='t' || c=='T') ret*= 1024.0*1024.0*1024.0*1024.0;
if(c=='p' || c=='P') ret*= 1024.0*1024.0*1024.0*1024.0*1024.0;
if(c=='e' || c=='E') ret*= 1024.0*1024.0*1024.0*1024.0*1024.0*1024.0;
if(c=='s' || c=='S') ret*= 2048.0;
return(ret);
}
/** Return a double representing seconds and microseconds since 1 Jan 1970 */
double Sfile_microtime(int flag)
{
struct timeval tv;
struct timezone tz;
gettimeofday(&tv,&tz);
return((double) (tv.tv_sec+1.0e-6*tv.tv_usec));
}
#ifndef Cdrskin_extra_leaN
/** Read a line from fp and strip LF or CRLF */
char *Sfile_fgets(char *line, int maxl, FILE *fp)
{
int l;
char *ret;
ret= fgets(line,maxl,fp);
if(ret==NULL) return(NULL);
l= strlen(line);
if(l>0) if(line[l-1]=='\r') line[--l]= 0;
if(l>0) if(line[l-1]=='\n') line[--l]= 0;
if(l>0) if(line[l-1]=='\r') line[--l]= 0;
return(ret);
}
/** Destroy a synthetic argument array */
int Sfile_destroy_argv(int *argc, char ***argv, int flag)
{
int i;
if(*argc>0 && *argv!=NULL){
for(i=0;i<*argc;i++){
if((*argv)[i]!=NULL)
free((*argv)[i]);
}
free((char *) *argv);
}
*argc= 0;
*argv= NULL;
return(1);
}
/** Read a synthetic argument array from a list of files.
@param progname The content for argv[0]
@param filenames The paths of the filex from where to read
@param filenamecount The number of paths in filenames
@param argc Returns the number of read arguments (+1 for progname)
@param argv Returns the array of synthetic arguments
@param argidx Returns source file indice of argv[] items
@param arglno Returns source file line numbers of argv[] items
@param flag Bitfield for control purposes:
bit0= read progname as first argument from line
bit1= just release argument array argv and return
bit2= tolerate failure to open file
@return 1=ok , 0=cannot open file , -1=cannot create memory objects
*/
int Sfile_multi_read_argv(char *progname, char **filenames, int filename_count,
int *argc, char ***argv, int **argidx, int **arglno,
int flag)
{
int ret,i,pass,maxl=0,l,argcount=0,line_no;
char buf[Cdrskin_strleN];
FILE *fp= NULL;
Sfile_destroy_argv(argc,argv,0);
if(flag&2)
return(1);
if((*argidx)!=NULL)
free((char *) *argidx);
if((*arglno)!=NULL)
free((char *) *arglno);
*argidx= *arglno= NULL;
for(pass=0;pass<2;pass++) {
if(!(flag&1)){
argcount= 1;
if(pass==0)
maxl= strlen(progname)+1;
else {
(*argv)[0]= (char *) malloc(strlen(progname)+1);
if((*argv)[0]==NULL)
{ret= -1; goto ex;}
strcpy((*argv)[0],progname);
}
} else {
argcount= 0;
if(pass==0)
maxl= 1;
}
for(i=0; i<filename_count;i++) {
if(strlen(filenames[i])==0)
continue;
fp= fopen(filenames[i],"rb");
if(fp==NULL) {
if(flag&4)
continue;
{ret= 0; goto ex;}
}
line_no= 0;
while(Sfile_fgets(buf,sizeof(buf)-1,fp)!=NULL) {
line_no++;
l= strlen(buf);
if(pass==0){
if(l>maxl)
maxl= l;
} else {
if(argcount >= *argc)
break;
(*argv)[argcount]= (char *) malloc(l+1);
if((*argv)[argcount]==NULL)
{ret= -1; goto ex;}
strcpy((*argv)[argcount],buf);
(*argidx)[argcount]= i;
(*arglno)[argcount]= line_no;
}
argcount++;
}
fclose(fp); fp= NULL;
}
if(pass==0){
*argc= argcount;
if(argcount>0) {
*argv= (char **) malloc(argcount*sizeof(char *));
*argidx= (int *) malloc(argcount*sizeof(int));
*arglno= (int *) malloc(argcount*sizeof(int));
if(*argv==NULL || *argidx==NULL || *arglno==NULL)
{ret= -1; goto ex;}
}
for(i=0;i<*argc;i++) {
(*argv)[i]= NULL;
(*argidx)[i]= -1;
(*arglno)[i]= -1;
}
}
}
ret= 1;
ex:;
if(fp!=NULL)
fclose(fp);
return(ret);
}
/** Combine environment variable HOME with given filename
@param filename Address relative to $HOME
@param fileadr Resulting combined address
@param fa_size Size of array fileadr
@param flag Unused yet
@return 1=ok , 0=no HOME variable , -1=result address too long
*/
int Sfile_home_adr_s(char *filename, char *fileadr, int fa_size, int flag)
{
char *home;
strcpy(fileadr,filename);
home= getenv("HOME");
if(home==NULL)
return(0);
if(strlen(home)+strlen(filename)+1>=fa_size)
return(-1);
strcpy(fileadr,home);
if(filename[0]!=0){
strcat(fileadr,"/");
strcat(fileadr,filename);
}
return(1);
}
#endif /* ! Cdrskin_extra_leaN */
/* --------------------------------------------------------------------- */
/** Address translation table for users/applications which do not look
for the output of -scanbus but guess a Bus,Target,Lun on their own.
*/
/** The maximum number of entries in the address translation table */
#define Cdradrtrn_leN 256
/** The address prefix which will prevent translation */
#define Cdrskin_no_transl_prefiX "LITERAL_ADR:"
struct CdradrtrN {
char *from_address[Cdradrtrn_leN];
char *to_address[Cdradrtrn_leN];
int fill_counter;
};
#ifndef Cdrskin_extra_leaN
/** Create a device address translator object */
int Cdradrtrn_new(struct CdradrtrN **trn, int flag)
{
struct CdradrtrN *o;
int i;
(*trn)= o= TSOB_FELD(struct CdradrtrN,1);
if(o==NULL)
return(-1);
for(i= 0;i<Cdradrtrn_leN;i++) {
o->from_address[i]= NULL;
o->to_address[i]= NULL;
}
o->fill_counter= 0;
return(1);
}
/** Release from memory a device address translator object */
int Cdradrtrn_destroy(struct CdradrtrN **o, int flag)
{
int i;
struct CdradrtrN *trn;
trn= *o;
if(trn==NULL)
return(0);
for(i= 0;i<trn->fill_counter;i++) {
if(trn->from_address[i]!=NULL)
free(trn->from_address[i]);
if(trn->to_address[i]!=NULL)
free(trn->to_address[i]);
}
free((char *) trn);
*o= NULL;
return(1);
}
/** Add a translation pair to the table
@param trn The translator which shall learn
@param from The user side address
@param to The cdrskin side address
@param flag Bitfield for control purposes:
bit0= "from" contains from+to address, to[0] contains delimiter
*/
int Cdradrtrn_add(struct CdradrtrN *trn, char *from, char *to, int flag)
{
char buf[2*Cdrskin_adrleN+1],*from_pt,*to_pt;
int cnt;
cnt= trn->fill_counter;
if(cnt>=Cdradrtrn_leN)
return(-1);
if(flag&1) {
if(strlen(from)>=sizeof(buf))
return(0);
strcpy(buf,from);
to_pt= strchr(buf,to[0]);
if(to_pt==NULL)
return(0);
*(to_pt)= 0;
from_pt= buf;
to_pt++;
} else {
from_pt= from;
to_pt= to;
}
if(strlen(from)>=Cdrskin_adrleN || strlen(to)>=Cdrskin_adrleN)
return(0);
trn->from_address[cnt]= malloc(strlen(from_pt)+1);
trn->to_address[cnt]= malloc(strlen(to_pt)+1);
if(trn->from_address[cnt]==NULL ||
trn->to_address[cnt]==NULL)
return(-2);
strcpy(trn->from_address[cnt],from_pt);
strcpy(trn->to_address[cnt],to_pt);
trn->fill_counter++;
return(1);
}
/** Apply eventual device address translation
@param trn The translator
@param from The address from which to translate
@param driveno With backward translation only: The libburn drive number
@param to The result of the translation
@param flag Bitfield for control purposes:
bit0= translate backward
@return <=0 error, 1=no translation found, 2=translation found,
3=collision avoided
*/
int Cdradrtrn_translate(struct CdradrtrN *trn, char *from, int driveno,
char to[Cdrskin_adrleN], int flag)
{
int i,ret= 1;
char *adr;
to[0]= 0;
adr= from;
if(flag&1)
goto backward;
if(strncmp(adr,Cdrskin_no_transl_prefiX,
strlen(Cdrskin_no_transl_prefiX))==0) {
adr= adr+strlen(Cdrskin_no_transl_prefiX);
ret= 2;
} else {
for(i=0;i<trn->fill_counter;i++)
if(strcmp(adr,trn->from_address[i])==0)
break;
if(i<trn->fill_counter) {
adr= trn->to_address[i];
ret= 2;
}
}
if(strlen(adr)>=Cdrskin_adrleN)
return(-1);
strcpy(to,adr);
return(ret);
backward:;
if(strlen(from)>=Cdrskin_adrleN)
sprintf(to,"%s%d",Cdrskin_no_transl_prefiX,driveno);
else
strcpy(to,from);
for(i=0;i<trn->fill_counter;i++)
if(strcmp(from,trn->to_address[i])==0 &&
strlen(trn->from_address[i])<Cdrskin_adrleN)
break;
if(i<trn->fill_counter) {
ret= 2;
strcpy(to,trn->from_address[i]);
} else {
for(i=0;i<trn->fill_counter;i++)
if(strcmp(from,trn->from_address[i])==0)
break;
if(i<trn->fill_counter)
if(strlen(from)+strlen(Cdrskin_no_transl_prefiX)<Cdrskin_adrleN) {
ret= 3;
sprintf(to,"%s%s",Cdrskin_no_transl_prefiX,from);
}
}
return(ret);
}
#endif /* Cdrskin_extra_leaN */
/* --------------------------------------------------------------------- */
#ifndef Cdrskin_extra_leaN
/* Program is to be linked with cdrfifo.c */
#include "cdrfifo.h"
#else /* ! Cdrskin_extra_leaN */
/* Dummy */
struct CdrfifO {
int dummy;
};
#endif /* Cdrskin_extra_leaN */
/* --------------------------------------------------------------------- */
/** This structure represents a track resp. a data source */
struct CdrtracK {
struct CdrskiN *boss;
int trackno;
char source_path[Cdrskin_strleN];
int source_fd;
double fixed_size;
double padding;
int track_type;
/** Optional fifo between input fd and libburn. It uses a pipe(2) to transfer
data to libburn.
*/
int fifo_enabled;
/** The fifo object knows the real input fd and the fd[1] of the pipe. */
struct CdrfifO *fifo;
/** fd[0] of the fifo pipe. This is from where libburn reads its data. */
int fifo_outlet_fd;
int fifo_size;
int fifo_start_empty;
struct burn_track *libburn_track;
};
int Cdrtrack_destroy(struct CdrtracK **o, int flag);
/** Create a track resp. data source object.
@param track Returns the address of the new object.
@param boss The cdrskin control object (corresponds to session)
@param trackno The index in the cdrskin tracklist array (is not constant)
@param flag Bitfield for control purposes:
bit0= set fifo_start_empty to 1
*/
int Cdrtrack_new(struct CdrtracK **track, struct CdrskiN *boss,
int trackno, int flag)
{
struct CdrtracK *o;
int ret;
int Cdrskin_get_source(struct CdrskiN *skin, char *source_path,
double *fixed_size, double *padding,
int *track_type, int flag);
int Cdrskin_get_fifo_par(struct CdrskiN *skin, int *fifo_enabled,
int *fifo_size, int *fifo_start_empty, int flag);
(*track)= o= TSOB_FELD(struct CdrtracK,1);
if(o==NULL)
return(-1);
o->boss= boss;
o->trackno= trackno;
o->source_path[0]= 0;
o->source_fd= -1;
o->fixed_size= 0.0;
o->padding= 0.0;
o->track_type= BURN_MODE1;
o->fifo_enabled= 0;
o->fifo= NULL;
o->fifo_outlet_fd= -1;
o->fifo_size= 0;
o->fifo_start_empty= 0;
o->libburn_track= NULL;
ret= Cdrskin_get_source(boss,o->source_path,&(o->fixed_size),&(o->padding),
&(o->track_type),0);
if(ret<=0)
goto failed;
#ifndef Cdrskin_extra_leaN
ret= Cdrskin_get_fifo_par(boss, &(o->fifo_enabled),&(o->fifo_size),
&(o->fifo_start_empty),0);
if(ret<=0)
goto failed;
#endif /* ! Cdrskin_extra_leaN */
if(flag&1)
o->fifo_start_empty= 1;
return(1);
failed:;
Cdrtrack_destroy(track,0);
return(-1);
}
/** Release from memory a track object previously created by Cdrtrack_new() */
int Cdrtrack_destroy(struct CdrtracK **o, int flag)
{
struct CdrtracK *track;
track= *o;
if(track==NULL)
return(0);
#ifndef Cdrskin_extra_leaN
Cdrfifo_destroy(&(track->fifo),0);
#endif
if(track->libburn_track!=NULL)
burn_track_free(track->libburn_track);
free((char *) track);
*o= NULL;
return(1);
}
int Cdrtrack_get_size(struct CdrtracK *track, double *size, double *padding,
int flag)
{
*size= track->fixed_size;
*padding= track->padding;
return(1);
}
int Cdrtrack_get_fifo(struct CdrtracK *track, struct CdrfifO **fifo, int flag)
{
*fifo= track->fifo;
return(1);
}
/** Deliver an open file descriptor corresponding to the source path of track.
@return <=0 error, 1 success
*/
int Cdrtrack_open_source_path(struct CdrtracK *track, int *fd, int flag)
{
if(track->source_path[0]=='-' && track->source_path[1]==0)
*fd= 0;
else if(track->source_path[0]=='#' &&
(track->source_path[1]>='0' && track->source_path[1]<='9'))
*fd= atoi(track->source_path+1);
else {
*fd= open(track->source_path,O_RDONLY);
if(*fd==-1) {
fprintf(stderr,"cdrskin: failed to open source address '%s'\n",
track->source_path);
fprintf(stderr,"cdrskin: errno=%d , \"%s\"\n",errno,
errno==0?"-no error code available-":strerror(errno));
} else {
if(track->fixed_size<=0) {
struct stat stbuf;
if(fstat(*fd,&stbuf)!=-1)
track->fixed_size= stbuf.st_size;
}
}
}
track->source_fd= *fd;
return(*fd>=0);
}
#ifndef Cdrskin_extra_leaN
/** Install a fifo object between data source and libburn.
Its parameters are known to track.
@param outlet_fd Returns the filedescriptor of the fifo outlet.
@param previous_fifo Object address for chaining or follow-up attachment.
@param flag Bitfield for control purposes:
bit0= Debugging verbosity
bit1= Do not create and attach a new fifo
but attach new follow-up fd pair to previous_fifo
@return <=0 error, 1 success
*/
int Cdrtrack_attach_fifo(struct CdrtracK *track, int *outlet_fd,
struct CdrfifO *previous_fifo, int flag)
{
struct CdrfifO *ff;
int source_fd,pipe_fds[2],ret;
*outlet_fd= -1;
if(track->fifo_size<=0)
return(2);
ret= Cdrtrack_open_source_path(track,&source_fd,0);
if(ret<=0)
return(ret);
if(pipe(pipe_fds)==-1)
return(0);
Cdrfifo_destroy(&(track->fifo),0);
if(flag&2) {
ret= Cdrfifo_attach_follow_up_fds(previous_fifo,source_fd,pipe_fds[1],0);
if(ret<=0)
return(ret);
} else {
ret= Cdrfifo_new(&ff,source_fd,pipe_fds[1],2048,track->fifo_size,0);
if(ret<=0)
return(ret);
if(previous_fifo!=NULL)
Cdrfifo_attach_peer(previous_fifo,ff,0);
track->fifo= ff;
}
track->fifo_outlet_fd= pipe_fds[0];
if(flag&1)
printf(
"cdrskin_debug: track %d fifo replaced source_address '%s' by '#%d'\n",
track->trackno+1,track->source_path,track->fifo_outlet_fd);
sprintf(track->source_path,"#%d",track->fifo_outlet_fd);
track->source_fd= track->fifo_outlet_fd;
*outlet_fd= track->fifo_outlet_fd;
return(1);
}
/** Read data into the fifo until either it is full or the data source is
exhausted.
@return <=0 error, 1 success
*/
int Cdrtrack_fill_fifo(struct CdrtracK *track, int flag)
{
int ret;
if(track->fifo==NULL || track->fifo_start_empty)
return(2);
printf("Waiting for reader process to fill input buffer ... ");
fflush(stdout);
ret= Cdrfifo_fill(track->fifo,0);
if(ret<=0)
return(ret);
return(1);
}
#endif /* ! Cdrskin_extra_leaN */
/** Create a corresponding libburn track object and add it to the libburn
session. This may change the trackno index set by Cdrtrack_new().
*/
int Cdrtrack_add_to_session(struct CdrtracK *track, int trackno,
struct burn_session *session, int flag)
/*
bit0= debugging verbosity
bit1= apply padding hack
*/
{
struct burn_track *tr;
struct burn_source *src= NULL;
double padding,lib_padding;
int ret;
#ifdef Cdrskin_libburn_with_fd_sourcE
double fixed_size;
int source_fd;
#endif /* ! Cdrskin_libburn_with_fd_sourcE */
track->trackno= trackno;
tr= burn_track_create();
track->libburn_track= tr;
if(track->padding>0)
padding= track->padding;
else
padding= 0.0;
if(flag&2)
lib_padding= 0.0;
else
lib_padding= padding;
if(flag&1)
ClN(fprintf(stderr,
"cdrskin_debug: track %d telling burn_track_define_data() to pad %.f bytes\n",
trackno+1,lib_padding));
burn_track_define_data(tr,0,(int) lib_padding,1,track->track_type);
#ifdef Cdrskin_libburn_with_fd_sourcE
if(track->source_fd==-1) {
ret= Cdrtrack_open_source_path(track,&source_fd,0);
if(ret<=0)
goto ex;
}
fixed_size= track->fixed_size;
if((flag&2) && track->padding>0) {
if(flag&1)
ClN(fprintf(stderr,"cdrskin_debug: padding hack : %.f + %.f = %.f\n",
track->fixed_size,track->padding,
track->fixed_size+track->padding));
fixed_size+= track->padding;
}
src= burn_fd_source_new(track->source_fd,-1,(off_t) fixed_size);
#else
src = burn_file_source_new3(track->source_path,NULL,(int) track->fixed_size);
#endif /* ! Cdrskin_libburn_with_fd_sourcE */
assert(src);
#ifndef Cdrskin_libburn_with_fd_sourcE
track->fixed_size= burn_source_get_size(src);
if((flag&2) && track->padding>0) {
if(flag&1)
ClN(fprintf(stderr,
"cdrskin_debug: padding : %.f + %.f = %.f\n",
(double) burn_source_get_size(src),track->padding,
((double) burn_source_get_size(src))+track->padding));
src->fixed_size = burn_source_get_size(src) + track->padding;
if(flag&1)
ClN(fprintf(stderr,"cdrskin_debug: source size now : %.f\n",
(double) burn_source_get_size(src)));
}
#endif /* ! Cdrskin_libburn_with_fd_sourcE */
if(burn_track_set_source(tr,src) != BURN_SOURCE_OK)
{ret= 0; goto ex;}
burn_session_add_track(session,tr,BURN_POS_END);
ret= 1;
ex:
if(src!=NULL)
burn_source_free(src);
return(ret);
}
/** Release libburn track information after a session is done */
int Cdrtrack_cleanup(struct CdrtracK *track, int flag)
{
if(track->libburn_track==NULL)
return(0);
burn_track_free(track->libburn_track);
track->libburn_track= NULL;
return(1);
}
#ifndef Cdrskin_extra_leaN
/** Try to read bytes from the track's fifo outlet and eventually discard
them. Not to be called unless the track is completely written.
*/
int Cdrtrack_has_input_left(struct CdrtracK *track, int flag)
{
struct timeval wt;
fd_set rds,wts,exs;
int ready,ret;
char buf[2];
if(track->fifo_outlet_fd<=0)
return(0);
FD_ZERO(&rds);
FD_ZERO(&wts);
FD_ZERO(&exs);
FD_SET(track->fifo_outlet_fd,&rds);
wt.tv_sec= 0;
wt.tv_usec= 0;
ready= select(track->fifo_outlet_fd+1,&rds,&wts,&exs,&wt);
if(ready<=0)
return(0);
ret= read(track->fifo_outlet_fd,buf,1);
if(ret>0)
return(1);
return(0);
}
#endif /* ! Cdrskin_extra_leaN */
/* --------------------------------------------------------------------- */
/** The list of startup file names */
#define Cdrpreskin_rc_nuM 3
static char Cdrpreskin_sys_rc_nameS[Cdrpreskin_rc_nuM][80]= {
"/etc/default/cdrskin",
"/etc/opt/cdrskin/rc",
"placeholder for $HOME/.cdrskinrc"
};
/** A structure which bundles several parameters for initialization of
libburn and creation of the CdrskiN object. It finally becomes a managed
subordinate of the CdrskiN object.
*/
struct CdrpreskiN {
/** Stores eventually given absolute device address before translation */
char raw_device_adr[Cdrskin_adrleN];
/** Stores an eventually given translated absolute device address between
Cdrpreskin_setup() and Cdrskin_create() .
*/
char device_adr[Cdrskin_adrleN];
/** The eventual address translation table */
struct CdradrtrN *adr_trn;
/** Memorizes the abort handling mode from presetup to creation of
control object. Defined handling modes are:
0= no abort handling
1= try to cancel, release, exit (leave signal mode as set by caller)
2= try to ignore all signals
3= mode 1 in normal operation, mode 2 during abort handling
4= mode 1 in normal operation, mode 0 during abort handling
-1= install abort handling 1 only in Cdrskin_burn() after burning started
*/
int abort_handler;
/** Wether to allow getuid()!=geteuid() */
int allow_setuid;
/** Wether to allow user provided addresses like #4 */
int allow_fd_source;
/** Wether an option is given which needs a full bus scan */
int no_whitelist;
/** Wether bus scans shall exit!=0 if no drive was found */
int scan_demands_drive;
/** Wether to abort when a busy drive is encountered during bus scan */
int abort_on_busy_drive;
/** Wether to try to avoid collisions when opening drives */
int drive_exclusive;
/** Wether to try to wait for unwilling drives to become willing to open */
int drive_blocking;
#ifndef Cdrskin_extra_leaN
/** List of startupfiles */
char rc_filenames[Cdrpreskin_rc_nuM][Cdrskin_strleN];
int rc_filename_count;
/** Non-argument options from startupfiles */
int pre_argc;
char **pre_argv;
int *pre_argidx;
int *pre_arglno;
#endif /* ! Cdrskin_extra_leaN */
};
/** Create a preliminary cdrskin program run control object. It will become
part of the final control object.
@param preskin Returns pointer to resulting
@param flag Bitfield for control purposes: unused yet
@return <=0 error, 1 success
*/
int Cdrpreskin_new(struct CdrpreskiN **preskin, int flag)
{
struct CdrpreskiN *o;
int i;
(*preskin)= o= TSOB_FELD(struct CdrpreskiN,1);
if(o==NULL)
return(-1);
o->raw_device_adr[0]= 0;
o->device_adr[0]= 0;
o->adr_trn= NULL;
o->abort_handler= 3;
o->allow_setuid= 0;
o->allow_fd_source= 0;
o->no_whitelist= 0;
o->scan_demands_drive= 0;
o->abort_on_busy_drive= 0;
o->drive_exclusive= 1;
o->drive_blocking= 0;
#ifndef Cdrskin_extra_leaN
o->rc_filename_count= Cdrpreskin_rc_nuM;
for(i=0;i<o->rc_filename_count-1;i++)
strcpy(o->rc_filenames[i],Cdrpreskin_sys_rc_nameS[i]);
o->rc_filenames[o->rc_filename_count-1][0]= 0;
o->pre_argc= 0;
o->pre_argv= NULL;
o->pre_argidx= NULL;
o->pre_arglno= NULL;
#endif /* ! Cdrskin_extra_leaN */
return(1);
}
int Cdrpreskin_destroy(struct CdrpreskiN **preskin, int flag)
{
struct CdrpreskiN *o;
o= *preskin;
if(o==NULL)
return(0);
#ifndef Cdrskin_extra_leaN
if((o->pre_arglno)!=NULL)
free((char *) o->pre_arglno);
if((o->pre_argidx)!=NULL)
free((char *) o->pre_argidx);
if(o->pre_argc>0 && o->pre_argv!=NULL)
Sfile_destroy_argv(&(o->pre_argc),&(o->pre_argv),0);
Cdradrtrn_destroy(&(o->adr_trn),0);
#endif /* ! Cdrskin_extra_leaN */
free((char *) o);
*preskin= NULL;
return(1);
}
/** Convert a cdrecord-style device address into a libburn device address or
into a libburn drive number. It depends on the "scsibus" number of the
cdrecord-style address which kind of libburn address emerges:
bus=0 : drive number , bus=1 : /dev/sgN , bus=2 : /dev/hdX
(This call intentionally has no CdrpreskiN argument)
@return <=0 error, 1 success
*/
int Cdrpreskin__cdrecord_to_dev(char *adr, char device_adr[Cdrskin_adrleN],
int *driveno, int flag)
{
int comma_seen= 0,digit_seen= 0,busno= 0,k;
*driveno= -1;
device_adr[0]= 0;
if(strlen(adr)==0)
return(0);
/* read the trailing numeric string as device address code */
/* accepts "1" , "0,1,0" , "ATA:0,1,0" , ... */
for(k= strlen(adr)-1;k>=0;k--) {
if(adr[k]==',' && !comma_seen) {
comma_seen= 1;
digit_seen= 0;
continue;
}
if(adr[k]<'0' || adr[k]>'9')
break;
digit_seen= 1;
}
if(!digit_seen) {
k= strlen(adr)-1;
if(adr[k]==':' || (adr[k]>='A' && adr[k]<='Z')) { /* empty prefix ? */
*driveno= 0;
return(1);
}
return(0);
}
sscanf(adr+k+1,"%d",driveno);
/* look for symbolic bus : 1=/dev/sgN 2=/dev/hdX */
digit_seen= 0;
if(k>0) if(adr[k]==',') {
for(k--;k>=0;k--) {
if(adr[k]<'0' || adr[k]>'9')
break;
digit_seen= 1;
}
if(digit_seen) {
sscanf(adr+k+1,"%d",&busno);
if(busno==1) {
sprintf(device_adr,"/dev/sg%d",*driveno);
} else if(busno==2) {
sprintf(device_adr,"/dev/hd%c",'a'+(*driveno));
} else if(busno!=0) {
fprintf(stderr,
"cdrskin: FATAL : dev=[Prefix:]Bus,Target,Lun expects Bus out of {0,1,2}\n");
return(0);
}
}
}
return(1);
}
#ifndef Cdrskin_extra_leaN
/** Load content startup files into preskin cache */
int Cdrpreskin_read_rc(struct CdrpreskiN *o, char *progname,int flag)
{
int ret,i;
char *filenames_v[3];
for(i=0;i<o->rc_filename_count;i++)
filenames_v[i]= o->rc_filenames[i];
Sfile_home_adr_s(".cdrskinrc",o->rc_filenames[o->rc_filename_count-1],
Cdrskin_strleN,0);
ret= Sfile_multi_read_argv(progname,filenames_v,o->rc_filename_count,
&(o->pre_argc),&(o->pre_argv),
&(o->pre_argidx),&(o->pre_arglno),4);
return(ret);
}
#endif /* ! Cdrskin_extra_leaN */
/** Interpret those arguments which do not need libburn or which influence the
startup of libburn and/or the creation of the CdrskiN object. This is run
before libburn gets initialized and before Cdrskin_new() is called.
Options which need libburn or a CdrskiN object are processed in a different
function named Cdrskin_setup().
@param flag Bitfield for control purposes:
bit0= do not finalize setup
bit1= do not read and interpret rc files
@return <=0 error, 1 success , 2 end program run with exit value 0
*/
int Cdrpreskin_setup(struct CdrpreskiN *o, int argc, char **argv, int flag)
/*
return:
<=0 error
1 ok
2 end program run (--help)
*/
{
int i,ret;
char *value_pt;
#ifndef Cdrskin_extra_leaN
if(argc>1)
if(strcmp(argv[1],"--no_rc")==0)
flag|= 2;
if(!(flag&2)) {
ret= Cdrpreskin_read_rc(o,argv[0],0);
if(ret<0)
return(-1);
if(o->pre_argc>1) {
ret= Cdrpreskin_setup(o,o->pre_argc,o->pre_argv,flag|1|2);
if(ret<=0)
return(ret);
/* ??? abort on ret==2 ? */
}
}
#endif
if(argc==1) {
fprintf(stderr,"cdrskin: SORRY : no options given. Try option --help\n");
return(0);
}
for (i= 1;i<argc;i++) {
if(strcmp(argv[i],"--abort_handler")==0) {
o->abort_handler= 3;
} else if(strcmp(argv[i],"--allow_setuid")==0) {
o->allow_setuid= 1;
} else if(strcmp(argv[i],"blank=help")==0 ||
strcmp(argv[i],"-blank=help")==0) {
#ifndef Cdrskin_extra_leaN
fprintf(stderr,"Blanking options:\n");
fprintf(stderr,"\tall\t\tblank the entire disk\n");
fprintf(stderr,"\tdisc\t\tblank the entire disk\n");
fprintf(stderr,"\tdisk\t\tblank the entire disk\n");
fprintf(stderr,
"\tfast\t\tminimally blank the entire disk\n");
fprintf(stderr,
"\tminimal\t\tminimally blank the entire disk\n");
#else /* ! Cdrskin_extra_leaN */
goto see_cdrskin_eng_html;
#endif /* ! Cdrskin_extra_leaN */
if(argc==2)
{ret= 2; goto final_checks;}
} else if(strcmp(argv[i],"--demand_a_drive")==0) {
o->scan_demands_drive= 1;
} else if(strcmp(argv[i],"--devices")==0) {
printf("Note: If this hangs for a while then there is a drive with\n");
printf(" unexpected problems (e.g. ill DMA).\n");
printf(" One may exclude such a device file by removing r- and w-\n");
printf(" permissions for all cdrskin users.\n");
o->no_whitelist= 1;
} else if(strncmp(argv[i],"dev_translation=",16)==0) {
#ifndef Cdrskin_extra_leaN
if(o->adr_trn==NULL) {
ret= Cdradrtrn_new(&(o->adr_trn),0);
if(ret<=0)
goto no_adr_trn_mem;
}
if(argv[i][16]==0) {
fprintf(stderr,
"cdrskin: FATAL : dev_translation= : missing separator character\n");
return(0);
}
ret= Cdradrtrn_add(o->adr_trn,argv[i]+17,argv[i]+16,1);
if(ret==-2) {
no_adr_trn_mem:;
fprintf(stderr,
"cdrskin: FATAL : address_translation= : cannot allocate memory\n");
} else if(ret==-1)
fprintf(stderr,
"cdrskin: FATAL : address_translation= : table full (%d items)\n",
Cdradrtrn_leN);
else if(ret==0)
fprintf(stderr,
"cdrskin: FATAL : address_translation= : no address separator '%c' found\n",
argv[i][17]);
if(ret<=0)
return(0);
#else /* ! Cdrskin_extra_leaN */
fprintf(stderr,
"cdrskin: FATAL : dev_translation= is not available in lean version\n");
return(0);
#endif /* Cdrskin_extra_leaN */
} else if(strncmp(argv[i],"-dev=",5)==0) {
value_pt= argv[i]+5;
goto set_dev;
} else if(strncmp(argv[i],"dev=",4)==0) {
value_pt= argv[i]+4;
set_dev:;
if(strcmp(value_pt,"help")==0) {
#ifndef Cdrskin_extra_leaN
printf("Supported SCSI transports for this platform:\n");
fflush(stdout);
fprintf(stderr,"\nTransport name:\t\tlibburn\n");
fprintf(stderr,
"Transport descr.:\tOpen-source library for writing optical discs\n");
fprintf(stderr,"Transp. layer ind.:\t\n");
fprintf(stderr,"Target specifier:\tbus,target,lun\n");
fprintf(stderr,"Target example:\t\t1,2,0\n");
fprintf(stderr,"SCSI Bus scanning:\tsupported\n");
fprintf(stderr,
"Open via UNIX device:\tsupported (see option --devices)\n");
#else /* ! Cdrskin_extra_leaN */
goto see_cdrskin_eng_html;
#endif /* Cdrskin_extra_leaN */
{ret= 2; goto final_checks;}
}
if(strlen(value_pt)>=sizeof(o->raw_device_adr))
goto dev_too_long;
strcpy(o->raw_device_adr,value_pt);
} else if(strcmp(argv[i],"--drive_abort_on_busy")==0) {
o->abort_on_busy_drive= 1;
} else if(strcmp(argv[i],"--drive_blocking")==0) {
o->drive_blocking= 1;
} else if(strcmp(argv[i],"--drive_not_exclusive")==0) {
o->drive_exclusive= 0;
} else if(strcmp(argv[i],"driveropts=help")==0 ||
strcmp(argv[i],"-driveropts=help")==0) {
#ifndef Cdrskin_extra_leaN
fprintf(stderr,"Driver options:\n");
fprintf(stderr,"burnfree\tPrepare writer to use BURN-Free technology\n");
fprintf(stderr,"noburnfree\tDisable using BURN-Free technology\n");
#else /* ! Cdrskin_extra_leaN */
goto see_cdrskin_eng_html;
#endif /* Cdrskin_extra_leaN */
if(argc==2 || (i==2 && argc==3 && strncmp(argv[1],"dev=",4)==0))
{ret= 2; goto final_checks;}
} else if(strcmp(argv[i],"--help")==0) {
#ifndef Cdrskin_extra_leaN
printf("\n");
printf("Usage: %s [options|source_addresses]\n", argv[0]);
printf("Burns preformatted data to CD-R or CD-RW via libburn.\n");
printf("For the cdrecord compatible options which control the work of\n");
printf(
"blanking and burning see output of option -help rather than --help.\n");
printf("Non-cdrecord options:\n");
printf(" --abort_handler do not leave the drive in busy state\n");
printf(" --allow_setuid disable setuid blocker (very insecure !)\n");
printf(
" --any_track allow source_addresses to match '^-.' or '='\n");
printf(" --demand_a_drive exit !=0 on bus scans with empty result\n");
printf(" --devices list accessible devices (tells /dev/...)\n");
printf(
" dev_translation=<sep><from><sep><to> set input address alias\n");
printf(" e.g.: dev_translation=+ATA:1,0,0+/dev/sg1\n");
printf(" --drive_abort_on_busy abort process if busy drive is found\n");
printf(" (might be triggered by a busy hard disk)\n");
printf(" --drive_blocking try to wait for busy drive to become free\n");
printf(" (might be stalled by a busy hard disk)\n");
printf(" --drive_not_exclusive do not ask kernel to prevent opening\n");
printf(" busy drives. Effect is kernel dependend.\n");
printf(
" eject_device=<path> set the device address for command eject\n");
printf(" --fifo_disable disable fifo despite any fs=...\n");
printf(" --fifo_per_track use a separate fifo for each track\n");
printf(
" --fifo_start_empty do not wait for full fifo before burn start\n");
printf(
" grab_drive_and_wait=<num> grab drive, wait given number of\n");
printf(
" seconds, release drive, and do normal work\n");
printf(
" --ignore_signals try to ignore any signals rather than to abort\n");
printf(" --no_abort_handler exit even if the drive is in busy state\n");
printf(" --no_blank_appendable refuse to blank appendable CD-RW\n");
printf(
" --no_rc as first argument: do not read startup files\n");
printf(
" --single_track accept only last argument as source_address\n");
printf(
" tao_to_sao_tsize=<num> substitute -tao by -sao and eventually\n");
printf(" augment input from \"-\" by tsize=<num>\n");
printf(" (set tao_to_sao_tsize=0 to disable it)\n");
printf(
"Preconfigured arguments are read from the following startup files\n");
printf(
"if they exist and are readable. The sequence is as listed here:\n");
printf(" /etc/default/cdrskin /etc/opt/cdrskin/rc $HOME/.cdrskinrc\n");
printf(
"Each file line is a single argument. No remarks, no whitespace.\n");
printf(
"By default any argument that does not match grep '^-.' or '=' is\n");
printf(
"used as track source. If it is \"-\" then stdin is used. In this\n");
printf("case the total byte count of the source must be announced via\n");
printf("tsize= previous to the source address.\n");
printf("cdrskin : http://scdbackup.sourceforge.net/cdrskin_eng.html\n");
printf(" mailto:scdbackup@gmx.net (Thomas Schmitt)\n");
printf("libburn : http://libburn.pykix.org\n");
printf("cdrecord : ftp://ftp.berlios.de/pub/cdrecord/\n");
printf("My respect to the authors of cdrecord and libburn.\n");
printf("scdbackup: http://scdbackup.sourceforge.net/main_eng.html\n");
printf("\n");
#else /* ! Cdrskin_extra_leaN */
see_cdrskin_eng_html:;
printf("This is a capability reduced lean version without help texts.\n");
printf("See http://scdbackup.sourceforge.net/cdrskin_eng.html\n");
#endif /* Cdrskin_extra_leaN */
{ret= 2; goto final_checks;}
} else if(strcmp(argv[i],"-help")==0) {
#ifndef Cdrskin_extra_leaN
fprintf(stderr,"Usage: %s [options|source_addresses]\n",argv[0]);
fprintf(stderr,"Note: This is not cdrecord. See cdrskin start message on stdout. See --help.\n");
fprintf(stderr,"Options:\n");
fprintf(stderr,"\t-version\tprint version information and exit\n");
fprintf(stderr,
"\tdev=target\tpseudo-SCSI target to use as CD-Recorder\n");
fprintf(stderr,
"\tgracetime=#\tset the grace time before starting to write to #.\n");
fprintf(stderr,"\t-v\t\tincrement verbose level by one\n");
fprintf(stderr,
"\tdriveropts=opt\topt= one of {burnfree,noburnfree,help}\n");
fprintf(stderr,
"\t-checkdrive\tcheck if a driver for the drive is present\n");
fprintf(stderr,"\t-scanbus\tscan the SCSI bus and exit\n");
fprintf(stderr,"\tspeed=#\t\tset speed of drive\n");
fprintf(stderr,"\tblank=type\tblank a CD-RW disc (see blank=help)\n");
fprintf(stderr,
"\tfs=#\t\tSet fifo size to # (0 to disable, default is 4 MB)\n");
fprintf(stderr,
"\t-eject\t\teject the disk after doing the work (might be ignored)\n");
fprintf(stderr,"\t-dummy\t\tdo everything with laser turned off\n");
fprintf(stderr,
"\t-atip\t\tretrieve media state, print \"Is *erasable\"\n");
fprintf(stderr,"\t-raw96r\t\tWrite disk in RAW/RAW96R mode\n");
fprintf(stderr,"\t-dao\t\tWrite disk in SAO mode.\n");
fprintf(stderr,"\t-sao\t\tWrite disk in SAO mode.\n");
fprintf(stderr,"\ttsize=#\t\tannounces exact size of source data\n");
fprintf(stderr,"\tpadsize=#\tAmount of padding\n");
fprintf(stderr,
"\t-data\t\tSubsequent tracks are CD-ROM data mode 1 (default)\n");
fprintf(stderr,"\t-pad\t\tpadsize=30k\n");
fprintf(stderr,"\t-nopad\t\tDo not pad (default)\n");
fprintf(stderr,"\t-help\t\tprint this text to stderr and exit\n");
fprintf(stderr,
"By default any argument that does not match grep '^-.' or '=' is used\n");
fprintf(stderr,
"as track source address. Address \"-\" means stdin. In this case\n");
fprintf(stderr,
"the total byte count of the source must be announced via tsize=#.\n");
fprintf(stderr,
"cdrskin will ensure that the announced tsize= is written even if\n");
#ifdef Cdrskin_burn_track_padding_brokeN
fprintf(stderr,
"the source delivers fewer bytes. If the source delivers surplus\n");
fprintf(stderr,
"bytes, they will replace the eventual padding.\n");
#else /* Cdrskin_burn_track_padding_brokeN */
fprintf(stderr,"the source delivers fewer bytes.\n");
#endif /* ! Cdrskin_burn_track_padding_brokeN */
#else /* ! Cdrskin_extra_leaN */
fprintf(stderr,"Note: This is not cdrecord. See cdrskin start message on stdout.\n");
fprintf(stderr,
"(writer profile: -atip retrieve, blank=type, -eject after work)\n");
goto see_cdrskin_eng_html;
#endif /* Cdrskin_extra_leaN */
{ret= 2; goto final_checks;}
} else if(strcmp(argv[i],"--ignore_signals")==0) {
o->abort_handler= 2;
} else if(strcmp(argv[i],"--no_abort_handler")==0) {
o->abort_handler= 0;
} else if(strcmp(argv[i],"--no_rc")==0) {
if(i!=1)
fprintf(stderr,
"cdrskin: NOTE : option --no_rc would only work as first argument.\n");
} else if(strcmp(argv[i],"-scanbus")==0) {
o->no_whitelist= 1;
} else if(strcmp(argv[i],"-version")==0) {
printf(
"Cdrecord 2.01-Emulation Copyright (C) 2006, see libburn + cdrskin\n");
printf("libburn version : %s\n",Cdrskin_libburn_versioN);
#ifndef Cdrskin_extra_leaN
printf("cdrskin version : %s\n",Cdrskin_prog_versioN);
#else
printf("cdrskin version : %s.lean (capability reduced lean version)\n",
Cdrskin_prog_versioN);
#endif
printf("Version timestamp : %s\n",Cdrskin_timestamP);
printf("Build timestamp : %s\n",Cdrskin_build_timestamP);
{ret= 2; goto final_checks;}
}
}
ret= 1;
final_checks:;
if(flag&1)
goto ex;
if(o->allow_setuid==0 && getuid()!=geteuid()) {
fprintf(stderr,
"cdrskin: SORRY : uid and euid differ. Will abort for safety concerns.\n");
fprintf(stderr,
"cdrskin: HINT : Consider to allow rw-access to the writer device and\n");
fprintf(stderr,
"cdrskin: HINT : to run cdrskin under your normal user identity.\n");
fprintf(stderr,
"cdrskin: HINT : Option --allow_setuid disables this safety check.\n");
ret= 0;
}
if(strlen(o->raw_device_adr)>0 && !o->no_whitelist) {
int driveno,hret;
char *adr,buf[Cdrskin_adrleN];
adr= o->raw_device_adr;
#ifndef Cdrskin_extra_leaN
if(o->adr_trn!=NULL) {
hret= Cdradrtrn_translate(o->adr_trn,adr,-1,buf,0);
if(hret<=0) {
fprintf(stderr,
"cdrskin: FATAL : address translation failed (address too long ?) \n");
{ret= 0; goto ex;}
}
adr= buf;
}
#endif /* ! Cdrskin_extra_leaN */
if(adr[0]=='/') {
if(strlen(adr)>=sizeof(o->device_adr)) {
dev_too_long:;
fprintf(stderr,
"cdrskin: FATAL : dev=... too long (max. %d characters)\n",
sizeof(o->device_adr)-1);
{ret= 0; goto ex;}
}
strcpy(o->device_adr,adr);
} else {
ret= Cdrpreskin__cdrecord_to_dev(adr,o->device_adr,&driveno,0);
if(ret<=0) {
fprintf(stderr,
"cdrskin: FATAL : dev= expects /dev/xyz, Bus,Target,0 or a number\n");
{ret= 0; goto ex;}
}
}
}
ex:;
#ifndef Cdrskin_extra_leaN
if(ret<=0 || !(flag&1))
Cdradrtrn_destroy(&(o->adr_trn),0);
#endif
return(ret);
}
/* --------------------------------------------------------------------- */
/** Verbosity level for pacifying progress messages */
#define Cdrskin_verbose_progresS 1
/** Verbosity level for command recognition and execution logging */
#define Cdrskin_verbose_cmD 2
/** Verbosity level for reporting of debugging messages */
#define Cdrskin_verbose_debuG 3
/** The maximum number of tracks */
#define Cdrskin_track_maX 99
/** Work around the fact that libburn leaves the track input fds open
after the track is done. This can hide a few overflow bytes buffered
by the fifo-to-libburn pipe which would cause a broken-pipe error
if libburn would close that outlet.
This macro enables a coarse workaround which tries to read bytes from
the track inlets after burning has ended. Probably not a good idea if
libburn would close the inlet fds.
*/
#define Cdrskin_libburn_leaves_inlet_opeN 1
/** List of furter wishes towards libburn:
- write mode which does not demand a track size in advance
- obtain minimum drive speed (for cdrskin -atip)
- obtain MMC profile of inserted media (for cdrskin -v -atip)
- a possibility to implement cdrskin -multi
- a possibilty to implement cdrskin -reset
*/
/** Limit to prevent int rollovers within libburn as long as not everything is
changed to 64 bit off_t : 2 GB minus 800 MB for eventual computations. */
#define Cdrskin_tracksize_maX 1308622848
/* Some constants obtained by hearsay and experiments */
/** The payload speed factor for reporting progress: 1x = 150 kB/s */
static double Cdrskin_cd_speed_factoR= 150.0*1024.0;
/** The speed conversion factor consumer x-speed to libburn speed as used with
burn_drive_set_speed() burn_drive_get_write_speed()
*/
static double Cdrskin_libburn_cd_speed_factoR= 176.0;
/** Add-on for burn_drive_set_speed() to accomodate to the slightley oversized
speed ideas of my LG DVDRAM GSA-4082B. LITE-ON LTR-48125S tolerates it.
*/
static double Cdrskin_libburn_cd_speed_addoN= 50.0;
/** The program run control object. Defaults: see Cdrskin_new(). */
struct CdrskiN {
/** Settings already interpreted by Cdrpreskin_setup */
struct CdrpreskiN *preskin;
/** Job: what to do, plus some parameters. */
int verbosity;
double x_speed;
int gracetime;
int dummy_mode;
int single_track;
int do_devices;
int do_scanbus;
int do_checkdrive;
int do_atip;
int do_blank;
int blank_fast;
int no_blank_appendable;
int do_burn;
int burnfree;
char write_mode_name[40];
/** The write mode (like SAO or RAW96/R). See libburn. */
enum burn_write_types write_type;
int block_type;
int do_eject;
char eject_device[Cdrskin_strleN];
/** The current data source and its eventual parameters.
source_path may be either "-" for stdin, "#N" for open filedescriptor N
or the address of a readable file.
*/
char source_path[Cdrskin_strleN];
double fixed_size;
double padding;
int set_by_padsize;
/** track_type may be set to BURN_MODE1, BURN_AUDIO, etc. */
int track_type;
/** The list of tracks with their data sources and parameters */
struct CdrtracK *tracklist[Cdrskin_track_maX];
int track_counter;
/* a guess about what track might be processing right now */
int supposed_track_idx;
int fifo_enabled;
/** Optional fifo between input fd and libburn. It uses a pipe(2) to transfer
data to libburn. This fifo may be actually the start of a chain of fifos
which are to be processed simultaneously.
The fifo object knows the real input fd and the fd[1] of the pipe.
This is just a reference pointer. The fifos are managed by the tracks
which either line up their fifos or share the fifo of the first track.
*/
struct CdrfifO *fifo;
/** fd[0] of the fifo pipe. This is from where libburn reads its data. */
int fifo_outlet_fd;
int fifo_size;
int fifo_start_empty;
int fifo_per_track;
/** User defined address translation */
struct CdradrtrN *adr_trn;
/** The drives known to libburn after scan */
struct burn_drive_info *drives;
unsigned int n_drives;
/** The drive selected for operation by CdrskiN */
int driveno;
/** Progress state info: wether libburn is actually processing payload data */
int is_writing;
/** abort parameters */
int abort_max_wait;
/** Engagement info for eventual abort */
int lib_is_initialized;
pid_t control_pid; /* pid of the thread that calls libburn */
int drive_is_grabbed;
int drive_is_busy; /* Wether drive was told to do something cancel-worthy */
struct burn_drive *grabbed_drive;
/** Abort test facility */
double abort_after_bytecount;
/** Some intermediate option info which is stored until setup finalization */
double tao_to_sao_tsize;
int stdin_source_used;
};
int Cdrskin_destroy(struct CdrskiN **o, int flag);
int Cdrskin_grab_drive(struct CdrskiN *skin, int flag);
int Cdrskin_release_drive(struct CdrskiN *skin, int flag);
/** Create a cdrskin program run control object.
@param skin Returns pointer to resulting
@param flag Bitfield for control purposes:
bit0= library is already initialized
@return <=0 error, 1 success
*/
int Cdrskin_new(struct CdrskiN **skin, struct CdrpreskiN *preskin, int flag)
{
struct CdrskiN *o;
int ret,i;
(*skin)= o= TSOB_FELD(struct CdrskiN,1);
if(o==NULL)
return(-1);
o->preskin= preskin;
o->verbosity= 0;
o->x_speed= -1.0;
o->gracetime= 0;
o->dummy_mode= 0;
o->single_track= 0;
o->do_devices= 0;
o->do_scanbus= 0;
o->do_checkdrive= 0;
o->do_atip= 0;
o->do_blank= 0;
o->blank_fast= 0;
o->no_blank_appendable= 0;
o->do_burn= 0;
strcpy(o->write_mode_name,"SAO");
o->write_type= BURN_WRITE_SAO;
o->block_type= BURN_BLOCK_SAO;
o->burnfree= 0;
o->do_eject= 0;
o->eject_device[0]= 0;
o->source_path[0]= 0;
o->fixed_size= 0.0;
o->padding= 0.0;
o->set_by_padsize= 0;
o->track_type= BURN_MODE1;
for(i=0;i<Cdrskin_track_maX;i++)
o->tracklist[i]= NULL;
o->track_counter= 0;
o->supposed_track_idx= -1;
o->fifo_enabled= 1;
o->fifo= NULL;
o->fifo_outlet_fd= -1;
o->fifo_size= 4*1024*1024;
o->fifo_start_empty= 0;
o->fifo_per_track= 0;
o->adr_trn= NULL;
o->drives= NULL;
o->n_drives= 0;
o->driveno= 0;
o->is_writing= 0;
o->abort_max_wait= 74*60;
o->lib_is_initialized= (flag&1);
o->control_pid= getpid();
o->drive_is_grabbed= 0;
o->drive_is_busy= 0;
o->grabbed_drive= NULL;
o->abort_after_bytecount= -1.0;
o->tao_to_sao_tsize= 0.0;
o->stdin_source_used= 0;
#ifndef Cdrskin_extra_leaN
ret= Cdradrtrn_new(&(o->adr_trn),0);
if(ret<=0)
goto failed;
#endif /* ! Cdrskin_extra_leaN */
return(1);
failed:;
Cdrskin_destroy(skin,0);
return(-1);
}
/** Release from memory a cdrskin object */
int Cdrskin_destroy(struct CdrskiN **o, int flag)
{
struct CdrskiN *skin;
int i;
skin= *o;
if(skin==NULL)
return(0);
if(skin->drive_is_grabbed)
Cdrskin_release_drive(skin,0);
for(i=0;i<skin->track_counter;i++)
Cdrtrack_destroy(&(skin->tracklist[i]),0);
#ifndef Cdrskin_extra_leaN
Cdradrtrn_destroy(&(skin->adr_trn),0);
#endif /* ! Cdrskin_extra_leaN */
Cdrpreskin_destroy(&(skin->preskin),0);
if(skin->drives!=NULL)
burn_drive_info_free(skin->drives);
free((char *) skin);
*o= NULL;
return(1);
}
/** Return information about current track source */
int Cdrskin_get_source(struct CdrskiN *skin, char *source_path,
double *fixed_size, double *padding,
int *track_type, int flag)
{
strcpy(source_path,skin->source_path);
*fixed_size= skin->fixed_size;
*padding= skin->padding;
*track_type= skin->track_type;
return(1);
}
#ifndef Cdrskin_extra_leaN
/** Return information about current fifo setting */
int Cdrskin_get_fifo_par(struct CdrskiN *skin, int *fifo_enabled,
int *fifo_size, int *fifo_start_empty, int flag)
{
*fifo_enabled= skin->fifo_enabled;
*fifo_size= skin->fifo_size;
*fifo_start_empty= skin->fifo_start_empty;
return(1);
}
/** Create and install fifo objects between track data sources and libburn.
The sources and parameters are known to skin.
@return <=0 error, 1 success
*/
int Cdrskin_attach_fifo(struct CdrskiN *skin, int flag)
{
struct CdrfifO *ff= NULL;
int ret,i,hflag;
skin->fifo= NULL;
for(i=0;i<skin->track_counter;i++) {
hflag= (skin->verbosity>=Cdrskin_verbose_debuG);
if(skin->verbosity>=Cdrskin_verbose_cmD) {
if(skin->fifo_per_track)
printf("cdrskin: track %d establishing fifo of %d bytes\n",
i+1,skin->fifo_size);
else if(i==0)
printf("cdrskin: establishing fifo of %d bytes\n",skin->fifo_size);
else {
if(skin->verbosity>=Cdrskin_verbose_debuG)
ClN(fprintf(stderr,"cdrskin_debug: attaching track %d to fifo\n",i+1));
hflag|= 2;
}
}
ret= Cdrtrack_attach_fifo(skin->tracklist[i],&(skin->fifo_outlet_fd),ff,
hflag);
if(ret<=0) {
fprintf(stderr,"cdrskin: FATAL : failed to attach fifo.\n");
return(0);
}
if(i==0 || skin->fifo_per_track)
Cdrtrack_get_fifo(skin->tracklist[i],&ff,0);
if(i==0)
skin->fifo= ff;
}
return(1);
}
/** Read data into the track fifos until either #1 is full or its data source
is exhausted.
@return <=0 error, 1 success
*/
int Cdrskin_fill_fifo(struct CdrskiN *skin, int flag)
{
int ret;
ret= Cdrtrack_fill_fifo(skin->tracklist[0],0);
if(ret<=0)
return(ret);
printf("input buffer ready.\n");
fflush(stdout);
return(1);
}
#endif /* ! Cdrskin_extra_leaN */
/** Inform libburn about the consumer x-speed factor of skin */
int Cdrskin_adjust_speed(struct CdrskiN *skin, int flag)
{
int k_speed;
if(skin->x_speed<0)
k_speed= 0; /* libburn.h promises 0 to be max speed. */
else if(skin->x_speed==0) /* cdrecord specifies 0 as minimum speed. */
k_speed= Cdrskin_libburn_cd_speed_factoR+Cdrskin_libburn_cd_speed_addoN;
else
k_speed= skin->x_speed*Cdrskin_libburn_cd_speed_factoR +
Cdrskin_libburn_cd_speed_addoN;
if(skin->verbosity>=Cdrskin_verbose_debuG)
ClN(fprintf(stderr,"cdrskin_debug: k_speed= %d\n",k_speed));
burn_drive_set_speed(skin->drives[skin->driveno].drive,k_speed,k_speed);
return(1);
}
/** Shutdown library and restart again on single drive which gets grabbed.
Does only work with a valid skin->driveno or with an already set
skin->preskin->device_adr .
@param flag Bitfield for control purposes:
bit0= skin->driveno points to a valid drive. The library
will get reopened with that drive listed as only one
and already grabbed.
@return 1 = success ,
0 = failure, drive is released, library initialized
-1 = failure, library is finished (and could not get initialized)
*/
int Cdrskin_reinit_lib_with_adr(struct CdrskiN *skin, int flag)
{
int ret;
if(skin->drive_is_grabbed)
Cdrskin_release_drive(skin,0);
if(flag&1)
burn_drive_get_adr(&(skin->drives[skin->driveno]),
skin->preskin->device_adr);
if(strlen(skin->preskin->device_adr)<=0) {
fprintf(stderr,
"cdrskin: FATAL : unable to determine persistent drive address\n");
ret= 0; goto ex;
}
/* >>> A60827: this causes a SIGSEGV when releasing the re-initialized drive
skin->drives[skin->driveno].drive after burn (then as
skin->grabbed_drive, but already being 0xb0 in Cdrskin_grab) )
burn_drive_info_free(skin->drives);
*/
burn_finish();
if(!burn_initialize()) {
fflush(stdout);
fprintf(stderr,"cdrskin : FATAL : Re-initialization of libburn failed\n");
{ret= -1; goto ex;}
}
ret= Cdrskin_grab_drive(skin,1); /* uses burn_drive_scan_and_grab() */
if(ret<=0)
{ret=0; goto ex;}
ret= 1;
ex:
return(ret);
}
/** Obtain access to a libburn drive for writing or information retrieval.
Extended behavior with Cdrskin_new_api_tesT:
If libburn is not restricted to a single persistent address then it
gets shutdown and restarted with the wanted drive only. Thus, after
this call, libburn is supposed to have open only the grabbed drive.
All other drives should be free for other use.
@param flag Bitfield for control purposes:
bit0= bus is unscanned, device is known,
use burn_drive_scan_and_grab()
@return <=0 error, 1 success
*/
int Cdrskin_grab_drive(struct CdrskiN *skin, int flag)
{
int restore_handler= 0,ret;
struct burn_drive *drive;
if(skin->drive_is_grabbed)
Cdrskin_release_drive(skin,0);
drive= skin->drives[skin->driveno].drive;
skin->grabbed_drive= drive;
#ifdef Cdrskin_grab_abort_brokeN
/* There seems to be no way to get a drive out of status BURN_DRIVE_GRABBING
So try to block out signals if there is a signal handler installed */
if(skin->preskin->abort_handler==1 ||
skin->preskin->abort_handler==3 ||
skin->preskin->abort_handler==4) {
Cleanup_set_handlers(NULL,NULL,2);
restore_handler= 1;
}
#endif /* Cdrskin_grab_abort_brokeN */
#ifdef Cdrskin_new_api_tesT
if(flag&1) {
fprintf(stderr,
"cdrskin: experimental: Cdrskin_grab_drive() from shutdown libburn\n");
ret= burn_drive_scan_and_grab(&(skin->drives),skin->preskin->device_adr,1);
if(ret<=0) {
fprintf(stderr,"cdrskin: experimental: burn_drive_scan_and_grab ret=%d\n",
ret);
goto unable;
}
skin->driveno= 0;
} else {
fprintf(stderr,
"cdrskin: experimental: Cdrskin_grab_drive() on active libburn\n");
if(strlen(skin->preskin->device_adr)<=0) {
fprintf(stderr,
"cdrskin: experimental: Cdrskin_grab_drive() restarting libburn\n");
ret= Cdrskin_reinit_lib_with_adr(skin,1);
goto ex; /* this calls Cdrskin_grab() with persistent address or fails */
}
#else
{
#endif /* ! Cdrskin_new_api_tesT */
/* RIP-14.5 + LITE-ON 48125S produce a false status if tray was unloaded */
/* Therefore the first grab is just for loading */
if(!burn_drive_grab(drive, 1)) {
unable:;
fprintf(stderr,"cdrskin: FATAL : unable to open drive %d\n",
skin->driveno);
ret= 0; goto ex;
}
skin->drive_is_grabbed= 1;
burn_drive_release(drive,0);
skin->drive_is_grabbed= 0;
/* now grab the drive for real */
if(!burn_drive_grab(drive, 1))
goto unable;
}
skin->drive_is_grabbed= 1;
ret= 1;
ex:;
#ifdef Cdrskin_grab_abort_brokeN
if(restore_handler) {
int Cdrskin_abort_handler(struct CdrskiN *, int, int);
Cleanup_set_handlers(skin,(Cleanup_app_handler_T) Cdrskin_abort_handler,4);
}
#endif /* Cdrskin_grab_abort_brokeN */
if(ret<=0) {
skin->drive_is_grabbed= 0;
skin->grabbed_drive= NULL;
}
return(ret);
}
/** Release grabbed libburn drive */
int Cdrskin_release_drive(struct CdrskiN *skin, int flag)
{
if((!skin->drive_is_grabbed) || skin->grabbed_drive==NULL) {
fprintf(stderr,"cdrskin: CAUGHT : release of non-grabbed drive.\n");
return(0);
}
burn_drive_release(skin->grabbed_drive,skin->do_eject);
skin->drive_is_grabbed= 0;
skin->grabbed_drive= NULL;
return(1);
}
/** Clean up resources in abort situations. To be called by Cleanup subsystem
but hardly ever by the application. The program must exit afterwards.
*/
int Cdrskin_abort_handler(struct CdrskiN *skin, int signum, int flag)
{
int wait_grain= 100000,first_status= 1;
struct burn_progress p;
enum burn_drive_status drive_status= BURN_DRIVE_GRABBING;
double start_time,last_time,current_time;
if(getpid()!=skin->control_pid) {
if(skin->verbosity>=Cdrskin_verbose_debuG)
ClN(fprintf(stderr,
"cdrskin_debug: ABORT : thread rejected: pid=%d, signum=%d\n",
getpid(),signum));
return(2); /* do only process the control thread */
}
if(skin->preskin->abort_handler==3)
Cleanup_set_handlers(NULL,NULL,2); /* ignore all signals */
else if(skin->preskin->abort_handler==4)
Cleanup_set_handlers(NULL,NULL,1); /* allow abort */
fprintf(stderr,
"\ncdrskin: ABORT : handling started. Please do not press CTRL+C now.\n");
if(skin->preskin->abort_handler==3)
fprintf(stderr,"cdrskin: ABORT : trying to ignore any further signals\n");
#ifndef Cdrskin_extra_leaN
if(skin->fifo!=NULL)
Cdrfifo_close_all(skin->fifo,0);
#endif
if(skin->grabbed_drive!=NULL) {
drive_status= burn_drive_get_status(skin->grabbed_drive,&p);
if(drive_status!=BURN_DRIVE_IDLE && !skin->drive_is_grabbed)
skin->drive_is_grabbed= 2;
if(drive_status!=BURN_DRIVE_IDLE && !skin->drive_is_busy)
skin->drive_is_busy= 2;
if(skin->verbosity>=Cdrskin_verbose_debuG)
ClN(fprintf(stderr,"cdrskin_debug: ABORT : drive status: %d\n",
(int) drive_status));
}
if(skin->verbosity>=Cdrskin_verbose_debuG)
ClN(fprintf(stderr,
"cdrskin_debug: ABORT : drive_is_grabbed=%d , drive_is_busy=%d (%X)\n",
skin->drive_is_grabbed,skin->drive_is_busy,
(unsigned int) skin->grabbed_drive));
if(skin->drive_is_grabbed) {
if(skin->drive_is_busy && skin->grabbed_drive!=NULL) {
if(drive_status==BURN_DRIVE_WRITING || drive_status==BURN_DRIVE_READING) {
fprintf(stderr,"cdrskin: ABORT : trying to cancel drive operation.\n");
burn_drive_cancel(skin->grabbed_drive);
} else if(drive_status==BURN_DRIVE_GRABBING) {
/* >>> what to do in this state ? */;
} else if(drive_status!=BURN_DRIVE_IDLE) {
fprintf(stderr,
"cdrskin: ABORT : will wait for current operation to end.\n");
}
last_time= start_time= Sfile_microtime(0);
while(1) {
drive_status= burn_drive_get_status(skin->grabbed_drive,&p);
if(drive_status==BURN_DRIVE_IDLE)
break;
usleep(wait_grain);
current_time= Sfile_microtime(0);
if(current_time-last_time>=1.0) {
if(first_status)
fprintf(stderr,"\n");
first_status= 0;
fprintf(stderr,"\rcdrskin: ABORT : Status %d. Waiting for status %d since %d seconds (%d max)",
(int) drive_status, (int) BURN_DRIVE_IDLE,
(int) (current_time-start_time),skin->abort_max_wait);
last_time= current_time;
}
if(current_time-start_time>=skin->abort_max_wait) {
fprintf(stderr,
"\ncdrskin: ABORT : cannot cancel burn session and release drive.\n");
return(0);
}
}
fprintf(stderr,"\ncdrskin: ABORT : Status %d.\n",(int) drive_status);
}
fprintf(stderr,"cdrskin: ABORT : trying to release drive.\n");
Cdrskin_release_drive(skin,0);
}
if(skin->lib_is_initialized) {
fprintf(stderr,"cdrskin: ABORT : trying to finish libburn.\n");
burn_finish();
}
fprintf(stderr,
"cdrskin: ABORT : Drive is released and library is shut down now.\n");
fprintf(stderr,
"cdrskin: ABORT : Program done. Even if you do not see a shell prompt.\n");
return(1);
}
/** Convert a libburn device address into a libburn drive number
@return <=0 error, 1 success
*/
int Cdrskin_driveno_of_location(struct CdrskiN *skin, char *devicename,
int *driveno, int flag)
{
int i,ret;
char adr[Cdrskin_adrleN];
for(i=0;i<skin->n_drives;i++) {
#ifdef Cdrskin_libburn_has_drive_get_adR
ret= burn_drive_get_adr(&(skin->drives[skin->driveno]), adr);
if(ret<=0)
continue;
#else
ret= 1; /* to please gcc -Wall */
strcpy(adr,skin->drives[i].location);
#endif
if(strcmp(adr,devicename)==0) {
*driveno= i;
return(1);
}
}
return(0);
}
/** Convert a cdrskin address into a libburn drive number
@return <=0 error, 1 success
*/
int Cdrskin_dev_to_driveno(struct CdrskiN *skin, char *in_adr, int *driveno,
int flag)
{
int ret;
char *adr,translated_adr[Cdrskin_adrleN],synthetic_adr[Cdrskin_adrleN];
adr= in_adr;
#ifndef Cdrskin_extra_leaN
/* user defined address translation */
ret= Cdradrtrn_translate(skin->adr_trn,adr,-1,translated_adr,0);
if(ret<=0) {
fprintf(stderr,
"cdrskin: FATAL : address translation failed (address too long ?) \n");
return(0);
}
if(skin->verbosity>=Cdrskin_verbose_cmD && strcmp(adr,translated_adr)!=0)
printf("cdrskin: dev_translation=... : dev='%s' to dev='%s'\n",
adr,translated_adr);
adr= translated_adr;
#endif /* ! Cdrskin_extra_leaN */
if(adr[0]=='/') {
ret= Cdrskin_driveno_of_location(skin,adr,driveno,0);
if(ret<=0) {
location_not_found:;
fprintf(stderr,
"cdrskin: FATAL : cannot find '%s' among accessible drive devices.\n",
adr);
fprintf(stderr,
"cdrskin: HINT : use option --devices for a list of drive devices.\n");
return(0);
}
return(1);
}
ret= Cdrpreskin__cdrecord_to_dev(adr,synthetic_adr,driveno,0);
if(ret<=0) {
wrong_devno:;
if(skin->n_drives<=0) {
fprintf(stderr,"cdrskin: FATAL : No accessible drives.\n");
} else {
fprintf(stderr,
"cdrskin: FATAL : Address does not lead to an accessible drive: %s\n",
in_adr);
fprintf(stderr,
"cdrskin: HINT : dev= expects /dev/xyz, Bus,Target,0 or a number [0,%d]\n",
skin->n_drives-1);
}
return(0);
}
if(strlen(synthetic_adr)>0) {
if(skin->verbosity>=Cdrskin_verbose_cmD)
printf("cdrskin: converted address '%s' to '%s'\n",adr,synthetic_adr);
ret= Cdrskin_driveno_of_location(skin,synthetic_adr,driveno,0);
if(ret<=0) {
fprintf(stderr,
"cdrskin: failure while using address converted from '%s'\n",adr);
adr= synthetic_adr;
goto location_not_found;
}
}
if((*driveno)>=skin->n_drives || (*driveno)<0) {
fprintf(stderr,"cdrskin: obtained drive number %d from '%s'\n",
*driveno,adr);
goto wrong_devno;
}
return(1);
}
/** Convert a libburn drive number into a cdrecord-style address which
represents a device address if possible and the drive number else.
@param flag Bitfield for control purposes:
bit0= do not apply user defined address translation
@return <0 error, 0 drive number, 1 /dev/sgN, 2 /dev/hdX
*/
int Cdrskin_driveno_to_btldev(struct CdrskiN *skin, int driveno,
char btldev[Cdrskin_adrleN], int flag)
{
int k,ret,still_untranslated= 1,hret;
char *loc= NULL,buf[Cdrskin_adrleN],adr[Cdrskin_adrleN];
if(driveno<0 || driveno>skin->n_drives)
goto fallback;
#ifdef Cdrskin_libburn_has_drive_get_adR
ret= burn_drive_get_adr(&(skin->drives[driveno]), adr);
if(ret<=0)
goto fallback;
loc= adr;
#else
adr[0]= 0; /* to please gcc -Wall */
loc= skin->drives[driveno].location;
#endif
if(loc==NULL)
goto fallback;
if(strncmp(loc,"/dev/sg",7)==0) {
for(k= 7;loc[k]!=0;k++)
if(loc[k]<'0' || loc[k]>'9')
break;
if(loc[k]==0 && k>7) {
sprintf(btldev,"1,%s,0",loc+7);
{ret= 1; goto ex;}
}
}
if(strncmp(loc,"/dev/hd",7)==0)
if(loc[7]>='a' && loc[7]<='z')
if(loc[8]==0) {
sprintf(btldev,"2,%d,0",loc[7]-'a');
{ret= 2; goto ex;}
}
fallback:;
sprintf(btldev,"0,%d,0",driveno);
ret= 0;
ex:;
#ifndef Cdrskin_extra_leaN
/* user defined address translation */
if(!(flag&1)) {
if((ret==1 || ret==2)) {
/* try wether a translation points to loc */
hret= Cdradrtrn_translate(skin->adr_trn,loc,driveno,buf,1);
if(hret==2) {
still_untranslated= 0;
strcpy(btldev,buf);
}
}
if(still_untranslated)
Cdradrtrn_translate(skin->adr_trn,btldev,driveno,buf,1);
strcpy(btldev,buf);
}
#endif /* ! Cdrskin_extra_leaN */
return(ret);
}
/** Report media status s to the user */
int Cdrskin_report_disc_status(struct CdrskiN *skin, enum burn_disc_status s,
int flag)
{
printf("cdrskin: status %d ",s);
if (s==BURN_DISC_FULL) {
printf("burn_disc_full \"There is a disc with data on it in the drive\"\n");
} else if(s==BURN_DISC_BLANK) {
printf("burn_disc_blank \"The drive holds a blank disc\"\n");
} else if(s==BURN_DISC_APPENDABLE) {
printf(
"BURN_DISC_APPENDABLE \"There is an incomplete disc in the drive\"\n");
} else if(s==BURN_DISC_EMPTY) {
printf("BURN_DISC_EMPTY \"There is no disc at all in the drive\"\n");
} else
printf("-unknown status code-\n");
return(1);
}
/** Perform operations -scanbus or --devices
@param flag Bitfield for control purposes:
bit0= perform --devices rather than -scanbus
@return <=0 error, 1 success
*/
int Cdrskin_scanbus(struct CdrskiN *skin, int flag)
{
int ret,i,busno,first_on_bus;
char shellsafe[5*Cdrskin_strleN+2],perms[40],btldev[Cdrskin_adrleN];
char adr[Cdrskin_adrleN];
struct stat stbuf;
if(flag&1) {
printf("cdrskin: Overview of accessible drives (%d found) :\n",
skin->n_drives);
printf("-----------------------------------------------------------------------------\n");
for(i=0;i<skin->n_drives;i++) {
#ifdef Cdrskin_libburn_has_drive_get_adR
ret= burn_drive_get_adr(&(skin->drives[i]), adr);
if(ret<=0) {
/* >>> one should massively complain */;
continue;
}
#else
strcpy(adr,skin->drives[i].location);
#endif
if(stat(adr,&stbuf)==-1) {
sprintf(perms,"errno=%d",errno);
} else {
strcpy(perms,"------");
if(stbuf.st_mode&S_IRUSR) perms[0]= 'r';
if(stbuf.st_mode&S_IWUSR) perms[1]= 'w';
if(stbuf.st_mode&S_IRGRP) perms[2]= 'r';
if(stbuf.st_mode&S_IWGRP) perms[3]= 'w';
if(stbuf.st_mode&S_IROTH) perms[4]= 'r';
if(stbuf.st_mode&S_IWOTH) perms[5]= 'w';
}
if(strlen(adr)>=Cdrskin_strleN)
Text_shellsafe("failure:oversized string",shellsafe,0);
else
Text_shellsafe(adr,shellsafe,0);
printf("%d dev=%s %s : '%s' '%s'\n",
i,shellsafe,perms,skin->drives[i].vendor,skin->drives[i].product);
}
printf("-----------------------------------------------------------------------------\n");
} else {
printf("Using libburn version '%s'.\n",Cdrskin_libburn_versioN);
for(busno= 0;busno<16;busno++) {
first_on_bus= 1;
for(i=0;i<skin->n_drives;i++) {
ret= Cdrskin_driveno_to_btldev(skin,i,btldev,1);
if(ret!=busno)
continue;
if(first_on_bus)
printf("scsibus%d:\n",busno);
first_on_bus= 0;
printf("\t%s\t %d) '%s' '%s' '%s' Removable CD-ROM\n",
btldev,i,skin->drives[i].vendor,skin->drives[i].product,"?");
}
}
}
return(1);
}
/** Perform -checkdrive .
@param flag Bitfield for control purposes:
bit0= do not print message about pseudo-checkdrive
@return <=0 error, 1 success
*/
int Cdrskin_checkdrive(struct CdrskiN *skin, int flag)
{
struct burn_drive_info *drive_info;
int ret;
char btldev[Cdrskin_adrleN];
if(!(flag&1))
printf("cdrskin: pseudo-checkdrive on drive %d\n",skin->driveno);
if(skin->driveno>=skin->n_drives || skin->driveno<0) {
fprintf(stderr,"cdrskin: FATAL : there is no drive #%d\n",skin->driveno);
{ret= 0; goto ex;}
}
drive_info= &(skin->drives[skin->driveno]);
ret= Cdrskin_driveno_to_btldev(skin,skin->driveno,btldev,0);
if(ret>=0)
fprintf(stderr,"scsidev: '%s'\n",btldev);
printf("Device type : %s\n","Removable CD-ROM");
printf("Vendor_info : '%s'\n",drive_info->vendor);
printf("Identifikation : '%s'\n",drive_info->product);
printf("Driver flags : %s\n","BURNFREE");
printf("Supported modes: %s\n","SAO RAW/R96R");
ret= 1;
ex:;
return(ret);
}
/** Perform -atip .
@return <=0 error, 1 success
*/
int Cdrskin_atip(struct CdrskiN *skin, int flag)
{
int ret,is_not_really_erasable= 0;
double x_speed;
enum burn_disc_status s;
struct burn_drive *drive;
printf("cdrskin: pseudo-atip on drive %d\n",skin->driveno);
ret= Cdrskin_checkdrive(skin,1);
if(ret<=0)
return(ret);
drive= skin->drives[skin->driveno].drive;
ret= Cdrskin_grab_drive(skin,0);
if(ret<=0)
return(ret);
while(burn_drive_get_status(drive,NULL))
sleep(2);
while ((s = burn_disc_get_status(drive)) == BURN_DISC_UNREADY)
sleep(2);
Cdrskin_report_disc_status(skin,s,0);
if(s==BURN_DISC_APPENDABLE && skin->no_blank_appendable) {
is_not_really_erasable= 1;
} else if(s==BURN_DISC_EMPTY) {
if(skin->verbosity>=Cdrskin_verbose_progresS)
printf("Current: none\n");
ret= 0; goto ex;
}
#ifdef Cdrskin_atip_speed_brokeN
/* <<< terrible stunt to get correct media speed info */
if(skin->verbosity>=Cdrskin_verbose_debuG)
ClN(fprintf(stderr,
"cdrskin_debug: redoing startup for speed inquiry stabilization\n"));
#ifdef Cdrskin_new_api_tesT
fprintf(stderr,
"cdrskin: experimental: Cdrskin_atip() on Cdrskin_new_api_tesT\n");
if(strlen(skin->preskin->device_adr)<=0)
burn_drive_get_adr(&(skin->drives[skin->driveno]),
skin->preskin->device_adr);
Cdrskin_release_drive(skin,0);
burn_finish();
if(!burn_initialize()) {
fflush(stdout);
fprintf(stderr,"cdrskin : FATAL : Re-initialization of libburn failed\n");
{ret= 0; goto ex;}
}
ret= Cdrskin_grab_drive(skin,1); /* uses burn_drive_scan_and_grab() */
if(ret<=0)
return(ret);
#else /* Cdrskin_new_api_tesT */
Cdrskin_release_drive(skin,0);
burn_finish();
if(!burn_initialize()) {
fflush(stdout);
fprintf(stderr,"cdrskin : FATAL : Re-initialization of libburn failed\n");
{ret= 0; goto ex;}
}
if(strlen(skin->preskin->device_adr)>0)
burn_drive_add_whitelist(skin->preskin->device_adr);
while (!burn_drive_scan(&(skin->drives), &(skin->n_drives))) ;
drive= skin->drives[skin->driveno].drive;
ret= Cdrskin_grab_drive(skin,0);
if(ret<=0)
return(ret);
#endif /* ! Cdrskin_new_api_tesT */
#endif /* Cdrskin_atip_speed_brokeN */
ret= burn_drive_get_write_speed(drive);
x_speed= ((double) ret)/Cdrskin_libburn_cd_speed_factoR;
printf("cdrskin: burn_drive_get_write_speed = %d (%.1fx)\n",ret,x_speed);
if(skin->verbosity>=Cdrskin_verbose_progresS) {
if(burn_disc_erasable(drive))
printf("Current: CD-RW\n");
else
printf("Current: CD-R\n");