reindent-20030715
[openafs.git] / src / update / client.c
1 /*
2  * Copyright 2000, International Business Machines Corporation and others.
3  * All Rights Reserved.
4  * 
5  * This software has been released under the terms of the IBM Public
6  * License.  For details, see the LICENSE file in the top-level source
7  * directory or online at http://www.openafs.org/dl/license10.html
8  */
9
10 #include <afsconfig.h>
11 #include <afs/param.h>
12
13 RCSID
14     ("$Header$");
15
16 #include <afs/stds.h>
17 #ifdef  AFS_AIX32_ENV
18 #include <signal.h>
19 #endif
20 #include <sys/types.h>
21 #include <sys/stat.h>
22 #ifdef AFS_NT40_ENV
23 #include <fcntl.h>
24 #include <winsock2.h>
25 #include <WINNT/afsevent.h>
26 #include <sys/utime.h>
27 #include <afs/dirent.h>
28 #include <direct.h>
29 #include <process.h>
30 #include <io.h>
31 #include <afs/procmgmt.h>
32 #else
33 #include <sys/file.h>
34 #include <netdb.h>
35 #include <netinet/in.h>
36 #include <sys/time.h>
37 #include <dirent.h>
38 #endif
39 #ifdef HAVE_STRING_H
40 #include <string.h>
41 #else
42 #ifdef HAVE_STRINGS_H
43 #include <strings.h>
44 #endif
45 #endif
46 #ifdef HAVE_UNISTD_H
47 #include <unistd.h>
48 #endif
49 #include <stdio.h>
50 #include <errno.h>
51 #include <rx/xdr.h>
52 #include <rx/rx.h>
53 #include <rx/rxkad.h>
54 #include <afs/com_err.h>
55 #include <afs/cellconfig.h>
56 #include <afs/afsutil.h>
57 #include <afs/fileutil.h>
58 #include "update.h"
59 #include "global.h"
60
61 char *whoami;
62 static int verbose;
63
64 /* prototypes */
65 static int GetFileFromUpServer(struct rx_connection *conn, char *filename,
66                                short uid, short gid, afs_uint32 mode,
67                                afs_int32 atime, afs_int32 mtime);
68 static int RenameNewFiles(struct filestr *modFiles);
69 static int PathsAreEquivalent(char *path1, char *path2);
70
71 afs_int32
72 GetServer(aname)
73      char *aname;
74 {
75     register struct hostent *th;
76     afs_int32 addr;
77
78     th = gethostbyname(aname);
79     if (!th) {
80         printf("host %s not found \n", aname);
81         exit(1);
82     }
83     memcpy(&addr, th->h_addr, sizeof(addr));
84     return addr;
85 }
86
87
88 int
89 osi_audit()
90 {
91 /* this sucks but it works for now.
92 */
93     return 0;
94 }
95
96 #ifndef AFS_NT40_ENV
97 #include "AFS_component_version_number.c"
98 #endif
99
100 int
101 main(argc, argv)
102      int argc;
103      char **argv;
104 {
105     struct rx_connection *conn;
106     struct rx_call *call;
107     struct afsconf_dir *cdir;
108     afs_int32 scIndex;
109     struct rx_securityClass *sc;
110
111     short uid, gid;
112     afs_uint32 u_uid, u_gid;    /*Unsigned long versions of the above */
113     struct stat tstat;
114
115     afs_uint32 mode;
116     int error;
117     char hostname[MAXSIZE];
118     FILE *stream;
119     afs_int32 time, length, atime;
120     struct filestr *df;
121     afs_int32 errcode;
122     int retrytime;
123     unsigned int interval;
124     afs_int32 host;
125     int a, cnt;
126     rxkad_level level;
127
128     char dirbuf[MAXSIZE], filename[MAXSIZE];
129     struct filestr *dirname, *ModFiles, *okhostfiles;
130 #ifdef  AFS_AIX32_ENV
131     /*
132      * The following signal action for AIX is necessary so that in case of a 
133      * crash (i.e. core is generated) we can include the user's data section 
134      * in the core dump. Unfortunately, by default, only a partial core is
135      * generated which, in many cases, isn't too useful.
136      */
137     struct sigaction nsa;
138
139     sigemptyset(&nsa.sa_mask);
140     nsa.sa_handler = SIG_DFL;
141     nsa.sa_flags = SA_FULLDUMP;
142     sigaction(SIGABRT, &nsa, NULL);
143     sigaction(SIGSEGV, &nsa, NULL);
144 #endif
145     whoami = argv[0];
146 #ifdef AFS_NT40_ENV
147     /* dummy signal call to force afsprocmgmt.dll to load on NT */
148     signal(SIGUSR1, SIG_DFL);
149
150     /* initialize winsock */
151     if (afs_winsockInit() < 0) {
152         ReportErrorEventAlt(AFSEVT_SVR_WINSOCK_INIT_FAILED, 0, argv[0], 0);
153         fprintf(stderr, "%s: Couldn't initialize winsock.\n", whoami);
154         exit(1);
155     }
156 #endif
157
158     /* Initialize dirpaths */
159     if (!(initAFSDirPath() & AFSDIR_SERVER_PATHS_OK)) {
160 #ifdef AFS_NT40_ENV
161         ReportErrorEventAlt(AFSEVT_SVR_NO_INSTALL_DIR, 0, argv[0], 0);
162 #endif
163         fprintf(stderr, "%s: Unable to obtain AFS server directory.\n",
164                 argv[0]);
165         exit(2);
166     }
167     retrytime = 60;
168     dirname = NULL;
169     ModFiles = NULL;
170     okhostfiles = NULL;
171
172     verbose = 0;
173     interval = TIMEOUT;
174     level = rxkad_crypt;        /* safest default */
175     strcpy(hostname, "");
176
177     /* Note that IsArg only checks as many bytes as specified in the command line arg,
178      * so that, for instance, -t still matches -time.
179      */
180     for (a = 1; a < argc; a++) {
181         if (argv[a][0] == '-') {        /* parse options */
182             int arglen = strlen(argv[a]);
183             char arg[256];
184             lcstring(arg, argv[a], sizeof(arg));
185 #define IsArg(a) (strncmp (arg,a, arglen) == 0)
186             if (IsArg("-time"))
187                 interval = atol(argv[++a]);
188             else if (IsArg("-crypt"))
189                 level = rxkad_crypt;
190             else if (IsArg("-clear"))
191                 level = rxkad_clear;
192             else if (IsArg("-verbose"))
193                 verbose++;
194             else {
195               usage:
196                 printf
197                     ("Usage: upclient <hostname> [-crypt] [-clear] [-t <retry time>] [-verbose]* <dir>+ [-help]\n");
198                 exit(1);
199             }
200         } else if (strlen(hostname) == 0)
201             strcpy(hostname, argv[a]);
202         else {
203             strcpy(filename, argv[a]);
204             FilepathNormalize(filename);
205             AddToList(&dirname, filename);
206         }
207     }
208     if (level == -1)
209         goto usage;
210     if (strlen(hostname) == 0)
211         goto usage;
212     host = GetServer(hostname);
213     if (interval < retrytime)
214         retrytime = interval;
215     if (dirname == 0)
216         goto usage;
217
218     errcode = rx_Init(0);
219     if (errcode) {
220         printf("Rx initialize failed \n");
221         com_err(whoami, errcode, "calling Rx init");
222         exit(1);
223     }
224
225     cdir = afsconf_Open(AFSDIR_SERVER_ETC_DIRPATH);
226     if (cdir == 0) {
227         fprintf(stderr, "Can't get server configuration info (%s)\n",
228                 AFSDIR_SERVER_ETC_DIRPATH);
229         exit(1);
230     }
231
232     if (level == rxkad_crypt)
233         errcode = afsconf_ClientAuthSecure(cdir, &sc, &scIndex);
234     else if (level == rxkad_clear)
235         errcode = afsconf_ClientAuth(cdir, &sc, &scIndex);
236     else {
237         printf("Unsupported security level %d\n", level);
238         exit(1);
239     }
240     if (errcode) {
241         com_err(whoami, errcode, "Couldn't get security obect for localAuth");
242         exit(1);
243     }
244
245   again:
246     conn =
247         rx_NewConnection(host, htons(AFSCONF_UPDATEPORT), UPDATE_SERVICEID,
248                          sc, scIndex);
249     cnt = 0;
250     while (1) {                 /*keep doing it */
251         char c, c1;
252         for (df = dirname; df; df = df->next) { /*for each directory do */
253             char *curDir;
254
255             if (verbose)
256                 printf("Checking dir %s\n", df->name);
257             /* initialize lists */
258             ZapList(&ModFiles);
259             ZapList(&okhostfiles);
260
261             /* construct local path from canonical (wire-format) path */
262             if ((errcode = ConstructLocalPath(df->name, "/", &curDir))) {
263                 com_err(whoami, errcode, "Unable to construct local path");
264                 return errcode;
265             }
266
267             if (stat(curDir, &tstat) < 0) {
268                 /* try to make the dir */
269 #ifdef AFS_NT40_ENV
270                 if (mkdir(curDir) < 0) {
271 #else
272                 if (mkdir(curDir, 0700) < 0) {
273 #endif
274                     com_err(whoami, errno, "can't create dir");
275                     printf("upclient: Can't update dir %s\n", curDir);
276                     return -1;
277                 }
278             } else {
279                 if ((tstat.st_mode & S_IFMT) != S_IFDIR) {
280                     printf(" file %s is not a directory; aborting\n", curDir);
281                     return -1;
282                 }
283             }
284             call = rx_NewCall(conn);
285
286             /* scratch pad file */
287             sprintf(dirbuf, "%s/upclient.%d", gettmpdir(), getpid());
288
289             errcode = FetchFile(call, df->name, dirbuf, 1);     /* get the names and relevant info about all the files in the directory df->name into file dirbuf */
290             error = rx_EndCall(call, 0);
291             if (error && !errcode) {
292                 printf("could not end rx call \n");
293                 com_err(whoami, error, "calling EndCall");
294                 goto fail;
295             }
296             if (errcode) {
297                 printf
298                     ("warning: could not fetch the contents of directory %s \n",
299                      df->name);
300                 com_err(whoami, errcode, "calling FetchFile");
301                 cnt++;
302                 goto fail;
303             }
304
305             stream = fopen(dirbuf, "r");
306             if (stream == NULL) {
307                 printf("fopen failed on %s \n", dirbuf);
308                 com_err(whoami, errno, "fopen");
309                 goto fail;
310             }
311             umask(00);
312
313             /* while there is more info about files in file dirbuf */
314             while (fscanf
315                    (stream, "%c%[^\"]%c %u %u %u %u %u %u\n", &c, filename,
316                     &c1, &time, &length, &mode, &u_uid, &u_gid,
317                     &atime) != EOF) {
318                 uid = (short)u_uid;
319                 gid = (short)u_gid;
320                 AddToList(&okhostfiles, filename);
321                 /*record all the file names which exist on the remote
322                  * sync site, to enable purging of redundant files */
323                 if (verbose >= 3)
324                     printf("    checking %s\n", filename);
325                 if (!IsCompatible(filename, time, length)) {
326                     /* if the file info has changed , record all the 
327                      *changed files in the ModFiles array*/
328                     if (verbose >= 2)
329                         printf("  getting %s\n", filename);
330                     AddToList(&ModFiles, filename);
331
332                     /* now get the file from the server. The received 
333                      * file is created under the name filename.NEW */
334                     errcode =
335                         GetFileFromUpServer(conn, filename, uid, gid, mode,
336                                             atime, time);
337                     if (errcode == 1)   /* this file failed, but keep trying */
338                         goto fail_dirbuf;
339                     if (errcode == -1) {        /* time to quit */
340                         fclose(stream);
341                         unlink(dirbuf);
342                         return -1;
343                     }
344                 }
345
346             }
347             fclose(stream);
348             unlink(dirbuf);
349
350             {                   /*delete all the redundant files on the client */
351                 DIR *dirp;
352                 struct dirent *dp;
353                 char filename[MAXSIZE];
354
355                 dirp = opendir(curDir);
356                 if (dirp == 0) {
357                     com_err(whoami, errno, "Can't open local dir %s", curDir);
358                     goto fail;
359                 }
360
361                 while ((dp = readdir(dirp))) {
362                     /* for all the files in the directory df->name do */
363                     strcpy(filename, curDir);
364                     strcat(filename, "/");
365                     strcat(filename, dp->d_name);
366                     /* if the file filename is redundant, delete it */
367                     errcode = NotOnHost(filename, okhostfiles);
368                     if (errcode == -1)
369                         return -1;
370                     if (errcode == 1) {
371                         if (verbose >= 2)
372                             printf("  flushing %s\n", filename);
373                         errcode = unlink(filename);
374                         if (errcode) {
375                             printf("could not delete file %s \n", filename);
376                             com_err(whoami, errno, "could not delete file %s",
377                                     filename);
378                         }
379                     }
380                 }
381                 closedir(dirp);
382             }
383             /* Now, rename the .NEW files created by FetchFile */
384             if (RenameNewFiles(ModFiles))
385                 return -1;
386
387             free(curDir);
388         }                       /* end for each dir loop */
389         /*delete the file with info on files in directory df->name */
390         IOMGR_Sleep(interval);
391         continue;
392
393       fail_dirbuf:
394         fclose(stream);
395         unlink(dirbuf);
396       fail:
397         IOMGR_Sleep(retrytime);
398         if (cnt > 10) {
399             rx_DestroyConnection(conn);
400             goto again;
401         }
402         /* start the cycle again */
403
404     }
405 }
406
407 /* returns 1 if the file is upto date else returns 0*/
408 int
409 IsCompatible(filename, time, length)    /*check the dir case more carefully */
410      char *filename;
411      afs_int32 time, length;
412 {
413     struct stat status;
414     afs_int32 error;
415     char *localname;
416
417     /* construct a local path from canonical (wire-format) path */
418     if ((error = ConstructLocalPath(filename, "/", &localname))) {
419         com_err(whoami, error, "Unable to construct local path");
420         return error;
421     }
422
423     error = stat(localname, &status);
424
425     free(localname);
426
427     if (error == -1)
428         return 0;               /*a non-existent file, has to be fetched fresh */
429     if ((status.st_mode & S_IFMT) == S_IFDIR
430         || ((status.st_mtime == time) && (status.st_size == length)))
431         return (1);
432     else
433         return 0;
434 }
435
436 int
437 FetchFile(call, remoteFile, localFile, dirFlag)
438      struct rx_call *call;
439      char *localFile, *remoteFile;
440      int dirFlag;
441 {
442     int fd = -1, error = 0;
443     struct stat status;
444
445     if (dirFlag) {
446         if (StartUPDATE_FetchInfo(call, remoteFile))
447             return UPDATE_ERROR;
448     } else {
449         if (StartUPDATE_FetchFile(call, remoteFile))
450             return UPDATE_ERROR;
451     }
452     fd = open(localFile, O_CREAT | O_TRUNC | O_WRONLY, 0666);
453     if (fd < 0) {
454         printf("Could not create %s\n", localFile);
455         com_err(whoami, errno, "Could not create %s", localFile);
456         error = UPDATE_ERROR;
457         return error;
458     }
459     if (fstat(fd, &status) < 0) {
460         com_err(whoami, errno, "Could not stat %s", localFile);
461         close(fd);
462         printf("could not stast %s\n", localFile);
463         return UPDATE_ERROR;
464     }
465     if (update_ReceiveFile(fd, call, &status))
466         error = UPDATE_ERROR;
467
468     close(fd);
469
470     return error;
471 }
472
473
474
475 int
476 update_ReceiveFile(fd, call, status)
477      register int fd;
478      register struct rx_call *call;
479      register struct stat *status;
480 {
481     register char *buffer = (char *)0;
482     afs_int32 length;
483 #ifdef notdef
484     XDR xdr;
485 #endif
486     register int blockSize;
487     afs_int32 error = 0, len;
488 #ifdef  AFS_AIX_ENV
489 #include <sys/statfs.h>
490     struct statfs tstatfs;
491 #endif
492
493 #ifdef  notdef
494     xdrrx_create(&xdr, call, XDR_DECODE);
495     if (!xdr_afs_int32(&xdr, &length))
496         return UPDATE_ERROR;
497 #else
498     len = rx_Read(call, &length, sizeof(afs_int32));
499     length = ntohl(length);
500     if (len != sizeof(afs_int32))
501         return UPDATE_ERROR;
502 #endif
503 #ifdef  AFS_AIX_ENV
504     /* Unfortunately in AIX valuable fields such as st_blksize are gone from the stat structure!! */
505     fstatfs(fd, &tstatfs);
506     blockSize = tstatfs.f_bsize;
507 #elif AFS_NT40_ENV
508     blockSize = 4096;
509 #else
510     blockSize = status->st_blksize;
511 #endif
512     buffer = (char *)malloc(blockSize);
513     if (!buffer) {
514         printf("malloc failed\n");
515         return UPDATE_ERROR;
516     }
517     while (!error && length) {
518         register int nbytes = (length > blockSize ? blockSize : length);
519         nbytes = rx_Read(call, buffer, nbytes);
520         if (!nbytes)
521             error = UPDATE_ERROR;
522         if (write(fd, buffer, nbytes) != nbytes) {
523             com_err(whoami, errno, "File system write failed!");
524             printf("File system write failed!\n");
525             error = UPDATE_ERROR;
526         }
527         length -= nbytes;
528     }
529     if (buffer)
530         free(buffer);
531     if (!error)
532         fstat(fd, status);
533     return error;
534 }
535
536
537 /*
538  * PathsAreEquivalent() -- determine if paths are equivalent
539  */
540 static int
541 PathsAreEquivalent(char *path1, char *path2)
542 {
543     int areEq = 0;
544     char pathNorm1[AFSDIR_PATH_MAX], pathNorm2[AFSDIR_PATH_MAX];
545
546 #ifdef AFS_NT40_ENV
547     /* case-insensitive comparison of normalized, same-flavor (short) paths */
548     DWORD status;
549
550     status = GetShortPathName(path1, pathNorm1, AFSDIR_PATH_MAX);
551     if (status == 0 || status > AFSDIR_PATH_MAX) {
552         /* can't convert path to short version; just use long version */
553         strcpy(pathNorm1, path1);
554     }
555     FilepathNormalize(pathNorm1);
556
557     status = GetShortPathName(path2, pathNorm2, AFSDIR_PATH_MAX);
558     if (status == 0 || status > AFSDIR_PATH_MAX) {
559         /* can't convert path to short version; just use long version */
560         strcpy(pathNorm2, path2);
561     }
562     FilepathNormalize(pathNorm2);
563
564     if (_stricmp(pathNorm1, pathNorm2) == 0) {
565         areEq = 1;
566     }
567 #else
568     /* case-sensitive comparison of normalized paths */
569     strcpy(pathNorm1, path1);
570     FilepathNormalize(pathNorm1);
571
572     strcpy(pathNorm2, path2);
573     FilepathNormalize(pathNorm2);
574
575     if (strcmp(pathNorm1, pathNorm2) == 0) {
576         areEq = 1;
577     }
578 #endif /* AFS_NT40_ENV */
579     return areEq;
580 }
581
582
583
584 /* returns 1 if filename does not exist on the host site (=> it should be
585  * deleted on client site) else it returns 0 */
586
587 int
588 NotOnHost(filename, okhostfiles)
589      char *filename;
590      struct filestr *okhostfiles;
591 {
592     int i, rc;
593     struct stat status;
594     struct filestr *tf;
595     char *hostfile;
596
597     stat(filename, &status);
598
599     if ((status.st_mode & S_IFMT) == S_IFDIR)
600         return 0;
601     i = strlen(filename);
602     if (!strcmp(&filename[i - 4], ".NEW"))
603         return 0;
604
605     for (tf = okhostfiles; tf; tf = tf->next) {
606         /* construct local path from canonical (wire-format) path */
607         if ((rc = ConstructLocalPath(tf->name, "/", &hostfile))) {
608             com_err(whoami, rc, "Unable to construct local path");
609             return -1;
610         }
611         if (PathsAreEquivalent(hostfile, filename)) {
612             free(hostfile);
613             return 0;
614         }
615         free(hostfile);
616     }
617     return 1;
618 }
619
620
621 /* RenameNewFiles() - renames all the newly copied files from the
622  * server. Looks for files with .NEW extension and renames them
623  */
624 static int
625 RenameNewFiles(struct filestr *modFiles)
626 {
627     char newname[MAXSIZE];
628     char *fname;
629     int errcode = 0;
630     struct filestr *tf;
631
632     for (tf = modFiles; tf; tf = tf->next) {
633         /* construct local path from canonical (wire-format) path */
634         if ((errcode = ConstructLocalPath(tf->name, "/", &fname))) {
635             com_err(whoami, errcode, "Unable to construct local path");
636             return errcode;
637         }
638         strcpy(newname, fname);
639         strcat(newname, ".NEW");
640         if (verbose >= 2)
641             printf("  renaming %s\n", newname);
642         errcode = renamefile(newname, fname);
643         if (errcode) {
644             printf("could not rename %s to %s\n", newname, fname);
645             com_err(whoami, errno, "could not rename %s to %s", newname,
646                     fname);
647         }
648         free(fname);
649     }
650     return errcode;
651 }
652
653
654
655 /* GetFileFromUpServer() - Makes the FetchFile() call and gets the
656  * file from the upserver. 
657  * Return Values:
658  *   0 -  Alls well
659  *   -1 - Serious error. Quit right away.
660  *   1  - Error, but keep trying for the other files 
661  * 
662  * The file obtained is written to the localized version of the filename.NEW
663  * and the uid, gid, file mode, access and modification times will be set to
664  * the passed in values.
665  */
666 static
667     int
668 GetFileFromUpServer(struct rx_connection *conn, /* handle for upserver */
669                     char *filename,     /* name of file to be fetched */
670                     short uid, short gid,       /* uid/gid for fetched file */
671                     afs_uint32 mode,    /* file mode */
672                     afs_int32 atime, afs_int32 mtime)
673 {                               /* access/modification times */
674     struct rx_call *call;
675     afs_int32 errcode;
676     char *lfile;
677 #ifdef AFS_NT40_ENV
678     struct _utimbuf utbuf;
679 #else
680     struct timeval tvp[2];
681 #endif
682     char newfile[MAXSIZE];
683
684     /* construct local path from canonical (wire-format) path */
685     errcode = ConstructLocalPath(filename, "/", &lfile);
686     if (errcode) {
687         com_err(whoami, errcode, "Unable to construct local path");
688         return -1;
689     }
690     strcpy(newfile, lfile);
691     free(lfile);
692
693     strcat(newfile, ".NEW");
694
695     /* fetch filename into newfile from the host, since the current file
696      * is outdated. the new versions of changed files is stored as
697      * oldname.new */
698     call = rx_NewCall(conn);
699     errcode = FetchFile(call, filename, newfile, 0);
700     errcode = rx_EndCall(call, errcode);
701
702     if (errcode) {
703         printf("failed to fetch file %s \n", filename);
704         com_err(whoami, errcode, "fetching file");
705         return 1;
706     }
707
708     /* now set the rest of the file status */
709     errcode = chmod(newfile, mode);
710     if (errcode) {
711         printf("could not change protection on %s to %u\n", newfile,
712                (unsigned int)mode);
713         com_err(whoami, errno, "could not change protection on %s to %u",
714                 newfile, mode);
715         return 1;
716     }
717 #ifdef AFS_NT40_ENV
718     utbuf.actime = atime;
719     utbuf.modtime = mtime;
720     errcode = _utime(newfile, &utbuf);
721 #else
722     errcode = chown(newfile, uid, gid);
723     if (errcode) {
724         printf("warning: could not change uid and gid on %s to %u and %u \n",
725                newfile, gid, uid);
726         com_err(whoami, errno,
727                 "warning: could not change uid and gid on %s to %u and %u",
728                 newfile, gid, uid);
729     }
730     tvp[0].tv_sec = atime;
731     tvp[0].tv_usec = 0;
732     tvp[1].tv_sec = mtime;
733     tvp[1].tv_usec = 0;
734     errcode = utimes(newfile, tvp);
735 #endif /* NT40 */
736     if (errcode) {
737         printf("could not change access and modify times on %s to %u %u\n",
738                newfile, (unsigned int)atime, (unsigned int)mtime);
739         com_err(whoami, errno,
740                 "could not change access and modify times on %s to %u %u",
741                 newfile, atime, mtime);
742         return 1;
743     }
744
745     return 0;
746 }