server.c revision 740638c8fa5a1c03618a9c75c3161dba57259a17
/*
* Copyright 2005 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
/*
* Copyright (c) 1983 Regents of the University of California.
* All rights reserved.
*
* Redistribution and use in source and binary forms are permitted
* provided that the above copyright notice and this paragraph are
* duplicated in all such forms and that any documentation,
* advertising materials, and other materials related to such
* distribution and use acknowledge that the software was developed
* by the University of California, Berkeley. The name of the
* University may not be used to endorse or promote products derived
* from this software without specific prior written permission.
*/
#pragma ident "%Z%%M% %I% %E% SMI"
#include "defs.h"
#include <signal.h>
#include <string.h>
#include <errno.h>
#include <limits.h>
#include <ctype.h>
#include <krb5defs.h>
/*
* If we want to write *to* the client rdist program, *from* the server
* side (server-side child `rdist -Server' process exec'ed off of in.rshd),
* to the outside world (which is why we use `wrem' and not `rem').
*/
int wrem = 1;
/*
* Set when a desread() is reqd. in response()
*/
char *tp; /* pointer to end of target name */
char *Tdest; /* pointer to last T dest */
int catname; /* cat name to target name */
int oumask; /* old umask for creating files */
void cleanup();
char *strsub();
static void comment(char *s);
static void note();
void error();
void log();
/*
* Server routine to read requests and process them.
* Commands are:
* Tname - Transmit file if out of date
* Vname - Verify if file out of date or not
* Qname - Query if file exists. Return mtime & size if it does.
*/
void
server()
{
char cmdbuf[RDIST_BUFSIZ];
register char *cp;
rem = 0;
for (;;) {
return;
if (*cp++ == '\n') {
error("server: expected control record\n");
continue;
}
do {
cleanup();
*--cp = '\0';
switch (*cp++) {
goto dotarget;
catname = 0;
continue;
while (*tp)
tp++;
ack();
continue;
case 'R': /* Transfer a regular file. */
continue;
case 'D': /* Transfer a directory. */
continue;
case 'K': /* Transfer symbolic link. */
continue;
case 'k': /* Transfer hard link. */
continue;
case 'E': /* End. (of directory) */
*tp = '\0';
if (catname <= 0) {
error("server: too many 'E's\n");
continue;
}
*tp = '\0';
ack();
continue;
case 'C': /* Clean. Cleanup a directory */
continue;
continue;
case 'S': /* Special. Execute commands */
continue;
#ifdef notdef
/*
* These entries are reserved but not currently used.
* The intent is to allow remote hosts to have master copies.
* Currently, only the host rdist runs on can have masters.
*/
case 'X': /* start a new list of files to exclude */
case 'x': /* add name to list of files to exclude */
if (*cp == '\0') {
ack();
continue;
}
if (*cp == '~') {
continue;
}
E_VARS);
else
E_VARS);
ack();
continue;
case 'I': /* Install. Transfer file if out of date. */
opts = 0;
if (*cp++ != ' ') {
error("server: options not delimited\n");
return;
}
continue;
case 'L': /* Log. save message in log file */
continue;
#endif
case '\1':
nerrs++;
continue;
case '\2':
return;
default:
case '\0':
continue;
}
}
}
/*
* Update the file(s) if they are different.
* destdir = 1 if destination should be a directory
* (i.e., more than one source is being copied to the same destination).
*/
void
{
char *rname;
char destcopy[RDIST_BUFSIZ];
}
if (nflag)
return;
}
return;
while (*tp)
tp++;
/*
* If we are renaming a directory and we want to preserve
* the directory heirarchy (-w), we must strip off the leading
* directory name and preserve the rest.
*/
while (*rname == '/')
rname++;
destdir = 1;
} else {
else
rname++;
}
if (debug)
/*
*/
sizeof (buf)) {
return;
}
if (debug)
if (response() < 0)
return;
if (destdir) {
} else {
}
Tdest = 0;
}
/*
* Transfer the file or directory in target[].
* rname is the name of the file on the remote host.
*/
void
char *rname;
int opts;
{
off_t i;
DIR *d;
if (debug)
return;
return;
}
error("file name '%s' contains an embedded newline - "
"can't update\n", rname);
return;
}
return;
}
}
}
if (u == 1) {
goto dospecial;
}
}
case S_IFDIR:
return;
}
closedir(d);
return;
}
if (debug)
if (response() < 0) {
closedir(d);
return;
}
continue;
(int)(RDIST_BUFSIZ - 1)) {
continue;
}
*tp++ = '/';
;
tp--;
}
closedir(d);
(void) response();
*tp = '\0';
return;
case S_IFLNK:
if (u != 1)
/* install link */
rname);
else
return;
}
if (debug)
(void) response();
return;
}
}
if (debug)
if (response() < 0)
return;
if (debug)
goto done;
case S_IFREG:
break;
default:
return;
}
if (u == 2) {
goto dospecial;
}
}
/* install link */
else
return;
}
if (debug)
(void) response();
return;
}
}
return;
}
if (debug)
if (response() < 0) {
(void) close(f);
return;
}
sizerr = 0;
int amt = RDIST_BUFSIZ;
sizerr = 1;
}
(void) close(f);
done:
if (sizerr) {
} else
f = response();
return;
continue;
continue;
continue;
if (debug)
while (response() > 0)
;
}
}
struct linkbuf *
int opts;
{
return (lp);
}
else {
if (Tdest)
else
}
return (NULL);
}
/*
* Check to see if file needs to be updated on the remote machine.
* Returns 0 if no update, 1 if remote doesn't exist, 2 if out of date
* and 3 if comparing binaries to determine if out of date.
*/
int
char *rname;
int opts;
{
register char *cp, *s;
if (debug)
/*
* Check to see if the file exists on the remote machine.
*/
return (0);
}
if (debug)
more:
do {
lostconn();
*cp = '\0';
if (debug) {
printf("update reply: ");
switch (*s) {
case 'Y':
case 'N':
putchar(*s);
break;
default:
if (iscntrl(*s)) {
putchar('^');
} else
break;
}
}
switch (*s++) {
case 'Y':
break;
case 'N': /* file doesn't exist so install it */
return (1);
case '\1':
nerrs++;
if (*s != '\n') {
if (!iamremote) {
}
}
/* preserve status code */
cp = s;
s = buf;
goto more;
}
return (0);
case '\3':
*--cp = '\0';
goto again;
default:
*--cp = '\0';
error("update: unexpected response '%s'\n", s);
return (0);
}
if (*s == '\n')
return (2);
return (3);
size = 0;
while (isdigit(*s))
if (*s++ != ' ') {
error("update: size not delimited\n");
return (0);
}
mtime = 0;
while (isdigit(*s))
if (*s != '\n') {
error("update: mtime not delimited\n");
return (0);
}
/*
* File needs to be updated?
*/
return (0);
return (0);
}
return (0);
return (2);
}
/*
* Query. Check to see if file exists. Return one of the following:
* N\n - doesn't exist
* Ysize mtime\n - exists and its a regular file (size & mtime of file)
* Y\n - exists and its a directory or symbolic link
* ^Aerror message\n
*/
static void
char *name;
{
if (catname) {
} else {
return;
}
}
else
*tp = '\0';
return;
}
case S_IFREG:
break;
case S_IFLNK:
case S_IFDIR:
break;
default:
break;
}
*tp = '\0';
}
static void
char *cmd;
int type;
{
register char *cp;
char new[RDIST_BUFSIZ];
extern char *tmpname;
opts = 0;
if (*cp++ != ' ') {
error("recvf: options not delimited\n");
return;
}
mode = 0;
if (*cp++ != ' ') {
error("recvf: mode not delimited\n");
return;
}
size = 0;
if (*cp++ != ' ') {
error("recvf: size not delimited\n");
return;
}
mtime = 0;
if (*cp++ != ' ') {
error("recvf: mtime not delimited\n");
return;
}
cp++;
if (*cp != ' ') {
error("recvf: owner name not delimited\n");
return;
}
*cp++ = '\0';
cp++;
if (*cp != ' ') {
error("recvf: group name not delimited\n");
return;
}
*cp++ = '\0';
int isdot;
isdot = 1;
else
isdot = 0;
error("%s:%s: too many directory levels\n",
return;
}
if (catname++) {
*tp++ = '/';
;
tp--;
}
ack();
return;
}
ack();
return;
}
sendrem("%s: Warning: remote mode %o != "
"local mode %o", target,
return;
}
ack();
return;
}
*tp = '\0';
return;
}
if (catname) {
} else {
return;
}
}
else {
*cp = '\0';
*cp = '/';
}
int j;
ack();
for (i = 0; i < size; i += j) {
cleanup();
cp += j;
}
*cp = '\0';
if (response() < 0) {
err();
return;
}
goto badn;
}
mode &= 0777;
char tbuf[MAXPATHLEN];
ack();
return;
}
goto differ;
}
goto fixup;
}
goto badn;
}
ack();
wrerr = 0;
for (i = 0; i < size; i += RDIST_BUFSIZ) {
int amt = RDIST_BUFSIZ;
do {
if (j <= 0) {
(void) close(f);
cleanup();
}
amt -= j;
cp += j;
} while (amt > 0);
amt = RDIST_BUFSIZ;
wrerr++;
}
}
(void) close(f);
if (response() < 0) {
err();
return;
}
if (wrerr) {
return;
}
int c;
goto badt;
badn:
return;
}
if (c == EOF) {
ack();
return;
}
return;
}
}
/*
* Set last modified time. For type == S_IFDIR, the lstat above filled
* in stb. Otherwise, do it now.
*/
}
return;
}
badt:
return;
}
} else
ack();
}
/*
* Creat a hard link to existing file.
*/
static void
char *cmd;
{
register char *cp;
char *oldname;
char oldnamebuf[RDIST_BUFSIZ];
opts = 0;
if (*cp++ != ' ') {
error("hardlink: options not delimited\n");
return;
}
cp++;
if (*cp != ' ') {
error("hardlink: oldname name not delimited\n");
return;
}
*cp++ = '\0';
if (catname) {
} else {
return;
}
}
return;
}
exists = 1;
}
error("%s:%s: %s (no parent)\n",
return;
}
ack();
return;
} else {
return;
}
}
error("%s:%s: %s (unlink)\n",
return;
}
if (*oldname == '~')
error("%s:can't link %s to %s\n",
return;
}
ack();
}
/*
* Check to see if parent directory exists and create one if not.
*/
int
char *name;
{
register char *cp;
return (0);
*cp = '\0';
*cp = '/';
return (0);
}
*cp = '/';
return (0);
}
*cp = '/';
return (-1);
}
/*
* Change owner, group and mode of file.
*/
int
int mode;
{
register int i;
extern char user[];
/*
* by default, set uid of file to the uid of the person running
* this program.
*/
/*
* We'll use available privileges so we just try to do what
* the client specifies. If the chown() fails we'll not
* add the set-[ug]id bits; and if we want to add the set-[ug]id
* bits and we're not permitted to do so, the OS will prevent us
* from doing so.
*/
if (*owner == ':') {
if (mode & 04000) {
note("%s:%s: unknown login name, "
mode &= ~04000;
}
} else {
}
} else {
}
if (*group == ':') {
goto ok;
}
gid = -1;
if ((*group == ':' &&
if (mode & 02000) {
mode &= ~02000;
}
} else
} else
ok:
note("%s: chown or chmod failed: file %s: %s",
}
return (0);
}
/*
* Check for files on the machine being updated that are not on the master
* machine and remove them.
*/
static void
int opts;
{
register char *cp, *s;
if (debug)
printf("rmchk()\n");
/*
* Tell the remote to clean the files from the last directory sent.
*/
if (debug)
if (response() < 0)
return;
for (;;) {
do {
lostconn();
switch (*s++) {
case 'Q': /* Query if file should be removed */
/*
* Return the following codes to remove query.
* N\n -- file exists - DON'T remove.
* Y\n -- file doesn't exist - REMOVE.
*/
*--cp = '\0';
if (debug)
else
break;
case '\0':
*--cp = '\0';
if (*s != '\0')
break;
case 'E':
*tp = '\0';
return;
case '\1':
case '\2':
nerrs++;
if (*s != '\n') {
if (!iamremote) {
}
}
if (buf[0] == '\2')
lostconn();
break;
default:
}
}
}
/*
* Check the current directory (initialized by the 'T' command to server())
* for extraneous files and remove them.
*/
static void
register char *cp;
{
DIR *d;
char *otp;
opts = 0;
if (*cp != '\0') {
error("clean: options not delimited\n");
return;
}
return;
}
ack();
continue;
(int)(RDIST_BUFSIZ - 1)) {
error("%s:%s/%s: Name too long\n",
continue;
}
*tp++ = '/';
;
tp--;
continue;
}
do {
cleanup();
*--cp = '\0';
if (*cp != 'Y')
continue;
} else
(void) recursive_remove(&stb);
}
closedir(d);
(void) response();
*tp = '\0';
}
/*
* Remove a file or directory (recursively) and send back an acknowledge
* or an error message.
*/
static void
{
DIR *d;
register char *cp;
char *otp;
int len;
case S_IFREG:
case S_IFLNK:
goto bad;
goto removed;
case S_IFDIR:
break;
default:
return;
}
goto bad;
continue;
(int)(RDIST_BUFSIZ - 1)) {
error("%s:%s/%s: Name too long\n",
continue;
}
*tp++ = '/';
;
tp--;
continue;
}
}
closedir(d);
*tp = '\0';
bad:
return;
}
}
/*
* Execute a shell command to handle special cases.
*/
static void
char *cmd;
{
register char *cp, *s;
char sbuf[RDIST_BUFSIZ];
return;
}
/*
* Return everything the shell commands print.
*/
(void) close(0);
(void) close(1);
(void) close(2);
_exit(127);
}
s = sbuf;
*s++ = '\0';
do {
*s++ = *cp++;
continue;
*s++ = '\n';
}
/*
* Throw away blank lines.
*/
if (s == &sbuf[2]) {
s--;
continue;
}
s = &sbuf[1];
} while (--i);
}
if (s > &sbuf[1]) {
*s++ = '\n';
}
;
if (i == -1)
status = -1;
if (status)
else
ack();
}
/*VARARGS2*/
void
char *fmt;
{
/* Print changes locally if not quiet mode */
if (!qflag)
/* Save changes (for mailing) if really updating files */
}
/*VARARGS1*/
void
char *fmt;
{
nerrs++;
return;
if (iamremote) {
} else {
}
}
}
/*VARARGS1*/
void
char *fmt;
{
nerrs++;
return;
if (iamremote) {
} else {
}
}
cleanup();
}
int
response()
{
char *cp, *s;
char resp[RDIST_BUFSIZ];
if (debug)
printf("response()\n");
more:
do {
lostconn();
switch (*s++) {
case '\0':
*--cp = '\0';
if (*s != '\0') {
return (1);
}
return (0);
case '\3':
*--cp = '\0';
return (response());
default:
s--;
/* fall into... */
case '\1':
case '\2':
nerrs++;
if (*s != '\n') {
if (!iamremote) {
}
}
/* preserve status code */
cp = s;
s = resp;
goto more;
}
if (resp[0] == '\2')
lostconn();
return (-1);
}
}
/*
* Remove temporary files and do any cleanup operations before exiting.
*/
void
cleanup()
{
exit(1);
}
static void
char *fmt;
{
static char buf[RDIST_BUFSIZ];
}
static void
comment(s)
char *s;
{
char three = '\3';
char nl = '\n';
}
/*
* Send message to other end.
* N.B.: uses buf[].
*/
void
char *fmt;
{
register int len;
buf[0] = '\0';
}
/*
* strsub(old, new, s)
*
* Return a pointer to a new string created by replacing substring old
* with substring new in string s. String s is assumed to begin with
* substring old.
*/
char *
{
register char *p, *q, *r, *plim;
/* prepend new to pbuf */
/* CSTYLED */
*q && (p < plim);)
*p++ = *q++;
/* p now points to the byte in pbuf where more copying should begin */
/* skip over the part of s which begins with old */
for (r = old, q = s; *r; q++, r++)
;
/* q now points to the byte in s where more copying should begin */
while (*q && (p < plim))
*p++ = *q++;
*p = '\0';
return (pbuf);
}