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 +---------------------------------------------------------------------*/ 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 +---------------------------------------------------------------------*/ 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 +---------------------------------------------------------------------*/ 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 +---------------------------------------------------------------------*/ 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 +---------------------------------------------------------------------*/ 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 +---------------------------------------------------------------------*/ 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 +---------------------------------------------------------------------*/ 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 +---------------------------------------------------------------------*/ 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