123
123

Reputation: 8951

Signal returned from SIGCHLD appears to be wrong

I've written a basic shell in C, and I'm trying to catch a SIGTSTP signal from a child process. To do this, I've set up a handler for SIGCHLD, but the signal number being returned is 20, when it should be 24.

I have my SIGCHLD handler:

signal(SIGCHLD, trapChld);

void trapChld(int signo) {
    printf("%d", signo);
}

This prints signal 20 when kill -SIGTSTP child_pid is run. Why might this be happening?

Here's my full code:

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

int statusCode;
int foregroundMode = 0;
int bg = 0;
int bgPsArray[20];
int bgPsCount = 0;
int i;
char line[256];

pid_t popBgProcess() {
    int size = sizeof(bgPsArray)/sizeof(bgPsArray[0]);
    if (size > 0) {
      return bgPsArray[size+1];
    } else {
        return 0;
    }
}

void trapInterrupt(int _) {
    int childStatus;
    pid_t child;
    while ((child = popBgProcess())) {
        if(child != getpid()) {
            kill(child, SIGKILL);
            waitpid(child, &childStatus, 0);
        }
    }
}

void trapChld(int signo) {
    printf("%d", signo);
    if(signo == 24) {
        if(foregroundMode == 0) {
            write(1, "Entering foreground-only mode (& is now ignored)\n", 49);
            write(1, ": ", 2);
            fflush(stdout);
            foregroundMode = 1;
        } else {
            write(1, "Exiting foreground-only mode\n", 29);
            write(1, ": ", 2);
            fflush(stdout);
            foregroundMode = 0;
        }
    }
}

int getCommand() {
    printf(": ");
    fflush(stdout);
    if(fgets(line, sizeof(line), stdin) != NULL) {
        char *position = strchr(line, '\n');
    *position = '\0'; // Replace '\n' with '\0'
        if(foregroundMode == 1) { // Foreground mode on
            if((position = strchr(line, '&')) != NULL) {
                *position = '\0'; // Replace '&' with '\0'
            }
            bg = 0; // Ignore '&' so do not create background process
        } else { // Foreground mode off
            if((position = strchr(line, '&')) != NULL) {
                *position = '\0'; // Replace '&' with '\0'
                bg = 1; // Is a background process
            } else {
                bg = 0;
            }
        }
  } else { // If input is null
        return 0;
  }
    return 1;
}

void checkProcessCompletion() {
    int status;
    for(i=0; i<bgPsCount; i++) {
        if(waitpid(bgPsArray[i], &status, WNOHANG) > 0) {
            if(WIFEXITED(status)) { // If exit
                printf("Background PID %d is done: exit value %d\n", bgPsArray[i], WEXITSTATUS(status));
                fflush(stdout);
            } else if(WIFSIGNALED(status)) { // If signal
                printf("Background PID %d is done: terminated by signal %d\n", bgPsArray[i], WTERMSIG(status));
                fflush(stdout);
            }
        }
    }
}

int runCommand(int cmd) {
    if(cmd == 0) { // Return if there was no command
        return 0;
    } else if(strcmp(line, "exit") == 0) {
        exit(0);
    } else if(strstr(line, "#")) { // Comment input (do nothing)
    } else if(strcmp(line, "status") == 0) {
        printf("exit value %d\n", statusCode);
        fflush(stdout);
    }
    else if(strncmp("cd", line, strlen("cd")) == 0) {
        if(line[2] == ' ') { // If space after 'cd' expect directory
            char cwd[1024];
            getcwd(cwd, sizeof(cwd));
            char *path = strstr(line, " ");
            if(path) {
                path += 1;
                char *value;
                value = malloc(strlen(path));
                memcpy(value, path, strlen(path));
                *(value + strlen(path)) = 0;
                sprintf(cwd, "%s/%s", cwd, value); // Directory to change to
                free(value);
            }
            chdir(cwd); // cd to new directory
        } else { // cd with no argument
            char *home = getenv("HOME");
            chdir(home); // cd to HOME directory
        }
    }
    else { // System commands
        pid_t pid, ppid;
        int status;
        char *command;
        char *args[256];
        int argCount;
        command = strtok(line, " ");

        // Create args array for execvp
        args[0] = command;
        argCount = 1;
        args[argCount] = strtok(NULL, " ");
        while(args[argCount] != NULL) { // Add arguments to array
            argCount++;
            args[argCount] = strtok(NULL, " ");
        }
        if((pid = fork()) < 0) { // Fork fails
            perror("fork");
            fflush(stdout);
            exit(1);
        }
        if(pid == 0) { // Child process
            for(i=0; i<argCount; i++) {
                if(strcmp(args[i], "<") == 0) { // Redirecting input
                    if(access(args[i+1], R_OK) == -1) { // File is unreadable
                        perror("access");
                        fflush(stdout);
                    } else { // File is readable
                        int file = open(args[i+1], O_RDONLY, 0);
                        dup2(file, STDIN_FILENO);
                        close(file);
                        execvp(command, &command);
                    }
                }
                else if(strcmp(args[i], ">") == 0) { // Redirecting output
                    int file = creat(args[i+1], 7777);
                    dup2(file, STDOUT_FILENO);
                    close(file);
                    execvp(command, args);
                } else { // No redirection
                    execvp(command, args);
                }
            }
            perror("execvp"); // Error for execvp
            exit(1);
        } else { // Parent process
            if (bg == 1) { // Background process
                int status;
                int process;
                printf("Background PID: %d\n", pid);
                fflush(stdout);
                bgPsArray[bgPsCount] = pid; // Add process to background process array
                bgPsCount++;
                process = waitpid(pid, &status, WNOHANG);
            } else { // Foreground process
                int status;
                waitpid(pid, &status, 0); // Wait on the process
                if(WIFEXITED(status)) {
                    statusCode = WEXITSTATUS(status);
                }
            }
        }
    }
    return 1;
}

int main(int argc, char *argv[], char *envp[]) {
    // Creating 'junk' manually is necessary because output redirection is broken,
    // and a large portion of the grading script is depedent upon it's existence.
    FILE *fp = fopen("junk", "ab+");
    const char *text;
    fprintf(fp, "Junk in junkfile\n");
    fclose(fp);
    signal(SIGINT, trapInterrupt);
    signal(SIGCHLD, trapChld);
    while(1) {
        checkProcessCompletion(); //Check the processes
        int cmd = getCommand(); // Get command from user
        int result = runCommand(cmd);
        if (result == 0) {
            break;
        }
    }
    return 0;
}

Upvotes: 1

Views: 434

Answers (1)

Michael Wojcik
Michael Wojcik

Reputation: 158

You haven't told us what platform you're running on, so this is just a guess, but perhaps it's because that platform defines SIGTSTP as 20?

Linux does, for example:

$ grep SIGTSTP /usr/include/asm/signal.h
#define SIGTSTP         20

A better question is why do you think it should be 24? On AIX it's 18. On HP-UX it's 25. Various Cygwin headers define it as 8, 18, or 24 (because the Cygwin headers come from glib and are full of platform-specific conditional-compilation shenanigans); 18 is the actual value used at runtime.

On Solaris, now, it happens to be 24. I believe Solaris 2 inherited that from SVR4, and subsequent Solaris releases kept it. But the signal numbers are not standardized by any of the applicable specifications (SUS and its ancestors, such as POSIX and XPG3).

Don't assume the signal values are fixed. That's why you have signal.h.

Oh, and sigaction(2) is preferable to signal(2) on platforms that support it, which is most of them.

Upvotes: 1

Related Questions