diff --git a/Makefile.am b/Makefile.am index 22093c4..6d09840 100644 --- a/Makefile.am +++ b/Makefile.am @@ -12,6 +12,8 @@ libburn_libburn_la_SOURCES = \ libburn/async.c \ libburn/async.h \ libburn/back_hacks.h \ + libburn/cleanup.c \ + libburn/cleanup.h \ libburn/crc.c \ libburn/crc.h \ libburn/debug.c \ @@ -119,7 +121,8 @@ test_iso_SOURCES = test/iso.c cdrskin_cdrskin_CPPFLAGS = -Ilibburn cdrskin_cdrskin_CFLAGS = -DCdrskin_libburn_0_2_3 cdrskin_cdrskin_LDADD = $(libburn_libburn_la_OBJECTS) $(THREAD_LIBS) -cdrskin_cdrskin_SOURCES = cdrskin/cdrskin.c cdrskin/cdrfifo.c cdrskin/cdrfifo.h cdrskin/cleanup.c cdrskin/cleanup.h cdrskin/cdrskin_timestamp.h +cdrskin_cdrskin_SOURCES = cdrskin/cdrskin.c cdrskin/cdrfifo.c cdrskin/cdrfifo.h cdrskin/cdrskin_timestamp.h +## cdrskin_cdrskin_SOURCES = cdrskin/cdrskin.c cdrskin/cdrfifo.c cdrskin/cdrfifo.h cdrskin/cleanup.c cdrskin/cleanup.h cdrskin/cdrskin_timestamp.h ## ## Open questions: how to compute $timestamp and express -DX="$timestamp" ## diff --git a/cdrskin/cdrskin.c b/cdrskin/cdrskin.c index 9a5ad47..9ce5cdd 100644 --- a/cdrskin/cdrskin.c +++ b/cdrskin/cdrskin.c @@ -186,6 +186,7 @@ or #ifdef Cdrskin_new_api_tesT /* put macros under test caveat here */ +#define Cdrskin_libburn_has_cleanup_handleR 1 #endif @@ -273,7 +274,12 @@ or #include +#ifdef Cdrskin_libburn_has_cleanup_handleR +#define Cleanup_set_handlers burn_set_signal_handling +#define Cleanup_app_handler_T burn_abort_handler_t +#else #include "cleanup.h" +#endif /** The size of a string buffer for pathnames and similar texts */ @@ -2652,11 +2658,10 @@ int Cdrskin_abort_handler(struct CdrskiN *skin, int signum, int flag) 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 */ + "\ncdrskin_debug: ABORT : [%d] Thread rejected: pid=%d, signum=%d\n", + skin->control_pid,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) diff --git a/cdrskin/cdrskin_timestamp.h b/cdrskin/cdrskin_timestamp.h index 6a9346d..fa75755 100644 --- a/cdrskin/cdrskin_timestamp.h +++ b/cdrskin/cdrskin_timestamp.h @@ -1 +1 @@ -#define Cdrskin_timestamP "2006.10.02.103418" +#define Cdrskin_timestamP "2006.10.03.162719" diff --git a/cdrskin/cleanup.c b/cdrskin/cleanup.c index e561893..2ac2ccc 100644 --- a/cdrskin/cleanup.c +++ b/cdrskin/cleanup.c @@ -3,7 +3,7 @@ A signal handler which cleans up an application and exits. - Provided under GPL license within cdrskin and under BSD license elsewise. + Provided under GPL license within GPL projects, BSD license elsewise. */ /* @@ -65,7 +65,7 @@ static int Cleanup_handler_exit(int exit_value, int signum, int flag) if(cleanup_perform_app_handler_first) if(cleanup_app_handler!=NULL) { ret= (*cleanup_app_handler)(cleanup_app_handle,signum,0); - if(ret==2) + if(ret==2 || ret==-2) return(2); } if(cleanup_exiting) { @@ -77,11 +77,14 @@ static int Cleanup_handler_exit(int exit_value, int signum, int flag) } cleanup_exiting= 1; if(cleanup_msg[0]!=0) - fprintf(stderr,"%s\n",cleanup_msg); + fprintf(stderr,"\n%s\n",cleanup_msg); alarm(0); if(!cleanup_perform_app_handler_first) - if(cleanup_app_handler!=NULL) - (*cleanup_app_handler)(cleanup_app_handle,signum,0); + if(cleanup_app_handler!=NULL) { + ret= (*cleanup_app_handler)(cleanup_app_handle,signum,0); + if(ret==2 || ret==-2) + return(2); + } exit(exit_value); } @@ -115,8 +118,12 @@ int Cleanup_set_handlers(void *handle, Cleanup_app_handler_T handler, int flag) cleanup_msg[0]= 0; cleanup_app_handle= handle; cleanup_app_handler= handler; + + /* <<< make cleanup_exiting thread safe to get rid of this */ if(flag&4) cleanup_perform_app_handler_first= 1; + + if(flag&1) sig_handler= SIG_DFL; else if(flag&2) diff --git a/cdrskin/cleanup.h b/cdrskin/cleanup.h index a155e05..a9d3551 100644 --- a/cdrskin/cleanup.h +++ b/cdrskin/cleanup.h @@ -3,7 +3,7 @@ A signal handler which cleans up an application and exits. - Provided under GPL license within cdrskin and under BSD license elsewise. + Provided under GPL license within GPL projects, BSD license elsewise. */ #ifndef Cleanup_includeD @@ -13,7 +13,7 @@ /** Layout of an application provided cleanup function using an application provided handle as first argument and the signal number as second argument. The third argument is a flag bit field with no defined bits yet. - If the handler returns 2 then it has delegated exit() to some other + If the handler returns 2 or -2 then it has delegated exit() to some other instance and the Cleanup handler shall return rather than exit. */ typedef int (*Cleanup_app_handler_T)(void *, int, int); diff --git a/cdrskin/compile_cdrskin.sh b/cdrskin/compile_cdrskin.sh index bd5465c..076bb12 100755 --- a/cdrskin/compile_cdrskin.sh +++ b/cdrskin/compile_cdrskin.sh @@ -7,6 +7,7 @@ debug_opts= def_opts= libvers="-DCdrskin_libburn_0_2_3" +cleanup_src_or_obj="cdrskin/cleanup.c" libdax_msgs_o="libburn/libdax_msgs.o" do_strip=0 static_opts= @@ -24,10 +25,12 @@ do then libvers="-DCdrskin_libburn_cvs_A60220_tS" libdax_msgs_o="libburn/message.o" + cleanup_src_or_obj="cdrskin/cleanup.c" elif test "$i" = "-libburn_0_2_2" then libvers="-DCdrskin_libburn_0_2_2" libdax_msgs_o="libburn/message.o" + cleanup_src_or_obj="cdrskin/cleanup.c" elif test "$i" = "-libburn_0_2_3" then libvers="-DCdrskin_libburn_0_2_3" @@ -35,9 +38,11 @@ do elif test "$i" = "-newapi" -o "$i" = "-experimental" then def_opts="$def_opts -DCdrskin_new_api_tesT" + cleanup_src_or_obj="libburn/cleanup.o" elif test "$i" = "-oldfashioned" then def_opts="$def_opts -DCdrskin_oldfashioned_api_usE" + cleanup_src_or_obj="cdrskin/cleanup.c" elif test "$i" = "-do_not_compile_cdrskin" then compile_cdrskin=0 @@ -85,7 +90,7 @@ echo "Build timestamp : $timestamp" if test "$compile_cdrskin" then - echo "compiling program cdrskin/cdrskin.c $static_opts $debug_opts $libvers $def_opts" + echo "compiling program cdrskin/cdrskin.c $static_opts $debug_opts $libvers $def_opts $cleanup_src_or_obj" cc $warn_opts -I. $static_opts $debug_opts $libvers $def_opts \ -DCdrskin_build_timestamP='"'"$timestamp"'"' \ \ @@ -93,7 +98,8 @@ then \ cdrskin/cdrskin.c \ $fifo_source \ - cdrskin/cleanup.c \ + \ + $cleanup_src_or_obj \ \ libburn/async.o \ libburn/debug.o \ diff --git a/libburn/cleanup.c b/libburn/cleanup.c new file mode 100644 index 0000000..2ac2ccc --- /dev/null +++ b/libburn/cleanup.c @@ -0,0 +1,191 @@ +/* + cleanup.c , Copyright 2006 Thomas Schmitt + + A signal handler which cleans up an application and exits. + + Provided under GPL license within GPL projects, BSD license elsewise. +*/ + +/* + cc -g -o cleanup -DCleanup_standalonE cleanup.c +*/ + +#include +#include +#include +#include +#include +#include + +#include +typedef void (*sighandler_t)(int); + + +#include "cleanup.h" + +/* Signals to be caught */ +static int signal_list[]= { + SIGHUP, SIGINT, SIGQUIT, SIGILL, SIGABRT, + SIGFPE, SIGSEGV, SIGPIPE, SIGALRM, SIGTERM, + SIGUSR1, SIGUSR2, SIGXCPU, SIGTSTP, SIGTTIN, + SIGTTOU, + SIGBUS, SIGPOLL, SIGPROF, SIGSYS, SIGTRAP, + SIGVTALRM, SIGXCPU, SIGXFSZ, -1 +}; +static char *signal_name_list[]= { + "SIGHUP", "SIGINT", "SIGQUIT", "SIGILL", "SIGABRT", + "SIGFPE", "SIGSEGV", "SIGPIPE", "SIGALRM", "SIGTERM", + "SIGUSR1", "SIGUSR2", "SIGXCPU", "SIGTSTP", "SIGTTIN", + "SIGTTOU", + "SIGBUS", "SIGPOLL", "SIGPROF", "SIGSYS", "SIGTRAP", + "SIGVTALRM", "SIGXCPU", "SIGXFSZ", "@" +}; +static int signal_list_count= 24; + +/* Signals not to be caught */ +static int non_signal_list[]= { + SIGKILL, SIGCHLD, SIGSTOP, SIGURG, -1 +}; +static int non_signal_list_count= 4; + + +/* run time dynamic part */ +static char cleanup_msg[4096]= {""}; +static int cleanup_exiting= 0; + +static void *cleanup_app_handle= NULL; +static Cleanup_app_handler_T cleanup_app_handler= NULL; +static int cleanup_perform_app_handler_first= 0; + + +static int Cleanup_handler_exit(int exit_value, int signum, int flag) +{ + int ret; + + if(cleanup_perform_app_handler_first) + if(cleanup_app_handler!=NULL) { + ret= (*cleanup_app_handler)(cleanup_app_handle,signum,0); + if(ret==2 || ret==-2) + return(2); + } + if(cleanup_exiting) { + if(cleanup_msg[0]!=0) + fprintf(stderr,"%s\n",cleanup_msg); + fprintf(stderr,"cleanup: ABORT : repeat by pid=%d, signum=%d\n", + getpid(),signum); + return(0); + } + cleanup_exiting= 1; + if(cleanup_msg[0]!=0) + fprintf(stderr,"\n%s\n",cleanup_msg); + alarm(0); + if(!cleanup_perform_app_handler_first) + if(cleanup_app_handler!=NULL) { + ret= (*cleanup_app_handler)(cleanup_app_handle,signum,0); + if(ret==2 || ret==-2) + return(2); + } + exit(exit_value); +} + + +static void Cleanup_handler_generic(int signum) +{ + int i; + + sprintf(cleanup_msg,"UNIX-SIGNAL caught: %d errno= %d",signum,errno); + for(i= 0; imax_sig) + max_sig= signal_list[i]; + if(signal_list[i]=non_signal_list_count) { + if(i==SIGABRT && (flag&8)) + signal(i,Cleanup_handler_generic); + else + signal(i,sig_handler); + } + } + return(1); +} + + +#ifdef Cleanup_standalonE + +struct Demo_apP { + char *msg; +}; + + +int Demo_app_handler(struct Demo_apP *demoapp, int signum, int flag) +{ + printf("Handling exit of demo application on signal %d. msg=\"%s\"\n", + signum,demoapp->msg); + return(1); +} + + +main() +{ + struct Demo_apP demoapp; + + demoapp.msg= "Good Bye"; + Cleanup_set_handlers(&demoapp,(Cleanup_app_handler_T) Demo_app_handler,0); + + if(1) { /* change to 0 in order to wait for external signals */ + char *cpt= NULL,c; + printf("Intentionally provoking SIGSEGV ...\n"); + c= *cpt; + } else { + printf("killme: %d\n",getpid()); + sleep(3600); + } + + Cleanup_set_handlers(NULL,NULL,1); + exit(0); +} + +#endif /* Cleanup_standalonE */ diff --git a/libburn/cleanup.h b/libburn/cleanup.h new file mode 100644 index 0000000..a9d3551 --- /dev/null +++ b/libburn/cleanup.h @@ -0,0 +1,34 @@ +/* + cleanup.c , Copyright 2006 Thomas Schmitt + + A signal handler which cleans up an application and exits. + + Provided under GPL license within GPL projects, BSD license elsewise. +*/ + +#ifndef Cleanup_includeD +#define Cleanup_includeD 1 + + +/** Layout of an application provided cleanup function using an application + provided handle as first argument and the signal number as second + argument. The third argument is a flag bit field with no defined bits yet. + If the handler returns 2 or -2 then it has delegated exit() to some other + instance and the Cleanup handler shall return rather than exit. +*/ +typedef int (*Cleanup_app_handler_T)(void *, int, int); + + +/** Establish exiting signal handlers on (hopefully) all signals that are + not ignored by default or non-catchable. + @param handle Opaque object which knows how to cleanup application + @param handler Function which uses handle to perform application cleanup + @param flag Control Bitfield + bit0= reset to default signal handling +*/ +int Cleanup_set_handlers(void *handle, Cleanup_app_handler_T handler, + int flag); + + +#endif /* ! Cleanup_includeD */ + diff --git a/libburn/init.c b/libburn/init.c index 2e9c83d..63e071e 100644 --- a/libburn/init.c +++ b/libburn/init.c @@ -38,6 +38,15 @@ int burn_sg_open_o_nonblock = 1; int burn_sg_open_abort_busy = 0; +/* ts A61002 */ + +#include "cleanup.h" + +/* Parameters for builtin abort handler */ +static char abort_message_prefix[81] = {"libburn : "}; +static pid_t abort_control_pid= 0; + + /* ts A60925 : ticket 74 */ /** Create the messenger object for libburn. */ int burn_msgs_initialize(void) @@ -197,3 +206,40 @@ ex: return ret; } +int burn_builtin_abort_handler(void *handle, int signum, int flag) +{ + if(getpid() != abort_control_pid) + return -2; + Cleanup_set_handlers(NULL, NULL, 2); + fprintf(stderr,"%sABORT : Trying to shut down drive and library\n", + abort_message_prefix); + fprintf(stderr, + "%sABORT : Wait the normal burning time before any kill -9\n", + abort_message_prefix); + close(0); /* somehow stdin as input blocks abort until EOF */ + burn_abort(4440, burn_abort_pacifier, abort_message_prefix); + fprintf(stderr, + "\n%sABORT : Program done. Even if you do not see a shell prompt.\n\n", + abort_message_prefix); + return(1); +} + +void burn_set_signal_handling(void *handle, burn_abort_handler_t handler, + int mode) +{ + if(handler == NULL && mode == 0) { + handler = burn_builtin_abort_handler; +/* + fprintf(stderr, "libburn_experimental: activated burn_builtin_abort_handler() with handle '%s'\n",(handle==NULL ? "libburn : " : (char *) handle)); +*/ + + } + strcpy(abort_message_prefix, "libburn : "); + if(handle != NULL) + strncpy(abort_message_prefix, (char *) handle, + sizeof(abort_message_prefix)-1); + abort_message_prefix[sizeof(abort_message_prefix)-1] = 0; + abort_control_pid= getpid(); + Cleanup_set_handlers(handle, (Cleanup_app_handler_T) handler, mode|4); +} + diff --git a/libburn/libburn.h b/libburn/libburn.h index 5a5418f..a00358c 100644 --- a/libburn/libburn.h +++ b/libburn/libburn.h @@ -392,7 +392,14 @@ int burn_initialize(void); void burn_finish(void); +/* ts A61002 */ /** Abort any running drive operation and finally call burn_finish(). + You MUST calm down the busy drive if an aborting event occurs during a + burn run. For that you may call this function either from your own signal + handling code or indirectly by activating the builtin signal handling: + burn_set_signal_handling("my_app_name : ", NULL, 0); + Else you may eventually call burn_drive_cancel() on the active drive and + wait for it to assume state BURN_DRIVE_IDLE. @param patience Maximum number of seconds to wait for drives to finish @param pacifier_func If not NULL: a function to produce appeasing messages. See burn_abort_pacifier() for an example. @@ -406,6 +413,8 @@ int burn_abort(int patience, /** A pacifier function suitable for burn_abort. @param handle If not NULL, a pointer to a text suitable for printf("%s") + @param patience Maximum number of seconds to wait + @param elapsed Elapsed number of seconds */ int burn_abort_pacifier(void *handle, int patience, int elapsed); @@ -1071,6 +1080,29 @@ int burn_msgs_obtain(char *minimum_severity, char severity[]); +/* ts A61002 */ +/* The prototype of a handler function suitable for burn_set_abort_handling(). + Such a function has to return -2 if it does not want the process to + exit with value 1. +*/ +typedef int (*burn_abort_handler_t)(void *handle, int signum, int flag); + +/** Control builtin signal handling. See also burn_abort(). + @param handle Opaque handle eventually pointing to an application + provided memory object + @param handler A function to be called on signals. It will get handle as + argument. It should finally call burn_abort(). See there. + @param mode : 0 call handler(handle, signum, 0) on nearly all signals + 1 enable system default reaction on all signals + 2 try to ignore nearly all signals + 10 like mode 2 but handle SIGABRT like with mode 0 + Arguments (text, NULL, 0) activate the builtin abort handler. It will + eventually call burn_abort() and then perform exit(1). If text is not NULL + then it is used as prefix for pacifier messages of burn_abort_pacifier(). +*/ +void burn_set_signal_handling(void *handle, burn_abort_handler_t handler, + int mode); + #ifndef DOXYGEN BURN_END_DECLS diff --git a/test/libburner.c b/test/libburner.c index 85b81f5..99d56dc 100644 --- a/test/libburner.c +++ b/test/libburner.c @@ -16,6 +16,8 @@ Before you can do anything, you have to initialize libburn by burn_initialize() + and provide some signal and abort handling, e.g. by the builtin handler, by + burn_set_signal_handling() as it is done in main() at the end of this file. Then you aquire a drive in an appropriate way conforming to the API. The two main approaches are shown here in application functions: @@ -248,10 +250,8 @@ int libburner_aquire_by_driveno(int *driveno) To our knowledge it is hardly possible to abort an ongoing blank operation because after start it is entirely handled by the drive. - So expect a blank run to survive the end of the blanking process and be - patient for the usual timespan of a normal blank run. Only after that - time has surely elapsed, only then you should start any rescue attempts - with the drive. If necessary at all. + So expect signal handling to wait the normal blanking timespan until it + can allow the process to end. External kill -9 will not help the drive. */ int libburner_blank_disc(struct burn_drive *drive, int blank_fast) { @@ -322,25 +322,11 @@ int libburner_regrab(struct burn_drive *drive) { /** Brings the preformatted image (ISO 9660, afio, ext2, whatever) onto media. - To make sure your image is fully readable on any Linux machine, this function adds 300 kB of padding to the track. - Without a signal handler it is quite dangerous to abort the process - while this function is active. See cdrskin/cdrskin.c and its usage - of cdrskin/cleanup.[ch] for an example of application provided - abort handling. It must cope with 2 of 3 threads reporting for - being handled. - - Without signal handler have ready a command line - cdrecord dev=... -reset - with a dev= previously inquired by cdrecord [dev=ATA] -scanbus - in order to get your drive out of shock state after raw abort. - Thanks to Joerg Schilling for helping out unquestioned. :) - - In general, libburn is less prone to system problems than cdrecord, - i believe. But cdrecord had long years of time to complete itself. - We are still practicing. Help us with that. :)) + In case of external signals expect abort handling of an ongoing burn to + last up to a minute. Wait the normal burning timespan before any kill -9. */ int libburner_payload(struct burn_drive *drive, const char *source_adr, off_t size) @@ -590,6 +576,10 @@ int main(int argc, char **argv) exit(33); } + /* Activate the default signal handler which eventually will try to + properly shutdown drive and library on aborting events. */ + burn_set_signal_handling("libburner : ", NULL, 0); + /** Note: driveno might change its value in this call */ ret = libburner_aquire_drive(drive_adr, &driveno); if (ret<=0) {