| /* |
| * |
| * 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 <sys/mman.h> |
| #include <ctype.h> |
| #include <errno.h> |
| #include <signal.h> |
| #include <stdarg.h> |
| #include <stdio.h> |
| #include <stdlib.h> |
| #include <string.h> |
| #include <time.h> |
| #include <unistd.h> |
| |
| #ifndef _LINUX |
| /* LINUX INCLUDES */ |
| #include <sys/mode.h> |
| #include <sys/timers.h> |
| #else |
| #include <sys/stat.h> |
| #include <time.h> |
| #include <sys/time.h> |
| #include <sys/ipc.h> |
| #endif |
| #include <sys/msg.h> |
| #include <sys/resource.h> |
| #include <sys/select.h> |
| #include <sys/sem.h> |
| #include <sys/shm.h> |
| #include <sys/types.h> |
| #include <sys/wait.h> |
| #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; |
| |
| } |