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