From: anders@broadcom.com Date: Wed, 7 Mar 2001 14:35:24 +0000 (-0800) Subject: A modified version of X-Git-Url: http://git.shadowcat.co.uk/gitweb/gitweb.cgi?a=commitdiff_plain;h=1dfe7606714789cb685cd524877388fe1e9008b4;p=p5sagit%2Fp5-mst-13.2.git A modified version of Subject: [ID 20010307.005] POSIX::sigaction has various problems Message-Id: <200103072235.OAA25368@dt-sj1-130.sj.broadcom.com> Currently the sigaction.t test #6 fails (and is fudged to look like an "ok") in Linux (at least in Debian 2.2 Linux 2.4.2 x86). This may well be a genuine bug in Linux sigaction() (since at least Tru64, Solaris, and HP-UX disagree with Linux). Anyone with POSIX / SUSv2 tome handy? The problem is that the flags of the oldaction don't match with the flags in the previously installed disposition. p4raw-id: //depot/perl@9086 --- diff --git a/MANIFEST b/MANIFEST index 3fa88be..33ee169 100644 --- a/MANIFEST +++ b/MANIFEST @@ -1501,6 +1501,7 @@ t/lib/sdbm.t See if SDBM_File works t/lib/searchdict.t See if Search::Dict works t/lib/selectsaver.t See if SelectSaver works t/lib/selfloader.t See if SelfLoader works +t/lib/sigaction.t See if POSIX::sigaction works t/lib/socket.t See if Socket works t/lib/soundex.t See if Soundex works t/lib/st-06compat.t See if Storable works diff --git a/ext/POSIX/POSIX.pm b/ext/POSIX/POSIX.pm index e1e6b28..2ec44f8 100644 --- a/ext/POSIX/POSIX.pm +++ b/ext/POSIX/POSIX.pm @@ -6,7 +6,7 @@ use AutoLoader; use XSLoader (); -our $VERSION = "1.03" ; +our $VERSION = "1.04" ; # Grandfather old foo_h form to new :foo_h form my $loaded; diff --git a/ext/POSIX/POSIX.xs b/ext/POSIX/POSIX.xs index 861900a..83498ed 100644 --- a/ext/POSIX/POSIX.xs +++ b/ext/POSIX/POSIX.xs @@ -2777,6 +2777,17 @@ not_there: return 0; } +static void +restore_sigmask(sigset_t *ossetp) +{ + /* Fortunately, restoring the signal mask can't fail, because + * there's nothing we can do about it if it does -- we're not + * supposed to return -1 from sigaction unless the disposition + * was unaffected. + */ + (void)sigprocmask(SIG_SETMASK, ossetp, (sigset_t *)0); +} + MODULE = SigSet PACKAGE = POSIX::SigSet PREFIX = sig POSIX::SigSet @@ -3374,9 +3385,9 @@ tanh(x) NV x SysRet -sigaction(sig, action, oldaction = 0) +sigaction(sig, optaction, oldaction = 0) int sig - POSIX::SigAction action + SV * optaction POSIX::SigAction oldaction CODE: #ifdef WIN32 @@ -3386,9 +3397,12 @@ sigaction(sig, action, oldaction = 0) # interface look beautiful, which is hard. { + POSIX__SigAction action; GV *siggv = gv_fetchpv("SIG", TRUE, SVt_PVHV); struct sigaction act; struct sigaction oact; + sigset_t sset; + sigset_t osset; POSIX__SigSet sigset; SV** svp; SV** sigsvp = hv_fetch(GvHVn(siggv), @@ -3397,11 +3411,61 @@ sigaction(sig, action, oldaction = 0) TRUE); STRLEN n_a; - /* Remember old handler name if desired. */ + /* Check optaction and set action */ + if(SvTRUE(optaction)) { + if(sv_isa(optaction, "POSIX::SigAction")) + action = (HV*)SvRV(optaction); + else + croak("action is not of type POSIX::SigAction"); + } + else { + action=0; + } + + /* sigaction() is supposed to look atomic. In particular, any + * signal handler invoked during a sigaction() call should + * see either the old or the new disposition, and not something + * in between. We use sigprocmask() to make it so. + */ + sigfillset(&sset); + RETVAL=sigprocmask(SIG_BLOCK, &sset, &osset); + if(RETVAL == -1) + XSRETURN(1); + ENTER; + /* Restore signal mask no matter how we exit this block. */ + SAVEDESTRUCTOR(restore_sigmask, &osset); + + RETVAL=-1; /* In case both oldaction and action are 0. */ + + /* Remember old disposition if desired. */ if (oldaction) { - char *hand = SvPVx(*sigsvp, n_a); svp = hv_fetch(oldaction, "HANDLER", 7, TRUE); - sv_setpv(*svp, *hand ? hand : "DEFAULT"); + if(!svp) + croak("Can't supply an oldaction without a HANDLER"); + if(SvTRUE(*sigsvp)) { /* TBD: what if "0"? */ + sv_setsv(*svp, *sigsvp); + } + else { + sv_setpv(*svp, "DEFAULT"); + } + RETVAL = sigaction(sig, (struct sigaction *)0, & oact); + if(RETVAL == -1) + XSRETURN(1); + /* Get back the mask. */ + svp = hv_fetch(oldaction, "MASK", 4, TRUE); + if (sv_isa(*svp, "POSIX::SigSet")) { + IV tmp = SvIV((SV*)SvRV(*svp)); + sigset = INT2PTR(sigset_t*, tmp); + } + else { + New(0, sigset, 1, sigset_t); + sv_setptrobj(*svp, sigset, "POSIX::SigSet"); + } + *sigset = oact.sa_mask; + + /* Get back the flags. */ + svp = hv_fetch(oldaction, "FLAGS", 5, TRUE); + sv_setiv(*svp, oact.sa_flags); } if (action) { @@ -3410,15 +3474,29 @@ sigaction(sig, action, oldaction = 0) svp = hv_fetch(action, "HANDLER", 7, FALSE); if (!svp) croak("Can't supply an action without a HANDLER"); - sv_setpv(*sigsvp, SvPV(*svp, n_a)); + sv_setsv(*sigsvp, *svp); mg_set(*sigsvp); /* handles DEFAULT and IGNORE */ - act.sa_handler = PL_sighandlerp; + if(SvPOK(*svp)) { + char *s=SvPVX(*svp); + if(strEQ(s,"IGNORE")) { + act.sa_handler = SIG_IGN; + } + else if(strEQ(s,"DEFAULT")) { + act.sa_handler = SIG_DFL; + } + else { + act.sa_handler = PL_sighandlerp; + } + } + else { + act.sa_handler = PL_sighandlerp; + } /* Set up any desired mask. */ svp = hv_fetch(action, "MASK", 4, FALSE); if (svp && sv_isa(*svp, "POSIX::SigSet")) { IV tmp = SvIV((SV*)SvRV(*svp)); - sigset = INT2PTR(sigset_t*, tmp); + sigset = INT2PTR(sigset_t*, tmp); act.sa_mask = *sigset; } else @@ -3427,35 +3505,16 @@ sigaction(sig, action, oldaction = 0) /* Set up any desired flags. */ svp = hv_fetch(action, "FLAGS", 5, FALSE); act.sa_flags = svp ? SvIV(*svp) : 0; - } - /* Now work around sigaction oddities */ - if (action && oldaction) - RETVAL = sigaction(sig, & act, & oact); - else if (action) + /* Don't worry about cleaning up *sigsvp if this fails, + * because that means we tried to disposition a + * nonblockable signal, in which case *sigsvp is + * essentially meaningless anyway. + */ RETVAL = sigaction(sig, & act, (struct sigaction *)0); - else if (oldaction) - RETVAL = sigaction(sig, (struct sigaction *)0, & oact); - else - RETVAL = -1; - - if (oldaction) { - /* Get back the mask. */ - svp = hv_fetch(oldaction, "MASK", 4, TRUE); - if (sv_isa(*svp, "POSIX::SigSet")) { - IV tmp = SvIV((SV*)SvRV(*svp)); - sigset = INT2PTR(sigset_t*, tmp); - } - else { - New(0, sigset, 1, sigset_t); - sv_setptrobj(*svp, sigset, "POSIX::SigSet"); - } - *sigset = oact.sa_mask; - - /* Get back the flags. */ - svp = hv_fetch(oldaction, "FLAGS", 5, TRUE); - sv_setiv(*svp, oact.sa_flags); } + + LEAVE; } #endif OUTPUT: diff --git a/t/lib/sigaction.t b/t/lib/sigaction.t new file mode 100644 index 0000000..cb3380b --- /dev/null +++ b/t/lib/sigaction.t @@ -0,0 +1,126 @@ +#!./perl + +BEGIN { + chdir 't' if -d 't'; + unshift @INC, '../lib'; +} + +BEGIN{ + # Don't do anything if POSIX is missing, or sigaction missing. + eval { use POSIX; }; + if($@ || $^O eq 'MSWin32') { + print "1..0\n"; + exit 0; + } +} + +use strict; +use vars qw/$bad7 $ok10 $bad18 $ok/; + +$^W=1; + +print "1..18\n"; + +sub IGNORE { + $bad7=1; +} + +sub DEFAULT { + $bad18=1; +} + +sub foo { + $ok=1; +} + +my $newaction=POSIX::SigAction->new('::foo', new POSIX::SigSet(SIGUSR1), 0); +my $oldaction=POSIX::SigAction->new('::bar', new POSIX::SigSet(), 0); + +{ + my $bad; + local($SIG{__WARN__})=sub { $bad=1; }; + sigaction(SIGHUP, $newaction, $oldaction); + if($bad) { print "not ok 1\n" } else { print "ok 1\n"} +} + +if($oldaction->{HANDLER} eq 'DEFAULT') + { print "ok 2\n" } else { print "not ok 2\n"} +print $SIG{HUP} eq '::foo' ? "ok 3\n" : "not ok 3\n"; + +sigaction(SIGHUP, $newaction, $oldaction); +if($oldaction->{HANDLER} eq '::foo') + { print "ok 4\n" } else { print "not ok 4\n"} +if($oldaction->{MASK}->ismember(SIGUSR1)) + { print "ok 5\n" } else { print "not ok 5\n"} +if($oldaction->{FLAGS}) { + if ($^O eq 'linux') { + print "ok 6 # Skip: sigaction() broken in $^O\n"; + } else { + print "not ok 6\n"; + } +} else { + print "ok 6\n"; +} + +$newaction=POSIX::SigAction->new('IGNORE'); +sigaction(SIGHUP, $newaction); +kill 'HUP', $$; +print $bad7 ? "not ok 7\n" : "ok 7\n"; + +print $SIG{HUP} eq 'IGNORE' ? "ok 8\n" : "not ok 8\n"; +sigaction(SIGHUP, POSIX::SigAction->new('DEFAULT')); +print $SIG{HUP} eq 'DEFAULT' ? "ok 9\n" : "not ok 9\n"; + +$newaction=POSIX::SigAction->new(sub { $ok10=1; }); +sigaction(SIGHUP, $newaction); +{ + local($^W)=0; + kill 'HUP', $$; +} +print $ok10 ? "ok 10\n" : "not ok 10\n"; + +print ref($SIG{HUP}) eq 'CODE' ? "ok 11\n" : "not ok 11\n"; + +sigaction(SIGHUP, POSIX::SigAction->new('::foo')); +# Make sure the signal mask gets restored after sigaction croak()s. +eval { + my $act=POSIX::SigAction->new('::foo'); + delete $act->{HANDLER}; + sigaction(SIGINT, $act); +}; +kill 'HUP', $$; +print $ok ? "ok 12\n" : "not ok 12\n"; + +undef $ok; +# Make sure the signal mask gets restored after sigaction returns early. +my $x=defined sigaction(SIGKILL, $newaction, $oldaction); +kill 'HUP', $$; +print !$x && $ok ? "ok 13\n" : "not ok 13\n"; + +$SIG{HUP}=sub {}; +sigaction(SIGHUP, $newaction, $oldaction); +print ref($oldaction->{HANDLER}) eq 'CODE' ? "ok 14\n" : "not ok 14\n"; + +eval { + sigaction(SIGHUP, undef, $oldaction); +}; +print $@ ? "not ok 15\n" : "ok 15\n"; + +eval { + sigaction(SIGHUP, 0, $oldaction); +}; +print $@ ? "not ok 16\n" : "ok 16\n"; + +eval { + sigaction(SIGHUP, bless({},'Class'), $oldaction); +}; +print $@ ? "ok 17\n" : "not ok 17\n"; + +$newaction=POSIX::SigAction->new(sub { $ok10=1; }); +sigaction(SIGCONT, POSIX::SigAction->new('DEFAULT')); +{ + local($^W)=0; + kill 'CONT', $$; +} +print $bad18 ? "not ok 18\n" : "ok 18\n"; +