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 */
print_shm(void)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 */
send_message(int id,mtyp_t type,char * text)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 */
severe(char * fmt,...)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 */
debugout(char * fmt,...)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 */
rm_msgqueue(void)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 */
rm_shmseg(void)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 */
rm_semseg(void)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 */
cleanup(int sig,int code,struct sigcontext * scp)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 */
nextofkin(int sig,int code,struct sigcontext * scp)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 */
sumit(int B,int D)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 */
put_proc_info(int tval)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 */
notify(int slot)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 */
semoper(int slot,int smid,int opval)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 */
spawn(int val)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