[comp.unix.questions] help with signals

cdash@boulder.Colorado.EDU (Charles Shub) (03/05/88)

on at&t unix if a system call to (eg) read is in progress when a signal
occurs, the system call fails. On sun unix and ultrix, the system call
restarts. On the sun, one can use siginterrupt(3) to make the system call
fail. How do I make the system call fail under ultrix???????
we are running 1.2 on microvaxen.
-- 

cdash   aka cdash@boulder.colorado.edu    aka ...hao!boulder!cdash
	aka ...nbires!boulder!cdash       aka  (303) 593-3492

wolfgang@mgm.mit.edu (Wolfgang Rupprecht) (03/06/88)

In article <1583@boulder.Colorado.EDU> cdash@boulder.Colorado.EDU (Charles Shub) writes:
>on at&t unix if a system call to (eg) read is in progress when a signal
>occurs, the system call fails. On sun unix and ultrix, the system call
>restarts. On the sun, one can use siginterrupt(3) to make the system call
>fail. How do I make the system call fail under ultrix???????

Another (hack) way to do this involves a setjump/longjump pair. Put a
setjump around the system call, set a signal handler for the signal in
question, and finally put a longjump in the signal handler to the
setjump saved point. (Don't forget to take down the signal handler
after use, or else you will find yourself longjumping back to the same
section of code everytime a signal of that type comes in.)

--
Wolfgang Rupprecht	ARPA:  wolfgang@mgm.mit.edu (IP 18.82.0.114)
326 Commonwealth Ave.	UUCP:  mit-eddie!mgm.mit.edu!wolfgang
Boston, Ma. 02115	TEL:   (617) 267-4365

simon@its63b.ed.ac.uk (Simon Brown) (03/07/88)

In article <1583@boulder.Colorado.EDU> cdash@boulder.Colorado.EDU (Charles Shub) writes:
>on at&t unix if a system call to (eg) read is in progress when a signal
>occurs, the system call fails. On sun unix and ultrix, the system call
>restarts. On the sun, one can use siginterrupt(3) to make the system call
>fail. How do I make the system call fail under ultrix???????
>we are running 1.2 on microvaxen.

A particularly disgusting way to do this is to close(2) the descriptor being
read from inside the signal-handling routine, keeping a dup(2)'d copy so
you can restore it later. The read(2) cannot restart on a closed descriptor,
so it fails (with errno==EBADF). I'm sure there must be a better way (though
it probably isn't so portable :-)):

	extern int descriptor;
	static int dupdescriptor;

	handler()
	{
		dup2(descriptor, dupdescriptor);
		close(descriptor);
	}

	readchar()
	{
		char ch;
		switch (read(descriptor,&ch,1)) {
		    case -1:
			if (errno == EBADF) {
				descriptor = dupdescriptor;
				return(INTERRUPTED);
			} else return(...);
		    case 0:
			return(EOF);
		    default:
			return(ch);
		}
	}

--
-- 
--------------------------------------------------
| Simon Brown                                    |
| Laboratory for Foundations of Computer Science |
| Department of Computer Science                 |
| University of Edinburgh, Scotland, UK.         |
--------------------------------------------------
 UUCP:  uunet!mcvax!ukc!lfcs!simon
 ARPA:  simon%lfcs.ed@nss.cs.ucl.ac.uk      "Life's like that, you know"
 JANET: simon@uk.ac.ed.lfcs

news@oberon.USC.EDU (USENET News) (02/16/89)

