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