You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
3956 lines
117 KiB
3956 lines
117 KiB
|
|
/* |
|
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. |
|
|
|
|
|
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.4" |
|
|
|
/** 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 |
|
|
|
/* <<< just for now: */ |
|
#define Cdrskin_libburn_no_burn_preset_device_opeN 1 |
|
|
|
#endif /* Cdrskin_libburn_0_2_1 */ |
|
|
|
#ifndef Cdrskin_libburn_versioN |
|
#define Cdrskin_libburn_versioN "0.2.ts" |
|
#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 */ |
|
#define Cdrskin_adrleN 80 |
|
|
|
|
|
/* --------------------------------------------------------------------- */ |
|
|
|
/* 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; |
|
|
|
/** 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 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->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),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,BURN_MODE1); |
|
|
|
#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( |
|
" --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_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 augment\n"); |
|
printf(" 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 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; |
|
|
|
/** 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_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->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; |
|
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 flag) |
|
{ |
|
strcpy(source_path,skin->source_path); |
|
*fixed_size= skin->fixed_size; |
|
*padding= skin->padding; |
|
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); |
|
} |
|
|
|
|
|
/** Obtain access to a libburn drive for writing or information retrieval |
|
@return <=0 error, 1 success |
|
*/ |
|
int Cdrskin_grab_drive(struct CdrskiN *skin, int flag) |
|
{ |
|
int restore_handler= 0,ret; |
|
struct burn_drive *drive; |
|
|
|
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 */ |
|
|
|
/* 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; |
|
|
|
for(i=0;i<skin->n_drives;i++) { |
|
if(strcmp(skin->drives[i].location,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]; |
|
|
|
if(driveno<0 || driveno>skin->n_drives) |
|
goto fallback; |
|
loc= skin->drives[driveno].location; |
|
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]; |
|
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++) { |
|
if(stat(skin->drives[i].location,&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(skin->drives[i].location)>=Cdrskin_strleN) |
|
Text_shellsafe("failure:oversized string",shellsafe,0); |
|
else |
|
Text_shellsafe(skin->drives[i].location,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( |