comerr-rename-20070410
[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 #ifdef  AFS_AIX_ENV
59 #include <sys/statfs.h>
60 #endif
61 #include "update.h"
62 #include "global.h"
63
64 char *whoami;
65 static int verbose;
66
67 /* prototypes */
68 static int GetFileFromUpServer(struct rx_connection *conn, char *filename,
69                                short uid, short gid, afs_uint32 mode,
70                                afs_int32 atime, afs_int32 mtime);
71 static int RenameNewFiles(struct filestr *modFiles);
72 static int PathsAreEquivalent(char *path1, char *path2);
73
74 afs_int32
75 GetServer(char *aname)
76 {
77     register struct hostent *th;
78     afs_int32 addr;
79
80     th = gethostbyname(aname);
81     if (!th) {
82         printf("host %s not found \n", aname);
83         exit(1);
84     }
85     memcpy(&addr, th->h_addr, sizeof(addr));
86     return addr;
87 }
88
89
90 int
91 osi_audit()
92 {
93 /* this sucks but it works for now.
94 */
95     return 0;
96 }
97
98 #ifndef AFS_NT40_ENV
99 #include "AFS_component_version_number.c"
100 #endif
101
102 int
103 main(int argc, 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         afs_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         afs_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                 afs_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                     afs_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                 afs_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                 afs_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                 afs_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                     afs_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                             afs_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 /*check the dir case more carefully */
409 int
410 IsCompatible(char *filename, afs_int32 time, afs_int32 length)  
411 {
412     struct stat status;
413     afs_int32 error;
414     char *localname;
415
416     /* construct a local path from canonical (wire-format) path */
417     if ((error = ConstructLocalPath(filename, "/", &localname))) {
418         afs_com_err(whoami, error, "Unable to construct local path");
419         return error;
420     }
421
422     error = stat(localname, &status);
423
424     free(localname);
425
426     if (error == -1)
427         return 0;       /*a non-existent file, has to be fetched fresh */
428     if ((status.st_mode & S_IFMT) == S_IFDIR
429         || ((status.st_mtime == time) && (status.st_size == length)))
430         return (1);
431     else
432         return 0;
433 }
434
435 int
436 FetchFile(struct rx_call *call, char *remoteFile, char *localFile, int dirFlag)
437 {
438     int fd = -1, error = 0;
439     struct stat status;
440
441     if (dirFlag) {
442         if (StartUPDATE_FetchInfo(call, remoteFile))
443             return UPDATE_ERROR;
444     } else {
445         if (StartUPDATE_FetchFile(call, remoteFile))
446             return UPDATE_ERROR;
447     }
448     fd = open(localFile, O_CREAT | O_TRUNC | O_WRONLY, 0666);
449     if (fd < 0) {
450         printf("Could not create %s\n", localFile);
451         afs_com_err(whoami, errno, "Could not create %s", localFile);
452         error = UPDATE_ERROR;
453         return error;
454     }
455     if (fstat(fd, &status) < 0) {
456         afs_com_err(whoami, errno, "Could not stat %s", localFile);
457         close(fd);
458         printf("could not stast %s\n", localFile);
459         return UPDATE_ERROR;
460     }
461     if (update_ReceiveFile(fd, call, &status))
462         error = UPDATE_ERROR;
463
464     close(fd);
465
466     return error;
467 }
468
469
470
471 int
472 update_ReceiveFile(register int fd, register struct rx_call *call, register struct stat *status)
473 {
474     register char *buffer = (char *)0;
475     afs_int32 length;
476     register int blockSize;
477     afs_int32 error = 0, len;
478 #ifdef  AFS_AIX_ENV
479     struct statfs tstatfs;
480 #endif
481
482     len = rx_Read(call, &length, sizeof(afs_int32));
483     length = ntohl(length);
484     if (len != sizeof(afs_int32))
485         return UPDATE_ERROR;
486 #ifdef  AFS_AIX_ENV
487     /* Unfortunately in AIX valuable fields such as st_blksize are gone from the stat structure!! */
488     fstatfs(fd, &tstatfs);
489     blockSize = tstatfs.f_bsize;
490 #elif AFS_NT40_ENV
491     blockSize = 4096;
492 #else
493     blockSize = status->st_blksize;
494 #endif
495     buffer = (char *)malloc(blockSize);
496     if (!buffer) {
497         printf("malloc failed\n");
498         return UPDATE_ERROR;
499     }
500     while (!error && length) {
501         register int nbytes = (length > blockSize ? blockSize : length);
502         nbytes = rx_Read(call, buffer, nbytes);
503         if (!nbytes)
504             error = UPDATE_ERROR;
505         if (write(fd, buffer, nbytes) != nbytes) {
506             afs_com_err(whoami, errno, "File system write failed!");
507             printf("File system write failed!\n");
508             error = UPDATE_ERROR;
509         }
510         length -= nbytes;
511     }
512     if (buffer)
513         free(buffer);
514     if (!error)
515         fstat(fd, status);
516     return error;
517 }
518
519
520 /*
521  * PathsAreEquivalent() -- determine if paths are equivalent
522  */
523 static int
524 PathsAreEquivalent(char *path1, char *path2)
525 {
526     int areEq = 0;
527     char pathNorm1[AFSDIR_PATH_MAX], pathNorm2[AFSDIR_PATH_MAX];
528
529 #ifdef AFS_NT40_ENV
530     /* case-insensitive comparison of normalized, same-flavor (short) paths */
531     DWORD status;
532
533     status = GetShortPathName(path1, pathNorm1, AFSDIR_PATH_MAX);
534     if (status == 0 || status > AFSDIR_PATH_MAX) {
535         /* can't convert path to short version; just use long version */
536         strcpy(pathNorm1, path1);
537     }
538     FilepathNormalize(pathNorm1);
539
540     status = GetShortPathName(path2, pathNorm2, AFSDIR_PATH_MAX);
541     if (status == 0 || status > AFSDIR_PATH_MAX) {
542         /* can't convert path to short version; just use long version */
543         strcpy(pathNorm2, path2);
544     }
545     FilepathNormalize(pathNorm2);
546
547     if (_stricmp(pathNorm1, pathNorm2) == 0) {
548         areEq = 1;
549     }
550 #else
551     /* case-sensitive comparison of normalized paths */
552     strcpy(pathNorm1, path1);
553     FilepathNormalize(pathNorm1);
554
555     strcpy(pathNorm2, path2);
556     FilepathNormalize(pathNorm2);
557
558     if (strcmp(pathNorm1, pathNorm2) == 0) {
559         areEq = 1;
560     }
561 #endif /* AFS_NT40_ENV */
562     return areEq;
563 }
564
565
566
567 /* returns 1 if filename does not exist on the host site (=> it should be
568  * deleted on client site) else it returns 0 */
569
570 int
571 NotOnHost(char *filename, struct filestr *okhostfiles)
572 {
573     int i, rc;
574     struct stat status;
575     struct filestr *tf;
576     char *hostfile;
577
578     stat(filename, &status);
579
580     if ((status.st_mode & S_IFMT) == S_IFDIR)
581         return 0;
582     i = strlen(filename);
583     if (!strcmp(&filename[i - 4], ".NEW"))
584         return 0;
585
586     for (tf = okhostfiles; tf; tf = tf->next) {
587         /* construct local path from canonical (wire-format) path */
588         if ((rc = ConstructLocalPath(tf->name, "/", &hostfile))) {
589             afs_com_err(whoami, rc, "Unable to construct local path");
590             return -1;
591         }
592         if (PathsAreEquivalent(hostfile, filename)) {
593             free(hostfile);
594             return 0;
595         }
596         free(hostfile);
597     }
598     return 1;
599 }
600
601
602 /* RenameNewFiles() - renames all the newly copied files from the
603  * server. Looks for files with .NEW extension and renames them
604  */
605 static int
606 RenameNewFiles(struct filestr *modFiles)
607 {
608     char newname[MAXSIZE];
609     char *fname;
610     int errcode = 0;
611     struct filestr *tf;
612
613     for (tf = modFiles; tf; tf = tf->next) {
614         /* construct local path from canonical (wire-format) path */
615         if ((errcode = ConstructLocalPath(tf->name, "/", &fname))) {
616             afs_com_err(whoami, errcode, "Unable to construct local path");
617             return errcode;
618         }
619         strcpy(newname, fname);
620         strcat(newname, ".NEW");
621         if (verbose >= 2)
622             printf("  renaming %s\n", newname);
623         errcode = renamefile(newname, fname);
624         if (errcode) {
625             printf("could not rename %s to %s\n", newname, fname);
626             afs_com_err(whoami, errno, "could not rename %s to %s", newname,
627                     fname);
628         }
629         free(fname);
630     }
631     return errcode;
632 }
633
634
635
636 /* GetFileFromUpServer() - Makes the FetchFile() call and gets the
637  * file from the upserver. 
638  * Return Values:
639  *   0 -  Alls well
640  *   -1 - Serious error. Quit right away.
641  *   1  - Error, but keep trying for the other files 
642  * 
643  * The file obtained is written to the localized version of the filename.NEW
644  * and the uid, gid, file mode, access and modification times will be set to
645  * the passed in values.
646  */
647 static int
648 GetFileFromUpServer(struct rx_connection *conn, /* handle for upserver */
649                     char *filename,     /* name of file to be fetched */
650                     short uid, short gid,       /* uid/gid for fetched file */
651                     afs_uint32 mode,    /* file mode */
652                     afs_int32 atime, afs_int32 mtime)
653 {                               /* access/modification times */
654     struct rx_call *call;
655     afs_int32 errcode;
656     char *lfile;
657 #ifdef AFS_NT40_ENV
658     struct _utimbuf utbuf;
659 #else
660     struct timeval tvp[2];
661 #endif
662     char newfile[MAXSIZE];
663
664     /* construct local path from canonical (wire-format) path */
665     errcode = ConstructLocalPath(filename, "/", &lfile);
666     if (errcode) {
667         afs_com_err(whoami, errcode, "Unable to construct local path");
668         return -1;
669     }
670     strcpy(newfile, lfile);
671     free(lfile);
672
673     strcat(newfile, ".NEW");
674
675     /* fetch filename into newfile from the host, since the current file
676      * is outdated. the new versions of changed files is stored as
677      * oldname.new */
678     call = rx_NewCall(conn);
679     errcode = FetchFile(call, filename, newfile, 0);
680     errcode = rx_EndCall(call, errcode);
681
682     if (errcode) {
683         printf("failed to fetch file %s \n", filename);
684         afs_com_err(whoami, errcode, "fetching file");
685         return 1;
686     }
687
688     /* now set the rest of the file status */
689     errcode = chmod(newfile, mode);
690     if (errcode) {
691         printf("could not change protection on %s to %u\n", newfile,
692                (unsigned int)mode);
693         afs_com_err(whoami, errno, "could not change protection on %s to %u",
694                 newfile, mode);
695         return 1;
696     }
697 #ifdef AFS_NT40_ENV
698     utbuf.actime = atime;
699     utbuf.modtime = mtime;
700     errcode = _utime(newfile, &utbuf);
701 #else
702     errcode = chown(newfile, uid, gid);
703     if (errcode) {
704         printf("warning: could not change uid and gid on %s to %u and %u \n",
705                newfile, gid, uid);
706         afs_com_err(whoami, errno,
707                 "warning: could not change uid and gid on %s to %u and %u",
708                 newfile, gid, uid);
709     }
710     tvp[0].tv_sec = atime;
711     tvp[0].tv_usec = 0;
712     tvp[1].tv_sec = mtime;
713     tvp[1].tv_usec = 0;
714     errcode = utimes(newfile, tvp);
715 #endif /* NT40 */
716     if (errcode) {
717         printf("could not change access and modify times on %s to %u %u\n",
718                newfile, (unsigned int)atime, (unsigned int)mtime);
719         afs_com_err(whoami, errno,
720                 "could not change access and modify times on %s to %u %u",
721                 newfile, atime, mtime);
722         return 1;
723     }
724
725     return 0;
726 }