BBN-V6/telnet/ioctrl.c

#
/*
 * IO control routines -- enter, delete, and run IO handlers
 */
/* Max number of simultaneous open files in a process */
#define NOFILE 40
/* Max number of fd-driven jobs */
#define MAXJOBS 16
/* Max number of other jobs */
#define MAXOTHERS 10

#define EINTR 4      /* Bad - luck error */
#define EBADDEV 62    /* Await not implemented */

/*
 * The job array is indexed by file descriptor.
 * A value of 0 means that no job has been assigned.
 * Note that these routines depend on j_rjobs and j_wjobs being 0 initially.
 */
struct jobs {
    int (*j_rjob)();
    int (*j_wjob)();
    int j_arg;
};
/*
 * The structure for the capacity call. There is an array of these,
 * one element per fd for which it is desired to take the capacity.
 * The fds are then used to index into the jobs array.
 */
struct capacities {
    int fdcapac;
    int c_read;
    int c_write;
};
/*
 * The structure for the non-capacity-controlled routines.
 * These routines operate entirely independently.
 */
struct others {
    int (*o_job)();
    int o_arg;
};

/* -------------------------- I O E N T E R ------------------------- */
/*
 * IoEnter(fd, rfunc, wfunc, arg) enters a file descriptor and its
 * read and write handlers into the "io queue". When there is activity
 * on the file descriptor, the handler is called with three arguments:
 * the file descriptor, the capacity, and the arg supplied in the enter
 * call. If the handler is 0, it is not called.
 * The handler must return 0 if it did work, or a nonzero value
 * if there was no work for it to do.
 */
IoEnter(fd, rfunc, wfunc, arg)
    int fd;
    int (*rfunc)();
    int (*wfunc)();
    int arg;
{
    extern int errno;
    struct jobs *jp;
    extern struct jobs jobs[];
    extern struct capacities capacities[];
    extern int njobs;
    extern int restart;

    if (fd < 0 || fd >= NOFILE)
        fatal(0, "IoEnter: bad fd %d\n", fd);
    jp = &jobs[fd];
    if (njobs >= MAXJOBS)
        fatal(0, "Unable to enter fd %d", fd);
    if (awtenb(fd) == -1)
        if (errno != EBADDEV)
            fatal(errno, "await-enabling %d", fd);
    capacities[njobs++].fdcapac = fd;
    jp->j_rjob = rfunc;
    jp->j_wjob = wfunc;
    jp->j_arg = arg;
    restart = 1;
    return;
}

/* -------------------------- I O X E N T E R ----------------------- */
/*
 * IoxEnter(funcp, arg) is similar to IoEnter but is designed for routines
 * for which taking the capacity of a fd is unnecessary or meaningless.
 * The routine will be called as follows:
 *     active_sw = (*funcp)(arg);
 * active_sw and arg are as for routines called by IoEnter.
 *
 * Note that routines supplied to IoxEnter will always be called whenever
 * there is activity on any other routine.
 */
IoxEnter(fp, arg)
    int (*fp)();
    int arg;
{
    extern int nothers;
    extern struct others others[];

    if (nothers >= MAXOTHERS)
        fatal(0, "Unable to enter Iox job");
    others[nothers].o_job = fp;
    others[nothers].o_arg = arg;
    nothers++;
}

/* -------------------------- I O D E L E T E ----------------------- */
/*
 * IoDelete(fd) deletes a file descriptor and its handler from the queue.
 * Note that, because the capacities structure is fed directly to the
 * capac system call, the structure must be kept "clean" (active entries
 * after the deleted one are moved up).
 */
