/* * Name: calculator * Author: Manuel Mausz, 0728348 * Description: upon start calculator forks a child. the parent reads messages * from stdin, forwards the messages to the child, which does the * calculation. the result is forwarded to the parent and dumped * to stdout. * Created: 24.05.2009 * Exercise: 3c */ #include #include #include #include #include #include #include #include "error.h" /* maximal input length */ #define MAX_LEN 15 /* install signal handler macro */ #define INSTALL_SIGNAL(signum, act, oldact) \ sigaction(signum, NULL, &oldact); \ if (oldact.sa_handler != SIG_IGN) \ sigaction(signum, &act, NULL); /* global variables */ static int pipew[2]; /* fds for write to child/read from parent */ static int piper[2]; /* fds for read from child/write to parent */ static FILE *streamw = NULL; /* stream for writing to child */ static FILE *streamr = NULL; /* stream for reading from child */ /* * NAME: sigpipe_handler * PURPOSE: * signal handler for SIGPIPE * sets global error flag to 1 * * PARAMETERS: * int signum ... signal number * * RETURN VALUE: * void * * GLOBAL VARS: * error, child */ static void sigpipe_handler(int signum) { #ifdef DEBUG (void) printf("%s SIGPIPE received\n", (child) ? "[CHILD] " : "[PARENT] "); #endif error = 1; } /* * NAME: usage * PURPOSE: * prints program usage to stderr and terminates with EXIT_FAILURE * * PARAMETERS: * void * * RETURN VALUE: * void * * GLOBAL VARS: * me */ static void usage() { (void) fprintf(stderr, "Usage: %s\n", me); (void) fprintf(stderr, "$> \n"); (void) fprintf(stderr, "BNF: ::= -?[0-9]+\n"); (void) fprintf(stderr, " ::= +|-|*|/\n"); (void) bailout(0, NULL); } /* * NAME: allocate_resources * PURPOSE: * allocate resources * * PARAMETERS: * void * * RETURN VALUE: * void * * GLOBAL VARS: * pipew, piper */ void allocate_resources(void) { #ifdef DEBUG (void) printf("[GLOBAL] allocating resources...\n"); #endif if (pipe(pipew) == -1) (void) bailout(1, "Unable to create write pipe!"); if (pipe(piper) == -1) (void) bailout(1, "Unable to create read pipe!"); } /* * NAME: free_resources * PURPOSE: * frees allocated resources * * PARAMETERS: * void * * RETURN VALUE: * void * * GLOBAL VARS: * error, child, streamw, streamr */ void free_resources(void) { #ifdef DEBUG (void) printf("%s freeing resources...\n", (child) ? "[CHILD] " : "[PARENT] "); #endif /* close write stream */ if (streamw != NULL) { if (fclose(streamw) == EOF && !error) { streamw = NULL; (void) bailout(1, "Unable to close write stream!"); } streamw = NULL; } /* close read stream */ if (streamr != NULL) { if (fclose(streamr) == EOF && !error) { streamr = NULL; (void) bailout(1, "Unable to close read stream!"); } streamr = NULL; } } /* * NAME: exec_parent * PURPOSE: * code block for parent process * prepares pipes and streams, forwards messages read from stdin to child, * and dumps responses from child to stdout * * PARAMETERS: * void * * RETURN VALUE: * void * * GLOBAL VARS: * error, pipew, piper, streamw, streamr */ void exec_parent(void) { char buffer[MAX_LEN + 1]; /* write pipe: close unused read end */ if (close(pipew[0]) == -1) (void) bailout(1, "Unable to close read end of write pipe!"); /* read pipe: close unused write end */ if (close(piper[1]) == -1) (void) bailout(1, "Unable to close write end of read pipe!"); /* create write + read stream */ if ((streamw = fdopen(pipew[1], "w")) == NULL) (void) bailout(1, "Unable to open write pipe for writing!"); if ((streamr = fdopen(piper[0], "r")) == NULL) (void) bailout(1, "Unable to open read pipe for reading!"); /* read data from stdin */ while(!error && fgets(buffer, MAX_LEN, stdin) != NULL) { /* write data to child */ if (!error && fprintf(streamw, buffer) < 0) (void) bailout(1, "Unable to write to pipe!"); if (!error && fflush(streamw) == EOF) (void) bailout(1, "Unable to flush write stream!"); /* read result from child */ if (!error && fgets(buffer, MAX_LEN, streamr) != NULL) { if (printf("Result: %s", buffer) < 0) (void) bailout(1, "Unable to write to stdout!"); if (fflush(stdout) == EOF) (void) bailout(1, "Unable to flush stdout!"); } if (!error && ferror(streamr)) (void) bailout(1, "Unable to read from pipe!"); } /* close write stream so the child can terminate */ if (fclose(streamw) == EOF && !error) { streamw = NULL; (void) bailout(1, "Unable to close write stream!"); } streamw = NULL; } /* * NAME: exec_parent * PURPOSE: * code block for child process * prepares pipes and streams, reads messages from parent, parses the messages * and forwards the calculated result to the parent back * * PARAMETERS: * void * * RETURN VALUE: * void * * GLOBAL VARS: * error, pipew, piper, streamw, streamr */ void exec_child(void) { char buffer[MAX_LEN + 1]; int num1, num2, result = -1; char op; /* write pipe: close unused write end */ if (close(pipew[1]) == -1) (void) bailout(1, "Unable to close write end of write pipe!"); /* read pipe: close unused read end */ if (close(piper[0]) == -1) (void) bailout(1, "Unable to close read end of read pipe!"); /* create write + read stream (note: swapped the two pipes) */ if ((streamr = fdopen(pipew[0], "r")) == NULL) (void) bailout(1, "Unable to open write pipe for writing!"); if ((streamw = fdopen(piper[1], "w")) == NULL) (void) bailout(1, "Unable to open read pipe for reading!"); /* read data from parent */ while(fgets(buffer, MAX_LEN, streamr) != NULL) { /* parse line */ if (sscanf(buffer, "%d %d %c\n", &num1, &num2, &op) != 3) (void) bailout(0, "Syntax error!"); /* calculate */ switch(op) { case '+': result = num1 + num2; break; case '-': result = num1 - num2; break; case '*': result = num1 * num2; break; case '/': result = (num2 == 0) ? 0 : num1 / num2; break; default: (void) bailout(0, "Unsupported operation!"); break; } /* write result to parent */ if (fprintf(streamw, "%d\n", result) < 0) (void) bailout(1, "Unable to write to pipe!"); if (fflush(streamw) == EOF) (void) bailout(1, "Unable to flush write stream!"); } if (ferror(streamr)) (void) bailout(1, "Unable to read from pipe!"); } /* * NAME: main * PURPOSE: * install signal handler, allocate resources, fork process, * call the corresponding methods and wait for child before terminating * * PARAMETERS: * standard parameters of main * * RETURN VALUE: * int ... 0 on success, 1 on error * * GLOBAL VARS: * me, error, child */ int main(int argc, char *argv[]) { struct sigaction new_action, old_action; pid_t pid, wpid; int status; /* save me */ me = argv[0]; /* install signal handler */ new_action.sa_handler = sigpipe_handler; sigemptyset(&new_action.sa_mask); new_action.sa_flags = 0; /* to avoid restart of blocking syscalls */ INSTALL_SIGNAL(SIGPIPE, new_action, old_action) /* check cmdline options */ if (argc > 1) (void) usage(); /* allocate resources */ (void) allocate_resources(); switch(pid = fork()) { case -1: (void) bailout(1, "Unable to fork!"); break; case 0: /* child */ #if DEBUG (void) printf("[CHILD] pid=%d\n", getpid()); #endif child = 1; (void) exec_child(); break; default: /* parent */ #if DEBUG (void) printf("[PARENT] pid=%d\n", getpid()); #endif (void) exec_parent(); #if DEBUG (void) printf("[PARENT] waiting for child...\n"); #endif do { wpid = waitpid(pid, &status, 0); if (wpid == -1) (void) bailout(1, "Error while waiting for child!"); else if (WIFEXITED(status)) error += WEXITSTATUS(status); else if (WIFSIGNALED(status)) error += WTERMSIG(status); } while(!WIFEXITED(status) && !WIFSIGNALED(status)); } /* free resources */ (void) free_resources(); return (error) ? 1 : 0; } /* vim: set et sw=2 ts=2: */