views:

111

answers:

3

I've been trying to figure out if this is possible the way I've done it or not. This program should fork a child process that loops printing to STDOUT, and the parent should exit to return the terminal prompt. The child should then be waiting for SIGINT to tell it when to close down. However I remember reading that SIGINT is only send to processes in the foreground, which explains why my abandoned child isn't affected by CTRL+C. Is there any way to either get the abandoned child to receive a signal sent from the terminal, or some system call in the terminal to bring it to the foreground where it can receive SIGINT? Or is my search hopeless?

Code:

#include  <stdio.h>
#include  <unistd.h>
#include  <stdlib.h>
#include  <signal.h>
#include  <sys/wait.h>
#include  <sys/types.h>

/* signal handler for the child process */
void catchInt (int signum)
{
 printf("\nMy sincerest apologies, master\n");
    exit(0);
}

/* signal handler for the parent process */
void ignoreInt (int signum)
{
 /* prevent any extra output from being printed */
 fflush(stdout); 
 /* wait for child to apologize before replying */
 wait(NULL);
 printf("You're welcome\n");
 exit(0);
}

/* signal handler for the child's alarm */
void catchAlarm (int signum)
{
 printf("It's great to be alive\n");
 /* reset the alarm */
 signal(SIGALRM, catchAlarm);
 alarm(3);
}

int main () {

 pid_t  pid;

 /* fork process */
 pid = fork();
 if (pid < 0) /* error handler */ 
 {   
  fprintf(stderr, "Fork Failed");
  exit(-1);
 }

 /* child */
 else if (pid == 0) 
 { 
  printf("It's great to be alive\n");
  /* catch SIGINT and handle as the child should */
  signal(SIGINT, catchInt);
  /* catch SIGALRM being sent by alarm() */
  signal(SIGALRM, catchAlarm);
  /* set alarm for 3 seconds */
  alarm(3);
  for ( ;; )
  {
   printf("I have 42001 children and not one comes to visit\n");
   usleep(500000);
  }   
 }

 /* parent */
 else 
 {
  /* exit to abandon child process in the background */
  exit(0);
 }

 return(0);
}
A: 

You can use the kill command to send a signal to a specified process number:

kill -2 12345

Of course, it helps if your code identifies which PID to kill (the child should report its PID when it starts looping). But with just trivial changes (like omitting the unused ignoreInt() function, and reporting the child process's PID), it worked OK as written - and the kill command worked on it too.

Compressed code:

#include  <stdio.h>
#include  <unistd.h>
#include  <stdlib.h>
#include  <signal.h>

static void catchInt(int signum)
{
    printf("\nMy sincerest apologies, master (%d)\n", signum);
    exit(0);
}

static void catchAlarm(int signum)
{
    printf("It's great to be alive (%d)\n", signum);
    signal(SIGALRM, catchAlarm);
    alarm(3);
}

int main(void)
{
    pid_t  pid = fork();

    if (pid < 0)
    {   
        fprintf(stderr, "Fork Failed");
        exit(-1);
    }
    else if (pid == 0) 
    { 
        printf("It's great to be alive (%d)\n", (int)getpid());
        signal(SIGINT, catchInt);
        signal(SIGALRM, catchAlarm);
        alarm(3);
        for ( ;; )
        {
            printf("I have 42001 children and not one comes to visit\n");
            usleep(500000);
        }   
    }
    return(0);
}

If you are going to include <sys/types.h>, it should normally be the first of the POSIX headers (and hence before <unistd.h>). The fact that it is listed last suggests that you should not need it at all - but I seem to remember a previous question where you asserted that it was necessary. (OTOH, there is no call to any of the wait() family, so <sys/wait.h> is unnecessary, and I'm moderately convinced that <sys/types.h> is not needed on most modern systems.)

Jonathan Leffler
Haha, I'm almost positive my netbook distro of Ubuntu is the source of my sys/types.h troubles, without it pid_t is undeclared for me. But there are other such headers missing on my system, so it's not shocking. The reason for the excessive headers is I believe I just copied all my headers over quickly when I made this and haven't trimmed the fat yet. Thanks for the response though, kill works well enough for my purposes atm.
JKomusin
A: 

No, the SIGINT signal is only sent to foreground processes when it's actioned by using CTRLC (or whatever stty has the intr keystroke set to).

You can always send a SIGINT to a specific process with kill -INT 99999 (where 99999 is the process ID).

paxdiablo
Thanks, kill -INT will have to do for now.
JKomusin
+1  A: 

If you want your child to recieve SIGINT when the interrupt character is hit on the controlling terminal, it needs to be in the foreground process group. You can achieve this:

int ctty = open("/dev/tty", O_RDONLY);
while (tcgetpgrp(ctty) == getpgrp())
    usleep(100000);
setpgid(0, tcgetpgrp(ctty));
close(ctty);

(You have to wait until the shell changes the foreground process group after your parent exits, though - I'm not sure of a better way to do that than spinning in a loop, as in the example. Suggestions welcome...)


PS: Be aware that the foreground process group can change at any time - for example, when another process is run from the shell. I'm not sure exactly what your end goal is, but maybe there's a better way to do it, whatever it is.

caf
This actually works perfectly! I'll have to look into process groups more. Thanks!
JKomusin
@JKomusin: It's not really a perfectly robust solution though, see update.
caf