/* * * Copyright (c) International Business Machines Corp., 2001 * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See * the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include #include #include #include #include #include #include #include #include #include #ifndef _LINUX /* LINUX INCLUDES */ #include #include #else #include #include #include #include #endif #include #include #include #include #include #include #include #include "lapi/semun.h" /* indexes into environment variable array */ #define ADBG 0 #define BNDX 1 #define DNDX 2 #define TNDX 3 #define MAXBVAL 70 #define MAXDVAL 11 #define SLOTDIR "./slot/" #ifdef _LINUX /* LINUX #defnes */ #ifndef TRUE #define TRUE 1 #endif #ifndef FALSE #define FALSE 0 #endif #endif #if defined _LINUX && defined DEBUG #define prtln() printf("At line number: %d\n", __LINE__); \ fflush(NULL) #define dprt(fmt, args...) printf(fmt, ## args) #else #define prtln() #define dprt(fmt, args...) #endif /* aliases for environment variable entries */ #define AUSDEBUG (*edat[ADBG].eval.vint) /* debug value */ #define BVAL (*edat[BNDX].eval.vint) /* # of childern per parent */ #define DVAL (*edat[DNDX].eval.vint) /* depth of process tree */ #define TVAL (*edat[TNDX].eval.vint) /* timer value */ #ifdef _LINUX typedef long mtyp_t; #endif /* structure of information stored about each process in shared memory */ typedef struct proc_info { #ifdef __64LDT__ pid_t pid; /* process id */ pid_t ppid; /* parent process id */ #else int pid; /* process id */ int ppid; /* parent process id */ #endif int msg; /* parent process id */ int err; /* error indicator */ int *list; /* pointer to list of parent and sibling slot locations */ } Pinfo; typedef struct messagebuf { mtyp_t mtyp; /* message type */ char mtext[80]; /* message text */ } Msgbuf; union semun semarg; /* structure of all environment variable used by program */ struct envstruct { char *env_name; union { char *chptr; int *vint; } eval; } envdata[] = { { "AUSDBG", { "0"}}, { "BVAL", { "3"}}, { "DVAL", { "2"}}, { "FORCE", { "0"}}, { "TVAL", { "1"}}, { "", { ""}} }; char *errfile; /* pointer to errfile name */ int msgid; /* message queue for leaf nodes */ int msgerr; /* message queue for errors */ int nodesum; /* total number of process to be created */ int sem_count; /* counter semaphore */ int sem_lock; /* locks access to counter semaphore */ int shmid; /* global shared memory id varible */ int procgrp; /* process group id */ timer_t timer; /* timer structure */ Pinfo *shmaddr; /* Start address of shared memory */ #ifndef _LINUX FILE *errfp = stderr; /* error file pointer, probably not necessary */ FILE *debugfp = stderr; /* debug file pointer, used if AUSDEBUG set */ #else #define errfp stderr #define debugfp stderr #endif struct envstruct *edat = envdata; /* pointer to environment data */ /* external function declarations */ extern int killpg(int procgrp, int sig); extern timer_t gettimerid(int Timer_type, int Notify_type); extern int reltimerid(timer_t timer); /* internal function declarations */ void cleanup(int sig, int code, struct sigcontext *scp); void nextofkin(int sig, int code, struct sigcontext *scp); void doit(void); void debugout(char *fmt, ...); int getenv_val(void); void messenger(void); void nextofkin(int sig, int code, struct sigcontext *scp); int notify(int slot); void parse_args(int argc, char *argv[]); void print_shm(void); Pinfo *put_proc_info(int tval); void rm_msgqueue(void); void rm_semseg(void); void rm_shmseg(void); int semoper(int slot, int smid, int opval); int send_message(int id, mtyp_t type, char *text); void set_timer(void); void set_signals(void *sighandler()); void setup_msgqueue(void); void setup_semaphores(void); void setup_shm(void); void severe(char *fmt, ...); Pinfo *shmgetseg(void); int spawn(int val); unsigned long sumit(int B, int D); /* * Prints out the data structures in shared memory. */ void print_shm(void) { extern int nodesum; /* total number of nodes created */ extern Pinfo *shmaddr; /* shared memory pointer */ extern int shmid; /* shared memory id */ Pinfo *pinfo; /* pointer to process info in shared memory */ int *listp; /* pointer to sibling info in shared memory */ int i, j; /* counters */ struct shmid_ds buf; if (shmctl(shmid, IPC_STAT, &buf)) return; for (pinfo = shmaddr, i = 0; i < nodesum; i++, pinfo++) { fprintf(errfp, "slot: %-4d pid: %-6d ppid: %-6d msg: %-2d err: %-2d lst:", i, pinfo->pid, pinfo->ppid, pinfo->msg, pinfo->err); for (j = 0, listp = pinfo->list; j < BVAL; j++, listp++) fprintf(errfp, " %d", *listp); fprintf(errfp, "\n"); } } /* * Generalized send routine. Sends a message on message queue. */ int send_message(int id, mtyp_t type, char *text) { int rc; Msgbuf sndbuf; strcpy(sndbuf.mtext, text); sndbuf.mtyp = type; while (TRUE) { rc = msgsnd(id, &sndbuf, sizeof(struct messagebuf), IPC_NOWAIT); if (rc == -1 && errno == EAGAIN) { debugout("msgqueue %d of mtyp %d not ready to send\n", msgid, type); errno = 0; } else return (rc); } } /* * Sends error message to initial parent (messenger).i */ void severe(char *fmt, ...) { va_list args; int rc; char mtext[80]; extern int msgerr; va_start(args, fmt); vsprintf(mtext, fmt, args); va_end(args); rc = send_message(msgerr, 2, mtext); if (rc == -1) { perror("cannot send message to msgerr"); exit(1); } } /* * if AUSDEBUG set will print information to file associated with slot number. */ void debugout(char *fmt, ...) { va_list args; if (AUSDEBUG) { va_start(args, fmt); vfprintf(debugfp, fmt, args); va_end(args); } } /* * Remove message queues. */ void rm_msgqueue(void) { extern int msgid; /* remove message queue id. */ if (msgctl(msgid, IPC_RMID, NULL) && errno != EINVAL) { fprintf(errfp, "msgctl failed msgid: errno %d\n", errno); perror("msgctl failed"); } /* remove message queue id. */ if (msgctl(msgerr, IPC_RMID, NULL) && errno != EINVAL) { fprintf(errfp, "msgctl failed msgerr: errno %d\n", errno); perror("msgctl failed"); } } /* * Remove shared memory segment. */ void rm_shmseg(void) { extern int shmid; /* Global shared memory id */ extern Pinfo *shmaddr; /* Global shared memory address */ /* remove shared memory id (and shared memory segment). */ if (shmctl(shmid, IPC_RMID, NULL) && errno != EINVAL) { fprintf(errfp, "shmctl failed: errno %d\n", errno); perror("shmctl failed"); } } /* * Remove semaphores. */ void rm_semseg(void) { extern int sem_lock; extern int sem_count; /* remove sem_lock semaphore id */ semarg.val = 0; /* to fix problem with 4th arg of semctl in 64 bits MARIOG */ if (semctl(sem_lock, 0, IPC_RMID, semarg.val) && errno != EINVAL) { fprintf(errfp, "semctl failed: errno %d\n", errno); perror("semctl failed"); } /* remove sem_count semaphore id. */ semarg.val = 0; /* to fix problem with 4th arg of semctl in 64 bits MARIOG */ if (semctl(sem_count, 0, IPC_RMID, semarg.val) && errno != EINVAL) { fprintf(errfp, "semctl failed: errno %d\n", errno); perror("semctl failed"); } } /* * Routine to clean up shared memory and return exit status (CHILD handler). */ void cleanup(int sig, int code, struct sigcontext *scp) { int rc; char mtext[80]; killpg(procgrp, SIGTERM); sprintf(mtext, "%d", sig); rc = send_message(msgerr, 3, mtext); if (rc == -1) { severe("msgsnd failed: %d msgid %d mtyp %d mtext %d\n", errno, msgerr, 3, mtext); } } /* * Routine to clean up shared memory and return exit status (PARENT handler). */ void nextofkin(int sig, int code, struct sigcontext *scp) { int rc; char mtext[80]; sprintf(mtext, "%d", sig); rc = send_message(msgerr, 3, mtext); if (rc == -1) { severe("msgsnd failed: %d msgid %d mtyp %d mtext %d\n", errno, msgerr, 3, mtext); } #ifndef _LINUX reltimerid(timer); #endif exit(1); } /* given breadth and depth of a tree, sum up total number of nodes created */ unsigned long sumit(int B, int D) { int i; int exp = 1; /* exponent of breadth */ unsigned long sum = 1; /* running sum of nodes */ for (sum = 1, i = 1; i <= D; i++) { exp = B * exp; sum += (int)exp; } return (sum); } /* Finds correct slot for current process in shared memory and stores * information about process in it. */ Pinfo *put_proc_info(int tval) { extern int nodesum; extern Pinfo *shmaddr; int sibslot = 0; /* sibling slot number */ int *listp; /* ptr to sibling info for current proc */ Pinfo *smp; /* ptr to current process data slot */ smp = shmaddr + tval; smp->pid = getpid(); smp->ppid = getppid(); smp->err = 0; smp->msg = 0; /* if very first process (slot 0), dont fill in info about siblings * and parent. Sibling and parent info is irrevelant in this case. */ if (!tval) return (smp); /* find parent of current process and store slot location */ smp->list = (int *)(Pinfo *) (shmaddr + nodesum) + (BVAL * tval); *smp->list = (tval - 1) / BVAL; listp = smp->list + 1; /* calculate and store sibling slot numbers of current process */ for (sibslot = *smp->list * BVAL + 1; listp < smp->list + BVAL; sibslot++) { if (tval != sibslot) *(listp++) = sibslot; } return (smp); } /* This routine sends a message from the current process to all of her * siblings and then waits to receive responses from them. A timer is * set so that if a message is lost or not received for some reason * we can exit gracefully. */ int notify(int slot) { extern int msgid; extern Pinfo *shmaddr; int i; int rc; int tslot; int *listp = (shmaddr + slot)->list; int cldcnt = 1; int ndx = 0; #ifdef __64LDT__ pid_t pid = 0; #else int pid = 0; #endif char mtext[80]; Msgbuf rcvbuf; for (i = 1, listp++; i < BVAL; i++, listp++) { sprintf(mtext, "%d %d %d", i, slot, (shmaddr + slot)->pid); rc = send_message(msgid, (mtyp_t) * listp, mtext); if (rc == -1) { severe ("notify: send_message Failed: %d msgid %d mtyp %d mtext %d\n", errno, msgid, *listp, mtext); exit(1); } } while (cldcnt < BVAL) { rc = msgrcv(msgid, &rcvbuf, sizeof(struct messagebuf), slot, 0); if (rc == -1) { switch (errno) { case EAGAIN: printf("msgqueue %d not ready to receive\n", msgid); fflush(stdout); errno = 0; break; case ENOMSG: printf("msgqueue %d no message\n", msgid); fflush(stdout); errno = 0; break; default: perror("msgrcv failed"); severe("msgrcv failed, errno: %d\n", errno); exit(1); } } else { sscanf(rcvbuf.mtext, "%d %d %d", &ndx, &tslot, &pid); if (*((shmaddr + tslot)->list + ndx) == slot && (shmaddr + tslot)->pid == pid) { debugout ("MSGRCV:slot: %d ndx: %d tslot: %d pid: %d\n", slot, ndx, tslot, pid); (shmaddr + slot)->msg++; cldcnt++; } else { (shmaddr + slot)->err--; debugout ("MSGRCV: slot: %d ndx: %d tslot: %d pid: %d\n", slot, ndx, tslot, pid); } } } return 0; } /* * Calculates semaphore number and sets semaphore (lock). */ int semoper(int slot, int smid, int opval) { int pslot; /* parent slot */ struct sembuf smop; /* semaphore operator */ pslot = (slot - 1) / BVAL; /* calculate parent node */ smop.sem_num = pslot; smop.sem_op = opval; smop.sem_flg = 0; semop(smid, &smop, 1); return (pslot); } /* * This is the meat and potatoes of the program. Spawn creates a tree * of processes with Dval depth and Bval breadth. Each parent will spawn * Bval children. Each child will store information about themselves * in shared memory. The leaf nodes will communicate the existence * of one another through message queues, once each leaf node has * received communication from all of her siblings she will reduce * the semaphore count and exit. Meanwhile all parents are waiting * to hear from their children through the use of semaphores. When * the semaphore count reaches zero then the parent knows all the * children have talked to one another. Locking of the connter semaphore * is provided by the use of another (binary) semaphore. */ int spawn(int val) { extern int sem_count; /* used to keep track of childern */ extern int sem_lock; /* used to lock access to sem_count semaphore */ int i; /* Breadth counter */ static int level = 0; /* level counter */ int lvlflg = 0; /* level toggle, limits parental spawning to one generation */ int pslot = 0; #ifdef __64LDT__ pid_t pid; /* pid of child process */ #else int pid; /* pid of child process */ #endif Pinfo *pinfo; /* pointer to process information in shared mem */ int semval; /* value of semaphore ( equals BVAL initially */ static int tval = 1; /* tree node value of child. */ char foo[1024]; level++; for (i = 1; i <= BVAL; i++) { tval = (val * BVAL) + i; if (!lvlflg) { pid = fork(); if (!pid) { /* CHILD */ if (AUSDEBUG) { sprintf(foo, "%sslot%d", SLOTDIR, tval); debugfp = fopen(foo, "a+"); } pinfo = put_proc_info(tval); debugout ("pid: %-6d ppid: %-6d lev: %-2d i: %-2d val: %-3d\n", pinfo->pid, pinfo->ppid, level, i, tval); set_timer(); /* set up signal handlers and initialize pgrp */ if (level < DVAL) { if (spawn(tval) == -1) { pslot = semoper(tval, sem_lock, -1); semarg.val = 0; /* to fix problem with 4th arg of semctl in 64 bits MARIOG */ semval = semctl(sem_count, pslot, GETVAL, semarg); semarg.val = --semval; /* to fix problem with 4th arg of semctl in 64 bits MARIOG */ semctl(sem_count, pslot, SETVAL, semarg); semarg.val = 1; /* to fix problem with 4th arg of semctl in 64 bits MARIOG */ semctl(sem_lock, pslot, SETVAL, semarg); } lvlflg++; } else { /* leaf node */ notify(tval); return (-1); } } #ifdef __64LDT__ else if (pid > 0 && i >= BVAL) { /* PARENT */ #else else if (pid > (pid_t) 0 && i >= BVAL) { /* PARENT */ #endif pslot = semoper(tval, sem_count, 0); pslot = semoper(pslot, sem_lock, -1); semarg.val = 0; /* to fix problem with 4th arg of semctl in 64 bits MARIOG */ semval = semctl(sem_count, pslot, GETVAL, semarg); semarg.val = --semval; /* to fix problem with 4th arg of semctl in 64 bits MARIOG */ semctl(sem_count, pslot, SETVAL, semarg); semarg.val = 1; /* to fix problem with 4th arg of semctl in 64 bits MARIOG */ semctl(sem_lock, pslot, SETVAL, semarg); (shmaddr + val)->msg++; } #ifdef __64LDT__ else if (pid < (pid_t) 0) { #else else if (pid < 0) { #endif perror("spawn: fork failed"); severe ("spawn: fork failed, exiting with errno %d\n", errno); exit(1); } else (shmaddr + val)->msg++; } } return (pslot); } /* * Allocate message queues. */ void setup_msgqueue(void) { extern int msgid; extern int msgerr; msgid = msgget(IPC_PRIVATE, IPC_CREAT | IPC_EXCL | S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP); if (msgid == -1) { perror("msgget msgid failed"); fprintf(stderr, " SEVERE : msgget msgid failed: errno %d\n", errno); exit(1); } msgerr = msgget(IPC_PRIVATE, IPC_CREAT | IPC_EXCL | S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP); if (msgerr == -1) { perror("msgget msgerr failed"); fprintf(stderr, " SEVERE : msgget msgerr failed: errno %d\n", errno); exit(1); } } /* * Set up and initialize all semaphores */ void setup_semaphores(void) { extern int sem_count; extern int sem_lock; int i; int rc; prtln(); sem_lock = semget(IPC_PRIVATE, nodesum - 1, IPC_CREAT | IPC_EXCL | S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP); dprt("nodesum = %d, sem_lock = %d\n", nodesum, sem_lock); prtln(); if (sem_lock == -1) { perror("semget failed for sem_lock"); fprintf(stderr, " SEVERE : semget failed for sem_lock, errno: %d\n", errno); rm_shmseg(); exit(1); } prtln(); sem_count = semget(IPC_PRIVATE, nodesum - 1, IPC_CREAT | IPC_EXCL | S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP); if (sem_count == -1) { perror("semget failed for sem_count"); fprintf(stderr, " SEVERE : semget failed for sem_count, errno: %d\n", errno); rm_shmseg(); exit(1); } prtln(); for (i = 0; i < (nodesum - 1); i++) { semarg.val = 1; /* to fix problem with 4th arg of semctl in 64 bits MARIOG */ rc = semctl(sem_lock, i, SETVAL, semarg); prtln(); if (rc == -1) { perror("semctl failed for sem_lock failed"); fprintf(stderr, " SEVERE : semctl failed for sem_lock, errno: %d\n", errno); rm_shmseg(); exit(1); } semarg.val = BVAL; /* to fix problem with 4th arg of semctl in 64 bits MARIOG */ rc = semctl(sem_count, i, SETVAL, semarg); prtln(); if (rc == -1) { perror("semctl failed for sem_lock failed"); fprintf(stderr, " SEVERE : semctl failed for sem_lock, errno: %d\n", errno); rm_shmseg(); exit(1); } } } /* * Set up and allocate shared memory. */ void setup_shm(void) { extern int nodesum; /* global shared memory id */ extern int shmid; /* global shared memory id */ extern Pinfo *shmaddr; int i, j; /* counters */ Pinfo *shmad = NULL; /* ptr to start of shared memory. */ Pinfo *pinfo = NULL; /* ptr to struct in shared memory. */ debugout("size = %d, size (in hex) = %#x nodes: %d\n", sizeof(Pinfo) * nodesum + (nodesum * BVAL * sizeof(int)), sizeof(Pinfo) * nodesum + (nodesum * BVAL * sizeof(int)), nodesum); /* Get shared memory id */ shmid = shmget(IPC_PRIVATE, sizeof(Pinfo) * nodesum + (nodesum * BVAL * sizeof(int)), IPC_CREAT | IPC_EXCL | S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP); if (shmid < 0) { perror("shmget failed"); fprintf(stderr, " SEVERE : shmget failed: errno %d\n", errno); exit(1); } /* allocate shared memory */ if ((shmad = shmat(shmid, (char *)shmad, 0)) == MAP_FAILED) { printf("SEVERE : shmat failed\n"); exit(1); } else { shmctl(shmid, IPC_RMID, NULL); } /* set all fields in shared memory to -1 */ for (pinfo = shmad, i = 0; i < nodesum; i++, pinfo++) { #ifdef __64LDT__ pinfo->pid = (pid_t) - 1; pinfo->ppid = (pid_t) - 1; #else pinfo->pid = -1; pinfo->ppid = -1; #endif pinfo->msg = -1; pinfo->err = -1; /* Changed 10/9/97 */ /* pinfo->list = (int *)((ulong)shmad + nodesum * sizeof(Pinfo) + (sizeof(int) * BVAL * i)); */ pinfo->list = (int *)((long)shmad + nodesum * sizeof(Pinfo) + (sizeof(int) * BVAL * i)); for (j = 0; j < BVAL; j++) *(pinfo->list + j) = -1; } shmaddr = shmad; } /* * Set up Signal handler and which signals to catch */ void set_signals(void *sighandler()) { int i; int rc; struct sigaction action; /* list of signals we want to catch */ static struct signalinfo { int signum; char *signame; } siginfo[] = { { SIGHUP, "SIGHUP"}, { SIGINT, "SIGINT"}, { SIGQUIT, "SIGQUIT"}, { SIGABRT, "SIGABRT"}, { SIGBUS, "SIGBUS"}, { SIGSEGV, "SIGSEGV"}, { SIGALRM, "SIGALRM"}, { SIGUSR1, "SIGUSR1"}, { SIGUSR2, "SIGUSR2"}, { -1, "ENDSIG"} }; char tmpstr[1024]; action.sa_handler = (void *)sighandler; #ifdef _LINUX sigfillset(&action.sa_mask); #else SIGINITSET(action.sa_mask); #endif action.sa_flags = 0; /* Set the signal handler up */ #ifdef _LINUX sigaddset(&action.sa_mask, SIGTERM); #else SIGADDSET(action.sa_mask, SIGTERM); #endif for (i = 0; siginfo[i].signum != -1; i++) { #ifdef _LINUX sigaddset(&action.sa_mask, siginfo[i].signum); #else SIGADDSET(action.sa_mask, siginfo[i].signum); #endif rc = sigaction(siginfo[i].signum, &action, NULL); if (rc == -1) { sprintf(tmpstr, "sigaction: %s\n", siginfo[i].signame); perror(tmpstr); fprintf(stderr, " SEVERE : Could not set %s signal action, errno=%d.", siginfo[i].signame, errno); exit(1); } } } /* * Get and set a timer for current process. */ #ifndef _LINUX void set_timer(void) { struct itimerstruc_t itimer, old_itimer; if ((timer = gettimerid(TIMERID_REAL, DELIVERY_SIGNALS)) == -1) { perror("gettimerid"); fprintf(stderr, " SEVERE : Could not get timer id, errno=%d.", errno); exit(1); } /* * Start the timer. */ itimer.it_interval.tv_nsec = 0; itimer.it_interval.tv_sec = 0; itimer.it_value.tv_nsec = 0; itimer.it_value.tv_sec = (time_t) (TVAL * 60.0); if (incinterval(timer, &itimer, &old_itimer) == -1) { perror("incinterval"); fprintf(stderr, " SEVERE : Could not set timer interval, errno=%d.", errno); (void)reltimerid(timer); exit(1); } } #else void set_timer(void) { struct itimerval itimer; memset(&itimer, 0, sizeof(struct itimerval)); /* * Start the timer. */ itimer.it_interval.tv_usec = 0; itimer.it_interval.tv_sec = 0; itimer.it_value.tv_usec = 0; itimer.it_value.tv_sec = (time_t) (TVAL * 60.0); if (setitimer(ITIMER_REAL, &itimer, NULL)) { perror("setitimer"); exit(1); } } #endif /* * parse_args * * Parse command line arguments. Any errors cause the program to exit * at this point. */ void parse_args(int argc, char *argv[]) { int i; int opt, errflag = 0; int dflag = 0, bflag = 0, fflag = 0, tflag = 0; extern int optind; extern char *optarg; /* DVAL: 0 1 2 3 4 5 6 7 8 9 10 11 */ int limits[] = { -1, -1, MAXBVAL, 17, 8, 5, 4, 3, 2, 2, 2, 2 }; while ((opt = getopt(argc, argv, "b:d:ft:D?")) != EOF) { switch (opt) { case 'b': if (bflag) errflag++; else { bflag++; errno = 0; BVAL = atoi(optarg); if (errno) { perror("atoi"); fprintf(stderr, " ERROR : atoi - errno %d.", errno); errflag++; } } break; case 'd': if (dflag) errflag++; else { dflag++; errno = 0; DVAL = atoi(optarg); if (errno) { perror("atoi"); fprintf(stderr, " ERROR : atoi - errno %d.", errno); errflag++; } } break; case 'f': fflag = 1; break; case 'D': AUSDEBUG = 1; break; case 't': if (tflag) errflag++; else { tflag++; errno = 0; TVAL = atoi(optarg); if (!TVAL || errno) { perror("atoi"); fprintf(stderr, " ERROR : atoi - errno %d.", errno); errflag++; } } break; case '?': errflag++; break; } } if (BVAL < 2) { errflag++; fprintf(stderr, "The value of b must be greater than 1\n"); } else if (DVAL < 2) { errflag++; fprintf(stderr, "The depth value must be greater than 1\n"); } else if (!fflag && (DVAL > MAXDVAL)) { /* || BVAL > limits[DVAL])) { */ fprintf(stderr, "\tExceeded process creation limits. \ \n\tParameters will generate %lu processes. \n\tThe preset limits are as \ follows:\n\t\tdepth\tbreadth\ttotal\n", sumit(BVAL, DVAL)); for (i = 2; i <= MAXDVAL; i++) fprintf(stderr, "\t\t %-3d\t %-5d\t%-5lu\n", i, limits[i], sumit(limits[i], i)); exit(1); } if (errflag) { fprintf(stderr, "usage: %s [-b number] [-d number] [-t number] \n", argv[0]); fprintf(stderr, "where:\n"); fprintf(stderr, "\t-b number\tnumber of children each parent will spawn ( > 1)\n"); fprintf(stderr, "\t-d number\tdepth of process tree ( > 1)\n"); fprintf(stderr, "\t-t\t\tset timeout value\n"); fprintf(stderr, " SEVERE : Command line parameter error.\n"); exit(1); } } /* * Initializes environment variables, using defaults if not set in env. */ int getenv_val(void) { char *c; /* character pointer */ struct envstruct *envd = envdata; /* pointer to environment data */ union { int *vint; char *chptr; } val; /* * Loop through envdata, set default first then set environment * variable value if present. */ for (; *envd->env_name != '\0'; envd++) { if ((val.chptr = getenv(envd->env_name)) == NULL) val.chptr = envd->eval.chptr; c = val.chptr; while (isdigit(*c)) c++; if (*c == '\0') { (envd->eval.vint) = malloc(sizeof(int)); *(envd->eval.vint) = atoi(val.chptr); } else { envd->eval.chptr = malloc(strlen(val.chptr) + 1); strcpy(envd->eval.chptr, val.chptr); } } return 0; } /* * Prints all errors coming from the children and terminates execution if * an error execption is received. In addition messenger() is sent the * process group id of the children so it can terminate all children. * This routine uses message queues to receive all communications. */ void messenger(void) { /* AKA Assassin */ Msgbuf rcvbuf; int discrim = 0; int rc; /* generic return code var */ int sig = -1; /* type of signal received */ extern int msgerr; /* message queue used to send error messages */ extern int procgrp; /* process group of children (used to kill them) */ /* * Infinite loop used to receive error messages from children and * to terminate process tree. */ while (TRUE) { rc = msgrcv(msgerr, &rcvbuf, sizeof(struct messagebuf), 0, 0); if (rc == -1) { switch (errno) { case EAGAIN: printf("msgqueue %d not ready to receive\n", msgid); fflush(stdout); errno = 0; break; case ENOMSG: printf("msgqueue %d no message\n", msgid); fflush(stdout); errno = 0; break; default: perror("msgrcv failed"); fprintf(stderr, " SEVERE : messenger - msgrcv failed, errno: %d\n", errno); errno = 0; break; } } else { switch ((int)rcvbuf.mtyp) { case 1: /* type 1: we received the process group id */ sscanf(rcvbuf.mtext, "%d", &procgrp); break; case 2: /* type 2: we received an error */ fprintf(stderr, " SEVERE : %s ", rcvbuf.mtext); /* rcvbuf.mtext type %s ou %d ??? */ break; case 3: /* type 3: somebody got a signal, now we terminate */ sscanf(rcvbuf.mtext, "%d", &sig); switch (sig) { case SIGALRM: /* a process is hung, we will terminate */ killpg(procgrp, sig); fprintf(errfp, "ALERT! ALERT! WE HAVE TIMED OUT\n"); fprintf(stderr, " SEVERE : SIGALRM: A process timed out, we failed\n"); shmaddr->err++; break; case SIGUSR1: /* Special: means everything went ok */ discrim = 1; break; default: /* somebody sent a signal, we will terminate */ killpg(procgrp, sig); fprintf(errfp, "We received signal %d\n", sig); fprintf(stderr, " SEVERE : signal %d received, A proc was killed\n", sig); break; } /* clean up and exit with status */ rm_msgqueue(); rm_semseg(); if (AUSDEBUG) print_shm(); prtln(); rm_shmseg(); prtln(); if (discrim) { prtln(); printf("Test exiting with SUCCESS\n"); exit(0); } exit(1); break; } } } } /* * This routine spawns off the first child (node 0) of the process tree. * This child set up the signal handler for all of the children and also * sets up a process group so that all children can be terminated easily. * The child then calls spawn which creates the process tree. After spawn * has returned the child contacts the parent and the parent exits. * The parent sets her own signal handler and then calls messenger. */ void doit(void) { pid_t pid; /* process id */ int rc; char mtext[80]; /* message text */ extern int msgerr; extern int procgrp; pid = fork(); #ifdef __64LDT__ if (pid == (pid_t) 0) { #else if (pid == 0) { #endif /* set the process group so we can terminate all children */ set_signals((void *)nextofkin); /* set up signal handlers and initialize pgrp */ #ifndef _LINUX procgrp = setpgrp(0, 0); #else procgrp = setpgrp(); #endif if (AUSDEBUG) { fprintf(stderr, "process group: %d\n", procgrp); fflush(stderr); } if (procgrp == -1) { perror("setpgid failed"); fprintf(stderr, " SEVERE : setpgid failed, errno: %d\n", errno); exit(1); } sprintf(mtext, "%d", procgrp); rc = send_message(msgerr, 1, mtext); if (rc == -1) { perror("send_message failed"); fprintf(stderr, " SEVERE : send_message failed, errno: %d\n", errno); exit(1); } put_proc_info(0); /* store process info for this (root) process */ spawn(0); if (shmaddr->pid == getpid()) { sprintf(mtext, "%d", SIGUSR1); rc = send_message(msgerr, 3, mtext); if (rc == -1) { severe ("msgsnd failed: %d msgid %d mtyp %d mtext %d\n", errno, msgerr, 3, mtext); exit(1); } } exit(0); } #ifdef __64LDT__ else if (pid > (pid_t) 0) { #else else if (pid > 0) { #endif set_signals((void *)cleanup); /* set up signal handlers and initialize pgrp */ messenger(); /* receives and acts upon messages */ exit(1); } else { perror("fork failed"); fprintf(stderr, " SEVERE : fork failed, exiting with errno %d\n", errno); exit(1); } } /* main */ int main(int argc, char *argv[]) { extern Pinfo *shmaddr; /* start address of shared memory */ prtln(); getenv_val(); /* Get and initialize all environment variables */ prtln(); if (argc < 2) { fprintf(stderr, "usage: %s [-b number] [-d number] [-t number] \n", argv[0]); fprintf(stderr, "where:\n"); fprintf(stderr, "\t-b number\tnumber of children each parent will spawn ( > 1)\n"); fprintf(stderr, "\t-d number\tdepth of process tree ( > 1)\n"); fprintf(stderr, "\t-t\t\tset timeout value\n"); fprintf(stderr, " SEVERE : Command line parameter error.\n"); exit(1); } parse_args(argc, argv); /* Get all command line arguments */ dprt("value of BVAL = %d, value of DVAL = %d\n", BVAL, DVAL); nodesum = sumit(BVAL, DVAL); #ifdef _LINUX if (nodesum > 250) { printf("total number of process to be created " "nodesum (%d) is greater\n than the allowed " "SEMMSL value (250)\n", nodesum); printf("reseting the value of nodesum to SEMMSL\n"); nodesum = 250; } #endif dprt("value of nodesum is initiallized to: %d\n", nodesum); prtln(); setup_shm(); /* Set up, allocate and initialize shared memory */ prtln(); setup_semaphores(); /* Set up, allocate and initialize semaphores */ prtln(); setup_msgqueue(); /* Set up, allocate and initialize message queues */ prtln(); doit(); /* spawn off processes */ prtln(); return 0; }