release #2, shar 3 of 7
Todd Brunhoff
toddb at tekcrl.UUCP
Wed Mar 12 05:37:41 AEST 1986
#!/bin/sh
#
# RFS, a kernel-resident remote file system. Shar 3 of 7
#
#
# This is a shell archive, meaning:
# 1. Remove everything above the #!/bin/sh line.
# 2. Save the resulting text in a file.
# 3. Execute the file with /bin/sh (not csh) to create the files:
# remote/info.c
# remote/init.c
# remote/list.c
# remote/make.base
# remote/make.base.M68
# remote/make.base.magnolia
# remote/make.base.pyramid
# remote/make.base.vax
# remote/make.base.vaxnorfs
# remote/make.log
# remote/new.c
# remote/rhost.c
# remote/rmtmnt.c
# remote/route.c
# remote/server.h
#
# remote/info.c
#
if [ -f remote/info.c ]; then
echo -n 'Hit <return> to overwrite remote/info.c or ^C to quit'
read ans
rm -f remote/info.c
fi
sed -e 's/^.//' << \SHAREOF > remote/info.c
X/*
X * Copyright 1985, Todd Brunhoff.
X *
X * This software was written at Tektronix Computer Research Laboratories
X * as partial fulfillment of a Master's degree at the University of Denver.
X * This is not Tektronix proprietary software and should not be
X * confused with any software product sold by Tektronix. No warranty is
X * expressed or implied on the reliability of this software; the author,
X * the University of Denver, and Tektronix, inc. accept no liability for
X * any damage done directly or indirectly by this software. This software
X * may be copied, modified or used in any way, without fee, provided this
X * notice remains an unaltered part of the software.
X *
X * $Log: info.c,v $
X * Revision 2.1 86/01/27 11:27:54 toddb
X * Changed the h_addr component of the hosts structure to be h_iaddr so
X * as not to conflict with the 4.3 define if h_addr in netdb.h.
X *
X * Revision 2.0 85/12/07 18:21:28 toddb
X * First public release.
X *
X */
Xstatic char *rcsid = "$Header: info.c,v 2.1 86/01/27 11:27:54 toddb Exp $";
X#include "server.h"
X#include <stdio.h>
X#include <sys/wait.h>
X#include <sys/file.h>
X#include <sys/stat.h>
X#include <netdb.h>
X#include <errno.h>
X
Xextern users *default_user;
Xextern hosts *host;
Xextern hosts *thishost;
Xextern char byteorder[];
Xextern char *logfile;
Xextern long serviceport;
Xextern long errno;
Xextern short current_pid;
Xextern process *wildcard;
Xextern boolean in_root_directory;
Xextern boolean i_am_asleep;
X
Xgetbyteorder(h)
X register hosts *h;
X{
X register char *p;
X
X /*
X * Read the byte order info.
X */
X alarm(5);
X p = (char *)h->h_byteorder;
X if (read(h->h_cmdfd, p, 4) != 4)
X {
X log("can't read mount info from \"%s\"\n", h->h_names[0]);
X alarm(0);
X bzero(p, 4);
X close(h->h_cmdfd);
X h->h_cmdfd = -1;
X return;
X }
X alarm(0);
X if (bcmp(p, byteorder, 4) == 0)
X h->h_byteorderok = TRUE;
X else
X h->h_byteorderok = FALSE;
X debug4("byteorder=%d,%d,%d,%d: %s ours\n",
X p[0], p[1], p[2], p[3],
X h->h_byteorderok ? "same as" : "different than");
X}
X
Xgetrusers(h)
X register hosts *h;
X{
X char buf[ BUFSIZ ];
X register rusers *ruser;
X register FILE *input;
X register char *p;
X register int uid;
X
X errno = 0;
X /*
X * Read in the users from the remote host and squirrel them away.
X * Actually it is the actual password file from the remote
X * host, and we ignore most of the info, and save the user name and
X * uid.
X */
X if ((input = fdopen(h->h_cmdfd, "r")) == NULL)
X log_fatal("getrusers: cannot fdopen\n");
X
X alarm(30);
X while (fgets(buf, BUFSIZ, input))
X {
X /*
X * First, the user name.
X */
X for(p=buf; *p && *p != ':'; p++) ;
X *p = '\0';
X
X /*
X * now the user id number
X */
X for(p++; *p && *p != ':'; p++) ;
X uid = atoi(p+1);
X
X /*
X * Now we need to add the info to our database on this remote
X * host. If the user is already present, just update the uid
X * number. If the user is not present, and there is a default
X * local user for this remote host, use that. If not, then
X * use the default user entry for this host (where the server
X * runs).
X */
X if (ruser = findrusername(&h->h_rusers, buf))
X {
X debug2("(existing) ");
X ruser->r_uid = uid;
X }
X else
X {
X debug2("(new, ");
X ruser = newruser();
X ruser->r_name = copy(buf);
X ruser->r_uid = uid;
X if (h->h_default_user)
X {
X debug2("%s default)", *h->h_names);
X ruser->r_user = h->h_default_user;
X }
X else
X {
X ruser->r_user = default_user;
X debug2("host default)");
X }
X addlist(&h->h_rusers, ruser);
X }
X debug2("host %s: user %s (%d) -> local user %s (%d)\n",
X h->h_names[ 0 ], ruser->r_name, ruser->r_uid,
X ruser->r_user->u_name, ruser->r_user->u_local_uid);
X }
X fclose(input);
X h->h_cmdfd = -1;
X alarm(0);
X if (errno == EINTR)
X log("can't get remote users from \"%s\"\n", h->h_names[0]);
X}
X
X/*
X * Try to obtain mount information for host 'h'.
X */
Xgetmount(h)
X register hosts *h;
X{
X long savefd = h->h_cmdfd;
X struct message msgbuf;
X register struct message *msg = &msgbuf;
X register long len;
X
X if (thishost == h) /* our own machine */
X {
X log("we are talking to ourselves\n");
X h->h_cmdfd = open("/etc/passwd", O_RDONLY);
X bcopy(byteorder, h->h_byteorder, 4);
X h->h_byteorderok = TRUE;
X }
X else
X {
X if ((h->h_cmdfd = tcpconnect(h)) < 0)
X goto done;
X len = R_MINRMSG + sizeof(long);
X msg->m_hdlen = htons(len);
X msg->m_totlen = htonl(len);
X msg->m_syscall = htons(RSYS_nosys);
X msg->m_args[ 0 ] = htonl(CMD_NEEDMOUNT);
X if (!sndmsg(h->h_cmdfd, msg, len, 0, 0))
X {
X log("can't ask for mount info\n");
X close(h->h_cmdfd);
X goto done;
X }
X getbyteorder(h);
X }
X getrusers(h); /* getrusers() closes the file descriptor */
Xdone:
X h->h_cmdfd = savefd;
X}
X
X/*
X * Send mount information. This includes a 4-byte header containing the
X * byte order for our machine, followed by /etc/passwd.
X */
Xsendmount(h)
X register hosts *h;
X{
X char buf[ BUFSIZ ];
X register long fd = open("/etc/passwd", O_RDONLY),
X cnt;
X register char *p = buf;
X
X write(h->h_cmdfd, byteorder, 4);
X while ((cnt = read(fd, p, BUFSIZ)) > 0)
X _rmtio(write, h->h_cmdfd, p, cnt);
X close(h->h_cmdfd);
X h->h_cmdfd = -1;
X close(fd);
X}
X
X/*
X * Mourne the death of any children.
X */
Xmourne()
X{
X union wait status;
X char buf[ BUFSIZ ];
X register char *p = buf;
X register long pid;
X
X while ((pid = wait3(&status, WNOHANG, 0)) > 0)
X {
X sprintf(p, "server %d found dead", pid);
X p += strlen(p);
X if (status.w_termsig)
X sprintf(p, " by sig #%d", status.w_termsig);
X p += strlen(p);
X if (status.w_coredump)
X sprintf(p, " with core dump", status.w_termsig);
X p += strlen(p);
X sprintf(p, " exit=%d\n", status.w_retcode);
X debug5("%s", buf);
X p = buf;
X }
X}
X
X/*
X * Catch signals and only report.
X */
Xcatch(sig, code, scp)
X register long sig,
X code;
X register struct sigcontext *scp;
X{
X log("caught signal #%d...", sig);
X if (sig == SIGILL || sig == SIGSEGV || sig == SIGBUS)
X {
X change_to_uid(0);
X chdir("/usr/tmp");
X log("aborting: code=%d, scp=%x, sp=%x, pc=%x, end of scp=%x\n",
X code, scp, scp->sc_sp, scp->sc_pc, scp+1);
X sendsig(current_pid, SIGEMT);
X log_fatal("could not abort\n");
X }
X else if (sig == SIGTERM) /* quietly go away */
X {
X /*
X * unlink the file only if we are allowed to and if it is
X * not the sentry server's logfile.
X */
X if ((remote_debug & 0x800) == 0 && host != NULL)
X unlink(logfile);
X cleanup();
X log("goodbye.\n");
X exit(0);
X }
X else
X log_fatal("exiting\n");
X}
X
X/*
X * Receive a wakeup call.
X */
Xwakeup_call()
X{
X if (! i_am_asleep)
X {
X log("recieved spurious wakeup call!\n");
X return;
X }
X i_am_asleep = FALSE;
X}
X
X/*
X * Provide name server function for kernel. At this point we have just
X * recieved a SIGURG signal because the kernel wants us to translate a
X * name.
X */
Xnameserver()
X{
X#ifdef CANREMOTE
X char path[ BUFSIZ ],
X hostname[ BUFSIZ ];
X struct sockaddr_in sinbuf;
X register char *p1, *p2, *name;
X register hosts *h = NULL;
X register struct sockaddr_in *sin;
X
X if (remotename(NM_WHATNAME, 0, BUFSIZ, path) < 0)
X return;
X /*
X * Find the end of the '/' prefix and copy up to the next '/' or
X * null character.
X */
X p1 = path;
X while (*p1 == '/')
X p1++;
X p2 = hostname;
X *p2++ = '/';
X while (*p1 && *p1 != '/')
X *p2++ = *p1++;
X *p2 = '\0';
X
X /*
X * Now look it up.
X */
X if (hostname[1])
X h = findhostname(hostname+1);
X if (h == NULL)
X {
X debug6("cannot find host for path \"%s\"\n", path);
X sin = NULL;
X }
X else
X {
X sin = &sinbuf;
X debug6("path %s mapped to host %s\n", path, h->h_names[0]);
X bzero((char *)sin, sizeof (struct sockaddr_in));
X bcopy(&h->h_iaddr, (char *)&sin->sin_addr,
X sizeof(struct in_addr));
X sin->sin_family = AF_INET;
X sin->sin_port = serviceport;
X }
X remotename(NM_NAMEIS, sin, sizeof(struct sockaddr_in), hostname);
X#endif CANREMOTE
X}
X
X/*
X * Decide if a file is really a local file to the client or not. We only
X * look for explicit references like
X * name1/name2/../name3 ...
X * And we then check to see whether name2 is the root directory. If it is
X * then we send the request back to the client.
X */
Xislocal(msg, type)
X register struct message *msg;
X{
X register char *p;
X register boolean checktwopaths;
X register short syscall = msg->m_syscall;
X register long offset1,
X offset2 = -1,
X localcnt = 0;
X register process *proc;
X char buf[ BUFSIZ ];
X
X debug10("cwd=%s\n", getwd(buf));
X checktwopaths = (type & NEED_2REMOTE);
X
X if (checktwopaths)
X p = twopath1addr(msg);
X else
X p = path1addr(msg);
X if ((offset1 = find_dotdot(p)) >= 0)
X localcnt++;
X if (checktwopaths)
X if ((offset2 = find_dotdot(twopath2addr(msg))) >= 0)
X localcnt++;
X if (localcnt)
X {
X debug10("%d paths are remote: \"%s\" @ %d, \"%s\" @ %d\n",
X localcnt,
X checktwopaths ? twopath1addr(msg) : path1addr(msg),
X offset1, checktwopaths ? twopath2addr(msg) : "",
X offset2);
X setup_proc(proc = wildcard, msg->m_uid, msg->m_pid);
X proc->p_errno = -1;
X proc->p_returnval = offset1;
X sendreturn(proc, host->h_cmdfd, NULL, 1, offset2);
X }
X return(localcnt);
X}
X
X/*
X * kernel code stolen for speed.
X */
Xmyaccess(st, user, perm)
X register struct stat *st;
X register long perm;
X register users *user;
X{
X register long *gp, i;
X
X perm <<= 6;
X if (user->u_local_uid != st->st_uid) {
X perm >>= 3;
X gp = user->u_local_groups;
X for (i=0; i < user->u_numgroups; i++, gp++)
X if (st->st_gid == *gp)
X goto found;
X perm >>= 3;
Xfound:
X ;
X }
X if ((st->st_mode & perm) != 0)
X return (TRUE);
X return(FALSE);
X}
X
X/*
X * look for a component of ".." terminated by a '/' or a null character.
X * If we find one, examine the previous component to see if it is our
X * root directory.
X */
Xfind_dotdot(path)
X register char *path;
X{
X struct stat statb;
X register char *p;
X register struct stat *statp = &statb;
X register long retval;
X extern struct stat root;
X
X for (p = path; *p;)
X {
X while (*p == '/')
X p++;
X if (p[0] == '.' && p[1] == '.'
X && (p[2] == '\0' || p[2] == '/'))
X {
X if (p == path)
X if (in_root_directory)
X return(0);
X else
X goto next_component;
X *p = '\0'; /* we know it is a '.' */
X retval = lstat(path, statp);
X *p = '.';
X if (retval < 0)
X return(retval);
X if (isroot(statp))
X return(p - path + 1);
X }
Xnext_component:
X while (*p && *p != '/')
X p++;
X }
X return(-1);
X}
SHAREOF
chmod 444 remote/info.c
#
# remote/init.c
#
if [ -f remote/init.c ]; then
echo -n 'Hit <return> to overwrite remote/init.c or ^C to quit'
read ans
rm -f remote/init.c
fi
sed -e 's/^.//' << \SHAREOF > remote/init.c
X/*
X * Copyright 1985, Todd Brunhoff.
X *
X * This software was written at Tektronix Computer Research Laboratories
X * as partial fulfillment of a Master's degree at the University of Denver.
X * This is not Tektronix proprietary software and should not be
X * confused with any software product sold by Tektronix. No warranty is
X * expressed or implied on the reliability of this software; the author,
X * the University of Denver, and Tektronix, inc. accept no liability for
X * any damage done directly or indirectly by this software. This software
X * may be copied, modified or used in any way, without fee, provided this
X * notice remains an unaltered part of the software.
X *
X * $Log: init.c,v $
X * Revision 2.3 86/03/10 13:55:58 toddb
X * Added support for a .rhosts entry that has only a host name.
X *
X * Revision 2.2 86/01/27 11:28:24 toddb
X * Changed the h_addr component of the hosts structure to be h_iaddr so
X * as not to conflict with the 4.3 define if h_addr in netdb.h.
X *
X * Revision 2.1 86/01/05 18:13:54 toddb
X * Added include for sys/stat.h because pyramid machines get upset.
X *
X * Revision 2.0 85/12/07 18:21:37 toddb
X * First public release.
X *
X */
Xstatic char *rcsid = "$Header: init.c,v 2.3 86/03/10 13:55:58 toddb Exp $";
X#include "server.h"
X#include <stdio.h>
X#include <pwd.h>
X#include <grp.h>
X#include <netdb.h>
X#include <fcntl.h>
X#include <sys/dir.h>
X#include <sys/user.h>
X#include <sys/signal.h>
X#include <sys/ioctl.h>
X#include <sys/stat.h>
X
Xextern hosts *hostlist;
Xextern hosts *thishost;
Xextern users *userlist;
Xextern users *default_user;
Xextern char hostname[];
Xextern char *service;
Xextern short current_uid;
Xextern short current_pid;
Xextern process *wildcard;
Xextern struct sigvec sig_vec;
Xextern struct sigvec sig_name;
Xextern struct sigvec sig_alarm;
Xextern struct sigvec sig_ignore;
Xextern struct sigvec sig_continue;
X#ifdef RFSDEBUG
Xextern struct sigvec sig_debug;
X#endif
Xextern struct stat root;
X
X/*
X * Initialize the host tables and user tables.
X */
Xinit()
X{
X long tt;
X struct hostent *gethostent();
X struct passwd *getpwent();
X struct group *getgrent();
X
X /*
X * catch signals.
X */
X sigvec(SIGHUP, &sig_ignore, (struct sigvec *)0);
X sigvec(SIGINT, &sig_vec, (struct sigvec *)0);
X sigvec(SIGQUIT, &sig_vec, (struct sigvec *)0);
X sigvec(SIGILL, &sig_vec, (struct sigvec *)0);
X#ifdef RFSDEBUG
X sigvec(SIGTRAP, &sig_debug, (struct sigvec *)0);
X#endif RFSDEBUG
X /* SIGIOT */
X /* SIGEMT */
X /* SIGFPE */
X /* SIGKILL */
X sigvec(SIGBUS, &sig_vec, (struct sigvec *)0);
X sigvec(SIGSEGV, &sig_vec, (struct sigvec *)0);
X sigvec(SIGSYS, &sig_vec, (struct sigvec *)0);
X sigvec(SIGPIPE, &sig_vec, (struct sigvec *)0);
X sigvec(SIGALRM, &sig_alarm, (struct sigvec *)0);
X sigvec(SIGTERM, &sig_vec, (struct sigvec *)0);
X sigvec(SIGURG, &sig_name, (struct sigvec *)0);
X /* SIGSTOP */
X /* SIGTSTP */
X /* SIGCONT */
X /* SIGCHLD */
X sigvec(SIGTTIN, &sig_vec, (struct sigvec *)0);
X sigvec(SIGTTOU, &sig_vec, (struct sigvec *)0);
X sigvec(SIGIO, &sig_continue, (struct sigvec *)0);
X sigvec(SIGXCPU, &sig_vec, (struct sigvec *)0);
X sigvec(SIGXFSZ, &sig_vec, (struct sigvec *)0);
X sigvec(SIGVTALRM, &sig_vec, (struct sigvec *)0);
X /* SIGPROF */
X
X /*
X * set up some important global values, including uid, pid,
X * the pipe file descriptors for messages to and from the gateway
X * server. Register as the nameserver. Get host name. Get service.
X * Get root stat info.
X */
X if (chdir("/") == -1)
X log_fatal("cannot chdir(\"/\")\n");
X wildcard = newprocess();
X fcntl(2, F_SETFL, FAPPEND);
X close(0);
X close(1);
X change_to_uid(0);
X if (gethostname(hostname, HOSTNAMELEN) < 0 || *hostname == '\0')
X log_fatal("host name not set!\n");
X if (stat("/", &root) < 0)
X log_fatal("cannot stat /\n");
X#ifdef CANREMOTE
X if (remotename(NM_SERVER, 0, 0, 0) < 0)
X log("cannot register as nameserver\n");
X /*
X * Turn off remote access, if we have any.
X */
X remoteoff(NULL);
X#endif
X tt = open("/dev/tty", 2);
X
X if (tt >= 0)
X {
X ioctl(tt, TIOCNOTTY, 0);
X close(tt);
X }
X setpgrp(0,0);
X
X initusers();
X initgroups();
X inithosts();
X initrhosts();
X}
X
X/*
X * build the list of users on this host (where the server runs).
X */
Xinitusers()
X{
X register struct passwd *pw;
X register users *user;
X char buf[ BUFSIZ ];
X register char *pbuf = buf;
X
X while(pw = getpwent())
X {
X if (*pw->pw_dir == '\0' || *pw->pw_name == '\0')
X {
X log("login \"%s\" has problems, dir=\"%s\"\n",
X pw->pw_name, pw->pw_dir);
X continue;
X }
X user = newuser();
X user->u_local_uid = pw->pw_uid;
X user->u_name = copy( pw->pw_name );
X addgroup(user, pw->pw_gid);
X user->u_dir = copy( pw->pw_dir );
X sprintf(pbuf, "%s/.rhosts", pw->pw_dir);
X user->u_rhosts = copy( pbuf );
X addlist(&userlist, user);
X }
X endpwent();
X if (user = findusername(DEFAULTUSER))
X default_user = user;
X else
X log_fatal("The user \"%s\" must be in /etc/passwd (%s)\n",
X DEFAULTUSER, "for default permissions");
X}
X
X/*
X * Build the list of groups that each user belongs to.
X */
Xinitgroups()
X{
X register struct group *gr;
X register users *user;
X register char **p;
X
X
X while(gr = getgrent())
X {
X for (p = gr->gr_mem; *p; p++)
X if (user = findusername(*p))
X addgroup(user, gr->gr_gid);
X else
X log("group %s: bad user=%s\n",
X gr->gr_name, *p);
X }
X endgrent();
X}
X
X/*
X * Then build the list of all hosts.
X */
Xinithosts()
X{
X register struct hostent *h;
X register rusers *ruser;
X register hosts *hst;
X register users *user;
X register long i;
X
X while (h = gethostent())
X {
X hst = newhost();
X hst->h_names = newname(hst->h_names, h->h_name);
X for (i=0; h->h_aliases[ i ]; i++)
X hst->h_names = newname(hst->h_names,
X h->h_aliases[ i ]);
X
X hst->h_iaddr = *((struct in_addr *)(h->h_addr));
X addlist(&hostlist, hst);
X
X /*
X * now if there exists a user on this machine having
X * the same name as the name of this host (NOT AN
X * ALIAS!), then that will be our defaut local user
X * to map to. Be sure that we don't allow a machine
X * to be mapped onto a user if the uid is real small:
X * e.g. a machine named root, where all its user ids
X * become root using the remote fs!
X */
X user = findusername(hst->h_names[ 0 ]);
X if (user && user->u_local_uid <= UID_TOO_LOW)
X {
X log("host/user %s: uid %d too low for alias\n",
X hst->h_names[ 0 ], user->u_local_uid);
X user = NULL;
X }
X else if (user)
X {
X hst->h_default_user = user;
X debug2("default user for host %s (%s) is %s\n",
X hst->h_names[ 0 ],
X inet_ntoa(hst->h_iaddr), user->u_name);
X }
X ruser = hst->h_default_ruser = newruser();
X if (user)
X ruser->r_user = user;
X else
X ruser->r_user = default_user;
X ruser->r_uid = -1;
X ruser->r_name = copy(BOGUSUSER);
X }
X endhostent();
X if ((thishost = findhostname(hostname)) == NULL)
X log_fatal("this host (\"%s\") is not in host file\n",
X hostname);
X}
X
X/*
X * Now for each user that has a .rhosts file, assemble the
X * references and attach them to the appropriate host.
X */
Xinitrhosts()
X{
X register hosts *hst;
X register rhost *rh;
X register users *user;
X char buf[ BUFSIZ ];
X register char *pbuf = buf;
X
X for (user=userlist; user; user=user->u_next)
X {
X setrhost(user->u_rhosts);
X while (rh = getrhostent(pbuf)) {
X if (rh->rh_user == NULL)
X rh->rh_user = user->u_name;
X if (hst = findhostname(rh->rh_host))
X addremoteuser(hst, user, rh->rh_user);
X }
X endrhost();
X }
X}
X
Xchar *copy(string)
X register char *string;
X{
X register char *ret = malloc( strlen(string)+1 );
X
X if (ret == NULL)
X log_fatal("cannot allocate space\n");
X strcpy(ret, string);
X return(ret);
X}
X
X/*
X * Add a remote user to those recognized on a certain host.
X */
Xaddremoteuser(h, user, remoteuser)
X register hosts *h;
X register users *user;
X register char *remoteuser;
X{
X register rusers *ruser;
X register long old = FALSE;
X
X debug2("\t%s!%s --> %s ", *h->h_names, remoteuser, user->u_name);
X if ((ruser = findrusername(&h->h_rusers, remoteuser)) == NULL)
X {
X debug2("\n");
X ruser = newruser();
X }
X else
X {
X old = TRUE;
X if (strcmp(remoteuser, user->u_name) != 0)
X {
X debug2("(old, ignored)\n");
X return;
X }
X else
X debug2("(old)\n");
X }
X ruser->r_name = copy(remoteuser);
X ruser->r_uid = -1;
X ruser->r_user = user;
X if (! old)
X addlist(&h->h_rusers, ruser);
X}
SHAREOF
chmod 444 remote/init.c
#
# remote/list.c
#
if [ -f remote/list.c ]; then
echo -n 'Hit <return> to overwrite remote/list.c or ^C to quit'
read ans
rm -f remote/list.c
fi
sed -e 's/^.//' << \SHAREOF > remote/list.c
X/*
X * Copyright 1985, Todd Brunhoff.
X *
X * This software was written at Tektronix Computer Research Laboratories
X * as partial fulfillment of a Master's degree at the University of Denver.
X * This is not Tektronix proprietary software and should not be
X * confused with any software product sold by Tektronix. No warranty is
X * expressed or implied on the reliability of this software; the author,
X * the University of Denver, and Tektronix, inc. accept no liability for
X * any damage done directly or indirectly by this software. This software
X * may be copied, modified or used in any way, without fee, provided this
X * notice remains an unaltered part of the software.
X *
X * $Log: list.c,v $
X * Revision 2.0 85/12/07 18:21:44 toddb
X * First public release.
X *
X */
Xstatic char *rcsid = "$Header: list.c,v 2.0 85/12/07 18:21:44 toddb Rel $";
X#include "server.h"
X#include <stdio.h>
X
X/*
X * Stick a new item of any type on top of the list.
X */
Xl_list *addlist(list, item)
X register l_list **list;
X register l_list *item;
X{
X item->l_next = *list;
X if (*list)
X {
X item->l_prev = (*list)->l_prev;
X (*list)->l_prev = item;
X }
X else
X item->l_prev = item;
X *list = item;
X return(item);
X}
X
X/*
X * delete an item from a list. The item itself is left intact. It is
X * the responsibility of the caller to deal with the deleted item.
X */
Xl_list *deletelist(list, item)
X register l_list **list;
X register l_list *item;
X{
X if (item == *list)
X {
X if (item->l_next == NULL)
X *list = NULL;
X else
X {
X *list = item->l_next;
X item->l_next->l_prev = item->l_next;
X }
X }
X else
X {
X item->l_prev->l_next = item->l_next;
X if (item->l_next != NULL)
X item->l_next->l_prev = item->l_prev;
X }
X return (*list);
X}
X
X/*
X * stick 'item' at the top of the 'list' ( if it isn't there
X * already.
X */
Xl_list *toplist(list, item)
X register l_list **list;
X register l_list *item;
X{
X if (item == *list)
X return;
X item->l_prev->l_next = item->l_next;
X if (item->l_next)
X item->l_next->l_prev = item->l_prev;
X item->l_next = (*list);
X /*
X * if our target is the last on the list, then
X * be careful that we don't make a cycle. Since
X * we want the head of the list's l_prev pointer
X * to point to the last in the list, we don't
X * have to do anything.
X */
X if (item != (*list)->l_prev) /* NOT last on list */
X item->l_prev = (*list)->l_prev;
X (*list)->l_prev = item;
X *list = item;
X}
SHAREOF
chmod 444 remote/list.c
#
# remote/make.base
#
if [ -f remote/make.base ]; then
echo -n 'Hit <return> to overwrite remote/make.base or ^C to quit'
read ans
rm -f remote/make.base
fi
sed -e 's/^.//' << \SHAREOF > remote/make.base
XSOBJS = change.$O file.$O fileserver.$O find.$O \
X info.$O init.$O list.$O new.$O \
X rhost.$O route.$O serverdata.$O serverdir.$O serverio.$O \
X serversyscall.$O
XCFILES = change.c file.c fileserver.c find.c \
X info.c init.c list.c new.c \
X rhost.c route.c serverdata.c serverdir.c serverio.c \
X serversyscall.c
X
Xall: $(ALL)
X
X$(RFS_SERVER): $(SOBJS)
X $(CC) -o $@ $(SOBJS) $(LDFLAGS)
X $(XINU) $(RFS_SERVER)
X$(RMTMNT): rmtmnt.$O
X $(CC) -o $@ rmtmnt.$O $(LDFLAGS)
X $(XINU) $(RMTMNT)
X$(DEBUG): debug.$O
X $(CC) -o $@ debug.$O $(LDFLAGS)
X $(XINU) $(DEBUG)
Xtags: $(CFILES)
X ctags $(CFILES) $(INCLUDE)
X$(SOBJS): $(INCLUDE)
X
Xinstall: all
X $(INSTALL) -c -m 755 $(RFS_SERVER) $(DEST)/etc/rfs_server
X $(INSTALL_RMTMNT) -c -m 0755 $(RMTMNT) $(DEST)/etc/rmtmnt
SHAREOF
chmod 664 remote/make.base
#
# remote/make.base.M68
#
if [ -f remote/make.base.M68 ]; then
echo -n 'Hit <return> to overwrite remote/make.base.M68 or ^C to quit'
read ans
rm -f remote/make.base.M68
fi
sed -e 's/^.//' << \SHAREOF > remote/make.base.M68
XDEST = /usr3/mag
XHOST =
XINCLUDE = $(DEST)/usr/include/remote/remotefs.h server.h
XCFLAGS = -G$(HOST) -O -DRFSDEBUG -DCANREMOTE -DBYTEORDER=3,2,1,0 -DREMOTEFS -DMACHTYPE=magnolia
XLDFLAGS = -z
XCC = cc68
XO = b
XRFS_SERVER = rfs_server.x
XRMTMNT = rmtmnt.x
XDEBUG = debug.x
XINSTALL_RMTMNT = install68
XINSTALL = install68
XALL = $(RFS_SERVER) $(RMTMNT)
XXINU = xinu68
X
X.SUFFIXES:
X.SUFFIXES: .b .c
X
X.c.b:
X $(CC) $(CFLAGS) $< -c
SHAREOF
chmod 664 remote/make.base.M68
#
# remote/make.base.magnolia
#
if [ -f remote/make.base.magnolia ]; then
echo -n 'Hit <return> to overwrite remote/make.base.magnolia or ^C to quit'
read ans
rm -f remote/make.base.magnolia
fi
sed -e 's/^.//' << \SHAREOF > remote/make.base.magnolia
XHOST = tekcrl
XINCLUDE = /usr/include/remote/remotefs.h server.h
XO = o
XCFLAGS = -G$(HOST) -O -DRFSDEBUG -DCANREMOTE -DBYTEORDER=3,2,1,0 -DREMOTEFS
XLDFLAGS = -z
XRFS_SERVER = rfs_server
XRMTMNT = rmtmnt
XDEBUG = debug
XINSTALL = install68
XINSTALL_RMTMNT = install68
XDEST =
XCC = cc
XALL = $(RFS_SERVER) $(RMTMNT)
XXINU = :
SHAREOF
chmod 664 remote/make.base.magnolia
#
# remote/make.base.pyramid
#
if [ -f remote/make.base.pyramid ]; then
echo -n 'Hit <return> to overwrite remote/make.base.pyramid or ^C to quit'
read ans
rm -f remote/make.base.pyramid
fi
sed -e 's/^.//' << \SHAREOF > remote/make.base.pyramid
XINCLUDE = ../usr.include/remote/remotefs.h server.h
XO = o
XLDFLAGS = -z
XRFS_SERVER = rfs_server
XINS_RFS_SERVER = rfs_server
XRMTMNT = rmtmnt
XDEBUG = debug
XINSTALL_RMTMNT = install
XINSTALL = install
XDEST =
XCC = cc
XALL = $(RFS_SERVER) $(RMTMNT)
XXINU = :
XDEFINES = -DRFSDEBUG -DCANREMOTE -DBYTEORDER=3,2,1,0 -DREMOTEFS -I/usr/include/sys
XCFLAGS = -O ${DEFINES} -I../usr.include
SHAREOF
chmod 644 remote/make.base.pyramid
#
# remote/make.base.vax
#
if [ -f remote/make.base.vax ]; then
echo -n 'Hit <return> to overwrite remote/make.base.vax or ^C to quit'
read ans
rm -f remote/make.base.vax
fi
sed -e 's/^.//' << \SHAREOF > remote/make.base.vax
XINCLUDE = ../usr.include/remote/remotefs.h server.h
XO = o
XLDFLAGS = -z
XRFS_SERVER = rfs_server
XINS_RFS_SERVER = rfs_server
XRMTMNT = rmtmnt
XDEBUG = debug
XINSTALL_RMTMNT = install
XINSTALL = install
XDEST =
XCC = cc
XALL = $(RFS_SERVER) $(RMTMNT)
XXINU = :
XDEFINES = -DRFSDEBUG -DCANREMOTE -DBYTEORDER=0,1,2,3 -DREMOTEFS
XCFLAGS = -O ${DEFINES} -I../usr.include
SHAREOF
chmod 664 remote/make.base.vax
#
# remote/make.base.vaxnorfs
#
if [ -f remote/make.base.vaxnorfs ]; then
echo -n 'Hit <return> to overwrite remote/make.base.vaxnorfs or ^C to quit'
read ans
rm -f remote/make.base.vaxnorfs
fi
sed -e 's/^.//' << \SHAREOF > remote/make.base.vaxnorfs
XINCLUDE = ../usr.include/remote/remotefs.h server.h
XO = o
XLDFLAGS = -z
XRFS_SERVER = rfs_server
XINS_RFS_SERVER = rfs_server
XRMTMNT = rmtmnt
XDEBUG = debug
XINSTALL_RMTMNT = :
XINSTALL = install
XDEST =
XCC = cc
XALL = $(RFS_SERVER)
XXINU = :
XCFLAGS = -O -DRFSDEBUG -DBYTEORDER=0,1,2,3 -DNREMOTE=1 -I../usr.include
SHAREOF
chmod 664 remote/make.base.vaxnorfs
#
# remote/make.log
#
if [ -f remote/make.log ]; then
echo -n 'Hit <return> to overwrite remote/make.log or ^C to quit'
read ans
rm -f remote/make.log
fi
sed -e 's/^.//' << \SHAREOF > remote/make.log
Xcc -O -DRFSDEBUG -DBYTEORDER=0,1,2,3 -DNREMOTE=1 -I../usr.include -c fileserver.c
Xcc -O -DRFSDEBUG -DBYTEORDER=0,1,2,3 -DNREMOTE=1 -I../usr.include -c info.c
Xcc -O -DRFSDEBUG -DBYTEORDER=0,1,2,3 -DNREMOTE=1 -I../usr.include -c init.c
Xcc -o rfs_server change.o file.o fileserver.o find.o info.o init.o list.o new.o rhost.o route.o serverdata.o serverdir.o serverio.o serversyscall.o -z newlibc.a
X: rfs_server
SHAREOF
chmod 664 remote/make.log
#
# remote/new.c
#
if [ -f remote/new.c ]; then
echo -n 'Hit <return> to overwrite remote/new.c or ^C to quit'
read ans
rm -f remote/new.c
fi
sed -e 's/^.//' << \SHAREOF > remote/new.c
X/*
X * Copyright 1985, Todd Brunhoff.
X *
X * This software was written at Tektronix Computer Research Laboratories
X * as partial fulfillment of a Master's degree at the University of Denver.
X * This is not Tektronix proprietary software and should not be
X * confused with any software product sold by Tektronix. No warranty is
X * expressed or implied on the reliability of this software; the author,
X * the University of Denver, and Tektronix, inc. accept no liability for
X * any damage done directly or indirectly by this software. This software
X * may be copied, modified or used in any way, without fee, provided this
X * notice remains an unaltered part of the software.
X *
X * $Log: new.c,v $
X * Revision 2.0 85/12/07 18:21:48 toddb
X * First public release.
X *
X */
Xstatic char *rcsid = "$Header: new.c,v 2.0 85/12/07 18:21:48 toddb Rel $";
X#include "server.h"
X#include <stdio.h>
X
Xextern short current_pid;
Xextern hosts *host;
X
Xusers *newuser()
X{
X register users *user;
X
X user = (users *)malloc(sizeof(users));
X if (user == NULL)
X log_fatal("cannot allocate space\n");
X bzero(user, sizeof(users));
X return(user);
X}
X
Xhosts *newhost()
X{
X register hosts *h;
X
X h = (hosts *)malloc(sizeof(hosts));
X if (h == NULL)
X log_fatal("cannot allocate space\n");
X bzero(h, sizeof(hosts));
X h->h_cmdfd = -1;
X return(h);
X}
X
Xrusers *newruser()
X{
X register rusers *ruser;
X
X ruser = (rusers *)malloc(sizeof(rusers));
X if (ruser == NULL)
X log_fatal("cannot allocate space\n");
X bzero(ruser, sizeof(rusers));
X return(ruser);
X}
X
Xprocess *newprocess()
X{
X register process *p;
X
X p = (process *)malloc(sizeof(process));
X if (p == NULL)
X log_fatal("cannot allocate space\n");
X bzero(p, sizeof(process));
X return(p);
X}
X
Xfreeproc(p)
X register process *p;
X{
X if (p->p_execfd >= 0)
X close(p->p_execfd);
X free(p);
X}
X
Xchar **newname(namelist, name)
X register char **namelist;
X register char *name;
X{
X register long i = 0;
X
X if (namelist == NULL)
X namelist = (char **)malloc(sizeof(char *) * 2);
X else
X {
X /*
X * count the elements in the list now.
X */
X for (i=0; namelist && namelist[i]; i++) ;
X
X namelist = (char **)realloc(namelist, sizeof(char *) * (i+2));
X }
X namelist[ i++ ] = copy(name);
X namelist[ i ] = NULL;
X return(namelist);
X}
X
X/*
X * Add a group to 'user' unless he has exceeded the limit or the group
X * is already in his domain.
X */
Xaddgroup(user, gid)
X register users *user;
X register short gid;
X{
X register long i = 0,
X *gr = user->u_local_groups;
X
X for (i=0; i < user->u_numgroups; i++)
X if (gr[ i ] == gid)
X return;
X if (i >= NGROUPS)
X return;
X gr[ user->u_numgroups++ ] = gid;
X}
X
Xprocess *add_new_process(uid, pid)
X register short uid, pid;
X{
X register process *p;
X register long i;
X
X debug0("allocate new proc: pid=%d uid=%d host=%s\n",
X pid, uid, host->h_names[0]);
X setup_proc(p = newprocess(), uid, pid);
X addlist(&host->h_proclist, p);
X
X /*
X * Initialize the file descriptors for this process.
X */
X for(i=0; i<NOFILE; i++)
X p->p_fds[ i ] = 0x80; /* -128 */
X
X return(p);
X}
X
Xsetup_proc(proc, uid, pid)
X register process *proc;
X register short uid, pid;
X{
X register rusers *ruser;
X
X proc->p_pid = pid;
X proc->p_uid = uid;
X proc->p_handler = current_pid;
X proc->p_returnval = 0;
X proc->p_execfd = -1;
X if (ruser = findremuid(&host->h_rusers, uid))
X proc->p_ruser = ruser;
X else
X proc->p_ruser = host->h_default_ruser;
X}
SHAREOF
chmod 444 remote/new.c
#
# remote/rhost.c
#
if [ -f remote/rhost.c ]; then
echo -n 'Hit <return> to overwrite remote/rhost.c or ^C to quit'
read ans
rm -f remote/rhost.c
fi
sed -e 's/^.//' << \SHAREOF > remote/rhost.c
X/*
X * Copyright 1985, Todd Brunhoff.
X *
X * This software was written at Tektronix Computer Research Laboratories
X * as partial fulfillment of a Master's degree at the University of Denver.
X * This is not Tektronix proprietary software and should not be
X * confused with any software product sold by Tektronix. No warranty is
X * expressed or implied on the reliability of this software; the author,
X * the University of Denver, and Tektronix, inc. accept no liability for
X * any damage done directly or indirectly by this software. This software
X * may be copied, modified or used in any way, without fee, provided this
X * notice remains an unaltered part of the software.
X *
X * $Log: rhost.c,v $
X * Revision 2.1 86/03/10 13:56:34 toddb
X * Added support for a .rhosts entry that has only a host name.
X *
X * Revision 2.0 85/12/07 18:21:52 toddb
X * First public release.
X *
X */
Xstatic char *rcsid = "$Header: rhost.c,v 2.1 86/03/10 13:56:34 toddb Exp $";
X#include <stdio.h>
X#include "server.h"
X
Xstatic char *rhostpath;
Xstatic FILE *fd;
Xstatic rhost rh;
X
Xsetrhost(path)
X register char *path;
X{
X extern int errno;
X
X if ((fd = fopen(path, "r")) != 0)
X debug2("rhost %s\n", path);
X errno = 0;
X}
X
Xrhost *getrhostent(buf)
X register char *buf;
X{
X register char *p;
X
X do {
X if (fd == NULL || fgets(buf, BUFSIZ, fd) == NULL)
X return(NULL);
X
X /*
X * assign the first token to rh_host and then look for the
X * second token on the line. If there is none, then
X * return a null user name and let our caller sort it out.
X */
X rh.rh_host = buf;
X for (p=buf; *p && *p != ' ' && *p != '\n'; p++) ;
X if (p == buf)
X continue;
X if (*p == '\n' || *p == '\0') {
X *p = '\0';
X rh.rh_user = NULL;
X } else {
X /*
X * remove the newline on the end
X */
X rh.rh_user = p+1;
X *p = '\0';
X for (p++; *p && *p != ' ' && *p != '\n'; p++) ;
X *p = '\0';
X }
X } while(FALSE);
X debug2("%s -> %s\n", rh.rh_host, rh.rh_user? rh.rh_user : "NULL");
X return(&rh);
X}
X
Xendrhost()
X{
X if (fd)
X {
X fclose(fd);
X fd = NULL;
X }
X}
SHAREOF
chmod 444 remote/rhost.c
#
# remote/rmtmnt.c
#
if [ -f remote/rmtmnt.c ]; then
echo -n 'Hit <return> to overwrite remote/rmtmnt.c or ^C to quit'
read ans
rm -f remote/rmtmnt.c
fi
sed -e 's/^.//' << \SHAREOF > remote/rmtmnt.c
X/*
X * Copyright 1985, Todd Brunhoff.
X *
X * This software was written at Tektronix Computer Research Laboratories
X * as partial fulfillment of a Master's degree at the University of Denver.
X * This is not Tektronix proprietary software and should not be
X * confused with any software product sold by Tektronix. No warranty is
X * expressed or implied on the reliability of this software; the author,
X * the University of Denver, and Tektronix, inc. accept no liability for
X * any damage done directly or indirectly by this software. This software
X * may be copied, modified or used in any way, without fee, provided this
X * notice remains an unaltered part of the software.
X *
X * $Log: rmtmnt.c,v $
X * Revision 2.0 85/12/07 18:21:56 toddb
X * First public release.
X *
X */
Xstatic char *rcsid = "$Header: rmtmnt.c,v 2.0 85/12/07 18:21:56 toddb Rel $";
X#include "server.h"
X#include <stdio.h>
X#include <sys/file.h>
X#include <netdb.h>
X#include <signal.h>
X#include <setjmp.h>
X#include <nlist.h>
X
Xextern int errno; /* for errors */
Xchar *service; /* service name */
Xchar byteorder[4] = { BYTEORDER };
X
X/*
X * for slow or dead remote hosts, we catch alarms.
X */
Xint onalrm();
Xstruct sigvec vec = { onalrm, 0, 0 };
X
X/*
X * Displaying current mount points requires that we read kernel space.
X */
Xstruct nlist nl[] = {
X#ifdef magnolia
X { "remote_info" },
X#else
X { "_remote_info" },
X#endif
X { "" },
X};
X#ifdef magnolia
Xchar *kernel = "/magix";
X#else
Xchar *kernel = "/vmunix";
X#endif
X
Xmain(argc, argv)
X int argc;
X char **argv;
X{
X long generic = FALSE,
X unmount = FALSE;
X char *mntpt = NULL,
X *host = NULL;
X
X /*
X * Parse the args.
X */
X for (argv++, argc--; argc; argv++, argc--)
X {
X if (**argv != '-')
X break;
X switch(argv[0][1]) {
X case 's': /* service name */
X if (argv[0][2])
X service = argv[0]+2;
X else
X argv++, argc--, service = argv[0];
X break;
X case 'g': /* Make this a generic mount point */
X generic = TRUE;
X break;
X case 'u': /* unmount this mount point */
X unmount = TRUE;
X break;
X default:
X fprintf(stderr, "unknown option = %s\n", *argv);
X usage();
X exit(1);
X }
X }
X
X if (! generic && ! unmount && argc-- > 0)
X host = *argv++;
X if (argc-- > 0)
X {
X mntpt = *argv++;
X if (*mntpt != '/')
X {
X fprintf(stderr, "mount point must begin with '/'\n");
X mntpt = NULL;
X }
X }
X else
X {
X show();
X exit(0);
X }
X
X if (argc > 0
X || (generic && unmount)
X || (mntpt == NULL))
X usage();
X
X if (unmount)
X turnoff(mntpt);
X else
X turnon(mntpt, host);
X}
X
X/*
X * Display the current mount points in the kernal.
X */
Xshow()
X{
X long index = 0,
X diff,
X now,
X kfd;
X char buf[BUFSIZ],
X *p;
X struct sockaddr_in hostaddr;
X struct sockaddr_in *sys;
X struct servent *servp;
X struct hostent *hostent;
X struct remoteinfo rinfo[ R_MAXSYS ],
X *rp;
X struct mbuf bufs[ R_MAXSYS ],
X *m;
X
X servp = getservbyname(REMOTE_FS_SERVER, "tcp");
X /*
X * Get the address of the remote mount point information
X * and read kernel memory.
X */
X nlist(kernel, nl);
X if(nl[0].n_type == 0)
X log_fatal("no %s for namelist\n", kernel);
X kfd = open("/dev/kmem", 0);
X if(kfd < 0)
X log_fatal("cannot open /dev/kmem\n");
X lseek(kfd, (long)nl[0].n_value, 0);
X read(kfd, rinfo, sizeof(struct remoteinfo) * R_MAXSYS);
X
X /*
X * Now get the mbufs on each mount point.
X */
X m = bufs;
X time(&now);
X for (index=0, rp = rinfo; rp < rinfo+R_MAXSYS; rp++, index++)
X {
X buf[0] = '\0';
X if (rp->r_name || rp->r_mntpt)
X printf("%d: ", index);
X else
X continue;
X if (rp->r_name)
X {
X lseek(kfd, (long)rp->r_name, 0);
X read(kfd, m, sizeof(struct mbuf));
X rp->r_name = m++;
X sys = mtod(rp->r_name, struct sockaddr_in *);
X hostent = gethostbyaddr(&sys->sin_addr,
X sizeof (struct in_addr), sys->sin_family);
X if (hostent == NULL)
X {
X log("no host entry for %s\n",
X inet_ntoa(sys->sin_addr));
X continue;
X }
X bprintf(buf, "%s(%s) on ",
X hostent->h_name, inet_ntoa(sys->sin_addr));
X }
X else
X bprintf(buf, "generic mount point ");
X bprintf(buf, "%s", rp->r_mntpath);
X if (rp->r_mntpt == NULL)
X bprintf(buf, ", implied");
X if (rp->r_name && sys->sin_port != servp->s_port)
X bprintf(buf, ", port %d", sys->sin_port);
X if (rp->r_sock)
X bprintf(buf, ", connected");
X if (rp->r_close)
X bprintf(buf, ", closing");
X if (rp->r_users)
X bprintf(buf, ", %d process%s",
X rp->r_users, rp->r_users > 1 ? "es" : "");
X if (rp->r_nfile)
X bprintf(buf, ", %d open file%s",
X rp->r_nfile, rp->r_nfile > 1 ? "s" : "");
X if (rp->r_nchdir)
X bprintf(buf, ", %d chdir%s",
X rp->r_nchdir, rp->r_nchdir > 1 ? "'s" : "");
X if (rp->r_opening)
X bprintf(buf, ", opening");
X if (rp->r_failed)
X {
X bprintf(buf, ", connect failed, retry ");
X diff = rp->r_age - now;
X if (diff <= 0)
X bprintf(buf, "time reached");
X else
X {
X bprintf(buf, "in ");
X if (diff / 60)
X bprintf(buf, "%d minute%s", diff/60,
X (diff/60) > 1 ? "s" : "");
X if (diff / 60 && diff % 60)
X bprintf(buf, " and ");
X if (diff % 60)
X bprintf(buf, "%d second%s", diff%60,
X (diff%60) > 1 ? "s" : "");
X }
X }
X else if(rp->r_sock == NULL && rp->r_age)
X {
X bprintf(buf, ", last closed %s",
X ctime(&rp->r_age));
X buf[ strlen(buf)-1 ] = '\0'; /* remove newline */
X }
X printf("%s\n", buf);
X }
X}
X
X/*
X * buffer printf. i.e. do a printf into a buffer, appending to whatever
X * is there. Split long lines.
X */
Xbprintf(buf, x0,x1,x2,x3,x4,x5,x6,x7,x8,x9)
X char *buf;
X{
X char xbuf[ BUFSIZ ],
X *pfrom, *pto, c;
X long col;
X
X sprintf(xbuf, x0,x1,x2,x3,x4,x5,x6,x7,x8,x9);
X for (pto = buf; *pto; pto++) ;
X for (pfrom = xbuf, col=0; *pfrom; pfrom++, col++)
X {
X c = *pfrom;
X *pto++ = c;
X if (c == '\n')
X col = -1;
X else if (c == ' ' && col > 50)
X {
X *pto++ = '\n';
X *pto++ = '\t';
X col = 7;
X }
X }
X *pto = '\0';
X}
X
X/*
X * Do a mount.
X */
Xturnon(mntpt, host)
X char *mntpt, *host;
X{
X int index, ret, fdout, fdin;
X struct message msgbuf, *msg = &msgbuf;
X struct sockaddr_in sys;
X char buf[ BUFSIZ ];
X
X if (strlen(mntpt) >= R_MNTPATHLEN)
X log_fatal("mount point must be < %d chars\n", R_MNTPATHLEN);
X
X /*
X * Connect to the machine and send it our byte order and
X * password file.
X */
X if (host)
X {
X if ((fdout = tcpname(&sys, host)) < 0)
X log("system unreachable now...");
X index = remoteon(mntpt, strlen(mntpt)+1,
X &sys, sizeof(struct sockaddr_in));
X }
X else
X index = remoteon(mntpt, strlen(mntpt), 0, 0);
X if (index == -1)
X log_fatal("cant mount remote fs\n");
X else if (host && fdout < 0)
X log(" system mounted anyway\n");
X if (host == NULL)
X return;
X if ((fdin = open("/etc/passwd", O_RDONLY)) == -1)
X log_fatal("can't open /etc/passwd\n");
X msg->m_syscall = htons(RSYS_nosys);
X msg->m_hdlen = htons(R_MINRMSG + sizeof(long));
X msg->m_totlen = htonl(R_MINRMSG + sizeof(long));
X msg->m_args[0] = htonl(CMD_MOUNT);
X write(fdout, msg, R_MINRMSG + sizeof(long));
X write(fdout, byteorder, 4);
X while ((ret = read(fdin, buf, BUFSIZ)) > 0)
X write(fdout, buf, ret);
X close(fdout);
X close(fdin);
X return;
X}
X
Xturnoff(mntpt)
X char *mntpt;
X{
X int index, fd;
X
X index = remoteoff(mntpt);
X if (index == -1)
X log_fatal("can't unmount remote fs\n");
X close(fd);
X}
X
Xusage()
X{
X fprintf(stderr, "Usage:\t%s\n\t%s\n\t%s\n",
X "rmtmnt [-sservicename] -g path",
X "rmtmnt [-sservicename] host path",
X "rmtmnt");
X exit(1);
X}
X
Xtcpname(sin, host)
X struct sockaddr_in *sin;
X char *host;
X{
X struct servent *servp;
X struct hostent *hostp;
X int s;
X
X servp = getservbyname(service ? service : REMOTE_FS_SERVER, "tcp");
X
X if (servp == NULL) {
X fprintf(stderr, "%s: unknown service\n", REMOTE_FS_SERVER);
X exit(1);
X }
X
X hostp = gethostbyname(host);
X if (hostp == NULL) {
X fprintf(stderr, "%s: unknown host\en", host);
X exit(1);
X }
X bzero((char *)sin, sizeof (struct sockaddr_in));
X bcopy(hostp->h_addr, (char *)&sin->sin_addr, hostp->h_length);
X sin->sin_family = hostp->h_addrtype;
X sin->sin_port = servp->s_port;
X
X /*
X * Ok, now make sure that the connection will work
X */
X if (sigvec(SIGALRM, &vec, (struct sigvec *)0) == -1) {
X perror("signals");
X return(-1);
X }
X s = socket(AF_INET, SOCK_STREAM, 0);
X if (s < 0)
X return(-1);
X alarm(5);
X if(connect(s, sin, sizeof(struct sockaddr_in)) < 0) {
X alarm(0);
X return(-1);
X }
X alarm(0);
X return(s);
X}
X
Xonalrm(sig)
X{
X fprintf(stderr, "timeout: ");
X}
X
Xlog_fatal(x0,x1,x2,x3,x4,x5,x6,x7,x8,x9)
X{
X log(x0,x1,x2,x3,x4,x5,x6,x7,x8,x9);
X exit(1);
X}
X
Xlog(x0,x1,x2,x3,x4,x5,x6,x7,x8,x9)
X{
X if (errno)
X perror("rmtmnt");
X errno = 0;
X fprintf(stderr, x0,x1,x2,x3,x4,x5,x6,x7,x8,x9);
X}
SHAREOF
chmod 444 remote/rmtmnt.c
#
# remote/route.c
#
if [ -f remote/route.c ]; then
echo -n 'Hit <return> to overwrite remote/route.c or ^C to quit'
read ans
rm -f remote/route.c
fi
sed -e 's/^.//' << \SHAREOF > remote/route.c
X/*
X * Copyright 1985, Todd Brunhoff.
X *
X * This software was written at Tektronix Computer Research Laboratories
X * as partial fulfillment of a Master's degree at the University of Denver.
X * This is not Tektronix proprietary software and should not be
X * confused with any software product sold by Tektronix. No warranty is
X * expressed or implied on the reliability of this software; the author,
X * the University of Denver, and Tektronix, inc. accept no liability for
X * any damage done directly or indirectly by this software. This software
X * may be copied, modified or used in any way, without fee, provided this
X * notice remains an unaltered part of the software.
X *
X * $Log: route.c,v $
X * Revision 2.2 86/01/05 18:14:47 toddb
X * Added a forgotten case to gateway_listen(): S_CORRUPTED.
X *
X * Revision 2.1 85/12/19 15:53:23 toddb
X * Changed declaration of a local variable (sigmask) because it conflicts
X * with a 4.3 define.
X *
X * Revision 2.0 85/12/07 18:22:04 toddb
X * First public release.
X *
X */
Xstatic char *rcsid = "$Header: route.c,v 2.2 86/01/05 18:14:47 toddb Exp $";
X#include "server.h"
X#include <sys/file.h>
X#include <sys/time.h>
X#include <setjmp.h>
X#include <errno.h>
X
Xextern short current_pid;
Xextern short current_ppid;
Xextern short gateway_server;
Xextern short current_server;
Xextern long blocking_servers;
Xextern long to_gateway;
Xextern long from_servers;
Xextern long errno;
Xextern boolean i_am_gateway;
Xextern boolean i_have_control;
Xextern boolean gateway_needs_control;
Xextern boolean route_to_gateway;
Xextern boolean watch_for_lock;
Xextern boolean i_am_asleep;
Xextern hosts *host;
X
X/*
X * Reroute to the server whose pid is 'pid'.
X */
Xreroute(pid, msg)
X register short pid;
X register struct message *msg;
X{
X if (route_to_gateway)
X {
X debug5("routing changed from server %d to gateway\n", pid);
X route_to_gateway = FALSE;
X pid = gateway_server;
X }
X watch_for_lock = gateway_needs_control = FALSE;
X dont_gobble_msg(msg);
X
X if (pid == current_pid)
X log_fatal("reroute myself???\n");
X debug5("%d waking up %d\n", current_pid, pid);
X
X /*
X * If we are the gateway, there may be some servers that are blocking
X * on a request. If so, then we lock file descriptor 2 with an
X * shared lock. This tells the server to always check to see if
X * the lock goes up to an exclusive lock. If so, then the server
X * must then return control to the gateway.
X */
X if (i_am_gateway)
X {
X set_label("reading messages");
X i_have_control = FALSE;
X if (blocking_servers)
X if (flock(2, LOCK_NB | LOCK_SH) < 0)
X log_fatal("cannot lock fd 2\n");
X }
X
X i_am_asleep = TRUE;
X if (pid == gateway_server)
X say_something(S_THIS_IS_YOURS, gateway_server);
X else
X wake_up(pid);
X slumber(FALSE);
X if (! i_am_gateway)
X {
X /*
X * Check for the lock on fd 2. But even if the gateway wants
X * control, go ahead an serve this request.
X */
X if (flock(2, LOCK_NB | LOCK_EX) < 0)
X if (flock(2, LOCK_NB | LOCK_SH) >= 0)
X {
X debug5("watch for lock on each request\n");
X watch_for_lock = TRUE;
X flock(2, LOCK_UN);
X }
X else
X {
X debug5("Gateway wants control\n");
X gateway_needs_control = TRUE;
X }
X else
X flock(2, LOCK_UN);
X }
X}
X
X
X/*
X * Tell the gateway something.
X */
Xsay_something(cmd, arg)
X register long cmd, arg;
X{
X gtmsgs gmsg[2];
X register gtmsgs *g = gmsg;
X register long len = sizeof(gtmsgs);
X
X
X if (cmd == S_NEWSERVER) /* actually 2 messages */
X {
X g->g_server = current_ppid;
X g->g_cmd = cmd;
X g->g_pid = current_pid;
X cmd = S_NEWPROCESS;
X g++;
X len += sizeof(gtmsgs);
X }
X g->g_server = current_pid;
X g->g_cmd = cmd;
X g->g_pid = arg;
X
X debug5("say: cmd=%d, arg=%d\n", cmd, arg);
X if (write(to_gateway, gmsg, len) != len)
X log_fatal("pid %d: can't write to gateway!!\n",
X current_pid);
X}
X
X/*
X * Read message from servers. We do the allocation of space and maintain it.
X */
Xgtmsgs *read_gtmsgs()
X{
X static gtmsgs *msgs;
X static long current_len,
X len_needed = 10;
X register long red;
X register gtmsgs *g;
X
X /*
X * Allocate space for the current read.
X */
X if (current_len < len_needed)
X {
X if (msgs)
X msgs = (gtmsgs *)realloc(msgs,
X len_needed * sizeof(gtmsgs));
X else
X msgs = (gtmsgs *)malloc(len_needed * sizeof(gtmsgs));
X current_len = len_needed;
X }
X
X /*
X * Now read the messages.
X */
X red = read(from_servers, msgs, (current_len-1) * sizeof(gtmsgs));
X if (red % sizeof(gtmsgs) != 0)
X log_fatal("partial message on read = %d\n", red);
X red /= sizeof(gtmsgs);
X if (red == current_len-1)
X len_needed++;
X msgs[ red ].g_server = 0;
X#ifdef RFSDEBUG
X for (g=msgs; g->g_server; g++)
X debug14("red: server %d, cmd %d, pid=%d\n",
X g->g_server, g->g_cmd, g->g_pid);
X#endif RFSDEBUG
X return(msgs);
X}
X
X/*
X * This process is called to gather incomming messages from servers out
X * there with something interesting to say. We return TRUE if we have
X * read a message from a process that is relinquishing control, FALSE
X * otherwise.
X */
Xgateway_listen()
X{
X register process *proc;
X register gtmsgs *msgs, *g;
X short dequeue();
X register short cmd, i, pid, server;
X
X msgs = read_gtmsgs();
X
X errno = 0;
X for (g=msgs; g->g_server; g++)
X {
X pid = g->g_pid;
X server = g->g_server;
X cmd = g->g_cmd;
X
X switch(cmd) {
X case S_NEWSERVER: /* a new server forked by another server */
X debug5("hear: %d forks new server %d\n", server, pid);
X break;
X case S_NEWPROCESS: /* a new process is being served */
X proc = add_new_process(0, pid);
X proc->p_handler = server;
X debug5("hear: pid %d serving pid %d\n", server, pid);
X break;
X case S_PROCEXIT: /* some server's client did an exit() call */
X debug5("hear: proc exit from server %d: pid=%d\n",
X server, pid);
X if ((proc = findprocess(pid, -1)) == NULL)
X log("can't find pid %d!\n", pid);
X else
X {
X deletelist(&host->h_proclist, proc);
X freeproc(proc);
X }
X break;
X case S_I_WOULD_BLOCK: /* server will block on I/O */
X debug5("hear: server %d blocks\n", server);
X blocking_servers++;
X goto gateway_control;
X case S_ALLDONE: /* an existing server is ready to die */
X case S_EOF: /* a server got eof on command socket */
X mourne();
X debug5("hear: server %d %s... ", server,
X cmd == S_ALLDONE ? "dead" : "got eof");
X for (proc=host->h_proclist; proc; proc=proc->p_next)
X if (proc->p_handler == server)
X {
X debug5("free proc %d...", proc->p_pid);
X deletelist(&host->h_proclist, proc);
X freeproc(proc);
X }
X debug5("\n");
X /* fall through */
X case S_THIS_IS_YOURS: /* just relinquish control */
X gateway_control:
X /*
X * Always unlock when we have control.
X */
X flock(2, LOCK_UN);
X if (cmd == S_THIS_IS_YOURS)
X debug5("hear: server %d gives us control\n",
X server);
X /*
X * Now that we have control, see about dequeing
X * a server that is ready to go. If there is one,
X * Then change this message so that it looks like
X * a message from ourself saying to reroute to
X * 'server'.
X */
X server = dequeue();
X if (server > 0)
X {
X debug5("server %d ready to go\n", server);
X wake_up(server);
X }
X else
X {
X debug5("gateway pid %d continuing\n",
X current_pid);
X set_label("active");
X i_have_control = TRUE;
X }
X break;
X case S_I_AM_READY:
X debug5("hear: server %d ready\n", server);
X blocking_servers--;
X if (flock(2, LOCK_EX) < 0)
X log_fatal("cannot lock fd 2\n");
X queue(server);
X break;
X case S_CORRUPTED:
X log_fatal("corrupted input stream\n");
X break;
X default:
X log("unknown message from %d = %d\n", server, cmd);
X break;
X }
X }
X
X return(FALSE);
X}
X
Xwake_up(pid)
X long pid;
X{
X sendsig(pid, SIGIO);
X}
X
Xsendsig(pid, sig)
X long pid,
X sig;
X{
X register func logger;
X extern long log(), log_fatal();
X
X change_to_uid(0);
X if (kill(pid, sig) < 0)
X {
X if (errno == ESRCH)
X logger = log;
X else
X logger = log_fatal;
X logger("couldn't signal %d w/ sig=%d\n", pid, sig);
X return(FALSE);
X }
X return(TRUE);
X}
X
Xstatic short *server_queue;
Xstatic short server_len;
Xstatic short last_server;
X/*
X * Put a server on a queue to be run again. Fifo queue.
X */
Xqueue(pid)
X register short pid;
X{
X if (++last_server > server_len)
X {
X server_len++;
X if (server_queue == NULL)
X server_queue = (short *)malloc(sizeof(short));
X else
X server_queue = (short *)realloc(server_queue,
X server_len*sizeof(short));
X }
X server_queue[ last_server - 1 ] = pid;
X}
X
X/*
X * Get the first server off the queue. Blech! We have to copy all the
X * queue back one.
X */
Xshort dequeue()
X{
X register short retval, i;
X
X if (last_server == 0)
X return(0);
X retval = server_queue[ 0 ];
X for (i=1; i<last_server; i++)
X server_queue[ i-1 ] = server_queue[ i ];
X last_server--;
X return( retval );
X}
X
X/*
X * Go to sleep awaiting a wakup call. Do not return until we receive it.
X * However, if we are the gateway, we, of course MUST return and go
X * on reading messages.
X *
X * Since there is a window between testing the i_am_asleep flag and doing
X * the pause, in which we could be awakened, we must always jump around
X * this loop using longjmp() from the interrupt routine.
X */
Xslumber(forked)
X boolean forked;
X{
X register long signalmask;
X
X if (i_am_gateway)
X {
X set_label("reading messages");
X i_have_control = FALSE;
X return;
X }
X set_label("asleep");
X signalmask = sigblock(1<<(SIGIO-1));
X while (i_am_asleep)
X sigpause(signalmask);
X sigsetmask(signalmask);
X
X debug5("pid %d continuing%s\n",
X current_pid, forked ? "after fork" : "");
X set_label("active");
X mourne();
X}
SHAREOF
chmod 444 remote/route.c
#
# remote/server.h
#
if [ -f remote/server.h ]; then
echo -n 'Hit <return> to overwrite remote/server.h or ^C to quit'
read ans
rm -f remote/server.h
fi
sed -e 's/^.//' << \SHAREOF > remote/server.h
X/*
X * Copyright 1985, Todd Brunhoff.
X *
X * This software was written at Tektronix Computer Research Laboratories
X * as partial fulfillment of a Master's degree at the University of Denver.
X * This is not Tektronix proprietary software and should not be
X * confused with any software product sold by Tektronix. No warranty is
X * expressed or implied on the reliability of this software; the author,
X * the University of Denver, and Tektronix, inc. accept no liability for
X * any damage done directly or indirectly by this software. This software
X * may be copied, modified or used in any way, without fee, provided this
X * notice remains an unaltered part of the software.
X *
X * $Header: server.h,v 2.1 86/01/27 11:31:33 toddb Exp $
X *
X * $Log: server.h,v $
X * Revision 2.1 86/01/27 11:31:33 toddb
X * Changed the h_addr component of the hosts structure to be h_iaddr so
X * as not to conflict with the 4.3 define if h_addr in netdb.h.
X *
X * Revision 2.0 85/12/07 18:22:12 toddb
X * First public release.
X *
X */
X#include <sys/param.h>
X#include <sys/mbuf.h>
X#include <sys/socket.h>
X#include <netinet/in.h>
X#include <remote/remotefs.h>
X
Xtypedef unsigned char boolean;
X
X/*
X * The maximum number of longs in a message that we accept
X */
X#define MAXMSGS ((R_MAXMBUFS*MLEN)/sizeof(long))
X/*
X * The name of a host for which we have no record. And the name of the user
X * to use if we don't recognize the user on the remote host.
X */
X#define BOGUSHOST "unknown host"
X#define BOGUSUSER "unknown user"
X#define DEFAULTUSER "guest"
X
X/*
X * The uid number below which we reserve for privilaged users.
X */
X#define UID_TOO_LOW 20
X
X/*
X * This is to make the debug? macro work for the server.
X *
X * The bits and what they turn on are as follows
X * 0x00000001 process switching
X * 0x00000002 system calls
X * 0x00000004 setuid/setgid, umask
X * 0x00000008 file descriptor allocation
X * 0x00000010 connections
X * 0x00000020 server switching
X * 0x00000040 nameserver
X * 0x00000080 directory nuxi
X * 0x00000100 message in and out
X * 0x00000200 don't fork child for gateway (good for adb)
X * 0x00000400 local/remote file decisions
X * 0x00000800 don't remove log file on exit (except exit on error)
X * 0x00001000 exec information
X * 0x00002000 auto debug for 0x20 (server switching)
X * 0x00004000 parsing messages to gateway
X */
X#define rmt_debug log
X#ifndef RFSDEBUG
X#define dumphost()
X#endif RFSDEBUG
X
X/*
X * The size of the initial allocation for internal io buffers.
X */
X#define BIGBUF (8*1024)
X
X/*
X * other Manifest constants...
X */
X#define HOSTNAMELEN 255
X
X/*
X * Map a file descriptor from the user's fd to our own internal fd.
X */
X#define MAPFD(fd, proc) \
X (((unsigned)fd > NOFILE) ? -1 : (proc)->p_fds[ fd ] )
X/*
X * requirements for different system calls.
X */
X#define NEED_ZIP 0x000 /* don't need anything special */
X#define NEED_CWD 0x001 /* need the current working directory set */
X#define NEED_PERM 0x002 /* need the user and group ids set */
X#define NEED_FD 0x004 /* need a file descriptor allocated */
X#define NEED_2PATH 0x010 /* uses two paths */
X#define NEED_MYSERVER 0x020 /* must be run by the assigned server */
X#define NEED_2REMOTE 0x040 /* both paths must be remote */
X
X/*
X * Commands to the server sent by external programs (like rmtmnt, to mount
X * a remote system.
X */
X#define CMD_SERVICE 1
X#define CMD_MOUNT 2 /* here is mount information */
X#define CMD_NEEDMOUNT 3 /* give me mount information */
X#define CMD_WHOAMI 4 /* what uid am I on your host */
X
X/*
X * Finally, some commands that are sent to the gateway server by other
X * servers.
X */
X#define S_NEWSERVER 0
X#define S_NEWPROCESS 1
X#define S_ALLDONE 2
X#define S_THIS_IS_YOURS 3
X#define S_PROCEXIT 4
X#define S_EOF 5
X#define S_I_WOULD_BLOCK 6
X#define S_I_AM_READY 7
X#define S_CORRUPTED 8
X
X/*
X * Macros for getting the address of the paths out of the incomming message.
X * Note that path1addr is for system calls that deal with only one path,
X * and twopath1addr() and twopath2addr() are for system calls that have
X * two paths (in the latter cases, path2 appears in the same position
X * as path1 does for single path system calls).
X */
X#define path1addr(msg) ((char *)&(msg)->m_args[R_PATHSTART])
X#define twopath1addr(msg) ((char *)(msg) + (msg)->m_args[R_PATHOFF])
X#define twopath2addr(msg) ((char *)&(msg)->m_args[R_PATHSTART])
X
X/*
X * Macro for preventing getmsg(), and thereby gobble_last_msg() from
X * reading the last message. This is used when control is being passed
X * from one server to another.
X */
X#define dont_gobble_msg(msg) (msg)->m_totlen = 0
X
X/*
X * Macro for determining whether a stat structure reflects the root inode
X * of our machine or not.
X */
X#define isroot(p) (p->st_ino == root.st_ino && p->st_dev == root.st_dev)
X
X/*
X * This structure is one-to-one with each local user where the server runs.
X * Note that the u_next and u_prev pointers must be located
X * in the first and second spots of the structure, respectively so that
X * they can be modified by the linked-list routines.
X */
Xtypedef struct users_type users;
Xstruct users_type {
X users *u_next; /* pointers for linked list, both forward... */
X users *u_prev; /* ... and back. */
X char *u_name; /* The ascii name of same. */
X char *u_dir; /* login directory for same */
X char *u_rhosts; /* path of the user's rhost file */
X short u_local_uid; /* A user id number on the local host */
X long u_local_groups[NGROUPS];/* The groups this user belongs to */
X char u_numgroups; /* The number of groups in u_local_groups */
X};
X
X/*
X * Remote user specification.
X */
Xtypedef struct ruser_type rusers;
Xstruct ruser_type {
X rusers *r_next;
X rusers *r_prev;
X short r_uid; /* Uid number on remote host ... */
X char *r_name; /* Uid name on remote host ... */
X users *r_user; /* corresponding local user */
X};
X
X/*
X * This is the important stuff. There is one of these structures for
X * each pid that we are providing service to.
X */
Xtypedef struct process_type process;
Xstruct process_type {
X process *p_next;
X process *p_prev;
X long p_returnval; /* return value from last syscall */
X rusers *p_ruser; /* info about the owner of this pid */
X short p_pid; /* process id number on remote host */
X short p_uid; /* remote uid that was last known */
X short p_handler; /* the handler for this process */
X short p_errno; /* errno for the system call */
X char p_execfd; /* file descriptor of exec file */
X boolean p_execstarted; /* whether we have done first read */
X char p_fds[ NOFILE ];/* fd's assoc. with this pid */
X};
X
X/*
X * This structure keeps track of the possible hosts that may make a connection
X * the the remote fs server. Note that the h_next and hprev pointers must be
X * located in the first and second spots of the structure, respectively,
X * so that they can be modified by the linked-list routines.
X */
Xtypedef struct hosts_type hosts;
Xstruct hosts_type {
X hosts *h_next;
X hosts *h_prev;
X char **h_names; /* name (and aliases) of a host */
X rusers *h_rusers; /* the user list for this host */
X users *h_default_user;/* default local user (if defined */
X rusers *h_default_ruser;/* default when the remote user is unknown */
X long h_portnum; /* port number that we connected on */
X char *h_mntpt; /* mount point for this machine */
X process *h_proclist; /* processes we know about on this host */
X struct in_addr h_iaddr;/* internet address */
X union h_bytes {
X long hu_mounted; /* non-zero if host has been mounted */
X u_char hu_byteorder[4]; /* byte order for this host */
X } h_bytes;
X#define h_mounted h_bytes.hu_mounted
X#define h_byteorder h_bytes.hu_byteorder
X char h_cmdfd; /* file descriptor for commands */
X boolean h_byteorderok; /* true if byte order same as ours */
X short h_serverpid; /* gateway server for this host */
X};
X
X/*
X * This structure is the mask structure that the linked list routines use
X * to modify any linked-list type of structure. Note that l_next and l_prev
X * must be in the first and second spots.
X */
Xtypedef struct l_list_type l_list;
Xstruct l_list_type {
X l_list *l_next;
X l_list *l_prev;
X long l_data; /* never used */
X};
X
X/*
X * This structure is for convenience: it simply defines an easy way of
X * storing the host/user line found in a .rhost file.
X */
Xtypedef struct rhost_type rhost;
Xstruct rhost_type {
X char *rh_host;
X char *rh_user;
X};
X
X/*
X * Each message from the servers to the gateway, is placed into this
X * structure, the "gateway message".
X */
Xtypedef struct gtmsg_type gtmsgs;
Xstruct gtmsg_type {
X short g_server; /* server that sent the message. */
X short g_pid; /* pid of whom this message is about */
X short g_cmd; /* what this message is about */
X};
X
X/*
X * Finally, this is the way we keep database info on the system calls
X * themselves.
X */
Xtypedef struct syscallmap syscallmap;
Xstruct syscallmap {
X func s_server;
X func s_syscall;
X char s_type;
X};
X
Xrhost *getrhostent();
Xhosts *tcpaccept();
Xhosts *findhostaddr();
Xhosts *findhostname();
Xhosts *newhost();
Xl_list *toplist();
Xl_list *bottomlist();
Xl_list *addlist();
Xl_list *deletelist();
Xprocess *newprocess();
Xprocess *findprocess();
Xprocess *change_to_proc();
Xprocess *add_new_process();
Xusers *finduid();
Xusers *findusername();
Xusers *newuser();
Xrusers *findremuid();
Xrusers *findrusername();
Xrusers *newruser();
Xchar **newname();
Xchar *copy();
Xchar *malloc();
Xchar *realloc();
Xchar *get_data_buf();
Xshort *newshortlist();
SHAREOF
chmod 444 remote/server.h
More information about the Comp.sources.unix
mailing list