2 * Copyright 2000, International Business Machines Corporation and others.
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
10 #include <afsconfig.h>
11 #include <afs/param.h>
14 #include <afs/procmgmt.h>
19 #include <WINNT/afsevent.h>
20 #include <sys/utime.h>
25 #include <sys/statfs.h>
31 #include <afs/com_err.h>
32 #include <afs/cellconfig.h>
33 #include <afs/afsutil.h>
34 #include <afs/fileutil.h>
38 #include "update_internal.h"
44 static int GetFileFromUpServer(struct rx_connection
*conn
, char *filename
,
45 short uid
, short gid
, afs_uint32 mode
,
46 afs_int32 atime
, afs_int32 mtime
);
47 static int RenameNewFiles(struct filestr
*modFiles
);
48 static int PathsAreEquivalent(char *path1
, char *path2
);
49 int FetchFile(struct rx_call
*, char *, char *, int);
50 int IsCompatible(char *, afs_int32
, afs_int32
);
51 int NotOnHost(char *, struct filestr
*);
52 int update_ReceiveFile(int, struct rx_call
*, struct stat
*);
55 GetServer(char *aname
)
60 th
= gethostbyname(aname
);
62 printf("host %s not found \n", aname
);
65 memcpy(&addr
, th
->h_addr
, sizeof(addr
));
73 /* this sucks but it works for now.
79 #include "AFS_component_version_number.c"
83 main(int argc
, char **argv
)
85 struct rx_connection
*conn
;
87 struct afsconf_dir
*cdir
;
89 struct rx_securityClass
*sc
;
92 afs_uint32 u_uid
, u_gid
; /*Unsigned long versions of the above */
97 char hostname
[MAXFNSIZE
];
99 afs_int32 time
, length
, atime
;
103 unsigned int interval
;
108 char dirbuf
[MAXFNSIZE
], filename
[MAXFNSIZE
];
109 struct filestr
*dirname
, *ModFiles
, *okhostfiles
;
112 * The following signal action for AIX is necessary so that in case of a
113 * crash (i.e. core is generated) we can include the user's data section
114 * in the core dump. Unfortunately, by default, only a partial core is
115 * generated which, in many cases, isn't too useful.
117 struct sigaction nsa
;
119 sigemptyset(&nsa
.sa_mask
);
120 nsa
.sa_handler
= SIG_DFL
;
121 nsa
.sa_flags
= SA_FULLDUMP
;
122 sigaction(SIGABRT
, &nsa
, NULL
);
123 sigaction(SIGSEGV
, &nsa
, NULL
);
127 /* dummy signal call to force afsprocmgmt.dll to load on NT */
128 signal(SIGUSR1
, SIG_DFL
);
130 /* initialize winsock */
131 if (afs_winsockInit() < 0) {
132 ReportErrorEventAlt(AFSEVT_SVR_WINSOCK_INIT_FAILED
, 0, argv
[0], 0);
133 fprintf(stderr
, "%s: Couldn't initialize winsock.\n", whoami
);
138 /* Initialize dirpaths */
139 if (!(initAFSDirPath() & AFSDIR_SERVER_PATHS_OK
)) {
141 ReportErrorEventAlt(AFSEVT_SVR_NO_INSTALL_DIR
, 0, argv
[0], 0);
143 fprintf(stderr
, "%s: Unable to obtain AFS server directory.\n",
154 level
= rxkad_crypt
; /* safest default */
155 strcpy(hostname
, "");
157 /* Note that IsArg only checks as many bytes as specified in the command line arg,
158 * so that, for instance, -t still matches -time.
160 for (a
= 1; a
< argc
; a
++) {
161 if (argv
[a
][0] == '-') { /* parse options */
162 int arglen
= strlen(argv
[a
]);
164 lcstring(arg
, argv
[a
], sizeof(arg
));
165 #define IsArg(a) (strncmp (arg,a, arglen) == 0)
167 interval
= atol(argv
[++a
]);
168 else if (IsArg("-crypt"))
170 else if (IsArg("-clear"))
172 else if (IsArg("-verbose"))
177 ("Usage: upclient <hostname> [-crypt] [-clear] [-t <retry time>] [-verbose]* <dir>+ [-help]\n");
180 } else if (strlen(hostname
) == 0) {
181 if (strlcpy(hostname
, argv
[a
], sizeof(hostname
))
182 >= sizeof(hostname
)) {
183 fprintf(stderr
, "Supplied hostname is too long\n");
187 if (strlcpy(filename
, argv
[a
], sizeof(filename
))
188 >= sizeof(filename
)) {
189 fprintf(stderr
, "Supplied filename is too long\n");
192 FilepathNormalize(filename
);
193 AddToList(&dirname
, filename
);
198 if (strlen(hostname
) == 0)
200 host
= GetServer(hostname
);
201 if (interval
< retrytime
)
202 retrytime
= interval
;
206 errcode
= rx_Init(0);
208 printf("Rx initialize failed \n");
209 afs_com_err(whoami
, errcode
, "calling Rx init");
213 cdir
= afsconf_Open(AFSDIR_SERVER_ETC_DIRPATH
);
215 fprintf(stderr
, "Can't get server configuration info (%s)\n",
216 AFSDIR_SERVER_ETC_DIRPATH
);
220 if (level
== rxkad_crypt
)
221 errcode
= afsconf_ClientAuthSecure(cdir
, &sc
, &scIndex
);
222 else if (level
== rxkad_clear
)
223 errcode
= afsconf_ClientAuth(cdir
, &sc
, &scIndex
);
225 printf("Unsupported security level %d\n", level
);
229 afs_com_err(whoami
, errcode
, "Couldn't get security obect for localAuth");
235 rx_NewConnection(host
, htons(AFSCONF_UPDATEPORT
), UPDATE_SERVICEID
,
238 while (1) { /*keep doing it */
240 for (df
= dirname
; df
; df
= df
->next
) { /*for each directory do */
244 printf("Checking dir %s\n", df
->name
);
245 /* initialize lists */
247 ZapList(&okhostfiles
);
249 /* construct local path from canonical (wire-format) path */
250 if ((errcode
= ConstructLocalPath(df
->name
, "/", &curDir
))) {
251 afs_com_err(whoami
, errcode
, "Unable to construct local path");
255 if (stat(curDir
, &tstat
) < 0) {
256 /* try to make the dir */
258 if (mkdir(curDir
) < 0) {
260 if (mkdir(curDir
, 0700) < 0) {
262 afs_com_err(whoami
, errno
, "can't create dir");
263 printf("upclient: Can't update dir %s\n", curDir
);
267 if ((tstat
.st_mode
& S_IFMT
) != S_IFDIR
) {
268 printf(" file %s is not a directory; aborting\n", curDir
);
272 call
= rx_NewCall(conn
);
274 /* scratch pad file */
275 sprintf(dirbuf
, "%s/upclient.%d", gettmpdir(), getpid());
277 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 */
278 error
= rx_EndCall(call
, 0);
279 if (error
&& !errcode
) {
280 printf("could not end rx call \n");
281 afs_com_err(whoami
, error
, "calling EndCall");
286 ("warning: could not fetch the contents of directory %s \n",
288 afs_com_err(whoami
, errcode
, "calling FetchFile");
293 stream
= fopen(dirbuf
, "r");
294 if (stream
== NULL
) {
295 printf("fopen failed on %s \n", dirbuf
);
296 afs_com_err(whoami
, errno
, "fopen");
301 /* while there is more info about files in file dirbuf */
303 (stream
, "%c%[^\"]%c %u %u %u %u %u %u\n", &c
, filename
,
304 &c1
, &time
, &length
, &mode
, &u_uid
, &u_gid
,
308 AddToList(&okhostfiles
, filename
);
309 /*record all the file names which exist on the remote
310 * sync site, to enable purging of redundant files */
312 printf(" checking %s\n", filename
);
313 if (!IsCompatible(filename
, time
, length
)) {
314 /* if the file info has changed , record all the
315 *changed files in the ModFiles array*/
317 printf(" getting %s\n", filename
);
318 AddToList(&ModFiles
, filename
);
320 /* now get the file from the server. The received
321 * file is created under the name filename.NEW */
323 GetFileFromUpServer(conn
, filename
, uid
, gid
, mode
,
325 if (errcode
== 1) /* this file failed, but keep trying */
327 if (errcode
== -1) { /* time to quit */
338 { /*delete all the redundant files on the client */
341 char filename
[MAXFNSIZE
];
343 dirp
= opendir(curDir
);
345 afs_com_err(whoami
, errno
, "Can't open local dir %s", curDir
);
349 while ((dp
= readdir(dirp
))) {
350 /* for all the files in the directory df->name do */
351 strcpy(filename
, curDir
);
352 strcat(filename
, "/");
353 strcat(filename
, dp
->d_name
);
354 /* if the file filename is redundant, delete it */
355 errcode
= NotOnHost(filename
, okhostfiles
);
360 printf(" flushing %s\n", filename
);
361 errcode
= unlink(filename
);
363 printf("could not delete file %s \n", filename
);
364 afs_com_err(whoami
, errno
, "could not delete file %s",
371 /* Now, rename the .NEW files created by FetchFile */
372 if (RenameNewFiles(ModFiles
))
376 } /* end for each dir loop */
377 /*delete the file with info on files in directory df->name */
378 IOMGR_Sleep(interval
);
385 IOMGR_Sleep(retrytime
);
387 rx_DestroyConnection(conn
);
390 /* start the cycle again */
395 /* returns 1 if the file is upto date else returns 0*/
396 /*check the dir case more carefully */
398 IsCompatible(char *filename
, afs_int32 time
, afs_int32 length
)
404 /* construct a local path from canonical (wire-format) path */
405 if ((error
= ConstructLocalPath(filename
, "/", &localname
))) {
406 afs_com_err(whoami
, error
, "Unable to construct local path");
410 error
= stat(localname
, &status
);
415 return 0; /*a non-existent file, has to be fetched fresh */
416 if ((status
.st_mode
& S_IFMT
) == S_IFDIR
417 || ((status
.st_mtime
== time
) && (status
.st_size
== length
)))
424 FetchFile(struct rx_call
*call
, char *remoteFile
, char *localFile
, int dirFlag
)
426 int fd
= -1, error
= 0;
430 if (StartUPDATE_FetchInfo(call
, remoteFile
))
433 if (StartUPDATE_FetchFile(call
, remoteFile
))
436 fd
= open(localFile
, O_CREAT
| O_TRUNC
| O_WRONLY
, 0666);
438 printf("Could not create %s\n", localFile
);
439 afs_com_err(whoami
, errno
, "Could not create %s", localFile
);
440 error
= UPDATE_ERROR
;
443 if (fstat(fd
, &status
) < 0) {
444 afs_com_err(whoami
, errno
, "Could not stat %s", localFile
);
446 printf("could not stat %s\n", localFile
);
449 if (update_ReceiveFile(fd
, call
, &status
))
450 error
= UPDATE_ERROR
;
460 update_ReceiveFile(int fd
, struct rx_call
*call
, struct stat
*status
)
462 char *buffer
= (char *)0;
465 afs_int32 error
= 0, len
;
467 struct statfs tstatfs
;
470 len
= rx_Read(call
, (char *)&length
, sizeof(afs_int32
));
471 length
= ntohl(length
);
472 if (len
!= sizeof(afs_int32
))
475 /* Unfortunately in AIX valuable fields such as st_blksize are gone from the stat structure!! */
476 fstatfs(fd
, &tstatfs
);
477 blockSize
= tstatfs
.f_bsize
;
481 blockSize
= status
->st_blksize
;
483 buffer
= malloc(blockSize
);
485 printf("malloc failed\n");
488 while (!error
&& length
) {
489 int nbytes
= (length
> blockSize
? blockSize
: length
);
490 nbytes
= rx_Read(call
, buffer
, nbytes
);
492 error
= UPDATE_ERROR
;
493 if (write(fd
, buffer
, nbytes
) != nbytes
) {
494 afs_com_err(whoami
, errno
, "File system write failed!");
495 printf("File system write failed!\n");
496 error
= UPDATE_ERROR
;
509 * PathsAreEquivalent() -- determine if paths are equivalent
510 * Returns 1 if yes, 0 if no, -1 on error.
513 PathsAreEquivalent(char *path1
, char *path2
)
516 char *pathNorm1
, *pathNorm2
;
519 /* case-insensitive comparison of normalized, same-flavor (short) paths */
522 pathNorm1
= malloc(AFSDIR_PATH_MAX
);
523 if (pathNorm1
== NULL
)
525 status
= GetShortPathName(path1
, pathNorm1
, AFSDIR_PATH_MAX
);
526 if (status
== 0 || status
> AFSDIR_PATH_MAX
) {
527 /* can't convert path to short version; just use long version */
529 pathNorm1
= strdup(path1
);
530 if (pathNorm1
== NULL
)
533 FilepathNormalize(pathNorm1
);
535 pathNorm2
= malloc(AFSDIR_PATH_MAX
);
536 if (pathNorm2
== NULL
) {
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 */
544 pathNorm2
= strdup(path2
);
545 if (pathNorm2
== NULL
) {
550 FilepathNormalize(pathNorm2
);
552 if (_stricmp(pathNorm1
, pathNorm2
) == 0) {
556 /* case-sensitive comparison of normalized paths */
557 pathNorm1
= strdup(path1
);
558 if (pathNorm1
== NULL
)
560 FilepathNormalize(pathNorm1
);
562 pathNorm2
= strdup(path2
);
563 if (pathNorm2
== NULL
) {
567 FilepathNormalize(pathNorm2
);
569 if (strcmp(pathNorm1
, pathNorm2
) == 0) {
572 #endif /* AFS_NT40_ENV */
577 return (code
!= 0) ? code
: areEq
;
582 /* returns 1 if filename does not exist on the host site (=> it should be
583 * deleted on client site) else it returns 0 */
586 NotOnHost(char *filename
, struct filestr
*okhostfiles
)
593 stat(filename
, &status
);
595 if ((status
.st_mode
& S_IFMT
) == S_IFDIR
)
597 i
= strlen(filename
);
598 if (!strcmp(&filename
[i
- 4], ".NEW"))
601 for (tf
= okhostfiles
; tf
; tf
= tf
->next
) {
602 /* construct local path from canonical (wire-format) path */
603 if ((rc
= ConstructLocalPath(tf
->name
, "/", &hostfile
))) {
604 afs_com_err(whoami
, rc
, "Unable to construct local path");
607 rc
= PathsAreEquivalent(hostfile
, filename
);
618 /* RenameNewFiles() - renames all the newly copied files from the
619 * server. Looks for files with .NEW extension and renames them
622 RenameNewFiles(struct filestr
*modFiles
)
624 char newname
[MAXFNSIZE
];
629 for (tf
= modFiles
; tf
; tf
= tf
->next
) {
630 /* construct local path from canonical (wire-format) path */
631 if ((errcode
= ConstructLocalPath(tf
->name
, "/", &fname
))) {
632 afs_com_err(whoami
, errcode
, "Unable to construct local path");
635 strcpy(newname
, fname
);
636 strcat(newname
, ".NEW");
638 printf(" renaming %s\n", newname
);
639 errcode
= rk_rename(newname
, fname
);
641 printf("could not rename %s to %s\n", newname
, fname
);
642 afs_com_err(whoami
, errno
, "could not rename %s to %s", newname
,
652 /* GetFileFromUpServer() - Makes the FetchFile() call and gets the
653 * file from the upserver.
656 * -1 - Serious error. Quit right away.
657 * 1 - Error, but keep trying for the other files
659 * The file obtained is written to the localized version of the filename.NEW
660 * and the uid, gid, file mode, access and modification times will be set to
661 * the passed in values.
664 GetFileFromUpServer(struct rx_connection
*conn
, /* handle for upserver */
665 char *filename
, /* name of file to be fetched */
666 short uid
, short gid
, /* uid/gid for fetched file */
667 afs_uint32 mode
, /* file mode */
668 afs_int32 atime
, afs_int32 mtime
)
669 { /* access/modification times */
670 struct rx_call
*call
;
674 struct _utimbuf utbuf
;
676 struct timeval tvp
[2];
678 char newfile
[MAXFNSIZE
];
680 /* construct local path from canonical (wire-format) path */
681 errcode
= ConstructLocalPath(filename
, "/", &lfile
);
683 afs_com_err(whoami
, errcode
, "Unable to construct local path");
686 strcpy(newfile
, lfile
);
689 strcat(newfile
, ".NEW");
691 /* fetch filename into newfile from the host, since the current file
692 * is outdated. the new versions of changed files is stored as
694 call
= rx_NewCall(conn
);
695 errcode
= FetchFile(call
, filename
, newfile
, 0);
696 errcode
= rx_EndCall(call
, errcode
);
699 printf("failed to fetch file %s \n", filename
);
700 afs_com_err(whoami
, errcode
, "fetching file");
705 /* now set the rest of the file status */
706 errcode
= chmod(newfile
, mode
);
708 printf("could not change protection on %s to %u\n", newfile
,
710 afs_com_err(whoami
, errno
, "could not change protection on %s to %u",
716 utbuf
.actime
= atime
;
717 utbuf
.modtime
= mtime
;
718 errcode
= _utime(newfile
, &utbuf
);
720 errcode
= chown(newfile
, uid
, gid
);
722 printf("warning: could not change uid and gid on %s to %u and %u \n",
724 afs_com_err(whoami
, errno
,
725 "warning: could not change uid and gid on %s to %u and %u",
728 tvp
[0].tv_sec
= atime
;
730 tvp
[1].tv_sec
= mtime
;
732 errcode
= utimes(newfile
, tvp
);
735 printf("could not change access and modify times on %s to %u %u\n",
736 newfile
, (unsigned int)atime
, (unsigned int)mtime
);
737 afs_com_err(whoami
, errno
,
738 "could not change access and modify times on %s to %u %u",
739 newfile
, atime
, mtime
);