home *** CD-ROM | disk | FTP | other *** search
- /*
- ** 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 */
- }
-