Download | Plain Text | Line Numbers
/*
* 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 <unistd.h>
#include <stdlib.h>
#include <stdio.h>
#include <errno.h>
#include <signal.h>
#include <sys/types.h>
#include <sys/wait.h>
#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, "$> <number> <number> <operator>\n");
(void) fprintf(stderr, "BNF: <number> ::= -?[0-9]+\n");
(void) fprintf(stderr, " <operator> ::= +|-|*|/\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: */