I have 2 simple questions about signals...which may get a little involved:

  1. How do you tell which signal has been sent from within the signal
     handler?

     Instead of typing
	if ( signal(SIGINT,SIG_IGN) != SIG_IGN )
	   signal(SIGINT,sig_catch1);

	if ( signal(SIGQUIT,SIG_IGN) != SIG_IGN )
	   signal(SIGQUIT,sig_catch2);

	if ( signal(SIGHUP,SIG_IGN) != SIG_IGN )
	   signal(SIGHUP,sig_catch3);
	   .
	   .
	   .
     for each signal, defining an individual
     handler to process a particular signal, I was thinking of doing 
     something along these lines:
     
	u_short si_arr[]= {SIGINT,SIGQUIT,SIGHUP,...};

        for ( i=0; i < MAX_SIGNO; i++ )
	    if ( signal(sig_arr[i],sig_catch) != SIG_IGN )
	       signal(sig_arr[i],sig_catch);

     and then in the function sig_catch(),
	
      sig_catch()
      {
	switch( signal_no ) {	/* whatever signal_no is */
	  case SIGINIT:
	     /* do something */
	     break;
	  case SIGQUIT:
	     /* do something more */
	     break;
          case SIGHUP:
	     /* and then some */
          default:
        }
      }
    Now, I did RTFM, or at least read signal(3), so I know I can declare
      sig_catch(signal_no,code,scp,addr)
      int signal_no,code;
      struct sigcontext *scp;
      char *addr;
      {
         switch( signal_no ) {	/* and indeed, this works! */
				/* I get the right signal number */
	   case SIGINT:		/* But now if I try to call signal again*/
	      signal(SIGINT,SIG_IGN);
	      break;		/* This WON'T work */
				/* When I try to compile, I get a improper 
					function error */
	   default:
	      break;
      }
    As long as I don't try to call signal() agian, my code compiles and runs
    just fine, but no matter if I try
	 	signal(SIGINT,sig_catch);
    or 		signal(SIGINT,SIG_IGN);
    I can't get the program to compile.  Why is this, and what should I do?
    Alternately, can I find out what signal was delivered, without declaring
    those signal_no,code,scp,addr arguments?

 2. How do you reset the signal mask so that wait() will continue to wait for 
    the child to finish, after a signal interrupts the parent?
    Here's what I mean:  I do a

	ppid = getpid();		/* parent's pid*/
	setjmp(reset_pt);
        if ( ppid != getpid() )		/* kill all child processes */
           exit();
	if ( signal(SIGINT,SIG_IGN) != SIG_IGN )
           signal(sigarr[i],sig_catch);
		.
		.
		.
	if ( !(cpid1=fork()) ) {	/* child 1 */
	   /* do something */
	   exit(0);
        }
	if ( !(cpid2=fork()) ) {	/* child 2 */
	   /* do something */
	   exit(0);
        }
	wait(&status);		/* for a child to finish */
	wait(&status);		/* for the other child to finish*/
  }

  sig_catch()
  {
	longjmp(reset_pt);
  }

Now let's say I start the program up, and let it advance to the wait()
calls. (The child processes take a long time to complete).  I hit ^C,
and all 3 processes longjmp to the reset_pt.  There, the children get
nuked, and only the parent process survives---to fork() off the second
generation of children.  However, when the parent hits the wait() statments,
it slips through!  I believe wait() is reading the exit signal of the
deceased children, not the newly hatched ones. How can I reset the signal
mask that wait() reads, so that wait() ignores the exit signal of the
first 2 child processes? 

Actually, I already do have a fix for this.  I do:
	while ( (pid=wait(&status)) != cpid1 && pid != -1 )
	  ;
	while ( (pid=wait(&status)) != cpid2 && pid != -1 )
	  ;
which works as intended.  However, I would like to understand the details
of how signals work, and how I could have resolved the wait() problem
without embedding it in the while loop.  I've tried reading sigvec(3)
for futher information, but I couldn't make heads or tails of it.

Can please someone help me?


Daniel
dwu@castor.usc.edu
=================================================================
	Daniel Wu		ARPA: dwu@castor.usc.edu
				UUCP: ? dwu@castor.UUCP ?
	I'm not very familiar with the backbone sites this 
	machine's connected to.  Somebody, please send me
	a diagram of major gateways.
=================================================================

dwu@nunki.usc.edu (Daniel Wu) (02/16/89)

