2021-10-11 18:20:23 -03:00

804 lines
20 KiB
C

/* @(#)rfadmin.c 1.1 92/07/30 SMI */
/* Copyright (c) 1984 AT&T */
/* All Rights Reserved */
/* THIS IS UNPUBLISHED PROPRIETARY SOURCE CODE OF AT&T */
/* The copyright notice above does not evidence any */
/* actual or intended publication of such source code. */
/* #ident "@(#)kern-port:nudnix/rfadmin.c 1.22" */
/*
* Kernel daemon for remote-file administration.
*/
#include <sys/types.h>
#include <sys/param.h>
#include <sys/signal.h>
#include <sys/file.h>
#include <sys/stream.h>
#include <sys/user.h>
#include <sys/vnode.h>
#include <sys/errno.h>
#include <sys/proc.h>
#include <sys/session.h>
#include <sys/wait.h>
#include <sys/kernel.h>
#include <sys/signal.h>
#include <sys/debug.h>
#include <sys/mount.h>
#include <sys/vfs.h>
#include <nettli/tihdr.h>
#include <rfs/sema.h>
#include <rfs/rfs_misc.h>
#include <rfs/nserve.h>
#include <rfs/comm.h>
#include <rfs/message.h>
#include <rfs/adv.h>
#include <rfs/cirmgr.h>
#include <rfs/recover.h>
#include <rfs/rdebug.h>
#include <rfs/rfsys.h>
#include <rfs/rfs_serve.h>
#include <rfs/rfs_mnt.h>
extern int msglistcnt; /* number of msgs in msglist */
extern int bootstate; /* state of RFS -- up, down, or inter */
extern int rec_flag; /* Controls activity of recovery daemon */
struct serv_list s_idle; /* server processes waiting for work */
struct serv_list s_active; /* server processes doing work */
extern struct proc *rec_proc; /* pointer to recovery process */
rcvd_t rd_recover; /* rd for daemon */
struct proc *rfsdp; /* pointer to daemon process */
struct vnode *rfs_cdir; /* server current directory */
int msgflag; /* create server if set */
sema_t rfadmin_sema;
struct bqctrl_st rfmsgq; /* queue for user-level daemon */
int rfmsgcnt; /* how many msgs in user-level q */
void reply(), user_msg(), que_umsg();
extern char *strcpy(), *strncpy();
/*
* File sharing daemon, started when file sharing starts.
*/
void
rfdaemon()
{
int i,s,size;
struct sndd reply_port;
mblk_t *bp, *deque();
queue_t *qp;
struct request *request;
struct response *response;
mblk_t *resp_bp;
char usr_msg [ULINESIZ]; /* tmp space for user daemon message */
struct proc *p = u.u_procp;
int killmask = sigmask(SIGKILL) | sigmask(SIGTERM);
struct advertise *ap;
struct gdp *tmp;
struct rfsmnt *rmp;
extern int nservers, msglistcnt;
extern void cl_fumount();
extern struct ucred *crcopy();
extern mblk_t *rfs_getbuf();
extern unadv();
rfsdp = u.u_procp;
/*
* If got unexpected signal, try to shutdown RFS and exit gracefully.
* This is the case when shutdown or kill -TERM 1 is executed.
* Try to terminate all the rfs kernel processes and cleanup all
* the state, then try to do rfstop()
*/
if (setjmp(&u.u_qsave)) {
u.u_signal[SIGKILL] = SIG_IGN;
u.u_signal[SIGTERM] = SIG_IGN;
p->p_sig = 0;
p->p_sigmask = ~0;
p->p_sigignore = ~0;
p->p_sigcatch = 0;
DUPRINT1(DB_RECOVER, "rfadmin: unexpected signal, exiting\n");
msgflag = NULL;
gsignal(rfsdp->p_pgrp,SIGTERM);
rfsdp = NULL;
/*
* Simulate a disconect on all existing circuits. This
* will do all necessary state and resource cleanup.
*/
for (tmp = gdp; tmp < &gdp[maxgdp]; tmp++)
if (tmp->flag != GDPFREE) {
tmp->flag |= GDPDISCONN;
rec_cleanup(tmp->queue);
tmp->flag = GDPRECOVER;
}
/*
* Wakeup the recovery process and wait for it to do all
* the cleanup required by the disconnects and then kill
* itself.
*/
rec_flag |= RFSKILL;
wakeup ((caddr_t) &rec_proc);
while (!(bootstate & DU_RECOVER_DOWN))
(void) sleep((caddr_t) &bootstate, PZERO);
/*
* Now all the advertised resources should be purged of
* clients. Unadvertise them.
*/
for (ap = advertise; ap < &advertise[nadvertise]; ap++) {
if (ap->a_flags & A_INUSE)
(void) unadv(ap);
}
/*
* Now try to purge the rfs mounts from the rest of the
* system by doing unmounts. We hope that all processes
* using them have already been killed off (e.g., by
* shutdown) otherwise an unmount will fail. Note in any
* case that this doesn't fix /etc/mtab.
*/
for (rmp = rfsmount; rmp < &rfsmount[nrfsmount]; rmp++) {
if (rmp->rm_flags & MINUSE)
dounmount(rmp->rm_vfsp);
}
bootstate |= DU_DAEMON_DOWN;
rfstop();
exit(0);
}
netmemfree();
rfs_cdir = u.u_cdir;
bcopy ("rfs:rfdaemon", u.u_comm, 14);
/* dissociate this process from terminal */
(void) setsid(SESS_NEW);
/* ignore all signals, except SIGKILL and SIGTERM */
for (i=0; i<NSIG; i++) {
u.u_signal[i] = SIG_IGN;
u.u_sigmask[i] = ~killmask;
}
u.u_signal[SIGKILL] = SIG_DFL;
u.u_signal[SIGTERM] = SIG_DFL;
p->p_sig = 0;
p->p_sigmask = ~killmask;
p->p_sigignore = ~killmask;
p->p_sigcatch = 0;
/* Don't share credentials with parent */
u.u_cred = crcopy(u.u_cred);
nzombies = 0;
nservers = 0;
s_idle.sl_count = 0;
s_idle.sl_head = (struct serv_proc *) NULL;
msgflag = NULL;
/* XXX -- should cleanup and exit if failure */
(void) creat_server(minserve);
bzero((caddr_t) &reply_port, sizeof(reply_port));
reply_port.sd_refcnt = 1;
reply_port.sd_stat = SDUSED;
/* Make user-daemon queue null. */
while ((bp = deque (&rfmsgq)) != NULL) {
DUPRINT1 (DB_RFSYS, "discarding old user-level message\n");
freemsg (bp);
}
rfmsgq.qc_head = (struct msgb *) NULL;
rfmsgq.qc_tail = (struct msgb *) NULL;
rfmsgcnt = 0;
reply_port.sd_stat = SDUSED;
reply_port.sd_refcnt = 1;
reply_port.sd_copycnt = 0;
bootstate = DU_UP;
wakeup((caddr_t) &bootstate); /* end of critical section begun in rfstart */
for (;;) {
/* raise priority because vsema is called from gdpserv */
s = splrf();
u.u_error = 0;
(void) sleep((caddr_t) &rd_recover->rd_qslp, PZERO + 1);
loop:
bp = NULL;
(void) dequeue(rd_recover, &bp, &reply_port, &size);
DUPRINT4 (DB_SERVE,"rfdaemon: awoke, bp %x, msgflag %x, zcnt %d\n",
bp, msgflag, nzombies);
if (nzombies > 0)
clean_proc_table();
if (bp) {
(void) splx(s);
qp = (queue_t *)((struct message *)bp->b_rptr)->m_queue;
request = (struct request *)
(bp->b_rptr + sizeof (struct message));
DUPRINT4(DB_RECOVER, "rfadmin: bp %x qp %x op %x\n",
bp, qp, request->rq_opcode);
switch (request->rq_opcode ) {
case REC_FUMOUNT:
{
rmp = &rfsmount[request->rq_mntindx];
cl_fumount((index_t) request->rq_mntindx,
(index_t) request->rq_srmntindx);
freemsg(bp);
reply(&reply_port, REC_FUMOUNT);
user_msg((long) RF_FUMOUNT, rmp->rm_name, NMSZ);
break;
}
case REC_MSG:
{
/* Got a message for user-level daemon.
* Enque message and wake up daemon.
*/
DUPRINT1 (DB_RFSYS,
"rfadmin: got a message for user daemon \n");
size = request->rq_count;
/* save message so we can free stream buf */
(void) strncpy(usr_msg, request->rq_data, size);
freemsg (bp);
reply (&reply_port, REC_MSG);
user_msg((long) RF_GETUMSG, usr_msg, size);
break;
}
case DUSYNCTIME:
DUPRINT1(DB_RFSYS, "rfadmin: date sync\n");
GDP(qp)->time = request->rq_synctime - time.tv_sec;
/*alocbuf and send reply */
resp_bp = rfs_getbuf(sizeof (struct response) -
DATASIZE, BPRI_MED);
response = (struct response *)
PTOMSG(resp_bp->b_rptr);
response->rp_type = RESP_MSG;
size = sizeof (struct response) - DATASIZE;
response->rp_opcode = DUSYNCTIME;
response->rp_synctime = time.tv_sec;
response->rp_errno = 0;
response->rp_sig = 0;
freemsg (bp);
(void) sndmsg (&reply_port, resp_bp, size,
(rcvd_t)NULL);
break;
case DUCACHEDIS:
{
DUPRINT4(DB_CACHE, "rfadmin: got disable cache message, queue %x, mntindx %x, fhandle %x\n",
qp, request->rq_mntindx, request->rq_fhandle);
/* Waiting for cacheing
invalidate_cache(qp, request->rq_mntindx,
request->rq_fhandle); */
freemsg (bp);
/* alocbuf and send reply */
resp_bp = rfs_getbuf(sizeof (struct response) -
DATASIZE, BPRI_MED);
response = (struct response *)
PTOMSG(resp_bp->b_rptr);
response->rp_type = RESP_MSG;
size = sizeof (struct response) - DATASIZE;
response->rp_opcode = DUCACHEDIS;
response->rp_errno = 0;
response->rp_sig = 0;
(void) sndmsg (&reply_port, resp_bp,
size, (rcvd_t)NULL);
/* If link is down server will recover */
break;
}
default:
u.u_error = EINVAL;
DUPRINT2 (DB_RFSYS, "rfadmin: unknown op %d\n",
request->rq_opcode);
freemsg (bp);
}
} else {
if (!msgflag) {
(void) splx(s);
continue;
}
if (msgflag & DISCONN) {
msgflag &= ~DISCONN;
(void) splx(s);
for(tmp = gdp; tmp < &gdp[maxgdp]; tmp++)
if(tmp->flag & GDPDISCONN) {
rec_cleanup(tmp->queue);
tmp->flag = GDPRECOVER;
}
}
else if (msgflag & RFSKILL) {
DUPRINT1(DB_RECOVER, "rfadmin: KILL case\n");
msgflag = NULL;
(void) splx(s);
gsignal(rfsdp->p_pgrp,SIGKILL);
rfsdp = NULL;
bootstate |= DU_DAEMON_DOWN;
wakeup((caddr_t) &bootstate);
exit(0);
}
else if (msgflag & MORE_SERVE) {
msgflag &= ~MORE_SERVE;
(void) splx(s);
if (s_idle.sl_count <= 1 && nservers < maxserve)
if (creat_server(1) == RFS_FAILURE &&
s_idle.sl_count == 0)
sendback(&reply_port,
ENOMEM,RESP_MSG);
}
else if (msgflag & DEADLOCK) {
msgflag &= ~DEADLOCK;
(void) splx(s);
if (s_idle.sl_count == 0 &&
nservers == maxserve)
sendback(&reply_port,ENOMEM,NACK_MSG);
}
}
s = splrf();
goto loop;
}
}
/*
* Create num servers.
*/
creat_server(num)
int num;
{
int i;
extern struct serv_proc *sp_alloc();
void do_server();
DUPRINT2 (DB_SERVE,"create %d server(s) \n", num);
for (i = 0; i < num; i++) { /* make minimum server */
/* Don't create more than maximum allowable number */
if (nservers >= maxserve)
goto err;
kern_proc(do_server, 0);
nservers++;
}
return(RFS_SUCCESS);
err:
DUPRINT1(DB_SERVE,"cannot create server\n");
/* post signal to last server so it won't go to sleep */
if (s_active.sl_head)
psignal(s_active.sl_head->s_proc, SIGUSR1);
return(RFS_FAILURE);
}
/*
* Server process routine started from rfadmin by doing a kernel fork
*/
void
do_server()
{
struct serv_proc *sp;
sp = sp_alloc();
serve(sp);
serve_exit(sp);
}
/*
* Request that the rfdaemon add servers.
*/
add_server()
{
int s;
s = splrf();
msgflag |= MORE_SERVE;
(void) splx(s);
wakeup((caddr_t) &rd_recover->rd_qslp);
}
/*
* All dying servers come through here. A server dies when it is
* awakened from de_queue with nothing on its queue. If the server
* process structure is NULL, it means that RFS is coming down, so
* some cleanup
*/
serve_exit(sp)
struct serv_proc *sp;
{
extern int nservers;
extern rcvd_t rd_recover;
DUPRINT3(DB_SERVE, "server_exit: nservers %d, zcnt %d\n",
nservers - 1, nzombies + 1);
u.u_rdir = NULL;
u.u_cdir = rfs_cdir;
if (sp->s_sdp)
free_sndd(sp->s_sdp);
--nservers;
nzombies++;
sp_free(sp);
wakeup((caddr_t) &rd_recover->rd_qslp);
/* RFS is on the way down */
if (nservers <= 0) {
bootstate |= DU_SERVE_DOWN;
wakeup((caddr_t) &bootstate);
}
exit(0);
}
/*
* Free zombie servers.
* Loop on wait4, cleaning up zombie server processes.
*/
clean_proc_table()
{
struct wait4_args args;
DUPRINT2 (DB_SERVE, "clean_proc_table: zombie count %d\n", nzombies);
ASSERT(nzombies > 0);
args.pid = 0;
args.status = (union wait *) NULL;
args.options = 0;
args.rusage = (struct rusage *) NULL;
while (nzombies) {
wait4(&args);
nzombies--;
}
}
/*
* Routines to allocate and free the server process structure *
*/
struct serv_proc *
sp_alloc()
{
register struct serv_proc *sp;
for (sp = &serv_proc[0]; sp < &serv_proc[maxserve]; sp++) {
if (!sp->s_proc)
break;
}
ASSERT((sp < &serv_proc[maxserve]) && !sp->s_proc);
sp->s_proc = u.u_procp;
sp->s_pid = u.u_procp->p_pid;
return(sp);
}
void
sp_free(sp)
struct serv_proc *sp;
{
bzero((caddr_t) sp, (u_int) sizeof(struct serv_proc));
}
/*
* Add server proc to the beginning of a list.
*/
void
add_proc(sp, list)
struct serv_proc *sp;
struct serv_list *list;
{
DUPRINT4(DB_RFSYS, "add_proc: proc %x, list %x, count %d\n",
sp, list, list->sl_count);
sp->s_rlink = list->sl_head;
list->sl_head = sp;
list->sl_count++;
return;
}
/*
* Remove server process from list. If it's not on the list, do nothing.
*/
void
del_proc(dsp, list)
register struct serv_proc *dsp;
struct serv_list *list;
{
register struct serv_proc *sp, *psp;
DUPRINT4(DB_RFSYS, "del_proc: proc %x, list %x, count %d, ",
dsp, list, list->sl_count);
if (list->sl_head == (struct serv_proc *) NULL) {
DUPRINT1(DB_RFSYS, "empty list\n");
return;
}
if (list->sl_head == dsp) { /* the one we want is first */
DUPRINT1(DB_RFSYS, "deleted first element\n");
list->sl_head = list->sl_head->s_rlink;
list->sl_count--;
dsp->s_rlink = (struct serv_proc *) NULL;
return;
}
sp = list->sl_head;
do { /* not first - traverse list */
psp = sp;
sp = sp->s_rlink;
} while ((sp != (struct serv_proc *) NULL) && (sp != dsp));
/* It's sane not to find the server to be removed, since it may be
newborn and not on any list, or it may already have been removed
to work on a new request in arrmsg() */
if (sp == (struct serv_proc *) NULL) {
DUPRINT1(DB_RFSYS, "not in list\n");
return;
}
DUPRINT1(DB_RFSYS, "deleted in middle\n");
psp->s_rlink = sp->s_rlink;
dsp->s_rlink = (struct serv_proc *) NULL;
list->sl_count--;
return;
}
/*
* Get first server process from a list.
*/
struct serv_proc *
get_proc(list)
struct serv_list *list;
{
struct serv_proc *found;
found = list->sl_head;
if (found != (struct serv_proc *) NULL) {
list->sl_head = found->s_rlink;
found->s_rlink = (struct serv_proc *) NULL;
list->sl_count--;
}
return(found);
}
/*
* Send reply with opcode over destination SD.
*/
void
reply(dest, opcode)
sndd_t dest; /* reply path */
int opcode; /* what we did */
{
mblk_t *resp_bp;
struct response *response;
int size;
/*alocbuf and send reply */
size = sizeof (struct response) - DATASIZE;
resp_bp = rfs_getbuf(size, BPRI_MED);
response = (struct response *)PTOMSG(resp_bp->b_rptr);
response->rp_type = RESP_MSG;
response->rp_opcode = opcode;
response->rp_errno = 0;
response->rp_sig = 0;
(void) sndmsg (dest, resp_bp, size, (rcvd_t)NULL);
}
/*
* Create message for local user-level daemon, and enque it.
*/
void
user_msg(opcode, name, size)
long opcode;
char *name;
int size;
{
mblk_t *bp;
struct u_d_msg *request;
if ((bp = allocb(sizeof(struct u_d_msg),BPRI_MED)) == NULL) {
printf("user_msg allocb fails: ");
printf("resource %s has been disconnected \n", name);
return;
}
request = (struct u_d_msg *) bp->b_wptr;
request->opcode = opcode;
(void) strcpy(request->res_name, name);
request->count = size;
que_umsg(bp);
}
/*
* If there's room on user_daemon queue, enque message and wake daemon.
*/
static void
que_umsg(bp)
mblk_t *bp;
{
register int s;
struct u_d_msg *request;
s = splrf();
request = (struct u_d_msg *) bp->b_wptr;
if (rfmsgcnt > maxgdp) {
printf("rfs user-daemon queue overflow: ");
printf("make sure rfudaemon is running\n ");
DUPRINT3(DB_RFSYS, "opcode %d, name %s\n",
request->opcode, request->res_name);
freemsg(bp);
(void) splx(s);
return;
}
++rfmsgcnt;
DUPRINT2(DB_RFSYS, "que_umsg: enque msg for user-daemon, count %d\n",
rfmsgcnt);
enque(&rfmsgq, bp);
(void) splx(s);
cvsema((caddr_t) &rfadmin_sema);
}
/*
*This routine is called when flow control is required.
*It is called with the buffer to be retransmitted along
*with the send descriptor which is already setup to retransmit
*to determine the destination. The buffer type is a 2k type
*which is a critical resource for the driver. Hence, this buffer
*is freed and a lesser critical buffer (smaller) can be allocated.
*One, however, never knows which application may be hogging this
*smaller buffer, so if one cannot be allocated, then use the one
*you have.
*/
sendback(sd,errno,msgtype)
sndd_t sd;
int errno;
int msgtype;
{
mblk_t *nbp, *bp;
mblk_t *sigmsg();
register struct rcvd *rd;
register struct response *msg_out;
register struct message *msg, *nmsg;
int size, opcode;
int s;
/*
* It is wise at this point to try and use a
* buffer of different size. If it is available
* then free up the buffer that just ran out of.
* If you can't get anther size, reuse the one that
* came in.
*/
for (rd = rcvd; (rd < &rcvd[nrcvd]); rd++) {
if (!(rd->rd_stat & RDUSED) ||
!(rd->rd_qtype & GENERAL) || rcvdemp(rd))
continue;
if (dequeue(rd, &bp, sd, &size) == RFS_SUCCESS) {
msg = (struct message *)bp->b_rptr;
opcode = ((struct request *)PTOMSG(msg))->rq_opcode;
if ((opcode == DUCLOSE || opcode == DUIPUT) &&
!(msg->m_stat & VER1) ) {
if ((nbp = allocb (sizeof(struct message)+
sizeof (struct response)-DATASIZE, BPRI_MED)) != NULL) {
nbp->b_wptr += sizeof(struct message);
nmsg = (struct message *)nbp->b_rptr;
bcopy ((caddr_t)msg, (caddr_t)nmsg,
sizeof(struct message)+
sizeof(struct request)-DATASIZE);
freemsg(bp);
DUPRINT2(DB_SYSCALL,"Sendback:3.0 DUCLOSE or DUIPUT opcode = %d\n",opcode);
s=splrf();
enque(&rd->rd_rcvdq,nbp);
rd->rd_qcnt++;
add_to_msgs_list(rd);
(void) splx(s);
continue;
}else { /* no small buffer enque and continue*/
s=splrf();
enque(&rd->rd_rcvdq,bp);
rd->rd_qcnt++;
add_to_msgs_list(rd);
(void) splx(s);
continue;
}
}
if (!(msg->m_stat & GIFT)) {
if (opcode != DURSIGNAL) {
freemsg(bp);
continue;
}
if ((nbp = sigmsg(sd,bp)) == NULL)
continue;
bp = nbp; /* message to be signalled */
msg = (struct message *)bp->b_rptr;
opcode = ((struct request *)
PTOMSG(msg))->rq_opcode;
msgtype = RESP_MSG;
errno = EINTR;
}
if ((nbp = allocb (sizeof(struct message)+
sizeof(struct response)-DATASIZE, BPRI_MED))
!= NULL) {
freemsg(bp); /* free scare buffer */
bp = nbp;
}
bp->b_rptr = bp->b_datap->db_base;
bp->b_wptr = bp->b_datap->db_base +
sizeof(struct message);
bzero((char *)bp->b_rptr, sizeof(struct message)+
sizeof(struct response)-DATASIZE);
msg_out = (struct response *)PTOMSG(bp->b_rptr);
msg_out->rp_opcode = opcode;
msg_out->rp_type = msgtype;
msg_out->rp_errno = errno;
msg_out->rp_bp = (long)bp;
msg_out->rp_sig = 0;
msg_out->rp_rval = 0;
msg_out->rp_subyte = 1; /* set to indicate no data */
msg_out->rp_cache = 0;
size = sizeof (struct response) - DATASIZE;
DUPRINT2(DB_SYSCALL,"SENDBACK: syscall=%d\n",opcode);
/* The only time sndmsg fails is if link is down at
which point you really don't care. */
(void)sndmsg (sd, bp, size, (rcvd_t)NULL);
}
}
}
/* Note, Even though the signal queue "sigrd" is a high priority queue
* it must be serviced since large numbers of signal messges could
* accumulate on the overflow queue and hence cause deadlock.
* This routine tries to identify the process to be signalled. If the
* request is still on the queue, it is taken off and returned.
* if there is one then send the signal back.
*/
mblk_t *
sigmsg(sd,bp)
struct sndd *sd;
mblk_t *bp;
{
register struct request *msg_in;
int cl_pid; /* Process id of signalling client */
int cl_sysid; /* Machine id of signalling client */
int cls_sysid; /* Server's version of machine id of signalling client*/
rcvd_t rdp; /* rd of signalling client's original request */
int s;
struct serv_proc *tp;
mblk_t *sbp = NULL;
struct message *msig;
extern mblk_t *chkrdq();
msig = (struct message *)bp->b_rptr;
msg_in = (struct request *) PTOMSG(msig);
/* Get pid, sysid and rd of signalling client process from message */
cl_pid = msg_in->rq_pid;
cl_sysid = msg_in->rq_sysid;
cls_sysid = GDP((queue_t *)msig->m_queue)->sysid;
rdp = inxtord(msig->m_dest);
freemsg(bp);
/* Look for currently active server serving signalling client */
for (tp = s_active.sl_head;
tp != (struct serv_proc *) NULL; tp = tp->s_rlink) {
if (tp->s_epid == cl_pid && tp->s_sysid == cls_sysid)
break;
}
/* If found, signal server; else set signal bit in matching client
request awaiting service (if any) */
if (tp)
psignal(tp->s_proc, SIGTERM);
else {
s = splrf();
if ((sbp = chkrdq(rdp, (long)cl_pid, (long)cl_sysid)) != NULL) {
msig = (struct message *)sbp->b_rptr;
msig->m_stat |= SIGNAL;
set_sndd(sd,(queue_t *)msig->m_queue,
(index_t) msig->m_gindex, (int) msig->m_gconnid);
}
(void) splx(s);
}
return(sbp);
}