Download | Plain Text | No Line Numbers


  1. /*
  2.  * Name: calculator
  3.  * Author: Manuel Mausz, 0728348
  4.  * Description: upon start calculator forks a child. the parent reads messages
  5.  * from stdin, forwards the messages to the child, which does the
  6.  * calculation. the result is forwarded to the parent and dumped
  7.  * to stdout.
  8.  * Created: 24.05.2009
  9.  * Exercise: 3c
  10.  */
  11.  
  12. #include <unistd.h>
  13. #include <stdlib.h>
  14. #include <stdio.h>
  15. #include <errno.h>
  16. #include <signal.h>
  17. #include <sys/types.h>
  18. #include <sys/wait.h>
  19. #include "error.h"
  20.  
  21. /* maximal input length */
  22. #define MAX_LEN 15
  23.  
  24. /* install signal handler macro */
  25. #define INSTALL_SIGNAL(signum, act, oldact) \
  26.   sigaction(signum, NULL, &oldact); \
  27.   if (oldact.sa_handler != SIG_IGN) \
  28.   sigaction(signum, &act, NULL);
  29.  
  30. /* global variables */
  31. static int pipew[2]; /* fds for write to child/read from parent */
  32. static int piper[2]; /* fds for read from child/write to parent */
  33. static FILE *streamw = NULL; /* stream for writing to child */
  34. static FILE *streamr = NULL; /* stream for reading from child */
  35.  
  36. /*
  37.  * NAME: sigpipe_handler
  38.  * PURPOSE:
  39.  * signal handler for SIGPIPE
  40.  * sets global error flag to 1
  41.  *
  42.  * PARAMETERS:
  43.  * int signum ... signal number
  44.  *
  45.  * RETURN VALUE:
  46.  * void
  47.  *
  48.  * GLOBAL VARS:
  49.  * error, child
  50.  */
  51. static void sigpipe_handler(int signum)
  52. {
  53. #ifdef DEBUG
  54. (void) printf("%s SIGPIPE received\n", (child) ? "[CHILD] " : "[PARENT] ");
  55. #endif
  56. error = 1;
  57. }
  58.  
  59. /*
  60.  * NAME: usage
  61.  * PURPOSE:
  62.  * prints program usage to stderr and terminates with EXIT_FAILURE
  63.  *
  64.  * PARAMETERS:
  65.  * void
  66.  *
  67.  * RETURN VALUE:
  68.  * void
  69.  *
  70.  * GLOBAL VARS:
  71.  * me
  72.  */
  73. static void usage()
  74. {
  75. (void) fprintf(stderr, "Usage: %s\n", me);
  76. (void) fprintf(stderr, "$> <number> <number> <operator>\n");
  77. (void) fprintf(stderr, "BNF: <number> ::= -?[0-9]+\n");
  78. (void) fprintf(stderr, " <operator> ::= +|-|*|/\n");
  79. (void) bailout(0, NULL);
  80. }
  81.  
  82. /*
  83.  * NAME: allocate_resources
  84.  * PURPOSE:
  85.  * allocate resources
  86.  *
  87.  * PARAMETERS:
  88.  * void
  89.  *
  90.  * RETURN VALUE:
  91.  * void
  92.  *
  93.  * GLOBAL VARS:
  94.  * pipew, piper
  95.  */
  96. void allocate_resources(void)
  97. {
  98. #ifdef DEBUG
  99. (void) printf("[GLOBAL] allocating resources...\n");
  100. #endif
  101. if (pipe(pipew) == -1)
  102. (void) bailout(1, "Unable to create write pipe!");
  103. if (pipe(piper) == -1)
  104. (void) bailout(1, "Unable to create read pipe!");
  105. }
  106.  
  107. /*
  108.  * NAME: free_resources
  109.  * PURPOSE:
  110.  * frees allocated resources
  111.  *
  112.  * PARAMETERS:
  113.  * void
  114.  *
  115.  * RETURN VALUE:
  116.  * void
  117.  *
  118.  * GLOBAL VARS:
  119.  * error, child, streamw, streamr
  120.  */
  121. void free_resources(void)
  122. {
  123. #ifdef DEBUG
  124. (void) printf("%s freeing resources...\n", (child) ? "[CHILD] " : "[PARENT] ");
  125. #endif
  126. /* close write stream */
  127. if (streamw != NULL)
  128. {
  129. if (fclose(streamw) == EOF && !error)
  130. {
  131. streamw = NULL;
  132. (void) bailout(1, "Unable to close write stream!");
  133. }
  134. streamw = NULL;
  135. }
  136.  
  137. /* close read stream */
  138. if (streamr != NULL)
  139. {
  140. if (fclose(streamr) == EOF && !error)
  141. {
  142. streamr = NULL;
  143. (void) bailout(1, "Unable to close read stream!");
  144. }
  145. streamr = NULL;
  146. }
  147. }
  148.  
  149. /*
  150.  * NAME: exec_parent
  151.  * PURPOSE:
  152.  * code block for parent process
  153.  * prepares pipes and streams, forwards messages read from stdin to child,
  154.  * and dumps responses from child to stdout
  155.  *
  156.  * PARAMETERS:
  157.  * void
  158.  *
  159.  * RETURN VALUE:
  160.  * void
  161.  *
  162.  * GLOBAL VARS:
  163.  * error, pipew, piper, streamw, streamr
  164.  */
  165. void exec_parent(void)
  166. {
  167. char buffer[MAX_LEN + 1];
  168.  
  169. /* write pipe: close unused read end */
  170. if (close(pipew[0]) == -1)
  171. (void) bailout(1, "Unable to close read end of write pipe!");
  172. /* read pipe: close unused write end */
  173. if (close(piper[1]) == -1)
  174. (void) bailout(1, "Unable to close write end of read pipe!");
  175.  
  176. /* create write + read stream */
  177. if ((streamw = fdopen(pipew[1], "w")) == NULL)
  178. (void) bailout(1, "Unable to open write pipe for writing!");
  179. if ((streamr = fdopen(piper[0], "r")) == NULL)
  180. (void) bailout(1, "Unable to open read pipe for reading!");
  181.  
  182. /* read data from stdin */
  183. while(!error && fgets(buffer, MAX_LEN, stdin) != NULL)
  184. {
  185. /* write data to child */
  186. if (!error && fprintf(streamw, buffer) < 0)
  187. (void) bailout(1, "Unable to write to pipe!");
  188. if (!error && fflush(streamw) == EOF)
  189. (void) bailout(1, "Unable to flush write stream!");
  190.  
  191. /* read result from child */
  192. if (!error && fgets(buffer, MAX_LEN, streamr) != NULL)
  193. {
  194. if (printf("Result: %s", buffer) < 0)
  195. (void) bailout(1, "Unable to write to stdout!");
  196. if (fflush(stdout) == EOF)
  197. (void) bailout(1, "Unable to flush stdout!");
  198. }
  199. if (!error && ferror(streamr))
  200. (void) bailout(1, "Unable to read from pipe!");
  201. }
  202.  
  203. /* close write stream so the child can terminate */
  204. if (fclose(streamw) == EOF && !error)
  205. {
  206. streamw = NULL;
  207. (void) bailout(1, "Unable to close write stream!");
  208. }
  209. streamw = NULL;
  210. }
  211.  
  212. /*
  213.  * NAME: exec_parent
  214.  * PURPOSE:
  215.  * code block for child process
  216.  * prepares pipes and streams, reads messages from parent, parses the messages
  217.  * and forwards the calculated result to the parent back
  218.  *
  219.  * PARAMETERS:
  220.  * void
  221.  *
  222.  * RETURN VALUE:
  223.  * void
  224.  *
  225.  * GLOBAL VARS:
  226.  * error, pipew, piper, streamw, streamr
  227.  */
  228. void exec_child(void)
  229. {
  230. char buffer[MAX_LEN + 1];
  231. int num1, num2, result = -1;
  232. char op;
  233.  
  234. /* write pipe: close unused write end */
  235. if (close(pipew[1]) == -1)
  236. (void) bailout(1, "Unable to close write end of write pipe!");
  237. /* read pipe: close unused read end */
  238. if (close(piper[0]) == -1)
  239. (void) bailout(1, "Unable to close read end of read pipe!");
  240.  
  241. /* create write + read stream (note: swapped the two pipes) */
  242. if ((streamr = fdopen(pipew[0], "r")) == NULL)
  243. (void) bailout(1, "Unable to open write pipe for writing!");
  244. if ((streamw = fdopen(piper[1], "w")) == NULL)
  245. (void) bailout(1, "Unable to open read pipe for reading!");
  246.  
  247. /* read data from parent */
  248. while(fgets(buffer, MAX_LEN, streamr) != NULL)
  249. {
  250. /* parse line */
  251. if (sscanf(buffer, "%d %d %c\n", &num1, &num2, &op) != 3)
  252. (void) bailout(0, "Syntax error!");
  253.  
  254. /* calculate */
  255. switch(op)
  256. {
  257. case '+':
  258. result = num1 + num2;
  259. break;
  260. case '-':
  261. result = num1 - num2;
  262. break;
  263. case '*':
  264. result = num1 * num2;
  265. break;
  266. case '/':
  267. result = (num2 == 0) ? 0 : num1 / num2;
  268. break;
  269. default:
  270. (void) bailout(0, "Unsupported operation!");
  271. break;
  272. }
  273.  
  274. /* write result to parent */
  275. if (fprintf(streamw, "%d\n", result) < 0)
  276. (void) bailout(1, "Unable to write to pipe!");
  277. if (fflush(streamw) == EOF)
  278. (void) bailout(1, "Unable to flush write stream!");
  279. }
  280. if (ferror(streamr))
  281. (void) bailout(1, "Unable to read from pipe!");
  282. }
  283.  
  284. /*
  285.  * NAME: main
  286.  * PURPOSE:
  287.  * install signal handler, allocate resources, fork process,
  288.  * call the corresponding methods and wait for child before terminating
  289.  *
  290.  * PARAMETERS:
  291.  * standard parameters of main
  292.  *
  293.  * RETURN VALUE:
  294.  * int ... 0 on success, 1 on error
  295.  *
  296.  * GLOBAL VARS:
  297.  * me, error, child
  298.  */
  299. int main(int argc, char *argv[])
  300. {
  301. struct sigaction new_action, old_action;
  302. pid_t pid, wpid;
  303. int status;
  304.  
  305. /* save me */
  306. me = argv[0];
  307.  
  308. /* install signal handler */
  309. new_action.sa_handler = sigpipe_handler;
  310. sigemptyset(&new_action.sa_mask);
  311. new_action.sa_flags = 0; /* to avoid restart of blocking syscalls */
  312. INSTALL_SIGNAL(SIGPIPE, new_action, old_action)
  313.  
  314. /* check cmdline options */
  315. if (argc > 1)
  316. (void) usage();
  317.  
  318. /* allocate resources */
  319. (void) allocate_resources();
  320.  
  321. switch(pid = fork())
  322. {
  323. case -1:
  324. (void) bailout(1, "Unable to fork!");
  325. break;
  326. case 0: /* child */
  327. #if DEBUG
  328. (void) printf("[CHILD] pid=%d\n", getpid());
  329. #endif
  330. child = 1;
  331. (void) exec_child();
  332. break;
  333. default: /* parent */
  334. #if DEBUG
  335. (void) printf("[PARENT] pid=%d\n", getpid());
  336. #endif
  337. (void) exec_parent();
  338.  
  339. #if DEBUG
  340. (void) printf("[PARENT] waiting for child...\n");
  341. #endif
  342. do
  343. {
  344. wpid = waitpid(pid, &status, 0);
  345. if (wpid == -1)
  346. (void) bailout(1, "Error while waiting for child!");
  347. else if (WIFEXITED(status))
  348. error += WEXITSTATUS(status);
  349. else if (WIFSIGNALED(status))
  350. error += WTERMSIG(status);
  351. }
  352. while(!WIFEXITED(status) && !WIFSIGNALED(status));
  353. }
  354.  
  355. /* free resources */
  356. (void) free_resources();
  357.  
  358. return (error) ? 1 : 0;
  359. }
  360.  
  361. /* vim: set et sw=2 ts=2: */
  362.