1/* 2 * 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#include <sys/mman.h> 21#include <ctype.h> 22#include <errno.h> 23#include <signal.h> 24#include <stdarg.h> 25#include <stdio.h> 26#include <stdlib.h> 27#include <string.h> 28#include <time.h> 29#include <unistd.h> 30 31#ifndef _LINUX 32 /* LINUX INCLUDES */ 33#include <sys/mode.h> 34#include <sys/timers.h> 35#else 36#include <sys/stat.h> 37#include <time.h> 38#include <sys/time.h> 39#include <sys/ipc.h> 40#endif 41#include <sys/msg.h> 42#include <sys/resource.h> 43#include <sys/select.h> 44#include <sys/sem.h> 45#include <sys/shm.h> 46#include <sys/types.h> 47#include <sys/wait.h> 48#include "lapi/semun.h" 49 50/* indexes into environment variable array */ 51#define ADBG 0 52#define BNDX 1 53#define DNDX 2 54#define TNDX 3 55#define MAXBVAL 70 56#define MAXDVAL 11 57#define SLOTDIR "./slot/" 58 59#ifdef _LINUX 60 /* LINUX #defnes */ 61#ifndef TRUE 62#define TRUE 1 63#endif 64#ifndef FALSE 65#define FALSE 0 66#endif 67#endif 68 69#if defined _LINUX && defined DEBUG 70#define prtln() printf("At line number: %d\n", __LINE__); \ 71 fflush(NULL) 72#define dprt(fmt, args...) printf(fmt, ## args) 73#else 74#define prtln() 75#define dprt(fmt, args...) 76#endif 77 78/* aliases for environment variable entries */ 79#define AUSDEBUG (*edat[ADBG].eval.vint) /* debug value */ 80#define BVAL (*edat[BNDX].eval.vint) /* # of childern per parent */ 81#define DVAL (*edat[DNDX].eval.vint) /* depth of process tree */ 82#define TVAL (*edat[TNDX].eval.vint) /* timer value */ 83 84#ifdef _LINUX 85typedef long mtyp_t; 86#endif 87 88/* structure of information stored about each process in shared memory */ 89typedef struct proc_info { 90#ifdef __64LDT__ 91 pid_t pid; /* process id */ 92 pid_t ppid; /* parent process id */ 93#else 94 int pid; /* process id */ 95 int ppid; /* parent process id */ 96#endif 97 int msg; /* parent process id */ 98 int err; /* error indicator */ 99 int *list; /* pointer to list of parent and sibling slot locations */ 100} Pinfo; 101 102typedef struct messagebuf { 103 mtyp_t mtyp; /* message type */ 104 char mtext[80]; /* message text */ 105} Msgbuf; 106 107union semun semarg; 108 109/* structure of all environment variable used by program */ 110struct envstruct { 111 char *env_name; 112 union { 113 char *chptr; 114 int *vint; 115 } eval; 116} envdata[] = { 117 { 118 "AUSDBG", { 119 "0"}}, { 120 "BVAL", { 121 "3"}}, { 122 "DVAL", { 123 "2"}}, { 124 "FORCE", { 125 "0"}}, { 126 "TVAL", { 127 "1"}}, { 128 "", { 129 ""}} 130}; 131 132char *errfile; /* pointer to errfile name */ 133 134int msgid; /* message queue for leaf nodes */ 135int msgerr; /* message queue for errors */ 136int nodesum; /* total number of process to be created */ 137int sem_count; /* counter semaphore */ 138int sem_lock; /* locks access to counter semaphore */ 139int shmid; /* global shared memory id varible */ 140int procgrp; /* process group id */ 141 142timer_t timer; /* timer structure */ 143 144Pinfo *shmaddr; /* Start address of shared memory */ 145 146#ifndef _LINUX 147FILE *errfp = stderr; /* error file pointer, probably not necessary */ 148FILE *debugfp = stderr; /* debug file pointer, used if AUSDEBUG set */ 149#else 150#define errfp stderr 151#define debugfp stderr 152#endif 153 154struct envstruct *edat = envdata; /* pointer to environment data */ 155 156/* external function declarations */ 157extern int killpg(int procgrp, int sig); 158extern timer_t gettimerid(int Timer_type, int Notify_type); 159extern int reltimerid(timer_t timer); 160 161/* internal function declarations */ 162void cleanup(int sig, int code, struct sigcontext *scp); 163void nextofkin(int sig, int code, struct sigcontext *scp); 164void doit(void); 165void debugout(char *fmt, ...); 166int getenv_val(void); 167void messenger(void); 168void nextofkin(int sig, int code, struct sigcontext *scp); 169int notify(int slot); 170void parse_args(int argc, char *argv[]); 171void print_shm(void); 172Pinfo *put_proc_info(int tval); 173void rm_msgqueue(void); 174void rm_semseg(void); 175void rm_shmseg(void); 176int semoper(int slot, int smid, int opval); 177int send_message(int id, mtyp_t type, char *text); 178void set_timer(void); 179void set_signals(void *sighandler()); 180void setup_msgqueue(void); 181void setup_semaphores(void); 182void setup_shm(void); 183void severe(char *fmt, ...); 184Pinfo *shmgetseg(void); 185int spawn(int val); 186unsigned long sumit(int B, int D); 187 188/* 189 * Prints out the data structures in shared memory. 190 */ 191void print_shm(void) 192{ 193 extern int nodesum; /* total number of nodes created */ 194 extern Pinfo *shmaddr; /* shared memory pointer */ 195 extern int shmid; /* shared memory id */ 196 197 Pinfo *pinfo; /* pointer to process info in shared memory */ 198 int *listp; /* pointer to sibling info in shared memory */ 199 int i, j; /* counters */ 200 struct shmid_ds buf; 201 202 if (shmctl(shmid, IPC_STAT, &buf)) 203 return; 204 205 for (pinfo = shmaddr, i = 0; i < nodesum; i++, pinfo++) { 206 fprintf(errfp, 207 "slot: %-4d pid: %-6d ppid: %-6d msg: %-2d err: %-2d lst:", 208 i, pinfo->pid, pinfo->ppid, pinfo->msg, pinfo->err); 209 for (j = 0, listp = pinfo->list; j < BVAL; j++, listp++) 210 fprintf(errfp, " %d", *listp); 211 fprintf(errfp, "\n"); 212 } 213} 214 215/* 216 * Generalized send routine. Sends a message on message queue. 217 */ 218int send_message(int id, mtyp_t type, char *text) 219{ 220 int rc; 221 222 Msgbuf sndbuf; 223 224 strcpy(sndbuf.mtext, text); 225 sndbuf.mtyp = type; 226 while (TRUE) { 227 rc = msgsnd(id, &sndbuf, sizeof(struct messagebuf), IPC_NOWAIT); 228 if (rc == -1 && errno == EAGAIN) { 229 debugout("msgqueue %d of mtyp %d not ready to send\n", 230 msgid, type); 231 errno = 0; 232 } else 233 return (rc); 234 } 235} 236 237/* 238 * Sends error message to initial parent (messenger).i 239 */ 240void severe(char *fmt, ...) 241{ 242 va_list args; 243 int rc; 244 char mtext[80]; 245 extern int msgerr; 246 247 va_start(args, fmt); 248 vsprintf(mtext, fmt, args); 249 va_end(args); 250 251 rc = send_message(msgerr, 2, mtext); 252 if (rc == -1) { 253 perror("cannot send message to msgerr"); 254 exit(1); 255 } 256} 257 258/* 259 * if AUSDEBUG set will print information to file associated with slot number. 260 */ 261void debugout(char *fmt, ...) 262{ 263 va_list args; 264 265 if (AUSDEBUG) { 266 va_start(args, fmt); 267 vfprintf(debugfp, fmt, args); 268 va_end(args); 269 } 270} 271 272/* 273 * Remove message queues. 274 */ 275void rm_msgqueue(void) 276{ 277 extern int msgid; 278 279 /* remove message queue id. */ 280 if (msgctl(msgid, IPC_RMID, NULL) && errno != EINVAL) { 281 fprintf(errfp, "msgctl failed msgid: errno %d\n", errno); 282 perror("msgctl failed"); 283 } 284 285 /* remove message queue id. */ 286 if (msgctl(msgerr, IPC_RMID, NULL) && errno != EINVAL) { 287 fprintf(errfp, "msgctl failed msgerr: errno %d\n", errno); 288 perror("msgctl failed"); 289 } 290} 291 292/* 293 * Remove shared memory segment. 294 */ 295void rm_shmseg(void) 296{ 297 extern int shmid; /* Global shared memory id */ 298 extern Pinfo *shmaddr; /* Global shared memory address */ 299 300 /* remove shared memory id (and shared memory segment). */ 301 if (shmctl(shmid, IPC_RMID, NULL) && errno != EINVAL) { 302 fprintf(errfp, "shmctl failed: errno %d\n", errno); 303 perror("shmctl failed"); 304 } 305} 306 307/* 308 * Remove semaphores. 309 */ 310void rm_semseg(void) 311{ 312 extern int sem_lock; 313 extern int sem_count; 314 315 /* remove sem_lock semaphore id */ 316 semarg.val = 0; /* to fix problem with 4th arg of semctl in 64 bits MARIOG */ 317 if (semctl(sem_lock, 0, IPC_RMID, semarg.val) && errno != EINVAL) { 318 fprintf(errfp, "semctl failed: errno %d\n", errno); 319 perror("semctl failed"); 320 } 321 /* remove sem_count semaphore id. */ 322 semarg.val = 0; /* to fix problem with 4th arg of semctl in 64 bits MARIOG */ 323 if (semctl(sem_count, 0, IPC_RMID, semarg.val) && errno != EINVAL) { 324 fprintf(errfp, "semctl failed: errno %d\n", errno); 325 perror("semctl failed"); 326 } 327} 328 329/* 330 * Routine to clean up shared memory and return exit status (CHILD handler). 331 */ 332void cleanup(int sig, int code, struct sigcontext *scp) 333{ 334 int rc; 335 char mtext[80]; 336 337 killpg(procgrp, SIGTERM); 338 sprintf(mtext, "%d", sig); 339 rc = send_message(msgerr, 3, mtext); 340 if (rc == -1) { 341 severe("msgsnd failed: %d msgid %d mtyp %d mtext %d\n", 342 errno, msgerr, 3, mtext); 343 } 344} 345 346/* 347 * Routine to clean up shared memory and return exit status (PARENT handler). 348 */ 349void nextofkin(int sig, int code, struct sigcontext *scp) 350{ 351 int rc; 352 char mtext[80]; 353 354 sprintf(mtext, "%d", sig); 355 rc = send_message(msgerr, 3, mtext); 356 if (rc == -1) { 357 severe("msgsnd failed: %d msgid %d mtyp %d mtext %d\n", 358 errno, msgerr, 3, mtext); 359 } 360#ifndef _LINUX 361 reltimerid(timer); 362#endif 363 exit(1); 364} 365 366/* given breadth and depth of a tree, sum up total number of nodes created */ 367unsigned long sumit(int B, int D) 368{ 369 int i; 370 int exp = 1; /* exponent of breadth */ 371 unsigned long sum = 1; /* running sum of nodes */ 372 373 for (sum = 1, i = 1; i <= D; i++) { 374 exp = B * exp; 375 sum += (int)exp; 376 } 377 return (sum); 378} 379 380/* Finds correct slot for current process in shared memory and stores 381 * information about process in it. 382 */ 383Pinfo *put_proc_info(int tval) 384{ 385 extern int nodesum; 386 extern Pinfo *shmaddr; 387 388 int sibslot = 0; /* sibling slot number */ 389 int *listp; /* ptr to sibling info for current proc */ 390 Pinfo *smp; /* ptr to current process data slot */ 391 392 smp = shmaddr + tval; 393 smp->pid = getpid(); 394 smp->ppid = getppid(); 395 smp->err = 0; 396 smp->msg = 0; 397 398 /* if very first process (slot 0), dont fill in info about siblings 399 * and parent. Sibling and parent info is irrevelant in this case. 400 */ 401 if (!tval) 402 return (smp); 403 404 /* find parent of current process and store slot location */ 405 smp->list = (int *)(Pinfo *) (shmaddr + nodesum) + (BVAL * tval); 406 *smp->list = (tval - 1) / BVAL; 407 listp = smp->list + 1; 408 409 /* calculate and store sibling slot numbers of current process */ 410 for (sibslot = *smp->list * BVAL + 1; listp < smp->list + BVAL; 411 sibslot++) { 412 if (tval != sibslot) 413 *(listp++) = sibslot; 414 } 415 return (smp); 416} 417 418/* This routine sends a message from the current process to all of her 419 * siblings and then waits to receive responses from them. A timer is 420 * set so that if a message is lost or not received for some reason 421 * we can exit gracefully. 422 */ 423int notify(int slot) 424{ 425 extern int msgid; 426 extern Pinfo *shmaddr; 427 428 int i; 429 int rc; 430 int tslot; 431 int *listp = (shmaddr + slot)->list; 432 int cldcnt = 1; 433 int ndx = 0; 434#ifdef __64LDT__ 435 pid_t pid = 0; 436#else 437 int pid = 0; 438#endif 439 char mtext[80]; 440 441 Msgbuf rcvbuf; 442 443 for (i = 1, listp++; i < BVAL; i++, listp++) { 444 sprintf(mtext, "%d %d %d", i, slot, (shmaddr + slot)->pid); 445 rc = send_message(msgid, (mtyp_t) * listp, mtext); 446 if (rc == -1) { 447 severe 448 ("notify: send_message Failed: %d msgid %d mtyp %d mtext %d\n", 449 errno, msgid, *listp, mtext); 450 exit(1); 451 } 452 } 453 454 while (cldcnt < BVAL) { 455 rc = msgrcv(msgid, &rcvbuf, sizeof(struct messagebuf), slot, 0); 456 if (rc == -1) { 457 switch (errno) { 458 case EAGAIN: 459 printf("msgqueue %d not ready to receive\n", 460 msgid); 461 fflush(stdout); 462 errno = 0; 463 break; 464 case ENOMSG: 465 printf("msgqueue %d no message\n", msgid); 466 fflush(stdout); 467 errno = 0; 468 break; 469 default: 470 perror("msgrcv failed"); 471 severe("msgrcv failed, errno: %d\n", errno); 472 exit(1); 473 } 474 } else { 475 sscanf(rcvbuf.mtext, "%d %d %d", &ndx, &tslot, &pid); 476 if (*((shmaddr + tslot)->list + ndx) == slot && 477 (shmaddr + tslot)->pid == pid) { 478 debugout 479 ("MSGRCV:slot: %d ndx: %d tslot: %d pid: %d\n", 480 slot, ndx, tslot, pid); 481 (shmaddr + slot)->msg++; 482 cldcnt++; 483 } else { 484 (shmaddr + slot)->err--; 485 debugout 486 ("MSGRCV: slot: %d ndx: %d tslot: %d pid: %d\n", 487 slot, ndx, tslot, pid); 488 } 489 } 490 } 491 return 0; 492} 493 494/* 495 * Calculates semaphore number and sets semaphore (lock). 496 */ 497int semoper(int slot, int smid, int opval) 498{ 499 int pslot; /* parent slot */ 500 struct sembuf smop; /* semaphore operator */ 501 502 pslot = (slot - 1) / BVAL; /* calculate parent node */ 503 smop.sem_num = pslot; 504 smop.sem_op = opval; 505 smop.sem_flg = 0; 506 semop(smid, &smop, 1); 507 return (pslot); 508} 509 510/* 511 * This is the meat and potatoes of the program. Spawn creates a tree 512 * of processes with Dval depth and Bval breadth. Each parent will spawn 513 * Bval children. Each child will store information about themselves 514 * in shared memory. The leaf nodes will communicate the existence 515 * of one another through message queues, once each leaf node has 516 * received communication from all of her siblings she will reduce 517 * the semaphore count and exit. Meanwhile all parents are waiting 518 * to hear from their children through the use of semaphores. When 519 * the semaphore count reaches zero then the parent knows all the 520 * children have talked to one another. Locking of the connter semaphore 521 * is provided by the use of another (binary) semaphore. 522 */ 523int spawn(int val) 524{ 525 extern int sem_count; /* used to keep track of childern */ 526 extern int sem_lock; /* used to lock access to sem_count semaphore */ 527 528 int i; /* Breadth counter */ 529 static int level = 0; /* level counter */ 530 int lvlflg = 0; /* level toggle, limits parental spawning 531 to one generation */ 532 int pslot = 0; 533#ifdef __64LDT__ 534 pid_t pid; /* pid of child process */ 535#else 536 int pid; /* pid of child process */ 537#endif 538 Pinfo *pinfo; /* pointer to process information in shared mem */ 539 int semval; /* value of semaphore ( equals BVAL initially */ 540 static int tval = 1; /* tree node value of child. */ 541 542 char foo[1024]; 543 544 level++; 545 546 for (i = 1; i <= BVAL; i++) { 547 tval = (val * BVAL) + i; 548 if (!lvlflg) { 549 pid = fork(); 550 if (!pid) { /* CHILD */ 551 if (AUSDEBUG) { 552 sprintf(foo, "%sslot%d", SLOTDIR, tval); 553 debugfp = fopen(foo, "a+"); 554 } 555 pinfo = put_proc_info(tval); 556 557 debugout 558 ("pid: %-6d ppid: %-6d lev: %-2d i: %-2d val: %-3d\n", 559 pinfo->pid, pinfo->ppid, level, i, tval); 560 561 set_timer(); /* set up signal handlers and initialize pgrp */ 562 if (level < DVAL) { 563 if (spawn(tval) == -1) { 564 pslot = 565 semoper(tval, sem_lock, -1); 566 semarg.val = 0; /* to fix problem with 4th arg of semctl in 64 bits MARIOG */ 567 semval = 568 semctl(sem_count, pslot, 569 GETVAL, semarg); 570 semarg.val = --semval; /* to fix problem with 4th arg of semctl in 64 bits MARIOG */ 571 semctl(sem_count, pslot, SETVAL, 572 semarg); 573 semarg.val = 1; /* to fix problem with 4th arg of semctl in 64 bits MARIOG */ 574 semctl(sem_lock, pslot, SETVAL, 575 semarg); 576 } 577 lvlflg++; 578 } else { /* leaf node */ 579 notify(tval); 580 return (-1); 581 } 582 } 583#ifdef __64LDT__ 584 else if (pid > 0 && i >= BVAL) { /* PARENT */ 585#else 586 else if (pid > (pid_t) 0 && i >= BVAL) { /* PARENT */ 587#endif 588 pslot = semoper(tval, sem_count, 0); 589 pslot = semoper(pslot, sem_lock, -1); 590 semarg.val = 0; /* to fix problem with 4th arg of semctl in 64 bits MARIOG */ 591 semval = 592 semctl(sem_count, pslot, GETVAL, semarg); 593 semarg.val = --semval; /* to fix problem with 4th arg of semctl in 64 bits MARIOG */ 594 semctl(sem_count, pslot, SETVAL, semarg); 595 semarg.val = 1; /* to fix problem with 4th arg of semctl in 64 bits MARIOG */ 596 semctl(sem_lock, pslot, SETVAL, semarg); 597 (shmaddr + val)->msg++; 598 } 599#ifdef __64LDT__ 600 else if (pid < (pid_t) 0) { 601#else 602 else if (pid < 0) { 603#endif 604 perror("spawn: fork failed"); 605 severe 606 ("spawn: fork failed, exiting with errno %d\n", 607 errno); 608 exit(1); 609 } else 610 (shmaddr + val)->msg++; 611 } 612 } 613 return (pslot); 614} 615 616/* 617 * Allocate message queues. 618 */ 619void setup_msgqueue(void) 620{ 621 extern int msgid; 622 extern int msgerr; 623 624 msgid = msgget(IPC_PRIVATE, 625 IPC_CREAT | IPC_EXCL | S_IRUSR | S_IWUSR | S_IRGRP | 626 S_IWGRP); 627 if (msgid == -1) { 628 perror("msgget msgid failed"); 629 fprintf(stderr, " SEVERE : msgget msgid failed: errno %d\n", 630 errno); 631 exit(1); 632 } 633 634 msgerr = msgget(IPC_PRIVATE, 635 IPC_CREAT | IPC_EXCL | S_IRUSR | S_IWUSR | S_IRGRP | 636 S_IWGRP); 637 if (msgerr == -1) { 638 perror("msgget msgerr failed"); 639 fprintf(stderr, " SEVERE : msgget msgerr failed: errno %d\n", 640 errno); 641 exit(1); 642 } 643} 644 645/* 646 * Set up and initialize all semaphores 647 */ 648void setup_semaphores(void) 649{ 650 extern int sem_count; 651 extern int sem_lock; 652 653 int i; 654 int rc; 655 656 prtln(); 657 sem_lock = semget(IPC_PRIVATE, nodesum - 1, 658 IPC_CREAT | IPC_EXCL | S_IRUSR | S_IWUSR | S_IRGRP | 659 S_IWGRP); 660 dprt("nodesum = %d, sem_lock = %d\n", nodesum, sem_lock); 661 662 prtln(); 663 if (sem_lock == -1) { 664 perror("semget failed for sem_lock"); 665 fprintf(stderr, 666 " SEVERE : semget failed for sem_lock, errno: %d\n", 667 errno); 668 rm_shmseg(); 669 exit(1); 670 } 671 672 prtln(); 673 sem_count = semget(IPC_PRIVATE, nodesum - 1, 674 IPC_CREAT | IPC_EXCL | S_IRUSR | S_IWUSR | S_IRGRP | 675 S_IWGRP); 676 677 if (sem_count == -1) { 678 perror("semget failed for sem_count"); 679 fprintf(stderr, 680 " SEVERE : semget failed for sem_count, errno: %d\n", 681 errno); 682 rm_shmseg(); 683 exit(1); 684 } 685 prtln(); 686 687 for (i = 0; i < (nodesum - 1); i++) { 688 semarg.val = 1; /* to fix problem with 4th arg of semctl in 64 bits MARIOG */ 689 rc = semctl(sem_lock, i, SETVAL, semarg); 690 prtln(); 691 if (rc == -1) { 692 perror("semctl failed for sem_lock failed"); 693 fprintf(stderr, 694 " SEVERE : semctl failed for sem_lock, errno: %d\n", 695 errno); 696 rm_shmseg(); 697 exit(1); 698 } 699 700 semarg.val = BVAL; /* to fix problem with 4th arg of semctl in 64 bits MARIOG */ 701 rc = semctl(sem_count, i, SETVAL, semarg); 702 prtln(); 703 if (rc == -1) { 704 perror("semctl failed for sem_lock failed"); 705 fprintf(stderr, 706 " SEVERE : semctl failed for sem_lock, errno: %d\n", 707 errno); 708 rm_shmseg(); 709 exit(1); 710 } 711 } 712} 713 714/* 715 * Set up and allocate shared memory. 716 */ 717void setup_shm(void) 718{ 719 extern int nodesum; /* global shared memory id */ 720 extern int shmid; /* global shared memory id */ 721 extern Pinfo *shmaddr; 722 723 int i, j; /* counters */ 724 Pinfo *shmad = NULL; /* ptr to start of shared memory. */ 725 Pinfo *pinfo = NULL; /* ptr to struct in shared memory. */ 726 727 debugout("size = %d, size (in hex) = %#x nodes: %d\n", 728 sizeof(Pinfo) * nodesum + (nodesum * BVAL * sizeof(int)), 729 sizeof(Pinfo) * nodesum + (nodesum * BVAL * sizeof(int)), 730 nodesum); 731 732 /* Get shared memory id */ 733 shmid = shmget(IPC_PRIVATE, 734 sizeof(Pinfo) * nodesum + (nodesum * BVAL * sizeof(int)), 735 IPC_CREAT | IPC_EXCL | S_IRUSR | S_IWUSR | S_IRGRP | 736 S_IWGRP); 737 if (shmid < 0) { 738 perror("shmget failed"); 739 fprintf(stderr, " SEVERE : shmget failed: errno %d\n", errno); 740 exit(1); 741 } 742 743 /* allocate shared memory */ 744 745 if ((shmad = shmat(shmid, (char *)shmad, 0)) == MAP_FAILED) { 746 printf("SEVERE : shmat failed\n"); 747 exit(1); 748 } else { 749 shmctl(shmid, IPC_RMID, NULL); 750 } 751 752 /* set all fields in shared memory to -1 */ 753 for (pinfo = shmad, i = 0; i < nodesum; i++, pinfo++) { 754#ifdef __64LDT__ 755 pinfo->pid = (pid_t) - 1; 756 pinfo->ppid = (pid_t) - 1; 757#else 758 pinfo->pid = -1; 759 pinfo->ppid = -1; 760#endif 761 pinfo->msg = -1; 762 pinfo->err = -1; 763 764 /* Changed 10/9/97 */ 765 /* pinfo->list = (int *)((ulong)shmad + nodesum * sizeof(Pinfo) 766 + (sizeof(int) * BVAL * i)); */ 767 pinfo->list = 768 (int *)((long)shmad + nodesum * sizeof(Pinfo) + 769 (sizeof(int) * BVAL * i)); 770 for (j = 0; j < BVAL; j++) 771 *(pinfo->list + j) = -1; 772 } 773 shmaddr = shmad; 774} 775 776/* 777 * Set up Signal handler and which signals to catch 778 */ 779void set_signals(void *sighandler()) 780{ 781 int i; 782 int rc; 783 784 struct sigaction action; 785 786 /* list of signals we want to catch */ 787 static struct signalinfo { 788 int signum; 789 char *signame; 790 } siginfo[] = { 791 { 792 SIGHUP, "SIGHUP"}, { 793 SIGINT, "SIGINT"}, { 794 SIGQUIT, "SIGQUIT"}, { 795 SIGABRT, "SIGABRT"}, { 796 SIGBUS, "SIGBUS"}, { 797 SIGSEGV, "SIGSEGV"}, { 798 SIGALRM, "SIGALRM"}, { 799 SIGUSR1, "SIGUSR1"}, { 800 SIGUSR2, "SIGUSR2"}, { 801 -1, "ENDSIG"} 802 }; 803 804 char tmpstr[1024]; 805 806 action.sa_handler = (void *)sighandler; 807 808#ifdef _LINUX 809 sigfillset(&action.sa_mask); 810#else 811 SIGINITSET(action.sa_mask); 812#endif 813 action.sa_flags = 0; 814 815 /* Set the signal handler up */ 816#ifdef _LINUX 817 sigaddset(&action.sa_mask, SIGTERM); 818#else 819 SIGADDSET(action.sa_mask, SIGTERM); 820#endif 821 for (i = 0; siginfo[i].signum != -1; i++) { 822#ifdef _LINUX 823 sigaddset(&action.sa_mask, siginfo[i].signum); 824#else 825 SIGADDSET(action.sa_mask, siginfo[i].signum); 826#endif 827 rc = sigaction(siginfo[i].signum, &action, NULL); 828 if (rc == -1) { 829 sprintf(tmpstr, "sigaction: %s\n", siginfo[i].signame); 830 perror(tmpstr); 831 fprintf(stderr, 832 " SEVERE : Could not set %s signal action, errno=%d.", 833 siginfo[i].signame, errno); 834 exit(1); 835 } 836 } 837} 838 839/* 840* Get and set a timer for current process. 841*/ 842#ifndef _LINUX 843void set_timer(void) 844{ 845 struct itimerstruc_t itimer, old_itimer; 846 847 if ((timer = gettimerid(TIMERID_REAL, DELIVERY_SIGNALS)) == -1) { 848 perror("gettimerid"); 849 fprintf(stderr, " SEVERE : Could not get timer id, errno=%d.", 850 errno); 851 exit(1); 852 } 853 854 /* 855 * Start the timer. 856 */ 857 itimer.it_interval.tv_nsec = 0; 858 itimer.it_interval.tv_sec = 0; 859 itimer.it_value.tv_nsec = 0; 860 itimer.it_value.tv_sec = (time_t) (TVAL * 60.0); 861 if (incinterval(timer, &itimer, &old_itimer) == -1) { 862 perror("incinterval"); 863 fprintf(stderr, 864 " SEVERE : Could not set timer interval, errno=%d.", 865 errno); 866 (void)reltimerid(timer); 867 exit(1); 868 } 869} 870#else 871 872void set_timer(void) 873{ 874 struct itimerval itimer; 875 876 memset(&itimer, 0, sizeof(struct itimerval)); 877 /* 878 * Start the timer. 879 */ 880 itimer.it_interval.tv_usec = 0; 881 itimer.it_interval.tv_sec = 0; 882 itimer.it_value.tv_usec = 0; 883 itimer.it_value.tv_sec = (time_t) (TVAL * 60.0); 884 885 if (setitimer(ITIMER_REAL, &itimer, NULL)) { 886 perror("setitimer"); 887 exit(1); 888 } 889} 890#endif 891 892/* 893 * parse_args 894 * 895 * Parse command line arguments. Any errors cause the program to exit 896 * at this point. 897 */ 898void parse_args(int argc, char *argv[]) 899{ 900 int i; 901 int opt, errflag = 0; 902 int dflag = 0, bflag = 0, fflag = 0, tflag = 0; 903 extern int optind; 904 extern char *optarg; 905 906 /* DVAL: 0 1 2 3 4 5 6 7 8 9 10 11 */ 907 int limits[] = { -1, -1, MAXBVAL, 17, 8, 5, 4, 3, 2, 2, 2, 2 }; 908 909 while ((opt = getopt(argc, argv, "b:d:ft:D?")) != EOF) { 910 switch (opt) { 911 case 'b': 912 if (bflag) 913 errflag++; 914 else { 915 bflag++; 916 errno = 0; 917 BVAL = atoi(optarg); 918 if (errno) { 919 perror("atoi"); 920 fprintf(stderr, 921 " ERROR : atoi - errno %d.", 922 errno); 923 errflag++; 924 } 925 } 926 break; 927 case 'd': 928 if (dflag) 929 errflag++; 930 else { 931 dflag++; 932 errno = 0; 933 DVAL = atoi(optarg); 934 if (errno) { 935 perror("atoi"); 936 fprintf(stderr, 937 " ERROR : atoi - errno %d.", 938 errno); 939 errflag++; 940 } 941 } 942 break; 943 case 'f': 944 fflag = 1; 945 break; 946 case 'D': 947 AUSDEBUG = 1; 948 break; 949 case 't': 950 if (tflag) 951 errflag++; 952 else { 953 tflag++; 954 errno = 0; 955 TVAL = atoi(optarg); 956 if (!TVAL || errno) { 957 perror("atoi"); 958 fprintf(stderr, 959 " ERROR : atoi - errno %d.", 960 errno); 961 errflag++; 962 } 963 } 964 break; 965 case '?': 966 errflag++; 967 break; 968 } 969 } 970 971 if (BVAL < 2) { 972 errflag++; 973 fprintf(stderr, "The value of b must be greater than 1\n"); 974 } else if (DVAL < 2) { 975 errflag++; 976 fprintf(stderr, "The depth value must be greater than 1\n"); 977 } else if (!fflag && (DVAL > MAXDVAL)) { 978/* || BVAL > limits[DVAL])) { */ 979 fprintf(stderr, "\tExceeded process creation limits. \ 980\n\tParameters will generate %lu processes. \n\tThe preset limits are as \ 981follows:\n\t\tdepth\tbreadth\ttotal\n", sumit(BVAL, DVAL)); 982 for (i = 2; i <= MAXDVAL; i++) 983 fprintf(stderr, "\t\t %-3d\t %-5d\t%-5lu\n", i, 984 limits[i], sumit(limits[i], i)); 985 exit(1); 986 } 987 988 if (errflag) { 989 fprintf(stderr, 990 "usage: %s [-b number] [-d number] [-t number] \n", 991 argv[0]); 992 fprintf(stderr, "where:\n"); 993 fprintf(stderr, 994 "\t-b number\tnumber of children each parent will spawn ( > 1)\n"); 995 fprintf(stderr, "\t-d number\tdepth of process tree ( > 1)\n"); 996 fprintf(stderr, "\t-t\t\tset timeout value\n"); 997 fprintf(stderr, " SEVERE : Command line parameter error.\n"); 998 exit(1); 999 } 1000} 1001 1002/* 1003 * Initializes environment variables, using defaults if not set in env. 1004 */ 1005int getenv_val(void) 1006{ 1007 char *c; /* character pointer */ 1008 struct envstruct *envd = envdata; /* pointer to environment data */ 1009 1010 union { 1011 int *vint; 1012 char *chptr; 1013 } val; 1014 1015 /* 1016 * Loop through envdata, set default first then set environment 1017 * variable value if present. 1018 */ 1019 for (; *envd->env_name != '\0'; envd++) { 1020 if ((val.chptr = getenv(envd->env_name)) == NULL) 1021 val.chptr = envd->eval.chptr; 1022 1023 c = val.chptr; 1024 while (isdigit(*c)) 1025 c++; 1026 1027 if (*c == '\0') { 1028 (envd->eval.vint) = malloc(sizeof(int)); 1029 *(envd->eval.vint) = atoi(val.chptr); 1030 } else { 1031 envd->eval.chptr = malloc(strlen(val.chptr) + 1); 1032 strcpy(envd->eval.chptr, val.chptr); 1033 } 1034 } 1035 return 0; 1036} 1037 1038/* 1039 * Prints all errors coming from the children and terminates execution if 1040 * an error execption is received. In addition messenger() is sent the 1041 * process group id of the children so it can terminate all children. 1042 * This routine uses message queues to receive all communications. 1043 */ 1044void messenger(void) 1045{ /* AKA Assassin */ 1046 Msgbuf rcvbuf; 1047 1048 int discrim = 0; 1049 int rc; /* generic return code var */ 1050 int sig = -1; /* type of signal received */ 1051 extern int msgerr; /* message queue used to send error messages */ 1052 extern int procgrp; /* process group of children (used to kill them) */ 1053 1054 /* 1055 * Infinite loop used to receive error messages from children and 1056 * to terminate process tree. 1057 */ 1058 while (TRUE) { 1059 rc = msgrcv(msgerr, &rcvbuf, sizeof(struct messagebuf), 0, 0); 1060 if (rc == -1) { 1061 switch (errno) { 1062 case EAGAIN: 1063 printf("msgqueue %d not ready to receive\n", 1064 msgid); 1065 fflush(stdout); 1066 errno = 0; 1067 break; 1068 case ENOMSG: 1069 printf("msgqueue %d no message\n", msgid); 1070 fflush(stdout); 1071 errno = 0; 1072 break; 1073 default: 1074 perror("msgrcv failed"); 1075 fprintf(stderr, 1076 " SEVERE : messenger - msgrcv failed, errno: %d\n", 1077 errno); 1078 errno = 0; 1079 break; 1080 } 1081 } else { 1082 switch ((int)rcvbuf.mtyp) { 1083 case 1: /* type 1: we received the process group id */ 1084 sscanf(rcvbuf.mtext, "%d", &procgrp); 1085 break; 1086 1087 case 2: /* type 2: we received an error */ 1088 fprintf(stderr, " SEVERE : %s ", rcvbuf.mtext); 1089 /* rcvbuf.mtext type %s ou %d ??? */ 1090 break; 1091 1092 case 3: /* type 3: somebody got a signal, now we terminate */ 1093 sscanf(rcvbuf.mtext, "%d", &sig); 1094 1095 switch (sig) { 1096 case SIGALRM: 1097 /* a process is hung, we will terminate */ 1098 killpg(procgrp, sig); 1099 fprintf(errfp, 1100 "ALERT! ALERT! WE HAVE TIMED OUT\n"); 1101 fprintf(stderr, 1102 " SEVERE : SIGALRM: A process timed out, we failed\n"); 1103 shmaddr->err++; 1104 break; 1105 1106 case SIGUSR1: 1107 /* Special: means everything went ok */ 1108 discrim = 1; 1109 break; 1110 1111 default: 1112 /* somebody sent a signal, we will terminate */ 1113 killpg(procgrp, sig); 1114 fprintf(errfp, 1115 "We received signal %d\n", sig); 1116 fprintf(stderr, 1117 " SEVERE : signal %d received, A proc was killed\n", 1118 sig); 1119 break; 1120 } 1121 /* clean up and exit with status */ 1122 rm_msgqueue(); 1123 rm_semseg(); 1124 if (AUSDEBUG) 1125 print_shm(); 1126 prtln(); 1127 rm_shmseg(); 1128 prtln(); 1129 if (discrim) { 1130 prtln(); 1131 printf("Test exiting with SUCCESS\n"); 1132 exit(0); 1133 } 1134 exit(1); 1135 1136 break; 1137 } 1138 } 1139 } 1140} 1141 1142/* 1143 * This routine spawns off the first child (node 0) of the process tree. 1144 * This child set up the signal handler for all of the children and also 1145 * sets up a process group so that all children can be terminated easily. 1146 * The child then calls spawn which creates the process tree. After spawn 1147 * has returned the child contacts the parent and the parent exits. 1148 * The parent sets her own signal handler and then calls messenger. 1149 */ 1150void doit(void) 1151{ 1152 pid_t pid; /* process id */ 1153 int rc; 1154 char mtext[80]; /* message text */ 1155 extern int msgerr; 1156 extern int procgrp; 1157 1158 pid = fork(); 1159#ifdef __64LDT__ 1160 if (pid == (pid_t) 0) { 1161#else 1162 if (pid == 0) { 1163#endif 1164 /* set the process group so we can terminate all children */ 1165 set_signals((void *)nextofkin); /* set up signal handlers and initialize pgrp */ 1166#ifndef _LINUX 1167 procgrp = setpgrp(0, 0); 1168#else 1169 procgrp = setpgrp(); 1170#endif 1171 if (AUSDEBUG) { 1172 fprintf(stderr, "process group: %d\n", procgrp); 1173 fflush(stderr); 1174 } 1175 if (procgrp == -1) { 1176 perror("setpgid failed"); 1177 fprintf(stderr, " SEVERE : setpgid failed, errno: %d\n", 1178 errno); 1179 exit(1); 1180 } 1181 sprintf(mtext, "%d", procgrp); 1182 rc = send_message(msgerr, 1, mtext); 1183 if (rc == -1) { 1184 perror("send_message failed"); 1185 fprintf(stderr, 1186 " SEVERE : send_message failed, errno: %d\n", 1187 errno); 1188 exit(1); 1189 } 1190 1191 put_proc_info(0); /* store process info for this (root) process */ 1192 spawn(0); 1193 if (shmaddr->pid == getpid()) { 1194 sprintf(mtext, "%d", SIGUSR1); 1195 rc = send_message(msgerr, 3, mtext); 1196 if (rc == -1) { 1197 severe 1198 ("msgsnd failed: %d msgid %d mtyp %d mtext %d\n", 1199 errno, msgerr, 3, mtext); 1200 exit(1); 1201 1202 } 1203 } 1204 exit(0); 1205 } 1206#ifdef __64LDT__ 1207 else if (pid > (pid_t) 0) { 1208#else 1209 else if (pid > 0) { 1210#endif 1211 set_signals((void *)cleanup); /* set up signal handlers and initialize pgrp */ 1212 messenger(); /* receives and acts upon messages */ 1213 exit(1); 1214 } else { 1215 perror("fork failed"); 1216 fprintf(stderr, 1217 " SEVERE : fork failed, exiting with errno %d\n", 1218 errno); 1219 exit(1); 1220 } 1221} 1222 1223/* main */ 1224int main(int argc, char *argv[]) 1225{ 1226 extern Pinfo *shmaddr; /* start address of shared memory */ 1227 1228 prtln(); 1229 getenv_val(); /* Get and initialize all environment variables */ 1230 prtln(); 1231 1232 if (argc < 2) { 1233 fprintf(stderr, 1234 "usage: %s [-b number] [-d number] [-t number] \n", 1235 argv[0]); 1236 fprintf(stderr, "where:\n"); 1237 fprintf(stderr, 1238 "\t-b number\tnumber of children each parent will spawn ( > 1)\n"); 1239 fprintf(stderr, "\t-d number\tdepth of process tree ( > 1)\n"); 1240 fprintf(stderr, "\t-t\t\tset timeout value\n"); 1241 fprintf(stderr, " SEVERE : Command line parameter error.\n"); 1242 exit(1); 1243 } 1244 1245 parse_args(argc, argv); /* Get all command line arguments */ 1246 dprt("value of BVAL = %d, value of DVAL = %d\n", BVAL, DVAL); 1247 nodesum = sumit(BVAL, DVAL); 1248#ifdef _LINUX 1249 if (nodesum > 250) { 1250 printf("total number of process to be created " 1251 "nodesum (%d) is greater\n than the allowed " 1252 "SEMMSL value (250)\n", nodesum); 1253 printf("reseting the value of nodesum to SEMMSL\n"); 1254 nodesum = 250; 1255 } 1256#endif 1257 1258 dprt("value of nodesum is initiallized to: %d\n", nodesum); 1259 1260 prtln(); 1261 setup_shm(); /* Set up, allocate and initialize shared memory */ 1262 prtln(); 1263 setup_semaphores(); /* Set up, allocate and initialize semaphores */ 1264 prtln(); 1265 setup_msgqueue(); /* Set up, allocate and initialize message queues */ 1266 prtln(); 1267 1268 doit(); /* spawn off processes */ 1269 prtln(); 1270 return 0; 1271 1272} 1273