home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Usenet 1994 October
/
usenetsourcesnewsgroupsinfomagicoctober1994disk2.iso
/
unix
/
volume21
/
coda
/
part03
/
client.c
next >
Wrap
C/C++ Source or Header
|
1990-04-08
|
15KB
|
727 lines
/*
** Copyright 1989 BBN Systems and Technologies Corporation.
** All Rights Reserved.
** This is free software, and may be distributed under the terms of the
** GNU Public License; see the file COPYING for more details.
**
** Main code for CODA client.
*/
#define MAINLINE
#include "client.h"
#ifndef VMS
#include <sys/types.h>
#include <sys/stat.h>
#else
#include <types.h>
#include <stat.h>
#endif /* VMS */
#ifdef RCSID
static char RCS[] =
"$Header: client.c,v 2.0 90/04/09 15:34:23 rsalz Exp $";
#endif /* RCSID */
/*
** A whole mess of global variables.
*/
STATIC char ACK[] = "ACK-"; /* Header if command was OK */
STATIC char DAT[] = "DAT ITEM "; /* Data file for from command */
STATIC char NAK[] = "NAK-"; /* Header if it wasn't */
STATIC char **Lines; /* Lines read from server */
STATIC char *Directory; /* Where sources reside */
STATIC char *Filename; /* File for server to read */
STATIC char *Root; /* Remote root directory */
STATIC char *ServerHost; /* Host server is on */
STATIC char *UserName; /* Remote user name */
STATIC char *UserPass; /* Remote user password */
STATIC int MaxLines; /* Number of lines allocated */
STATIC BOOL Noaction; /* Just say what's outdated? */
STATIC int NumLines; /* Number of lines used */
STATIC int Port = PORTNUMBER; /* Port server is listening */
STATIC int Verbose; /* Tell what we're doing? */
STATIC BOOL ChangeOwner; /* Try to give away files? */
STATIC BOOL SlowMode; /* Do byte comparisons? */
STATIC BOOL YoungerMode; /* Can client have newer files? */
/*
** Print an error message and exit.
*/
void
Fatal(p)
char *p;
{
(void)fprintf(stderr, "Coda fatal error:\n\t%s\n", p);
exit(EXITERR);
/* NOTREACHED */
}
/*
** Get a line from the server; if it's a NAK, quit.
*/
STATIC void
QuitOnNack()
{
char buff[SIZE];
if (!SRVget(buff, sizeof buff))
Fatal("Server did not reply");
if (strncmp(buff, ACK, sizeof ACK - 1) == 0)
return;
if (strncmp(buff, NAK, sizeof NAK - 1) == 0)
Fatal(&buff[sizeof NAK - 1]);
(void)fprintf(stderr, "Bad from server:\n\t%s\n", buff);
Fatal("Protocol error");
}
/*
** Print a warning if user wants to see them.
*/
STATIC void
Cant(Always, Action, Name)
BOOL Always;
char *Action;
char *Name;
{
char buff[SIZE];
int e;
if (Always || Verbose > 2) {
e = errno;
(void)sprintf(buff, "Can't %s %s", Action, Name);
errno = e;
perror(buff);
}
}
/*
** Set the mode, ownership, etc., on an item to be what they should be.
*/
STATIC void
SetStuff(Name, Mode, Uid, Gid, Time)
char *Name;
int Mode;
int Uid;
int Gid;
long Time;
{
struct stat Sb;
#ifdef ATTsysv
struct utime_t {
time_t x;
time_t y;
} t, *vec = &t;
#else
time_t vec[2];
#endif /* ATTsysv */
/* Get current status. */
if (stat(Name, &Sb) < 0) {
Cant(TRUE, "stat", Name);
return;
}
/* Fix up the permissions on files. */
if ((Sb.st_mode & 0777) != Mode) {
if (chmod(Name, Mode) < 0)
Cant(FALSE, "chmod", Name);
else if (Verbose > 1)
(void)printf("chmod 0%o %s\n", Mode, Name);
}
/* Fix up the modication time. */
if (Sb.st_mtime != Time) {
#ifdef ATTsysv
t.x = t.y = Time;
#else
vec[0] = vec[1] = Time;
#endif /* ATTsysv */
if (utime(Name, vec) < 0)
Cant(FALSE, "set time", Name);
else if (Verbose > 1)
(void)printf("settime %ld %s\n", (long)Time, Name);
}
/* Fix up the owner. */
if (ChangeOwner && (Sb.st_uid != Uid || Sb.st_gid != Gid)) {
if (chown(Name, Uid, Gid) < 0)
Cant(FALSE, "chown", Name);
else if (Verbose > 1)
(void)printf("chown %d chgrp %d %s\n", Uid, Gid, Name);
}
}
/*
** Do what's necessary to create a directory.
*/
STATIC void
DoDir(Name, Uid, Gid, Mode, Time)
char *Name;
int Uid;
int Gid;
int Mode;
long Time;
{
struct stat Sb;
#ifdef VMS
char buff[SIZE];
#endif /* VMS */
#ifdef VMS
(void)sprintf(buff, "%s.dir", Name);
Name = buff;
#endif /* VMS */
/* If directory doesn't exist, create it. */
if (stat(Name, &Sb) < 0) {
if (Verbose > 0 || Noaction) {
(void)printf("mkdir %s\n", Name);
if (Noaction)
return;
}
if (mkdir(Name, Mode) < 0)
Cant(TRUE, "mkdir", Name);
}
/* Set the other bits. */
if (!Noaction)
SetStuff(Name, Mode, Uid, Gid, Time);
}
/*
** Compare two files, if they're different, move the second onto
** the first.
*/
STATIC void
GetFile(Name, Size, KnowItsWrong)
char *Name;
REGISTER long Size;
BOOL KnowItsWrong;
{
REGISTER FILE *F;
REGISTER FILE *Old;
REGISTER char *p;
REGISTER int c;
REGISTER int c2;
char buff[SIZE];
char temp[SIZE];
if (!KnowItsWrong && Verbose > 4)
(void)printf("Checking %s\n", Name);
/* Make a temporary filename in the same directory. */
(void)strcpy(temp, Name);
if (p = strrchr(temp, '/'))
p++;
else
p = temp;
(void)strcpy(p, "codaXXXXXX");
(void)mktemp(p);
if ((F = fopen(temp, "w")) == NULL) {
Cant(TRUE, "fopen", temp);
return;
}
/* Tell the server to send us the file. */
(void)sprintf(buff, "SEND %s\n", Name);
SRVput(buff);
if (!SRVget(buff, sizeof buff)
|| strncmp(buff, NAK, sizeof NAK - 1) == 0) {
Cant(TRUE, "start to read", temp);
(void)fclose(F);
(void)unlink(temp);
return;
}
/* Read it from the server. */
while (--Size >= 0) {
c = SRVcget();
(void)putc(c, F);
}
(void)fclose(F);
QuitOnNack();
/* If we know it's wrong, just move it. */
if (KnowItsWrong) {
(void)unlink(Name);
if (rename(temp, Name) < 0)
Cant(TRUE, "rename to", Name);
(void)sprintf(buff, "MESG took %s\n", Name);
SRVput(buff);
QuitOnNack();
return;
}
/* Open both old and new files. */
if ((F = fopen(temp, "r")) == NULL) {
Cant(TRUE, "reopen", temp);
return;
}
if ((Old = fopen(Name, "r")) == NULL) {
Cant(TRUE, "fopen", Name);
(void)fclose(F);
return;
}
/* Do byte-for-byte comparisons. */
while ((c = getc(F)) == (c2 = getc(Old)))
if (c == EOF)
break;
(void)fclose(F);
(void)fclose(Old);
/* Different? */
if (c != c2) {
if (Verbose || Noaction) {
(void)printf("Update %s\n", Name);
if (Noaction) {
(void)unlink(temp);
return;
}
}
(void)unlink(Name);
if (rename(temp, Name) < 0)
Cant(TRUE, "rename to", Name);
(void)sprintf(buff, "MESG took %s\n", Name);
}
else
(void)sprintf(buff, "MESG ignore %s\n", Name);
SRVput(buff);
QuitOnNack();
(void)unlink(temp);
}
/*
** Do what's necessary to create a file.
*/
STATIC void
DoFile(Name, Uid, Gid, Mode, Time, Size)
char *Name;
int Uid;
int Gid;
int Mode;
long Time;
long Size;
{
struct stat Sb;
if (stat(Name, &Sb) < 0 || Sb.st_size != Size) {
/* File doesn't exist or size is wrong; get it. */
if (YoungerMode && Sb.st_mtime > Time) {
(void)printf("Younger %s\n", Name);
return;
}
if (Verbose || Noaction) {
(void)printf("Update %s\n", Name);
if (Noaction)
return;
}
GetFile(Name, Size, TRUE);
}
else {
if (YoungerMode && Sb.st_mtime > Time) {
(void)printf("Younger %s\n", Name);
return;
}
if (SlowMode || Sb.st_mtime != Time)
/* Slow mode or the times are different; get and check. */
GetFile(Name, Size, FALSE);
}
/* Set the other bits. */
if (!Noaction)
SetStuff(Name, Mode, Uid, Gid, Time);
}
/*
** Read the list of files from the server, than parse them.
*/
STATIC void
ParseListResult()
{
REGISTER char **L;
REGISTER char **Lend;
REGISTER char *p;
char buff[SIZE];
char *Name;
int Uid;
int Gid;
long Size;
long Time;
int Mode;
int What;
/* Read lines from server. */
for (NumLines = 0; SRVget(buff, sizeof buff); ) {
/* Check for end of list or a bad line. */
if (strncmp(buff, NAK, sizeof NAK - 1) == 0) {
(void)printf("%s\n", &buff[sizeof NAK - 1]);
return;
}
if (strncmp(buff, ACK, sizeof ACK - 1) == 0) {
(void)printf("%s\n", &buff[sizeof ACK - 1]);
break;
}
if (strncmp(buff, DAT, sizeof DAT - 1)) {
(void)fprintf(stderr, "Bogus line from the server:\n\t%s\n", buff);
continue;
}
/* Need more room for this line? */
if (NumLines == MaxLines - 1) {
MaxLines += LINES_DELTA;
Lines = GROW(Lines, char*, MaxLines);
}
Lines[NumLines++] = COPY(&buff[sizeof DAT - 1]);
}
/* Now loop over what we got and parse it. */
for (L = Lines, Lend = &Lines[NumLines]; L < Lend; L++) {
/* Set defaults to show the stuff is bad. */
Name = NULL;
Uid = -1;
Gid = -1;
Size = -1;
Time = -1;
Mode = -1;
What = -1;
for (p = strcpy(buff, *L); *p; ) {
switch (*p) {
default:
(void)fprintf(stderr, "Bad field from server:\n\t%s\n", *L);
break;
case 'N':
Name = &p[2];
break;
case 'W':
if (p[2] == 'd' || p[2] == 'f')
What = p[2];
break;
case 'U':
Uid = atoi(&p[2]);
break;
case 'G':
Gid = atoi(&p[2]);
break;
case 'M':
Mode = atoi(&p[2]);
break;
case 'S':
Size = atol(&p[2]);
break;
case 'T':
Time = atol(&p[2]);
break;
}
/* Move to next field. */
while (*p && !WHITE(*p))
p++;
if (*p)
for (*p++ = '\0'; *p && WHITE(*p); )
p++;
}
/* Got everything? */
if (What < 0 || Mode < 0 || Gid < 0 || Size < 0 || Time < 0
#ifdef NOBODY
|| (Uid < 0 && Uid != NOBODY)
#else
|| Uid < 0
#endif /* NOBODY */
|| Name == NULL)
(void)fprintf(stderr, "Badly formed line:\n\t%s\n", *L);
else if (What == 'd')
DoDir(Name, Uid, Gid, Mode, Time);
else
DoFile(Name, Uid, Gid, Mode, Time, Size);
}
}
/*
** Scan a word for yes or no.
*/
STATIC int
GetYesOrNo(p, where, flag)
char *p;
char *where;
char flag;
{
char buff[SIZE];
switch (*p++) {
case 'Y': case 'y':
if (p[0] == '\0')
return TRUE;
if ((p[1] == 'E' || p[1] == 'e')
&& (p[2] == 'S' || p[2] == 's')
&& p[3] == '\0')
break;
case 'N': case 'n':
if (p[0] == '\0')
return TRUE;
if ((p[1] == 'O' || p[1] == 'o')
&& p[2] == '\0')
return TRUE;
break;
}
(void)sprintf(buff, "Expecting one of [YyNn] %s for '%c' flag", where, flag);
Fatal(buff);
/* NOTREACHED */
}
/*
** Skip past the current word and any whitespace that follows it.
*/
STATIC char *
Skip(p)
char *p;
{
while (*p && !WHITE(*p))
p++;
while (*p && WHITE(*p))
p++;
return p;
}
/*
** Read the init file.
*/
STATIC void
ReadInitFile()
{
static char Where[] = "in init file";
FILE *F;
char *p;
char buff[SIZE];
/* Get the filename, open the file. */
if ((p = getenv("CODA")) == NULL || (F = fopen(p, "r")) == NULL) {
#ifdef VMS
if ((F = fopen(CODA_INIT, "r")) == NULL)
return;
#else
if ((p = getenv("HOME")) == NULL)
return;
(void)sprintf(buff, CODA_INIT, p);
if ((F = fopen(buff, "r")) == NULL)
return;
#endif /* VMS */
}
while (fgets(buff, sizeof buff, F)) {
/* Skip blank and comment lines. */
if (p = strchr(buff, '\n'))
*p = '\0';
if (buff[0] == '\0' || buff[0] == '#')
continue;
if (strncmp(buff, "dir", 3) == 0) {
p = Skip(buff);
Directory = COPY(p);
}
else if (strncmp(buff, "file", 4) == 0) {
p = Skip(buff);
Filename = COPY(p);
}
else if (strncmp(buff, "host", 4) == 0) {
p = Skip(buff);
ServerHost = COPY(p);
}
else if (strncmp(buff, "owner", 5) == 0) {
p = Skip(buff);
ChangeOwner = GetYesOrNo(p, Where, 'o');
}
else if (strncmp(buff, "pass", 4) == 0) {
p = Skip(buff);
UserPass = COPY(p);
}
else if (strncmp(buff, "port", 4) == 0) {
p = Skip(buff);
Port = atoi(p);
}
else if (strncmp(buff, "root", 4) == 0) {
p = Skip(buff);
Root = COPY(p);
}
else if (strncmp(buff, "slow", 4) == 0) {
p = Skip(buff);
SlowMode = GetYesOrNo(p, Where, 's');
}
else if (strncmp(buff, "user", 4) == 0) {
p = Skip(buff);
UserName = COPY(p);
}
else if (strncmp(buff, "verb", 4) == 0) {
p = Skip(buff);
Verbose = atoi(p);
}
else if (strncmp(buff, "young", 5) == 0) {
p = Skip(buff);
YoungerMode = GetYesOrNo(p, Where, 'y');
}
else {
(void)fprintf(stderr, "Unknown line:\n\t%s\n", buff);
Fatal("Bad init file");
}
}
(void)fclose(F);
}
main(ac, av)
int ac;
char *av[];
{
static char Where[] = "on command line";
char buff[SIZE];
char pass[SIZE];
int i;
/* Get defaults from environment if possible. */
ReadInitFile();
(void)umask(0);
/* Parse JCL. */
while ((i = getopt(ac, av, "cd:f:h:no:p:r:s:tu:v:x:y:")) != EOF)
switch (i) {
default:
Fatal("Bad calling sequence");
/* NOTREACHED */
case 'c':
Copyright();
exit(EXITOK);
/* NOTREACHED */
case 'd':
Directory = optarg;
break;
case 'f':
Filename = optarg;
break;
case 'h':
ServerHost = optarg;
break;
case 'n':
Noaction = TRUE;
break;
case 'o':
ChangeOwner = GetYesOrNo(optarg, Where, 'o');
break;
case 'p':
Port = atoi(optarg);
break;
case 'r':
Root = optarg;
break;
case 's':
SlowMode = GetYesOrNo(optarg, Where, 's');
break;
case 't':
SRVtrace = TRUE;
break;
case 'u':
UserName = optarg;
break;
case 'v':
Verbose = atoi(optarg);
break;
case 'x':
UserPass = optarg;
break;
case 'y':
YoungerMode = GetYesOrNo(optarg, Where, 'y');
break;
}
av += optind;
/* Got everything we need? */
if (ServerHost == NULL)
Fatal("Need name of server host");
if (Port == 0)
Fatal("Need port to connect to");
if (UserName == NULL)
Fatal("Need your name on the server machine");
/* Get passwword. */
if (UserPass == NULL) {
(void)printf("Enter password:");
(void)fflush(stdout);
GetPassword(pass, sizeof pass);
UserPass = pass;
}
/* Talk to the server */
if (!SRVopen(ServerHost, Port))
Fatal("Can't open connection to server");
QuitOnNack();
/* Log in. */
(void)sprintf(buff, "USER %s %s", UserName, UserPass);
SRVput(buff);
QuitOnNack();
/* Tell the server where to go. */
if (Directory) {
(void)sprintf(buff, "GOTO %s", Directory);
SRVput(buff);
QuitOnNack();
}
/* Read the file. */
if (Filename) {
(void)sprintf(buff, "READ %s", Filename);
SRVput(buff);
}
else
SRVput("READ");
QuitOnNack();
/* Set the root directory. */
if (Root) {
(void)sprintf(buff, "ROOT %s", Root);
SRVput(buff);
QuitOnNack();
}
/* Any other arguments are block names. */
if (*av) {
/* Get some space. */
MaxLines = LINES_DELTA;
Lines = NEW(char*, MaxLines);
for ( ; *av; av++) {
(void)printf("Doing %s...\n", *av);
(void)sprintf(buff, "LIST %s", *av);
SRVput(buff);
ParseListResult();
}
}
else {
/* No arguments means do the whole shebang. */
MaxLines = LINES_DELTA * 2;
Lines = NEW(char*, MaxLines);
(void)printf("Working...\n");
SRVput("LIST");
ParseListResult();
}
/* That's all she wrote. */
SRVclose();
exit(EXITOK);
/* NOTREACHED */
}