2.9BSD/usr/net/sys/sys/syslocal.c
/*
* SCCS id @(#)syslocal.c 2.1 (Berkeley) 9/4/83
*/
#include "param.h"
#include <sys/dir.h>
#include <sys/proc.h>
#include <sys/seg.h>
#include <sys/user.h>
#include <sys/systm.h>
#include <sys/inode.h>
#include <sys/ino.h>
#include <sys/file.h>
#include <sys/conf.h>
#ifdef UCB_QUOTAS
#include <sys/quota.h>
#include <sys/qstat.h>
#include <sys/buf.h>
#endif
#ifdef UCB_VHANGUP
#include <sys/tty.h>
#endif
#include <sys/autoconfig.h>
#ifdef UCB_NET
#include <sys/mbuf.h>
#include <sys/protosw.h>
#include <sys/socket.h>
#include <sys/socketvar.h>
#include <sys/ubavar.h>
#include <sys/map.h>
#include "../net/if.h"
#include "../net/in_systm.h"
#endif
/*
* These routines implement local system calls
*/
/*
* nostk -- Set up the process to have no stack segment.
* The process is responsible for the management
* of its own stack, and can thus use the full
* 64k byte address space.
*/
nostk()
{
#ifndef VIRUS_VFORK
register size;
size = u.u_procp->p_size - u.u_ssize;
if(estabur(u.u_tsize, u.u_dsize, 0, u.u_sep, RO))
return;
u.u_ssize = 0;
expand(size);
#else
if(estabur(u.u_tsize, u.u_dsize, 0, u.u_sep, RO))
return;
expand(0,S_STACK);
u.u_ssize = 0;
#endif
}
#if !defined(NONSEPARATE) && defined(NONFP)
/*
* fetchi -- fetch from user I space
* required because the mfpi instruction
* does not work if current and previous
* are both user.
*/
fetchi()
{
struct a {
caddr_t iaddr;
};
u.u_r.r_val1 = fuiword(((struct a *)u.u_ap)->iaddr);
}
#endif
#ifdef UCB_QUOTAS
/*
* quota -- establish or change the quota on a directory
*/
quota()
{
register struct inode *ip;
register struct a {
char *name;
daddr_t current;
daddr_t max;
} *uap;
if (!suser())
return;
#ifndef UCB_SYMLINKS
ip = namei(uchar, LOOKUP);
#else
ip = namei(uchar, LOOKUP, 1);
#endif
if (ip == NULL)
return;
else
if ((ip->i_mode & IFMT) != IFQUOT) {
u.u_error = EACCES;
return;
}
/*
* Round the current up to be a multiple of the
* counting unit.
*/
uap = (struct a *) u.u_ap;
#if QCOUNT==2
ip->i_un.i_qused = ((uap->current + QCOUNT - 1) >> 1) << 1;
#else
ip->i_un.i_qused = (uap->current + QCOUNT - 1) / QCOUNT * QCOUNT;
#endif
ip->i_un.i_qmax = uap->max;
ip->i_flag |= IUPD|IACC;
iput(ip);
}
#endif
#ifdef UCB_QUOTAS
/*
* the qfstat system call.
*/
qfstat()
{
register struct file *fp;
register struct a {
int fdes;
struct qstat *sb;
} *uap;
uap = (struct a *) u.u_ap;
fp = getf(uap->fdes);
if(fp == NULL)
return;
qstat1(fp->f_inode, uap->sb);
}
/*
* the qstat system call.
*/
qstat()
{
register struct inode *ip;
struct a {
char *fname;
struct qstat *sb;
};
#ifndef UCB_SYMLINKS
ip = namei(uchar, LOOKUP);
#else
ip = namei(uchar, LOOKUP, 0);
#endif
if(ip == (struct inode *) NULL)
return;
qstat1(ip, ((struct a *) u.u_ap)->sb);
iput(ip);
}
/*
* The basic routine for qfstat and qstat:
* get the inode and pass all of it back.
*/
qstat1(ip, ub)
register struct inode *ip;
struct inode *ub;
{
register struct dinode *dp;
register struct buf *bp;
struct qstat qs, *qp;
#ifdef UCB_FSFIX
iupdat(ip, &time, &time, 0);
#else
iupdat(ip, &time, &time);
#endif
/*
* first copy from inode table
*/
qp = &qs;
*((struct inode *) qp) = *ip;
/*
* next the dates in the disk
*/
bp = bread(ip->i_dev, itod(ip->i_number));
dp = (struct dinode *) mapin(bp);
dp += itoo(ip->i_number);
qs.qs_atime = dp->di_atime;
qs.qs_mtime = dp->di_mtime;
qs.qs_ctime = dp->di_ctime;
mapout(bp);
brelse(bp);
if (copyout((caddr_t) & qs, (caddr_t) ub, sizeof qs) < 0)
u.u_error = EFAULT;
}
#endif
#ifndef MENLO_JCL
/*
* killpg -- send all processes the specified
* process group the given signal.
*/
killpg()
{
struct a {
int pgrp;
int sig;
};
killgrp(((struct a *) u.u_ap)->pgrp, ((struct a *) u.u_ap)->sig, 0);
}
#endif
#ifdef UCB_SUBM
/*
* killbkg -- signal processes in the specified group that
* have not been blessed by a submit call
*/
killbkg()
{
struct a {
int pgrp;
int sig;
};
killgrp(((struct a *) u.u_ap)->pgrp, ((struct a *) u.u_ap)->sig, SSUBM);
}
#endif
#if defined(UCB_SUBM) || !defined(MENLO_JCL)
/*
* common code for killpg and killbkg
*
* if mask is non-zero, send signal
* only to processes whose (p_flag & mask)
* is zero.
*/
killgrp(pgrp, sig, mask)
register pgrp;
register sig;
{
register struct proc *p;
int count;
if(!suser())
return;
count = 0;
for(p = &proc[2]; p <= maxproc; p++) {
if(p->p_stat == SZOMB || p->p_pgrp != pgrp)
continue;
/*
* include following if suser is immune
*
if(p->p_uid == 0)
continue;
*
*/
if(mask && (mask & p->p_flag)) {
continue;
}
count++;
psignal(p, sig);
}
if(count == 0)
u.u_error = ESRCH;
}
#endif
#ifdef UCB_SUBM
/*
* submit -- mark the specified process to allow execution after logout
*/
submit()
{
register struct proc *p;
#ifndef MENLO_JCL
register group;
#endif
register pid;
struct a {
int pid;
};
pid = ((struct a *) u.u_ap)->pid;
#ifndef MENLO_JCL
group = u.u_procp->p_pgrp;
#endif
for(p = &proc[2]; p <= maxproc; p++)
if(p->p_pid == pid) {
#ifndef MENLO_JCL
if(p->p_pgrp != group && !suser())
return;
#else
if (p->p_uid != u.u_uid && !suser())
return;
#endif
p->p_flag |= SSUBM;
return;
}
u.u_error = ESRCH;
}
#endif UCB_SUBM
#ifdef UCB_LOGIN
/*
* login -- mark a process as a login process,
* and record accounting information.
*/
login()
{
register i;
struct a {
int tslot;
char crn[4];
};
if (suser()) {
u.u_login = ((struct a *) u.u_ap)->tslot;
for (i = 0; i < sizeof u.u_crn; i++)
u.u_crn[i] = ((struct a *) u.u_ap)->crn[i];
}
}
#endif
#ifndef MENLO_JCL
/*
* establish a new process group
*/
setpgrp()
{
register struct proc *pp;
if (suser()) {
pp = u.u_procp;
pp->p_pgrp = pp->p_pid;
}
}
#endif
#ifdef UCB_LOAD
/*
* gldav -- get the load averages
*/
gldav()
{
extern short avenrun[];
struct a {
short *ptr;
};
if (copyout( (caddr_t) avenrun, (caddr_t) (((struct a *) u.u_ap)->ptr),
3 * sizeof(short)) < 0)
u.u_error = EFAULT;
}
#endif
#ifndef NONFP
/*
* fperr - return floating point error registers
*/
fperr()
{
u.u_r.r_val1 = u.u_fperr.f_fec;
u.u_r.r_val2 = u.u_fperr.f_fea;
}
#endif
#ifdef UCB_VHANGUP
/*
* Revoke access to the current tty by all processes.
* Used only by the super-user in init
* to give ``clean'' terminals at login.
*/
vhangup()
{
if (suser()) {
if (u.u_ttyp == NULL)
return;
forceclose(u.u_ttyd);
if ((u.u_ttyp->t_state) & ISOPEN)
gsignal(u.u_ttyp->t_pgrp, SIGHUP);
}
}
forceclose(dev)
dev_t dev;
{
register struct file *fp;
register struct inode *ip;
register int n = 0;
for (fp = file; fp < fileNFILE; fp++) {
#ifdef UCB_NET
if (fp->f_flag & FSOCKET)
continue;
#endif
if (fp->f_count == 0)
continue;
ip = fp->f_inode;
if ((ip->i_mode & IFMT) != IFCHR)
continue;
if (ip->i_un.i_rdev != dev)
continue;
fp->f_flag &= ~(FREAD | FWRITE);
n++;
}
return (n);
}
#endif UCB_VHANGUP
#ifdef UCB_RENICE
/*
* renice -- change the nice value of a process
*/
renice()
{
register struct proc *p;
register struct a {
int pid;
int nice;
} *uap;
uap = (struct a *) u.u_ap;
for (p = &proc[2]; p <= maxproc; p++)
if (p->p_pid == uap->pid) {
if (suser()) {
u.u_r.r_val1 = p->p_nice;
p->p_nice = MAX(uap->nice, -127);
}
else if (p->p_uid == u.u_uid) {
u.u_r.r_val1 = p->p_nice;
p->p_nice = MIN(MAX(p->p_nice, uap->nice), 127);
u.u_error = 0;
}
return;
}
u.u_error = ESRCH;
}
#endif UCB_RENICE
int conf_int = CONF_MAGIC; /* Used to pass result from int service to probe() */
/*
* Routine to allow user level code to call various internal
* functions; in configuration it calls for the probe and
* attach functions of the various drivers.
*/
ucall()
{
register struct a {
int priority;
int (*routine)();
int arg0;
int arg1;
} *uap;
if (suser()) {
uap = (struct a *) u.u_ap;
(void) splx(uap->priority);
u.u_r.r_val1 = (*uap->routine)(uap->arg0, uap->arg1);
(void) _spl0();
}
}
#ifdef UCB_NET
extern u_long LocalAddr; /* Generic local net address */
int nlbase; /* net error log area in clicks */
int nlsize = 01000;
int nlclick, nlbyte;
int netoff = 0;
int protoslow;
int protofast;
int ifnetslow;
int nselcoll;
/*
* Initialize network code. Called from main().
*/
netinit()
{
extern struct uba_device ubdinit[];
register struct uba_driver *udp;
register struct uba_device *ui = &ubdinit;
if (netoff)
return;
nlbase = nlclick = malloc(coremap, nlsize); /* net error log */
MAPSAVE();
mbinit();
for (ui = &ubdinit ; udp = ui->ui_driver ; ui++) {
if (badaddr(ui->ui_addr, 2))
continue;
ui->ui_alive = 1;
udp->ud_dinfo[ui->ui_unit] = ui;
(*udp->ud_attach)(ui);
}
#ifdef INET
loattach(); /* XXX */
ifinit();
pfinit(); /* must follow interfaces */
#endif
MAPREST();
}
/*
* Entered via software interrupt vector at spl1. Check netisr bit array
* for tasks requesting service.
*/
netintr()
{
int onetisr;
mapinfo map;
savemap(map);
while (spl7(), (onetisr = netisr)) {
netisr = 0;
splnet();
if (onetisr & (1 << NETISR_RAW))
rawintr();
if (onetisr & (1 << NETISR_IP))
ipintr();
if (protofast <= 0) {
protofast = hz / PR_FASTHZ;
pffasttimo();
}
if (protoslow <= 0) {
protoslow = hz / PR_SLOWHZ;
pfslowtimo();
}
if (ifnetslow <= 0) {
ifnetslow = hz / IFNET_SLOWHZ;
if_slowtimo();
}
}
restormap(map);
}
int nprint = 0; /* enable nprintf */
/*
* net printf. prints to net log area in memory (nlbase, nlsize).
*/
nprintf(fmt, x1)
char *fmt;
unsigned x1;
{
if (enprint)
prf(fmt, &x1, 4);
}
/*
* Select system call.
*/
select()
{
register struct uap {
int nfd;
fd_set *rp, *wp;
long timo;
} *ap = (struct uap *)u.u_ap;
fd_set rd, wr;
int nfds = 0;
long selscan();
long readable = 0, writeable = 0;
time_t t = time;
int s, tsel, ncoll, rem;
if (ap->nfd > NOFILE)
ap->nfd = NOFILE;
if (ap->nfd < 0) {
u.u_error = EBADF;
return;
}
if (ap->rp && copyin((caddr_t)ap->rp, (caddr_t)&rd, sizeof(fd_set)))
return;
if (ap->wp && copyin((caddr_t)ap->wp, (caddr_t)&wr, sizeof(fd_set)))
return;
retry:
ncoll = nselcoll;
u.u_procp->p_flag |= SSEL;
if (ap->rp)
readable = selscan(ap->nfd, rd, &nfds, FREAD);
if (ap->wp)
writeable = selscan(ap->nfd, wr, &nfds, FWRITE);
if (u.u_error)
goto done;
if (readable || writeable)
goto done;
rem = (ap->timo + 999) / 1000 - (time - t);
if (ap->timo == 0 || rem <= 0)
goto done;
s = spl6();
if ((u.u_procp->p_flag & SSEL) == 0 || nselcoll != ncoll) {
u.u_procp->p_flag &= ~SSEL;
splx(s);
goto retry;
}
u.u_procp->p_flag &= ~SSEL;
tsel = tsleep((caddr_t)&selwait, PZERO + 1, rem);
splx(s);
switch (tsel) {
case TS_OK:
goto retry;
case TS_SIG:
u.u_error = EINTR;
return;
case TS_TIME:
break;
}
done:
rd.fds_bits[0] = readable;
wr.fds_bits[0] = writeable;
s = sizeof (fd_set);
if (s * NBBY > ap->nfd)
s = (ap->nfd + NBBY - 1) / NBBY;
u.u_r.r_val1 = nfds;
if (ap->rp)
(void) copyout((caddr_t)&rd, (caddr_t)ap->rp, sizeof(fd_set));
if (ap->wp)
(void) copyout((caddr_t)&wr, (caddr_t)ap->wp, sizeof(fd_set));
}
long
selscan(nfd, fds, nfdp, flag)
int nfd;
fd_set fds;
int *nfdp, flag;
{
struct file *fp;
struct inode *ip;
long bits,res = 0;
int i, able;
bits = fds.fds_bits[0];
while (i = ffs(bits)) {
if (i >= nfd)
break;
bits &= ~(1L << (i - 1));
fp = u.u_ofile[i - 1];
if (fp == NULL) {
u.u_error = EBADF;
return (0);
}
if (fp->f_flag & FSOCKET)
able = soselect(fp->f_socket, flag);
else {
ip = fp->f_inode;
switch (ip->i_mode & IFMT) {
case IFCHR:
able = (*cdevsw[major(ip->i_un.i_rdev)].d_select)
((int)ip->i_un.i_rdev, flag);
break;
case IFBLK:
case IFREG:
case IFDIR:
able = 1;
break;
}
}
if (able) {
res |= (1L << (i - 1));
(*nfdp)++;
}
}
return (res);
}
ffs(mask)
long mask;
{
register int i;
register imask;
if (mask == 0)
return (0);
imask = loint(mask);
for (i = 1; i < 16; i++) {
if (imask & 1)
return (i);
imask >>= 1;
}
imask = hiint(mask);
for(; i <= 32; i++) {
if (imask & 1)
return (i);
imask >>= 1;
}
return (0); /* can't get here anyway! */
}
/*ARGSUSED*/
seltrue(dev, flag)
dev_t dev;
int flag;
{
return (1);
}
selwakeup(p, coll)
register struct proc *p;
int coll;
{
int s;
if (coll) {
nselcoll++;
wakeup((caddr_t) &selwait);
}
s = spl6();
if (p)
if (p->p_wchan == (caddr_t) &selwait)
setrun(p);
else
if (p->p_flag & SSEL)
p->p_flag &= ~SSEL;
splx(s);
}
char hostname[32] = "hostnameunknown";
int hostnamelen = 16;
gethostname()
{
register struct a {
char *hostname;
int len;
} *uap = (struct a *) u.u_ap;
register int len;
len = uap->len;
if (len > hostnamelen)
len = hostnamelen;
if (copyout((caddr_t) hostname, (caddr_t) uap->hostname, len))
u.u_error = EFAULT;
}
sethostname()
{
register struct a {
char *hostname;
int len;
} *uap = (struct a *) u.u_ap;
if (suser()) {
if (uap->len > sizeof (hostname) - 1) {
u.u_error = EINVAL;
return;
}
hostnamelen = uap->len;
if (copyin((caddr_t) uap->hostname, hostname, uap->len + 1))
u.u_error = EFAULT;
}
}
/*
* Sleep on chan at pri.
* Return in no more than the indicated number of seconds.
* (If seconds==0, no timeout implied)
* Return TS_OK if chan was awakened normally
* TS_TIME if timeout occurred
* TS_SIG if asynchronous signal occurred
*
* SHOULD HAVE OPTION TO SLEEP TO ABSOLUTE TIME OR AN
* INCREMENT IN MILLISECONDS!
*/
tsleep(chan, pri, seconds)
caddr_t chan;
int pri, seconds;
{
label_t lqsav;
register struct proc *pp;
register sec, n, rval;
pp = u.u_procp;
n = spl7();
sec = 0;
rval = 0;
if (pp->p_clktim && pp->p_clktim < seconds)
seconds = 0;
if (seconds) {
pp->p_flag |= STIMO;
sec = pp->p_clktim - seconds;
pp->p_clktim = seconds;
}
bcopy((caddr_t) u.u_qsav, (caddr_t) lqsav, sizeof (label_t));
if (save(u.u_qsav))
rval = TS_SIG;
else {
sleep(chan, pri);
if ((pp->p_flag & STIMO) == 0 && seconds)
rval = TS_TIME;
else
rval = TS_OK;
}
pp->p_flag &= ~STIMO;
bcopy((caddr_t) lqsav, (caddr_t) u.u_qsav, sizeof (label_t));
if (sec > 0)
pp->p_clktim += sec;
else
pp->p_clktim = 0;
splx(n);
return (rval);
}
/*
* Provide about n microseconds of delay
*/
delay(n)
long n;
{
register hi,low;
low = (n & 0177777);
hi = n >> 16;
if (hi == 0)
hi = 1;
do {
do { } while (--low);
} while(--hi);
}
/*
* compare bytes; same result as VAX cmpc3.
*/
bcmp(s1, s2, n)
register char *s1, *s2;
register n;
{
do
if (*s1++ != *s2++)
break;
while (--n);
return(n);
}
struct vaxque { /* queue format expected by VAX queue instr's */
struct vaxque *vq_next;
struct vaxque *vq_prev;
};
/*
* Insert an entry onto queue.
*/
_insque(e,prev)
register struct vaxque *e,*prev;
{
e->vq_prev = prev;
e->vq_next = prev->vq_next;
prev->vq_next->vq_prev = e;
prev->vq_next = e;
}
/*
* Remove an entry from queue.
*/
_remque(e)
register struct vaxque *e;
{
e->vq_prev->vq_next = e->vq_next;
e->vq_next->vq_prev = e->vq_prev;
}
setreuid()
{
struct a {
int ruid;
int euid;
} *uap;
register int ruid, euid;
uap = (struct a *) u.u_ap;
ruid = uap->ruid;
if (ruid == -1)
ruid = u.u_ruid;
if (u.u_ruid != ruid && u.u_uid != ruid && !suser())
return;
euid = uap->euid;
if (euid == -1)
euid = u.u_uid;
if (u.u_ruid != euid && u.u_uid != euid && !suser())
return;
/*
* Everything's okay, do it.
*/
u.u_procp->p_uid = ruid;
u.u_ruid = ruid;
u.u_uid = euid;
}
setregid()
{
register struct a {
int rgid;
int egid;
} *uap;
register int rgid, egid;
uap = (struct a *) u.u_ap;
rgid = uap->rgid;
if (rgid == -1)
rgid = u.u_rgid;
if (u.u_rgid != rgid && u.u_gid != rgid && !suser())
return;
egid = uap->egid;
if (egid == -1)
egid = u.u_gid;
if (u.u_rgid != egid && u.u_gid != egid && !suser())
return;
if (u.u_rgid != rgid)
u.u_rgid = rgid;
if (u.u_gid != egid)
u.u_gid = egid;
}
/*
* Get/Set our local internet address.
* Names changed from Vax code because of PDP11 length restrictions
*/
gethostid()
{
u.u_r.r_off = (off_t) LocalAddr;
}
sethostid()
{
struct a {
u_long hostid;
} *uap = (struct a *) u.u_ap;
if (suser())
LocalAddr = uap->hostid;
}
#endif UCB_NET