1 /*
2 * Copyright (C) Bull S.A. 1996
3 * Copyright (c) International Business Machines Corp., 2001
4 *
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 2 of the License, or
8 * (at your option) any later version.
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See
13 * the GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License
16 * along with this program; if not, write to the Free Software
17 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
18 */
19 /*---------------------------------------------------------------------+
20 | pipe_test_02 |
21 | ==================================================================== |
22 | |
23 | Description: Max data transfer through pipe interprocess channel |
24 | in non-blocking mode |
25 | |
26 | Algorithm: o Create a pipe |
27 | o Make write & read end of pipe non-blocking |
28 | o Spawn a child process |
29 | o parent: |
30 | - create & send data packets to the child |
31 | - compute checksum on sent packets |
32 | o child: |
33 | - recieve packets from parent & compute checksum |
34 | - send final checksum to parent |
35 | o parent: |
36 | - compare checksum of sent packets with the |
37 | child's checksum |
38 | |
39 | System calls: The following system calls are tested: |
40 | |
41 | pipe () - Creates an interprocess channel |
42 | fork () - Creates a new process |
43 | fcntl () - |
44 | waitpid () - Waits for a child process to stop or |
45 | |
46 | Usage: pipe_test_02 |
47 | |
48 | To compile: cc -o pipe_test_02 pipe_test_02.c |
49 | |
50 | Last update: Ver. 1.3, 3/3/94 12:06:38 |
51 | |
52 | Change Activity |
53 | |
54 | Version Date Name Reason |
55 | 0.1 010393 DJK Initial version for AIX 4.1 |
56 | 1.2 021394 DJK Move to "prod" directory |
57 | |
58 +---------------------------------------------------------------------*/
59
60 #include <errno.h>
61 #include <fcntl.h>
62 #include <signal.h>
63 #include <stdio.h>
64 #include <stdlib.h>
65 #include <string.h>
66 #include <sys/types.h>
67 #include <sys/wait.h>
68 #include <unistd.h>
69
70 /* Defines:
71 *
72 * MB: one megabyte (MB)
73 *
74 * VALID_PACKET: value sent with each packet, used to verify that the
75 * packets contents were not garbled.
76 *
77 * DEFAULT_NUM_CHILDREN: default number of child processes spawned
78 *
79 * DEFAULT_PACKETS_TO_SEND: default number of packets sent to each child
80 * process.
81 *
82 * MAXCHILD: maximum number of child processes which may be spawned
83 * (based upon the number of file descriptors that a process may use)
84 *
85 * USAGE: usage statement
86 */
87 #define MB (1024*1024)
88 #define DEFAULT_PACKETS_TO_SEND 1024
89 #define DEFAULT_NUM_CHILDREN 1
90 #define OPEN_MAX 256
91 #define MAXCHILD (OPEN_MAX/2 - 2)
92 #define VALID_PACKET 0xabcdef01
93 #define USAGE "\nUsage: %s [-n] [-p nprocs] [{-m totmegs | -b totbytes}]\n\n" \
94 "\t-n transfer data with NON-BLOCKING reads & writes\n" \
95 "\t-p nprocs number of child processes to spawn\n" \
96 "\t-m totmegs number of MB to send through pipe\n" \
97 "\t-b totmegs number of bytes to send through pipe\n" \
98 "\t (must be less than %d)\n\n"
99
100 /*
101 * Function Prototypes:
102 *
103 * setup (): Parse command line arguments and intialize variables
104 * child (): Child process
105 * cleanup (): Close all pipes and kill child processes
106 * sys_error (): System error message function
107 * error (): Error message function
108 * setup_signal_handlers (): Sets up signal catching functions
109 * handler (): Signal catching function
110 */
111 void setup(int, char **);
112 void child(int[], int[]);
113 void cleanup();
114 void sys_error(const char *, int);
115 void error(const char *, int);
116 void setup_signal_handlers();
117 void handler(int, int, struct sigcontext *);
118
119 /*
120 * Structures & Global variables
121 *
122 * num_children: number of child processes to be spawned
123 *
124 * num_packets: number of packets to be sent to each child process
125 *
126 * non_blocking_flag: uses NON-BLOCKING
127 *
128 * pid: process id's of the spawned processes
129 *
130 * p2child: half duplex pipes from parent to child (parent writes,
131 * child reads).
132 *
133 * p2parent: half duplex pipe from child to parent (child writes,
134 * parent reads).
135 */
136
137 enum { READ, WRITE }; /* Pipe read & write end indices */
138
139 struct data_packet {
140 pid_t pid; /* Child process id */
141 int last; /* Indicates last packet when set */
142 long valid; /* Insure packet was not garbled */
143 long seq_number; /* Packet sequence number */
144 unsigned long checksum; /* Cumulative checksum so far */
145 unsigned char data; /* Data sent in packet */
146 };
147 typedef struct data_packet data_packet;
148
149 int num_children = DEFAULT_NUM_CHILDREN;
150 long num_packets = DEFAULT_PACKETS_TO_SEND;
151 int non_blocking_flag = 0; /* Uses NON-BLOCKING pipes when set */
152 int bflg = 0; /* Data quantity flag (MB) */
153 int mflg = 0; /* Data quantity flag (bytes) */
154
155 pid_t parent_pid; /* Parent's process id */
156 pid_t pid[MAXCHILD]; /* Process id's of spawned processes */
157 int p2child[MAXCHILD][2]; /* Pipes from parent to child processes */
158 int p2parent[2]; /* Pipe from child processes to parent */
159 char err_msg[256]; /* Generic error message buffer */
160
161 /*---------------------------------------------------------------------+
162 | main () |
163 | ==================================================================== |
164 | |
165 | Function: Main program (see prolog for more details) |
166 | |
167 | Returns: (0) Successful completion |
168 | (-1) Error occurred |
169 | |
170 +---------------------------------------------------------------------*/
main(int argc,char ** argv)171 int main(int argc, char **argv)
172 {
173 int i;
174 int n; /* Number of bytes written */
175 int status; /* Child's exit status */
176 long packets_sent;
177 unsigned char data;
178 unsigned long cksum_parent = 0;
179 data_packet packet;
180
181 /*
182 * Parse command line arguments, initialize global variables and
183 * print program header
184 */
185 setup(argc, argv);
186 printf("%s: IPC Pipe TestSuite program\n", *argv);
187 fflush(stdout);
188
189 /*
190 * Create two sets of half duplex pipes:
191 *
192 * p2child: for sending packets from the parent process to the child
193 * processes and
194 * p2parent: for sending checksums from the child processes to the
195 * parent
196 *
197 * If the non-blocking command line option was specified, use fcntl ()
198 * to set the O_NONBLOCK file descriptor status flags. This will
199 * prevent reads & and writes from blocking if the data is not yet
200 * available
201 */
202 printf("\n\tCreating pipes...\n");
203 fflush(stdout);
204
205 if (pipe(p2parent) < 0)
206 sys_error("pipe failed", __LINE__);
207
208 if (non_blocking_flag) {
209 printf("\n\tSending data NON-BLOCKING!\n");
210 fflush(stdout);
211 }
212
213 for (i = 0; i < num_children; i++) {
214 if (pipe(&p2child[i][0]) < 0)
215 sys_error("pipe failed", __LINE__);
216 if (non_blocking_flag) {
217 if (fcntl(p2child[i][READ], F_SETFL, O_NONBLOCK) < 0)
218 sys_error("fcntl (O_NONBLOCK) failed",
219 __LINE__);
220 if (fcntl(p2child[i][WRITE], F_SETFL, O_NONBLOCK) < 0)
221 sys_error("fcntl (O_NONBLOCK) failed",
222 __LINE__);
223 }
224 }
225
226 /*
227 * Spawn num_children processes
228 *
229 * Fork of the child process & record the newly created process's
230 * id for future reference.
231 *
232 * Then close the READ end of the p2child pipe, since the parent
233 * process will be writing into this pipe rather than reading.
234 * Also close the WRITE end of the p2parent pipe, for just the
235 * the reverse reasons...
236 */
237 printf("\n\tSpawning %d child processes ... \n", num_children);
238 fflush(stdout);
239
240 for (i = 0; i < num_children; i++) {
241
242 if ((pid[i] = fork()) == 0) {
243
244 /* Child process */
245 child(&p2child[i][0], p2parent);
246 exit(0);
247
248 } else if (pid[i] < (pid_t) 0)
249 sys_error("fork failed", __LINE__);
250
251 if (close(p2child[i][READ]) < 0)
252 sys_error("close failed", __LINE__);
253 }
254 if (close(p2parent[WRITE]) < 0)
255 sys_error("close failed", __LINE__);
256
257 /*
258 * Send data packets to the child processes
259 *
260 * Build packets (initialize all of the packets fields) and then
261 * send the packets to all of the child processes.
262 *
263 * Might have to make several attempts with the NON-BLOCKING writes
264 * if the resource is not immediately available.
265 */
266 printf
267 ("\n\tParent: sending %ld packets (%ld bytes) to child processes ...\n",
268 num_packets, num_packets * sizeof(struct data_packet));
269
270 packet.last = 0;
271 packet.valid = VALID_PACKET;
272
273 for (packets_sent = data = 0; num_packets > 0; num_packets--) {
274
275 packet.seq_number = ++packets_sent;
276 packet.data = data++;
277 packet.pid = pid[i];
278 packet.checksum = cksum_parent += packet.data;
279
280 for (i = 0; i < num_children; i++) {
281 try_write_ETXN_again:
282 if ((n = write(p2child[i][WRITE], &packet,
283 sizeof(packet))) < 0) {
284 if (non_blocking_flag && errno == EAGAIN) {
285 goto try_write_ETXN_again;
286 } else {
287 sys_error("write failed", __LINE__);
288 }
289 }
290 }
291 }
292
293 /*
294 * Send the last packet to the child processes
295 *
296 * [ Upon receiving this packet, the child process will know that all
297 * of the packets have been sent and that the parent process is
298 * expecting the child to send it's checksum back. ]
299 *
300 * After sending the last packet, close the WRITE end of the p2child
301 * pipe as we are finish sending packets to the child processes.
302 *
303 * Then wait for all of the child processes to send the checksum
304 * packets. Upon receiving the checksum packets verify that the
305 * child's checksum matches that of the parent.
306 *
307 * Might have to make several attempts with the NON-BLOCKING writes
308 * if the resource is not immediately available.
309 *
310 * Finally, close READ end of p2parent pipe as we have finished
311 * receiving checksums from the child.
312 */
313 packet.last = 1;
314 printf
315 ("\n\tParent: done sending packets & waiting for children to complete!\n");
316 for (i = 0; i < num_children; i++) {
317 try_read_again:
318 if (write(p2child[i][WRITE], &packet, sizeof(packet)) < 0) {
319 if (non_blocking_flag && errno == EAGAIN) {
320 goto try_read_again;
321 } else {
322 sys_error("write failed", __LINE__);
323 }
324 }
325 if (close(p2child[i][WRITE]) < 0)
326 sys_error("close failed", __LINE__);
327
328 if (read(p2parent[READ], &packet, sizeof(packet)) <= 0)
329 sys_error("read failed", __LINE__);
330
331 if (packet.valid != VALID_PACKET)
332 error("received packet with corrupted data from child!",
333 __LINE__);
334
335 if (cksum_parent != packet.checksum) {
336 sprintf(err_msg, "checksum of data sent by parent "
337 "does not match checksum of data received by "
338 "child [pid %d]\n"
339 "\tchild's checksum: %08lx\n"
340 "\tparent's checksum: %08lx\n",
341 packet.pid, packet.checksum, cksum_parent);
342 error(err_msg, __LINE__);
343 }
344 }
345 if (close(p2parent[READ]) < 0)
346 sys_error("close failed", __LINE__);
347
348 /*
349 * Wait for all of the child processes to complete & check their
350 * exit status.
351 *
352 * Upon completion of the child proccesses, exit program with success.
353 */
354 for (i = 0; i < num_children; i++) {
355 waitpid(pid[i], &status, 0);
356
357 if (!WIFEXITED(status))
358 sys_error("child process terminated abnormally",
359 __LINE__);
360 }
361 printf
362 ("\n\tParent: children received all packets & exited successfully\n");
363
364 /* Program completed successfully -- exit */
365 printf("\nsuccessful!\n");
366
367 return (0);
368 }
369
370 /*---------------------------------------------------------------------+
371 | child () |
372 | ==================================================================== |
373 | |
374 | Function: Receive packets from the parent, insure they are valid |
375 | and not out of sequence, and calculate a running |
376 | checksum. Upon receiving the last packet from the |
377 | parent, build a checksum packet and send it to the parent.|
378 | |
379 | Args: p2child - Pipe from parent to child |
380 | p2parent - Pipe from child to parent |
381 | |
382 | Returns: Exits with (-1) if an error occurs |
383 | |
384 +---------------------------------------------------------------------*/
child(int p2child[],int p2parent[])385 void child(int p2child[], int p2parent[])
386 {
387 int n; /* Bytes read */
388 pid_t pid = getpid(); /* Process id of child */
389 int end_of_transmission = 0;
390 long packets_received = 0; /* Number of packets received
391 * from parent
392 */
393
394 data_packet packet; /* Packet used to transmiting data */
395 unsigned long cksum_child = 0; /* Checksum of data fields received */
396
397 /*
398 * Close the WRITE end of the p2child pipe, since the child
399 * process will be reading from this pipe rather than writing.
400 * Also close the READ end of the p2parent pipe, for just the
401 * the reverse reasons...
402 */
403 if (close(p2child[WRITE]) < 0)
404 sys_error("close failed", __LINE__);
405 if (close(p2parent[READ]) < 0)
406 sys_error("close failed", __LINE__);
407
408 /*
409 * Receive packets from parent & insure packets are valid
410 *
411 * Read packets from the parent through p2child pipe. Upon
412 * recieving the packet, verify that it is valid, in sequence
413 * and that both the parent's and child's checksums match.
414 *
415 * Might have to make several attempts with the NON-BLOCKING
416 * reads if the resource is not immediately available.
417 *
418 * Continue reading packets until the "last" packet is received
419 * from the parent. Upon receiving the last packet, close
420 * the p2child READ pipe, as we are finished receiving packets
421 * from the parent.
422 */
423 while (!end_of_transmission) {
424 try_write_again:
425 n = read(p2child[READ], &packet, sizeof(packet));
426 if (n < 0) {
427 /* Resource not available */
428 if (non_blocking_flag && errno == EAGAIN)
429 goto try_write_again;
430 else
431 sys_error("read failed", __LINE__);
432 } else if (n > 0) {
433 /* Insure packet is valid */
434 if (packet.valid != VALID_PACKET) {
435 sprintf(err_msg,
436 "child received invalid packet "
437 "from parent:\n\tpacket #: %ld\n",
438 packets_received);
439 error(err_msg, __LINE__);
440 }
441 /* Received last packet */
442 if (packet.last) {
443 end_of_transmission = 1;
444 } else {
445
446 /* Insure packet was not received out of sequence */
447 packets_received++;
448 if (packets_received != packet.seq_number) {
449 sprintf(err_msg,
450 "child received packet out of sequence\n"
451 "\texpecting packet: %ld\n"
452 "\treceived packet: %ld\n",
453 packets_received,
454 packet.seq_number);
455 error(err_msg, __LINE__);
456 }
457
458 /* Insure checksums still match */
459 cksum_child += packet.data;
460 if (cksum_child != packet.checksum) {
461 sprintf(err_msg,
462 "child & parent checksums do not match\n"
463 "\tchild checksum: %08lx\n"
464 "\tparent checksum: %08lx\n"
465 "\tpacket number: %ld\n",
466 cksum_child, packet.checksum,
467 packets_received);
468 error(err_msg, __LINE__);
469 }
470 }
471 }
472 }
473 if (close(p2child[READ]) < 0)
474 sys_error("close failed", __LINE__);
475
476 /*
477 * Send parent packet containing child's checksum
478 *
479 * Build a checksum packet (initialize packet fields) and then
480 * send the packet to the parent.
481 *
482 * Then close the WRITE p2parent pipe as we have finished sending packets
483 * to the parent.
484 */
485 printf("\t\tChild: pid [%d] received %ld packets from parent\n",
486 pid, packets_received);
487
488 packet.pid = pid;
489 packet.valid = VALID_PACKET;
490 packet.checksum = cksum_child;
491
492 if (write(p2parent[WRITE], &packet, sizeof(packet)) < 0)
493 sys_error("write failed", __LINE__);
494 if (close(p2parent[WRITE]) < 0)
495 sys_error("close failed", __LINE__);
496 }
497
498 /*---------------------------------------------------------------------+
499 | setup () |
500 | ==================================================================== |
501 | |
502 | Function: Parse the command line arguments & initialize global |
503 | variables. |
504 | |
505 | Updates: (command line options) |
506 | |
507 | [-n] non_blocking_flag: prevents read & write calls from |
508 | from blocking if the resource is not available. |
509 | |
510 | [-p] num_packets: number of packets ... |
511 | |
512 | [-c] num_children: number of child processes to spawn ... |
513 | |
514 +---------------------------------------------------------------------*/
setup(int argc,char ** argv)515 void setup(int argc, char **argv)
516 {
517 int i;
518 int errflag = 0;
519 int bytes = 0, megabytes = 0;
520 char *program_name = *argv;
521 extern char *optarg; /* Command line option */
522
523 while ((i = getopt(argc, argv, "nm:b:p:?")) != EOF) {
524 switch (i) {
525 case 'n': /* NON-BLOCKING flag */
526 non_blocking_flag++;
527 break;
528 case 'm': /* MB */
529 mflg++;
530 megabytes = atoi(optarg);
531 break;
532 case 'b': /* bytes */
533 bflg++;
534 bytes = atoi(optarg);
535 break;
536 case 'p': /* number of child procs */
537 num_children = atoi(optarg);
538 break;
539 case '?':
540 errflag++;
541 break;
542 }
543 }
544 if (mflg) {
545 num_packets = megabytes * MB / sizeof(struct data_packet);
546 } else if (bflg) {
547 num_packets = bytes / sizeof(struct data_packet);
548 }
549
550 if (num_packets == 0 || num_children == 0 || num_children > MAXCHILD)
551 errflag++;
552
553 if (errflag) {
554 fprintf(stderr, USAGE, program_name, MAXCHILD);
555 exit(2);
556 }
557 /*
558 * Setup signal catching function for SIGPIPE & SIGINT, record
559 * the process id of the parent and initialize the child process
560 * id array.
561 */
562 setup_signal_handlers();
563
564 parent_pid = getpid();
565
566 for (i = 0; i < num_children; i++) {
567 pid[i] = (pid_t) 0;
568 }
569 }
570
571 /*---------------------------------------------------------------------+
572 | setup_handler () |
573 | ==================================================================== |
574 | |
575 | Function: Setup the signal handler for SIGPIPE. |
576 | |
577 +---------------------------------------------------------------------*/
setup_signal_handlers()578 void setup_signal_handlers()
579 {
580 struct sigaction invec;
581
582 invec.sa_handler = (void (*)(int))handler;
583 sigemptyset(&invec.sa_mask);
584 invec.sa_flags = 0;
585
586 if (sigaction(SIGINT, &invec, NULL) < 0)
587 sys_error("sigaction failed", __LINE__);
588
589 if (sigaction(SIGPIPE, &invec, NULL) < 0)
590 sys_error("sigaction failed", __LINE__);
591 }
592
593 /*---------------------------------------------------------------------+
594 | handler () |
595 | ==================================================================== |
596 | |
597 | Function: Signal catching function for SIGPIPE signal. |
598 | |
599 | o SIGPIPE: Print message and abort program... |
600 | |
601 | o SIGINT: Parent process calls cleanup, child processes |
602 | simply exit |
603 | |
604 | o Other: Print message and abort program... |
605 | |
606 +---------------------------------------------------------------------*/
handler(int sig,int code,struct sigcontext * scp)607 void handler(int sig, int code, struct sigcontext *scp)
608 {
609 char msg[100]; /* Buffer for error message */
610
611 if (sig == SIGPIPE) {
612 error("wrote to pipe with closed read end", __LINE__);
613 } else if (sig == SIGINT) {
614 if (getpid() == parent_pid) {
615
616 fprintf(stderr, "Received SIGINT -- cleaning up...\n");
617 fflush(stderr);
618
619 cleanup();
620 } else
621 exit(-1);
622 } else {
623 sprintf(msg, "Received an unexpected signal (%d)", sig);
624 error(msg, __LINE__);
625 }
626 }
627
628 /*---------------------------------------------------------------------+
629 | cleanup () |
630 | ==================================================================== |
631 | |
632 | Function: Closes all of the pipes, kills all of the child |
633 | processes and exits the program... |
634 | |
635 +---------------------------------------------------------------------*/
cleanup()636 void cleanup()
637 {
638 int i;
639
640 if (getpid() == parent_pid) {
641 for (i = 0; i < num_children; i++) {
642 if (pid[i] > (pid_t) 0 && kill(pid[i], SIGKILL) < 0)
643 sys_error("signal failed", __LINE__);
644
645 close(p2child[i][READ]);
646 close(p2child[i][WRITE]);
647 close(p2parent[READ]);
648 close(p2parent[WRITE]);
649 }
650 }
651
652 exit(-1);
653 }
654
655 /*---------------------------------------------------------------------+
656 | sys_error () |
657 | ==================================================================== |
658 | |
659 | Function: Creates system error message and calls error () |
660 | |
661 +---------------------------------------------------------------------*/
sys_error(const char * msg,int line)662 void sys_error(const char *msg, int line)
663 {
664 char syserr_msg[256];
665
666 sprintf(syserr_msg, "%s: %s\n", msg, strerror(errno));
667 error(syserr_msg, line);
668 }
669
670 /*---------------------------------------------------------------------+
671 | error () |
672 | ==================================================================== |
673 | |
674 | Function: Prints out message and calls cleanup... |
675 | |
676 +---------------------------------------------------------------------*/
error(const char * msg,int line)677 void error(const char *msg, int line)
678 {
679 fprintf(stderr, "ERROR [line: %d] %s\n", line, msg);
680 fflush(stderr);
681 cleanup();
682 }
683