IoDelete(fd)
    int fd;
{
    register int i;
    extern int errno;
    struct jobs *jp;
    extern struct jobs jobs[];
    extern struct capacities capacities[];
    extern int restart;
    extern int njobs;

    for (i = 0; i < njobs; i++)
        if (capacities[i].fdcapac == fd)
        {
            awtdis(fd);
            capacities[i].fdcapac = -1;
            for (; i < njobs-1; i++)
                capacities[i].fdcapac = capacities[i+1].fdcapac;
            njobs--;
            jp = &jobs[fd];
            jp->j_wjob = 0;
            jp->j_rjob = 0;
            jp->j_arg = 0;    /* Not that it matters... */
            restart = 1;
            return;
        }

    com_err(0, "IoDelete: cannot find %d\n", fd);
    return;
}

/* -------------------------- I O X D E L E T E --------------------- */
/*
 * IoxDelete(funcp, arg) is similar to IoDelete but is for routines entered
 * via IoxEnter.
 */
IoxDelete(fp, arg)
    int (*fp)();
    int arg;
{
    register int i;
    extern int nothers;
    extern struct others others[];

    for (i = 0; i < nothers; i++)
    {
	if ((others[i].o_job == fp) && (others[i].o_arg == arg))
	{
	    others[i].o_job = 0;
	    others[i].o_arg = 0;
	    for (nothers--; i < nothers; i++)
	    {
		others[i].o_job = others[i+1].o_job;
		others[i].o_arg = others[i+1].o_arg;
	    }
	    return;
	}
    }
    com_err(0, "IoxDelete: cannot find 0%o 0%o\n", fp, arg);
    return;
}

/* -------------------------- I O R U N --------------------------- */
/*
 * IoRun(awttim) is called to actually do the work. It does not return;
 * it continually does capacity calls and calls the appropriate
 * routines. When there is no work to do, it awaits for awttim seconds.
 */
IoRun(awttim)
    int awttim;
{
    int fd;
    int (*f)();
    int inactive, i;
    struct capacities *cp;
    struct jobs *jp;
    struct others *op;
    extern int errno;
    extern struct jobs jobs[];
    extern struct capacities capacities[];
    extern int njobs;
    extern int restart;
    extern struct others others[];
    extern int nothers;

    for (;;)
    {
	inactive = 1;
RETRY:
	restart = 0;
	errno = 0;

	if (capac(-1, capacities, njobs * sizeof(capacities[0])) == -1)
	    if (errno == EINTR)    /* Just a signal */
		goto RETRY;
	    else
		fatal(errno, "Getting capacities");

	for (cp = &capacities[0]; cp < &capacities[njobs]; cp++)
	{
	    fd = cp->fdcapac;
	    if (fd < 0) /* Replaced by negative of error code */
		fatal(-fd, "Getting a capacity, cannot continue");
	    jp = &jobs[fd];
	    if ((f = jp->j_wjob) && cp->c_write != 0)
	    {
		i = (*f)(fd, cp->c_write, jp->j_arg);
		if (inactive) inactive = i;
		if (restart) goto RETRY;     /* Func called IoEnter or IoDelete */
	    }
	    if ((f = jp->j_rjob) && cp->c_read != 0)
	    {
		i = (*f)(fd, cp->c_read, jp->j_arg);
		if (inactive) inactive = i;
		if (restart) goto RETRY;
	    }
	}

	for (op = &others[0]; op < &others[nothers]; op++)
	{
	    i = (*(op->o_job))(op->o_arg);
	    if (inactive) inactive = i;
	}

	if (inactive)
	{
	    if (await(awttim, 0, 0) == -1)
		if (errno != EINTR)	 /* Signal during await just takes us out */
		    fatal(errno, "in await");/* Any other problem and we die */
	}
    }
}

/* -------------------------- G L O B A L S ------------------------- */

struct jobs jobs[NOFILE];
struct capacities capacities[MAXJOBS];
int njobs 0;        /* Number of active entries in capacities array */
int restart 0;/* Set when IoEnter or IoDelete called -- makes IoRun retry */

struct others others[MAXOTHERS];
int nothers 0;     /* Number of active entries in others array */