123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300(* $Id$ *)openPrintfexternal_exit:int->unit="netsys__exit";;(* needed here to avoid dep on Netsys *)typeaction=[`Callbackofint->unit|`Installofunit->unit]typeentry={sig_number:int;sig_library:stringoption;sig_priority:int;sig_keep_default :bool;sig_name:string;sig_action :action;}moduleDebug=structletenable=reffalseendletdlog=Netlog.Debug.mk_dlog"Netsys_signal"Debug.enableletdlogr=Netlog.Debug.mk_dlogr"Netsys_signal"Debug.enablelet()=Netlog.Debug.register_module"Netsys_signal"Debug.enableletis_win32=matchSys.os_typewith|"Win32"->true|_->falseletsig_enable=Hashtbl.create30(** Maps signal number to [`Unchanged], [`Enabled], [`Disabled], or
[`Exclusive].
[`Unchanged] is the same as if there was no entry for the signal
number and means that [Sys.set_signal] has not been called yet.
[`Enabled] means this function has been called. [`Disabled] means
that the signal is on the "keep away list". [`Exclusive] is like
[`Enabled] for exclusive handlers.
*)letsig_definition=Hashtbl.create30(** Maps signal number to [entry] in priority order. *)letotp=!Netsys_oothr.providerletmutex=otp#create_mutex()letwhile_locked f=Netsys_oothr.serializemutexf()letlethal_default_actions =[Sys.sighup;Sys.sigint;Sys.sigquit;Sys.sigill;Sys.sigabrt;Sys.sigfpe;(* Sys.sigkill *)Sys.sigsegv;(* Sys.sigpipe *)Sys.sigalrm;Sys.sigterm;Sys.sigusr1;Sys.sigusr2;]lethandlesigno=dlogr(fun()->sprintf"handle %d" signo);letentries=tryHashtbl.findsig_definitionsignowithNot_found ->[]inletemulate_default=reftrueinList.iter(funentry->emulate_default :=!emulate_default &&entry.sig_keep_default;trymatch entry.sig_actionwith|`Install_->()|`Callbackcb->dlogr(fun()->sprintf "signal %d calling back %s handler '%s'"signo(matchentry.sig_librarywith|None->"application"|Somelib->"library '"^lib^"'")entry.sig_name);cbsignowith_->()(* Behard with exceptions here! *))entries;if!emulate_defaultthen(dlogr(fun()->sprintf "signal %d emulating default"signo);(* If the default signal action is not "ignore", it is an action that
will terminate the process. So we can emulate it by setting the
signal handler again, and by sending the signal to the process.
*)ifList.memsignolethal_default_actionsthen(_exit126;(* - This does not reliably work in multi-threaded programs: *)(*
ignore(Sys.signal signo Sys.Signal_default);
Unix.kill (Unix.getpid()) signo;
(* The signal signo is pending but usually blocked because this function
* is called from within the signal handler for signo. To force that
* the signal is delivered we must unblock the signal.
*)
ignore(Unix.sigprocmask Unix.SIG_UNBLOCK [ signo ]);
(* Wait for any signal - at least signo will happen! *)
while true do Unix.pause() done;
(* Never return to this point of execution! *)
assert false
*)))letsig_managesigno=(* must not be calledfor `Exclusive handlers! *)letis_disabled=tryHashtbl.findsig_enablesigno=`DisabledwithNot_found->falseinifnotis_disabledthen((trydlogr(fun()->sprintf"signal %d installing normal handler"signo);Sys.set_signalsigno(Sys.Signal_handle handle)withInvalid_argument_->());Hashtbl.replacesig_enablesigno`Enabled)let sig_installentry=(* only for `Exclusive handlers! *)letis_disabled=tryHashtbl.findsig_enableentry.sig_number=`DisabledwithNot_found->falseinifnotis_disabledthen(matchentry.sig_actionwith|`Callback_->assertfalse|`Installf->dlogr(fun()->sprintf"signal %d installing excl handler"entry.sig_number);f();Hashtbl.replacesig_enableentry.sig_number`Exclusive)letrestore_managementsigno=while_locked(fun()->letstate=tryHashtbl.findsig_enablesignowithNot_found->`Unchangedinmatchstatewith|`Enabled->sig_managesigno|`Exclusive->(matchHashtbl.findsig_definitionsignowith|[e]->sig_installe|_->assertfalse)|_->())letkeep_away_fromsigno=while_locked(fun()->letstate=tryHashtbl.findsig_enablesignowithNot_found->`Unchangedindlogr(fun()->sprintf"signal %d keep away %s"signo(matchstatewith|`Enabled ->"- HANDLER ALREADY DEFINED!"|`Exclusive->"- EXCLUSIVE HANDLER ALREADY INSTALLED!"|_->""));Hashtbl.replacesig_enablesigno`Disabled)letregister_handler?library?priority?(keep_default=false)~name ~signal~callback()=while_locked(fun()->letstate=tryHashtbl.findsig_enablesignalwithNot_found->`Unchangedinifstate=`Exclusivethenfailwith("Netsys_signal.register_handler: \
Cannot override an exclusive handler (signal " ^string_of_intsignal^")");letentry={sig_number =signal;sig_library =library;sig_priority =iflibrary=Nonethen100else0;sig_keep_default=keep_default;sig_name=name;sig_action =`Callbackcallback}inletold_list=tryHashtbl.findsig_definitionsignalwithNot_found ->[]inletsame_handlere1e2=e1.sig_number=e2.sig_number&&e1.sig_library=e2.sig_library&&e1.sig_name=e2.sig_nameinlethdl_exists=List.exists(fune->same_handlerentrye)old_listinletrm_list=ifhdl_exists thenList.filter(fune->not(same_handlerentrye))old_listelseold_list inletnew_list=List.sort(fune1e2->matchcompare e1.sig_prioritye2.sig_prioritywith|0->comparee1.sig_namee2.sig_name|n->n)(entry::rm_list)inHashtbl.replacesig_definition signalnew_list;sig_managesignal)letregister_exclusive_handler~name~signal~install()=while_locked(fun()->letstate=tryHashtbl.findsig_enablesignalwithNot_found->`Unchangedinifstate=`Enabledthenfailwith("Netsys_signal.register_exclusive_handler: \
There is already a handler definition (signal "^string_of_intsignal^")");letentry={sig_number =signal;sig_library =None;sig_priority =100;sig_keep_default=false;sig_name=name;sig_action=`Installinstall}inHashtbl.replacesig_definitionsignal[entry];sig_installentry)letlist()=while_locked(fun()->Hashtbl.fold(fun_lacc->l@acc)sig_definition[])letkeep_away_list()=while_locked(fun()->Hashtbl.fold(funsignostateacc->ifstate=`Disabled thensigno::accelseacc)sig_enable[])let()=ifis_win32then((* Disable all except Sys.sigint: *)List.iter(funsigno->Hashtbl.addsig_enablesigno`Disabled)[Sys.sigabrt;Sys.sigalrm;Sys.sigfpe;Sys.sighup;Sys.sigill;Sys.sigkill;Sys.sigpipe;Sys.sigquit;Sys.sigsegv;Sys.sigterm;Sys.sigusr1;Sys.sigusr2;Sys.sigchld;Sys.sigcont;Sys.sigstop;Sys.sigtstp;Sys.sigttin;Sys.sigttou;Sys.sigvtalrm;Sys.sigprof])let()=register_handler~library:"netsys"~name:"Sigpipe default handler"~signal:Sys.sigpipe~callback:(fun_->())()letinit()=()