I have 2 simple questions about signals...which may get a little involved:

  1. How do you tell which signal has been sent from within the signal
     handler?

     Instead of typing
	if ( signal(SIGINT,SIG_IGN) != SIG_IGN )
	   signal(SIGINT,sig_catch1);

	if ( signal(SIGQUIT,SIG_IGN) != SIG_IGN )
	   signal(SIGQUIT,sig_catch2);

	if ( signal(SIGHUP,SIG_IGN) != SIG_IGN )
	   signal(SIGHUP,sig_catch3);
	   .
	   .
	   .
     for each signal, defining an individual
     handler to process a particular signal, I was thinking of doing 
     something along these lines:
     
	u_short si_arr[]= {SIGINT,SIGQUIT,SIGHUP,...};

        for ( i=0; i < MAX_SIGNO; i++ )
	    if ( signal(sig_arr[i],sig_catch) != SIG_IGN )
	       signal(sig_arr[i],sig_catch);

     and then in the function sig_catch(),
	
      sig_catch()
      {
	switch( signal_no ) {	/* whatever signal_no is */
	  case SIGINIT:
	     /* do something */
	     break;
	  case SIGQUIT:
	     /* do something more */
	     break;
          case SIGHUP:
	     /* and then some */
          default:
        }
      }
    Now, I did RTFM, or at least read signal(3), so I know I can declare
      sig_catch(signal_no,code,scp,addr)
      int signal_no,code;
      struct sigcontext *scp;
      char *addr;
      {
         switch( signal_no ) {	/* and indeed, this works! */
				/* I get the right signal number */
	   case SIGINT:		/* But now if I try to call signal again*/
	      signal(SIGINT,SIG_IGN);
	      break;		/* This WON'T work */
				/* When I try to compile, I get a improper 
					function error */
	   default:
	      break;
      }
    As long as I don't try to call signal() agian, my code compiles and runs
    just fine, but no matter if I try
	 	signal(SIGINT,sig_catch);
    or 		signal(SIGINT,SIG_IGN);
    I can't get the program to compile.  Why is this, and what should I do?
    Alternately, can I find out what signal was delivered, without declaring
    those signal_no,code,scp,addr arguments?

 2. How do you reset the signal mask so that wait() will continue to wait for 
    the child to finish, after a signal interrupts the parent?
    Here's what I mean:  I do a

	ppid = getpid();		/* parent's pid*/
	setjmp(reset_pt);
        if ( ppid != getpid() )		/* kill all child processes */
           exit();
	if ( signal(SIGINT,SIG_IGN) != SIG_IGN )
           signal(sigarr[i],sig_catch);
		.
		.
		.
	if ( !(cpid1=fork()) ) {	/* child 1 */
	   /* do something */
	   exit(0);
        }
	if ( !(cpid2=fork()) ) {	/* child 2 */
	   /* do something */
	   exit(0);
        }
	wait(&status);		/* for a child to finish */
	wait(&status);		/* for the other child to finish*/
  }

  sig_catch()
  {
	longjmp(reset_pt);
  }

Now let's say I start the program up, and let it advance to the wait()
calls. (The child processes take a long time to complete).  I hit ^C,
and all 3 processes longjmp to the reset_pt.  There, the children get
nuked, and only the parent process survives---to fork() off the second
generation of children.  However, when the parent hits the wait() statments,
it slips through!  I believe wait() is reading the exit signal of the
deceased children, not the newly hatched ones. How can I reset the signal
mask that wait() reads, so that wait() ignores the exit signal of the
first 2 child processes? 

Actually, I already do have a fix for this.  I do:
	while ( (pid=wait(&status)) != cpid1 && pid != -1 )
	  ;
	while ( (pid=wait(&status)) != cpid2 && pid != -1 )
	  ;
which works as intended.  However, I would like to understand the details
of how signals work, and how I could have resolved the wait() problem
without embedding it in the while loop.  I've tried reading sigvec(3)
for futher information, but I couldn't make heads or tails of it.

Can please someone help me?


Daniel
dwu@castor.usc.edu

guy@auspex.UUCP (Guy Harris) (02/18/89)

>    Alternately, can I find out what signal was delivered, without declaring
>    those signal_no,code,scp,addr arguments?

Not without declaring "signal_no", but you can blow off the other three
arguments.  You can just do

	sig_catch(signal_no)
		int signal_no;

which all sufficiently modern UNIX implementations should support.  The
fact that the manual page mentions other arguments merely indicates that
the implementors have made it possible for handlers to be declared with
the extra arguments and that, for some signals in some circumstances, those
arguments will have meaningful (and possibly useful) values, not that it
won't work if you declare the function with only one argument.

(If they couldn't make it work regardless of whether you declared it
with only one "int" argument or all the arguments, they'd better have
provided a different mechanism for giving you the other arguments, since
they'll break one hell of a lot of code otherwise.  Fortunately, from
the appearance of the "addr" argument, you're probably working on a Sun
- or a Solbourne :-) - in which case I know it works either way.)

(BTW, note that the SVID, issue 2, actually allows for extra arguments;
on page 123 in SIGNAL(BA_OS), it says:

	...Additional arguments may be passed to the signal-catching
	function for hardware-generated signals.

)

>How can I reset the signal mask that wait() reads, so that wait()
>ignores the exit signal of the first 2 child processes? 

You can't.  "wait" *doesn't* read the signal mask.

>Actually, I already do have a fix for this.  I do:
>	while ( (pid=wait(&status)) != cpid1 && pid != -1 )
>	  ;
>	while ( (pid=wait(&status)) != cpid2 && pid != -1 )
>	  ;
>which works as intended.

That is the correct way to do what you want.