You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
1272 lines
30 KiB
1272 lines
30 KiB
/*
|
|
*
|
|
* 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 <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;
|
|
|
|
}
|