1 /* $NetBSD: supcmeat.c,v 1.36 2009/10/16 22:45:18 christos Exp $ */
4 * Copyright (c) 1992 Carnegie Mellon University
7 * Permission to use, copy, modify and distribute this software and its
8 * documentation is hereby granted, provided that both the copyright
9 * notice and this permission notice appear in all copies of the
10 * software, derivative works or modified versions, and any portions
11 * thereof, and that both notices appear in supporting documentation.
13 * CARNEGIE MELLON ALLOWS FREE USE OF THIS SOFTWARE IN ITS "AS IS"
14 * CONDITION. CARNEGIE MELLON DISCLAIMS ANY LIABILITY OF ANY KIND FOR
15 * ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE.
17 * Carnegie Mellon requests users of this software to return to
19 * Software Distribution Coordinator or Software.Distribution@CS.CMU.EDU
20 * School of Computer Science
21 * Carnegie Mellon University
22 * Pittsburgh PA 15213-3890
24 * any improvements or extensions that they make and grant Carnegie Mellon
25 * the rights to redistribute these changes.
29 **********************************************************************
32 * 7-July-93 Nate Williams at Montana State University
33 * Modified SUP to use gzip based compression when sending files
34 * across the network to save BandWidth
36 * Revision 1.16 92/09/09 22:04:51 mrt
37 * Really added bww's recvone changes this time.
38 * Added code to support non-crypting version of sup.
41 * Revision 1.15 92/08/11 12:07:09 mrt
42 * Added support to add release to FILEWHEN name.
43 * Updated variable arguemnt list usage - bww
44 * Updated recvone() to take a va_list - bww
45 * Changed conditional for rpausing code from CMUCS to MACH
48 * Revision 1.14 92/02/08 18:24:12 mja
49 * Only apply "keep" mode when local file is strictly newer
50 * otherwise allow update as before if necessary.
51 * [92/02/08 18:09:00 mja]
53 * Added support for -k (keep) option to needone(). Rewrote and
54 * commented other parts of needone().
57 * Revision 1.13 91/05/16 14:49:41 ern
58 * Add timestap to fileserver.
59 * Drop day of the week from 5 messages.
60 * [91/05/16 14:47:53 ern]
62 * Revision 1.12 89/08/23 14:55:44 gm0w
63 * Changed msgf routines to msg routines.
66 * Revision 1.11 89/08/03 19:49:10 mja
67 * Updated to use v*printf() in place of _doprnt().
70 * Revision 1.10 89/06/18 14:41:27 gm0w
71 * Fixed up some notify messages of errors to use "SUP:" prefix.
74 * Revision 1.9 89/06/10 15:12:17 gm0w
75 * Changed to always use rename to install targets. This breaks hard
76 * links and recreates those known to sup, other links will be orphaned.
79 * Revision 1.8 89/05/24 15:04:23 gm0w
80 * Added code to check for EINVAL from FSPARAM ioctl for disk
81 * space check failures when the ioctl is not implemented.
84 * Revision 1.7 89/01/16 18:22:28 gm0w
85 * Changed needone() to check that mode of files match before
86 * setting update if times also match.
89 * 10-Feb-88 Glenn Marcy (gm0w) at Carnegie-Mellon University
90 * Added timeout to backoff.
92 * 27-Dec-87 Glenn Marcy (gm0w) at Carnegie-Mellon University
93 * Added crosspatch support.
95 * 09-Sep-87 Glenn Marcy (gm0w) at Carnegie-Mellon University
96 * Added code to be less verbose when updating files that have
97 * already been successfully upgraded.
99 * 28-Jun-87 Glenn Marcy (gm0w) at Carnegie-Mellon University
100 * Added code for "release" support.
102 * 26-May-87 Doug Philips (dwp) at Carnegie-Mellon University
103 * Converted to end connection with more information.
104 * Added done routine. Modified goaway routine to free old
107 * 26-May-87 Doug Philips (dwp) at Carnegie-Mellon University
108 * Use computeBackoff from scm instead of doing it ourselves.
110 * 25-May-87 Doug Philips (dwp) at Carnegie-Mellon University
111 * Split off from sup.c and reindented goaway calls.
113 **********************************************************************
116 #include "supcdefs.h"
117 #include "supextern.h"
118 #include <sys/param.h>
119 #include <sys/wait.h>
121 TREE
*lastT
; /* last filenames in collection */
122 jmp_buf sjbuf
; /* jump location for network errors */
123 int dontjump
; /* flag to void sjbuf */
124 int cancompress
= FALSE
; /* Can we do compression? */
125 int docompress
= FALSE
; /* Do we do compression? */
127 extern COLLECTION
*thisC
; /* collection list pointer */
128 extern int rpauseflag
; /* don't disable resource pausing */
129 extern int portdebug
; /* network debugging ports */
130 extern int noutime
; /* don't set utimes */
132 /*************************************************
133 *** U P G R A D E C O L L E C T I O N ***
134 *************************************************/
136 static int needone(TREE
*, void *);
137 static int recvone(TREE
*, va_list);
138 static int denyone(TREE
*, void *);
139 static int deleteone(TREE
*, void *);
140 static int linkone(TREE
*, void *);
141 static int execone(TREE
*, void *);
142 static int finishone(TREE
*, void *);
145 /* The next two routines define the fsm to support multiple fileservers
149 getonehost(TREE
* t
, void *v
)
152 if (t
->Tflags
!= *state
)
154 if (*state
!= 0 && t
->Tmode
== SCMEOF
) {
167 getcollhost(int *tout
, int *backoff
, long int *state
, int *nhostsp
)
169 static long laststate
= 0;
170 static int nhosts
= 0;
172 if (*state
!= laststate
) {
177 if (Tprocess(thisC
->Chtree
, getonehost
, state
) == SCMEOF
) {
178 if (*state
!= 0 && nhosts
== 0 && !dobackoff(tout
, backoff
))
181 return (thisC
->Chost
);
189 return (getcollhost(tout
, backoff
, state
, nhostsp
));
191 /* Upgrade a collection from the file server on the appropriate
200 int tout
, backoff
, nhosts
;
203 collname
= thisC
->Cname
;
204 tout
= thisC
->Ctimeout
;
210 t
= getcollhost(&tout
, &backoff
, &state
, &nhosts
);
213 notify((char *) NULL
);
218 if (!setjmp(sjbuf
) && !signon(t
, nhosts
, &tout
) && !setup(t
))
231 if (thisC
->Clockfd
>= 0) {
232 (void) close(thisC
->Clockfd
);
236 notify((char *) NULL
);
238 /*** Sign on to file server ***/
241 signon(TREE
* t
, int nhosts
, int *tout
)
247 if ((thisC
->Cflags
& CFLOCAL
) == 0 && thishost(thisC
->Chost
->Tname
)) {
248 vnotify("SUP: Skipping local collection %s\n", collname
);
256 x
= request(portdebug
? DEBUGFPORT
: FILEPORT
,
257 thisC
->Chost
->Tname
, &timeout
);
262 notify("SUP: Can't connect to host %s\n",
263 thisC
->Chost
->Tname
);
270 x
= msgsignon(); /* signon to fileserver */
272 goaway("Error sending signon request to fileserver");
273 x
= msgsignonack(); /* receive signon ack from fileserver */
275 goaway("Error reading signon reply from fileserver");
276 tloc
= time((time_t *) NULL
);
277 vnotify("SUP Fileserver %d.%d (%s) %d on %s at %.8s\n",
278 protver
, pgmver
, scmver
, fspid
, remotehost(), ctime(&tloc
) + 11);
283 goaway("Fileserver sup protocol version is obsolete.");
284 notify("SUP: This version of sup can only communicate with a fileserver using at least\n");
285 notify("SUP: version 4 of the sup network protocol. You should either run a newer\n");
286 notify("SUP: version of the sup fileserver or find an older version of sup.\n");
290 /* If protocol is > 7 then try compression */
296 /*** Tell file server what to connect to ***/
301 char relsufix
[STRINGLENGTH
];
305 if (chdir(thisC
->Cbase
) < 0)
306 goaway("Can't change to base directory %s", thisC
->Cbase
);
307 if (stat("sup", &sbuf
) < 0) {
308 (void) mkdir("sup", 0755);
309 if (stat("sup", &sbuf
) < 0)
310 goaway("Can't create directory %s/sup", thisC
->Cbase
);
311 vnotify("SUP Created directory %s/sup\n", thisC
->Cbase
);
313 if (thisC
->Cprefix
&& chdir(thisC
->Cprefix
) < 0)
314 goaway("Can't change to %s from base directory %s",
315 thisC
->Cprefix
, thisC
->Cbase
);
316 if (stat(".", &sbuf
) < 0)
317 goaway("Can't stat %s directory %s",
318 thisC
->Cprefix
? "prefix" : "base",
319 thisC
->Cprefix
? thisC
->Cprefix
: thisC
->Cbase
);
321 (void) chdir(thisC
->Cbase
);
322 /* read time of last upgrade from when file */
324 if ((thisC
->Cflags
& CFURELSUF
) && thisC
->Crelease
)
325 (void) sprintf(relsufix
, ".%s", thisC
->Crelease
);
328 lasttime
= getwhen(collname
, relsufix
);
329 /* setup for msgsetup */
330 basedir
= thisC
->Chbase
;
331 basedev
= sbuf
.st_dev
;
332 baseino
= sbuf
.st_ino
;
333 listonly
= (thisC
->Cflags
& CFLIST
);
334 newonly
= ((thisC
->Cflags
& (CFALL
| CFDELETE
| CFOLD
)) == 0);
335 release
= thisC
->Crelease
;
338 goaway("Error sending setup request to file server");
341 goaway("Error reading setup reply from file server");
342 if (setupack
== FSETUPOK
) {
343 /* Test encryption */
344 if (netcrypt(thisC
->Ccrypt
) != SCMOK
)
345 goaway("Running non-crypting sup");
346 crypttest
= CRYPTTEST
;
349 goaway("Error sending encryption test request");
352 goaway("Data encryption test failed");
354 goaway("Error reading encryption test reply");
359 notify("SUP: Attempt to upgrade from same host to same directory\n");
360 done(FDONESRVERROR
, "Overwrite error");
362 notify("SUP: This host has no permission to access %s\n",
364 done(FDONESRVERROR
, "Permission denied");
366 notify("SUP: This version of SUP is too old for the fileserver\n");
367 done(FDONESRVERROR
, "Obsolete client");
369 notify("SUP: Invalid release %s for collection %s\n",
370 release
== NULL
? DEFRELEASE
: release
, collname
);
371 done(FDONESRVERROR
, "Invalid release");
373 vnotify("SUP Fileserver is currently busy\n");
375 doneack
= FDONESRVERROR
;
376 donereason
= "Fileserver is busy";
377 (void) netcrypt((char *) NULL
);
381 goaway("Unrecognized file server setup status %d", setupack
);
386 /*** Tell file server what account to use ***/
391 char buf
[STRINGLENGTH
];
394 /* lock collection if desired */
395 (void) sprintf(buf
, FILELOCK
, collname
);
396 f
= open(buf
, O_RDONLY
, 0);
400 #define TESTLOCK(f) flock(f, LOCK_EX|LOCK_NB)
401 #define SHARELOCK(f) flock(f, LOCK_SH|LOCK_NB)
402 #define WAITLOCK(f) flock(f, LOCK_EX)
403 #elif defined(F_LOCK)
404 #define TESTLOCK(f) lockf(f, F_TLOCK, 0)
405 #define SHARELOCK(f) 1
406 #define WAITLOCK(f) lockf(f, F_LOCK, 0)
408 #define TESTLOCK(f) (close(f), f = -1, 1)
409 #define SHARELOCK(f) 1
410 #define WAITLOCK(f) 1
412 if (TESTLOCK(f
) < 0) {
413 if (errno
!= EWOULDBLOCK
&& errno
!= EAGAIN
) {
415 goaway("Can't lock collection %s", collname
);
417 if (SHARELOCK(f
) < 0) {
419 if (errno
== EWOULDBLOCK
&& errno
!= EAGAIN
)
420 goaway("Collection %s is locked by another sup", collname
);
421 goaway("Can't lock collection %s", collname
);
423 vnotify("SUP Waiting for exclusive access lock\n");
424 if (WAITLOCK(f
) < 0) {
426 goaway("Can't lock collection %s", collname
);
430 vnotify("SUP Locked collection %s for exclusive access\n", collname
);
432 logcrypt
= (char *) NULL
;
433 loguser
= thisC
->Clogin
;
434 logpswd
= thisC
->Cpswd
;
436 #ifndef CRYPTING /* Define CRYPTING for backwards compatibility
437 * with old supfileservers */
438 if (thisC
->Clogin
!= (char *) NULL
) /* othewise we only encrypt if
439 * there is a login id */
440 #endif /* CRYPTING */
442 logcrypt
= CRYPTTEST
;
443 (void) netcrypt(PSWDCRYPT
); /* encrypt password data */
447 if (thisC
->Clogin
!= (char *) NULL
)
449 (void) netcrypt((char *) NULL
); /* turn off encryption */
451 goaway("Error sending login request to file server");
454 goaway("Error reading login reply from file server");
455 if (logack
== FLOGNG
) {
456 notify("SUP: %s\n", logerror
);
459 notify("SUP: Improper login to %s account",
460 thisC
->Clogin
? thisC
->Clogin
: "default");
461 done(FDONESRVERROR
, "Improper login");
463 if (netcrypt(thisC
->Ccrypt
) != SCMOK
) /* restore encryption */
464 goaway("Running non-crypting sup");
467 * send list of files that we are not interested in. receive list of
468 * files that are on the repository that could be upgraded. Find the
469 * ones that we need. Receive the list of files that the server could
470 * not access. Delete any files that have been upgraded in the past
471 * which are no longer on the repository.
477 char buf
[STRINGLENGTH
];
478 char relsufix
[STRINGLENGTH
];
484 if ((thisC
->Cflags
& CFURELSUF
) && release
)
485 (void) sprintf(relsufix
, ".%s", release
);
488 (void) sprintf(buf
, FILELAST
, collname
, relsufix
);
491 while ((p
= fgets(buf
, STRINGLENGTH
, f
))) {
492 if ((q
= strchr(p
, '\n')))
494 if (strchr("#;:", *p
))
496 (void) Tinsert(&lastT
, p
, FALSE
);
501 (void) sprintf(buf
, FILEREFUSE
, collname
);
504 while ((p
= fgets(buf
, STRINGLENGTH
, f
))) {
505 if ((q
= strchr(p
, '\n')))
507 if (strchr("#;:", *p
))
509 (void) Tinsert(&refuseT
, p
, FALSE
);
513 vnotify("SUP Requesting changes since %s", ctime(&lasttime
) + 4);
516 goaway("Error sending refuse list to file server");
520 goaway("Error reading file list from file server");
522 (void) chdir(thisC
->Cprefix
);
524 (void) Tprocess(listT
, needone
, NULL
);
528 goaway("Error sending needed files list to file server");
533 goaway("Error reading denied files list from file server");
534 if (thisC
->Cflags
& CFVERBOSE
)
535 (void) Tprocess(denyT
, denyone
, NULL
);
537 if (thisC
->Cflags
& (CFALL
| CFDELETE
| CFOLD
))
538 (void) Trprocess(lastT
, deleteone
, NULL
);
543 needone(TREE
* t
, void *dummy __unused
)
549 newt
= Tinsert(&lastT
, t
->Tname
, TRUE
);
550 newt
->Tflags
|= FUPDATE
;
552 if ((thisC
->Cflags
& CFALL
) == 0) {
553 if ((t
->Tflags
& FNEW
) == 0 && (thisC
->Cflags
& CFOLD
) == 0)
555 if (S_ISLNK(t
->Tmode
))
556 exists
= (lstat(t
->Tname
, &sbuf
) == 0);
558 exists
= (stat(t
->Tname
, &sbuf
) == 0);
559 /* This is moderately complicated: If the file is the wrong
560 * type or doesn't exist, we need to fetch the whole file. If
561 * the file is a special file, we rely solely on the server:
562 * if the file changed, we do an update; otherwise nothing. If
563 * the file is a normal file, we check timestamps. If we are
564 * in "keep" mode, we fetch if the file on the server is
565 * newer, and do nothing otherwise. Otherwise, we fetch if the
566 * timestamp is wrong; if the file changed on the server but
567 * the timestamp is right, we do an update. (Update refers to
568 * updating stat information, i.e. timestamp, owner, mode
570 if (exists
&& (sbuf
.st_mode
& S_IFMT
) == (t
->Tmode
& S_IFMT
)) {
571 if (!S_ISREG(t
->Tmode
))
572 if (t
->Tflags
& FNEW
)
576 else if ((thisC
->Cflags
& CFKEEP
) &&
577 sbuf
.st_mtime
> t
->Tmtime
)
579 else if (sbuf
.st_mtime
== t
->Tmtime
) {
580 if (t
->Tflags
& FNEW
)
587 /* If we get this far, we're either doing an update or a full fetch. */
588 newt
= Tinsert(&needT
, t
->Tname
, TRUE
);
589 if (!fetch
&& S_ISREG(t
->Tmode
))
590 newt
->Tflags
|= FUPDATE
;
595 denyone(TREE
* t
, void *v __unused
)
597 vnotify("SUP: Access denied to %s\n", t
->Tname
);
602 deleteone(TREE
* t
, void *v __unused
)
604 struct stat sbuf
, pbuf
;
606 char *name
= t
->Tname
;
607 char pname
[MAXPATHLEN
];
609 if (t
->Tflags
& FUPDATE
)/* in current upgrade list */
611 if (lstat(name
, &sbuf
) < 0) /* doesn't exist */
613 /* is it a symbolic link ? */
614 if (S_ISLNK(sbuf
.st_mode
)) {
615 if (Tlookup(refuseT
, name
)) {
616 vnotify("SUP Would not delete symbolic link %s\n",
620 if (thisC
->Cflags
& CFLIST
) {
621 vnotify("SUP Would delete symbolic link %s\n", name
);
624 if ((thisC
->Cflags
& CFDELETE
) == 0) {
625 notify("SUP Please delete symbolic link %s\n", name
);
626 t
->Tflags
|= FUPDATE
;
631 notify("SUP: Unable to delete symbolic link %s (%s)\n",
632 name
, strerror(errno
));
633 t
->Tflags
|= FUPDATE
;
636 vnotify("SUP Deleted symbolic link %s\n", name
);
639 /* is it a directory ? */
640 if (S_ISDIR(sbuf
.st_mode
)) {
641 if (Tlookup(refuseT
, name
)) {
642 vnotify("SUP Would not delete directory %s\n", name
);
645 if (thisC
->Cflags
& CFLIST
) {
646 vnotify("SUP Would delete directory %s\n", name
);
649 if ((thisC
->Cflags
& CFDELETE
) == 0) {
650 notify("SUP Please delete directory %s\n", name
);
651 t
->Tflags
|= FUPDATE
;
654 if (rmdir(name
) < 0) {
655 (void) chmod(name
, sbuf
.st_mode
| S_IRWXU
);
656 if (strlen(name
) < MAXPATHLEN
- 3) {
657 sprintf(pname
, "%s/..", name
);
658 if (stat(pname
, &pbuf
) == 0)
659 (void) chmod(pname
, pbuf
.st_mode
| S_IRWXU
);
661 runp("rm", "rm", "-rf", name
, 0);
663 if (rmdir(name
) < 0 && errno
!= ENOENT
) {
664 notify("SUP: Unable to delete directory %s (%s)\n",
665 name
, strerror(errno
));
666 t
->Tflags
|= FUPDATE
;
669 vnotify("SUP Deleted directory %s\n", name
);
673 if (Tlookup(refuseT
, name
)) {
674 vnotify("SUP Would not delete file %s\n", name
);
677 if (thisC
->Cflags
& CFLIST
) {
678 vnotify("SUP Would delete file %s\n", name
);
681 if ((thisC
->Cflags
& CFDELETE
) == 0) {
682 notify("SUP Please delete file %s\n", name
);
683 t
->Tflags
|= FUPDATE
;
688 notify("SUP: Unable to delete file %s (%s)\n", name
,
690 t
->Tflags
|= FUPDATE
;
693 vnotify("SUP Deleted file %s\n", name
);
696 /***************************************
697 *** R E C E I V E F I L E S ***
698 ***************************************/
700 /* Note for these routines, return code SCMOK generally means
701 * NETWORK communication is OK; it does not mean that the current
702 * file was correctly received and stored. If a file gets messed
703 * up, too bad, just print a message and go on to the next one;
704 * but if the network gets messed up, the whole sup program loses
705 * badly and best just stop the program as soon as possible.
714 /* Does the protocol support compression */
716 /* Check for compression on sending files */
717 docompress
= (thisC
->Cflags
& CFCOMPRESS
);
720 goaway("Error sending compression check to server");
722 vnotify("SUP Using compressed file transfer\n");
729 goaway("Error sending receive file request to file server");
730 (void) Tinsert(&upgradeT
, (char *) NULL
, FALSE
);
731 x
= msgrecv(recvone
, &recvmore
);
733 goaway("Error receiving file from file server");
737 /* prepare the target, if necessary */
739 prepare(char *name
, int mode
, int *newp
, struct stat
* statp
)
742 char pname
[MAXPATHLEN
];
747 *newp
= (lstat(name
, statp
) < 0);
749 *newp
= (stat(name
, statp
) < 0);
751 if (thisC
->Cflags
& CFLIST
)
753 if (establishdir(name
))
757 if (mode
== (statp
->st_mode
& S_IFMT
))
760 switch (statp
->st_mode
& S_IFMT
) {
765 type
= "symbolic link";
768 type
= "regular file";
771 type
= "unknown file";
774 if (thisC
->Cflags
& CFLIST
) {
775 vnotify("SUP Would remove %s %s\n", type
, name
);
778 if (S_ISDIR(statp
->st_mode
)) {
779 if (rmdir(name
) < 0) {
780 (void) chmod(name
, statp
->st_mode
| S_IRWXU
);
781 if (strlen(name
) < MAXPATHLEN
- 3) {
782 sprintf(pname
, "%s/..", name
);
783 if (stat(pname
, &pbuf
) == 0)
784 (void) chmod(pname
, pbuf
.st_mode
| S_IRWXU
);
786 runp("rm", "rm", "-rf", name
, 0);
791 if (unlink(name
) < 0)
794 if (stat(name
, statp
) < 0) {
795 vnotify("SUP Removed %s %s\n", type
, name
);
798 notify("SUP: Couldn't remove %s %s (%s)\n", type
, name
, strerror(er
));
803 recvone(TREE
* t
, va_list ap
)
810 recvmore
= va_arg(ap
, int *);
811 /* check for end of file list */
816 /* check for failed access at fileserver */
818 notify("SUP: File server unable to transfer file %s\n",
820 thisC
->Cnogood
= TRUE
;
823 if (prepare(t
->Tname
, t
->Tmode
& S_IFMT
, &new, &sbuf
)) {
824 notify("SUP: Can't prepare path for %s (%s)\n", t
->Tname
,
826 if (S_ISREG(t
->Tmode
)) {
827 x
= readskip(); /* skip over file */
829 goaway("Can't skip file transfer");
831 thisC
->Cnogood
= TRUE
;
834 /* make file mode specific changes */
835 switch (t
->Tmode
& S_IFMT
) {
837 x
= recvdir(t
, new, &sbuf
);
840 x
= recvsym(t
, new, &sbuf
);
843 x
= recvreg(t
, new, &sbuf
);
846 goaway("Unknown file type %o\n", t
->Tmode
& S_IFMT
);
849 thisC
->Cnogood
= TRUE
;
852 if (S_ISREG(t
->Tmode
))
853 (void) Tprocess(t
->Tlink
, linkone
, t
->Tname
);
854 (void) Tprocess(t
->Texec
, execone
, NULL
);
859 recvdir(TREE
* t
, int new, struct stat
* statp
)
860 { /* receive directory from network */
861 struct timeval tbuf
[2];
864 if (thisC
->Cflags
& CFLIST
) {
865 vnotify("SUP Would create directory %s\n", t
->Tname
);
868 if (makedir(t
->Tname
, 0755, statp
) == -1) {
869 notify("SUP: Can't create directory %s (%s)\n",
870 t
->Tname
, strerror(errno
));
874 if ((t
->Tflags
& FNOACCT
) == 0) {
875 /* convert user and group names to local ids */
876 ugconvert(t
->Tuser
, t
->Tgroup
, &t
->Tuid
, &t
->Tgid
, &t
->Tmode
);
878 if (!new && (t
->Tflags
& FNEW
) == 0 && statp
->st_mtime
== t
->Tmtime
) {
879 if (t
->Tflags
& FNOACCT
)
881 if (statp
->st_uid
== t
->Tuid
&& statp
->st_gid
== t
->Tgid
)
884 if (thisC
->Cflags
& CFLIST
) {
885 vnotify("SUP Would update directory %s\n", t
->Tname
);
888 if ((t
->Tflags
& FNOACCT
) == 0) {
889 (void) chown(t
->Tname
, t
->Tuid
, t
->Tgid
);
890 (void) chmod(t
->Tname
, t
->Tmode
& S_IMODE
);
892 tbuf
[0].tv_sec
= time((time_t *) NULL
);
894 tbuf
[1].tv_sec
= t
->Tmtime
;
897 (void) utimes(t
->Tname
, tbuf
);
898 vnotify("SUP %s directory %s\n", new ? "Created" : "Updated", t
->Tname
);
903 recvsym(TREE
* t
, int new, struct stat
* statp
)
904 { /* receive symbolic link */
905 char buf
[STRINGLENGTH
];
909 if (t
->Tlink
== NULL
|| t
->Tlink
->Tname
== NULL
) {
910 notify("SUP: Missing linkname for symbolic link %s\n",
914 linkname
= t
->Tlink
->Tname
;
916 if (!new && (t
->Tflags
& FNEW
) == 0 &&
917 (n
= readlink(t
->Tname
, buf
, sizeof(buf
) - 1)) >= 0 &&
918 (n
== strlen(linkname
)) && (strncmp(linkname
, buf
, n
) == 0))
922 if (thisC
->Cflags
& CFLIST
) {
923 vnotify("SUP Would %s symbolic link %s to %s\n",
924 new ? "create" : "update", t
->Tname
, linkname
);
928 (void) unlink(t
->Tname
);
929 if (symlink(linkname
, t
->Tname
) < 0 || lstat(t
->Tname
, statp
) < 0) {
930 notify("SUP: Unable to create symbolic link %s (%s)\n",
931 t
->Tname
, strerror(errno
));
934 vnotify("SUP Created symbolic link %s to %s\n", t
->Tname
, linkname
);
939 recvreg(TREE
* t
, int new, struct stat
* statp
)
940 { /* receive file from network */
942 char dirpart
[STRINGLENGTH
], filepart
[STRINGLENGTH
];
943 char filename
[STRINGLENGTH
], buf
[STRINGLENGTH
];
944 struct timeval tbuf
[2];
948 if (t
->Tflags
& FUPDATE
) {
949 if ((t
->Tflags
& FNOACCT
) == 0) {
950 /* convert user and group names to local ids */
951 ugconvert(t
->Tuser
, t
->Tgroup
, &t
->Tuid
, &t
->Tgid
,
954 if (!new && (t
->Tflags
& FNEW
) == 0 &&
955 statp
->st_mtime
== t
->Tmtime
) {
956 if (t
->Tflags
& FNOACCT
)
958 if (statp
->st_uid
== t
->Tuid
&&
959 statp
->st_gid
== t
->Tgid
)
962 if (thisC
->Cflags
& CFLIST
) {
963 vnotify("SUP Would update file %s\n", t
->Tname
);
966 vnotify("SUP Updating file %s\n", t
->Tname
);
967 if ((t
->Tflags
& FNOACCT
) == 0) {
968 (void) chown(t
->Tname
, t
->Tuid
, t
->Tgid
);
969 (void) chmod(t
->Tname
, t
->Tmode
& S_IMODE
);
971 tbuf
[0].tv_sec
= time((time_t *) NULL
);
973 tbuf
[1].tv_sec
= t
->Tmtime
;
976 (void) utimes(t
->Tname
, tbuf
);
979 if (thisC
->Cflags
& CFLIST
) {
982 else if (statp
->st_mtime
< t
->Tmtime
)
984 else if (statp
->st_mtime
> t
->Tmtime
)
988 vnotify("SUP Would %s file %s\n", p
, t
->Tname
);
991 vnotify("SUP Receiving file %s\n", t
->Tname
);
992 if (!new && S_ISREG(t
->Tmode
) &&
993 (t
->Tflags
& FBACKUP
) && (thisC
->Cflags
& CFBACKUP
)) {
994 fin
= fopen(t
->Tname
, "r"); /* create backup */
996 x
= readskip(); /* skip over file */
998 goaway("Can't skip file transfer");
999 notify("SUP: Can't open %s to create backup\n",
1001 return (TRUE
); /* mark upgrade as nogood */
1003 path(t
->Tname
, dirpart
, filepart
);
1004 (void) sprintf(filename
, FILEBACKUP
, dirpart
, filepart
);
1005 fout
= fopen(filename
, "w");
1007 (void) sprintf(buf
, FILEBKDIR
, dirpart
);
1008 (void) mkdir(buf
, 0755);
1009 fout
= fopen(filename
, "w");
1012 x
= readskip(); /* skip over file */
1014 goaway("Can't skip file transfer");
1015 notify("SUP: Can't create %s for backup\n", filename
);
1019 ffilecopy(fin
, fout
);
1021 (void) fclose(fout
);
1022 vnotify("SUP Backup of %s created\n", t
->Tname
);
1024 x
= copyfile(t
->Tname
, (char *) NULL
);
1027 if ((t
->Tflags
& FNOACCT
) == 0) {
1028 /* convert user and group names to local ids */
1029 ugconvert(t
->Tuser
, t
->Tgroup
, &t
->Tuid
, &t
->Tgid
, &t
->Tmode
);
1030 (void) chown(t
->Tname
, t
->Tuid
, t
->Tgid
);
1031 (void) chmod(t
->Tname
, t
->Tmode
& S_IMODE
);
1033 tbuf
[0].tv_sec
= time((time_t *) NULL
);
1034 tbuf
[0].tv_usec
= 0;
1035 tbuf
[1].tv_sec
= t
->Tmtime
;
1036 tbuf
[1].tv_usec
= 0;
1038 (void) utimes(t
->Tname
, tbuf
);
1043 linkone(TREE
* t
, void *fv
)
1044 { /* link to file already received */
1046 struct stat fbuf
, sbuf
;
1047 char *name
= t
->Tname
;
1051 if (lstat(fname
, &fbuf
) < 0) { /* source file */
1052 if (thisC
->Cflags
& CFLIST
) {
1053 vnotify("SUP Would link %s to %s\n", name
, fname
);
1056 notify("SUP: Can't link %s to missing file %s\n", name
, fname
);
1057 thisC
->Cnogood
= TRUE
;
1060 if (prepare(name
, S_IFLNK
, &new, &sbuf
)) {
1061 thisC
->Cnogood
= TRUE
;
1064 if (!new && (t
->Tflags
& FNEW
) == 0 &&
1065 fbuf
.st_dev
== sbuf
.st_dev
&& fbuf
.st_ino
== sbuf
.st_ino
)
1067 if (thisC
->Cflags
& CFLIST
) {
1068 vnotify("SUP Would link %s to %s\n", name
, fname
);
1071 (void) unlink(name
);
1073 if (S_ISDIR(fbuf
.st_mode
) || (x
= link(fname
, name
)) < 0) {
1075 x
= symlink(fname
, name
);
1077 if (x
< 0 || lstat(name
, &sbuf
) < 0) {
1078 notify("SUP: Unable to create %slink %s (%s)\n", type
, name
,
1082 vnotify("SUP Created %slink %s to %s\n", type
, name
, fname
);
1087 execone(TREE
* t
, void *v __unused
)
1088 { /* execute command for file */
1091 if (thisC
->Cflags
& CFLIST
) {
1092 vnotify("SUP Would execute %s\n", t
->Tname
);
1095 if ((thisC
->Cflags
& CFEXECUTE
) == 0) {
1096 notify("SUP Please execute %s\n", t
->Tname
);
1099 vnotify("SUP Executing %s\n", t
->Tname
);
1101 w
= system(t
->Tname
);
1102 if (WIFEXITED(w
) && WEXITSTATUS(w
) != 0) {
1103 notify("SUP: Execute command returned failure status %#o\n",
1105 thisC
->Cnogood
= TRUE
;
1106 } else if (WIFSIGNALED(w
)) {
1107 notify("SUP: Execute command killed by signal %d\n",
1109 thisC
->Cnogood
= TRUE
;
1110 } else if (WIFSTOPPED(w
)) {
1111 notify("SUP: Execute command stopped by signal %d\n",
1113 thisC
->Cnogood
= TRUE
;
1118 /* from will be 0 if reading from network */
1120 copyfile(char *to
, char *from
)
1122 int fromf
, tof
, istemp
, x
;
1123 char dpart
[STRINGLENGTH
], fpart
[STRINGLENGTH
];
1124 char tname
[STRINGLENGTH
];
1125 static int returntrue
= 1;
1127 static int thispid
= 0; /* process id # */
1129 if (from
) { /* reading file */
1130 fromf
= open(from
, O_RDONLY
, 0);
1132 notify("SUP: Can't open %s to copy to %s (%s)\n",
1133 from
, to
, strerror(errno
));
1136 } else /* reading network */
1138 istemp
= TRUE
; /* try to create temp file */
1139 lockout(TRUE
); /* block interrupts */
1142 /* Now try hard to find a temp file name. Try VERY hard. */
1144 /* try destination directory */
1145 path(to
, dpart
, fpart
);
1146 (void) sprintf(tname
, "%s/#%d.sup", dpart
, thispid
);
1147 tof
= open(tname
, (O_WRONLY
| O_CREAT
| O_TRUNC
| O_EXCL
), 0600);
1150 /* try sup directory */
1152 (void) chdir(thisC
->Cbase
);
1153 (void) sprintf(tname
, "sup/#%d.sup", thispid
);
1154 tof
= open(tname
, (O_WRONLY
| O_CREAT
| O_TRUNC
| O_EXCL
), 0600);
1157 (void) chdir(thisC
->Cprefix
);
1160 /* try base directory */
1161 (void) sprintf(tname
, "#%d.sup", thispid
);
1162 tof
= open(tname
, (O_WRONLY
| O_CREAT
| O_TRUNC
| O_EXCL
), 0600);
1164 (void) chdir(thisC
->Cprefix
);
1169 (void) sprintf(tname
, "/var/tmp/#%d.sup", thispid
);
1170 tof
= open(tname
, (O_WRONLY
| O_CREAT
| O_TRUNC
| O_EXCL
), 0600);
1175 (void) sprintf(tname
, "/usr/tmp/#%d.sup", thispid
);
1176 tof
= open(tname
, (O_WRONLY
| O_CREAT
| O_TRUNC
| O_EXCL
), 0600);
1181 (void) sprintf(tname
, "/tmp/#%d.sup", thispid
);
1182 tof
= open(tname
, (O_WRONLY
| O_CREAT
| O_TRUNC
| O_EXCL
), 0600);
1186 /* give up: try to create output file */
1188 tof
= open(to
, (O_WRONLY
| O_CREAT
| O_TRUNC
| O_EXCL
), 0600);
1192 notify("SUP: Can't create %s or temp file for it (%s)\n", to
,
1196 (void) close(fromf
);
1200 goaway("Can't skip file transfer");
1205 if (fromf
>= 0) { /* read file */
1206 x
= filecopy(fromf
, tof
);
1207 (void) close(fromf
);
1210 notify("SUP: Error in copying %s to %s\n", from
, to
);
1212 (void) unlink(tname
);
1216 } else { /* read network */
1222 x
= prereadcount(&fsize
);
1225 (void) unlink(tname
);
1229 goaway("Can't skip file transfer");
1230 goaway("Error in server space check");
1231 logquit(1, "Error in server space check");
1234 if (ioctl(tof
, FIOCFSPARAM
, (char *) &fsp
) < 0 &&
1237 (void) unlink(tname
);
1241 goaway("Can't skip file transfer");
1242 goaway("Error in disk space check");
1243 logquit(1, "Error in disk space check");
1246 fsize
= (fsize
+ 1023) / 1024;
1247 x
= fsp
.fsp_size
* MAX(fsp
.fsp_minfree
, 1) / 100;
1249 if (fsize
> MAX(fsp
.fsp_free
, 0)) {
1251 (void) unlink(tname
);
1255 goaway("Can't skip file transfer");
1256 goaway("No disk space for file %s", to
);
1257 logquit(1, "No disk space for file %s", to
);
1266 (void) unlink(tname
);
1268 goaway("Error in receiving %s\n", to
);
1271 if (!istemp
) { /* no temp file used */
1276 ** If the file is compressed, uncompress it in place. We open the
1277 ** temp file for reading, unlink the file, and then open the same
1278 ** file again for writing. Then we pipe through gzip. When
1279 ** finished the temp file contains the uncompressed version and we
1280 ** can continue as before.
1282 ** Since sup prefers to write close to the original file the
1283 ** benefits of atomic updates probably outweigh the cost of the
1284 ** extra filecopy which occurs when the temp file is on a different
1285 ** filesystem from the original.
1295 if ((infd
= open(tname
, O_RDONLY
, 0)) == -1 ||
1296 unlink(tname
) == -1 ||
1297 (outfd
= open(tname
, O_WRONLY
| O_CREAT
| O_TRUNC
| O_EXCL
, 0600)) == -1 ||
1298 runiofd(av
, infd
, outfd
, 2) != 0) {
1299 notify("SUP: Error in uncompressing file %s (%s)\n",
1301 (void) unlink(tname
);
1305 (void) close(outfd
);
1310 (void) close(outfd
);
1312 /* move to destination */
1313 if (rename(tname
, to
) == 0) {
1314 (void) unlink(tname
);
1318 fromf
= open(tname
, O_RDONLY
, 0);
1320 notify("SUP: Error in moving temp file to %s (%s)\n",
1321 to
, strerror(errno
));
1322 (void) unlink(tname
);
1326 tof
= open(to
, (O_WRONLY
| O_CREAT
| O_TRUNC
| O_EXCL
), 0600);
1328 (void) close(fromf
);
1329 notify("SUP: Can't create %s from temp file (%s)\n",
1330 to
, strerror(errno
));
1331 (void) unlink(tname
);
1335 x
= filecopy(fromf
, tof
);
1336 (void) close(fromf
);
1338 (void) unlink(tname
);
1341 notify("SUP: Error in storing data in %s\n", to
);
1346 /*** Finish connection with file server ***/
1351 char tname
[STRINGLENGTH
], fname
[STRINGLENGTH
];
1352 char relsufix
[STRINGLENGTH
];
1353 char collrelname
[STRINGLENGTH
];
1355 FILE *finishfile
; /* record of all filenames */
1357 if ((thisC
->Cflags
& CFURELSUF
) && release
) {
1358 (void) sprintf(relsufix
, ".%s", release
);
1359 (void) sprintf(collrelname
, "%s-%s", collname
, release
);
1362 (void) strcpy(collrelname
, collname
);
1364 dontjump
= TRUE
; /* once here, no more longjmp */
1365 (void) netcrypt((char *) NULL
);
1367 /* done with server */
1369 goaway((char *) NULL
);
1370 (void) requestend();
1372 tloc
= time((time_t *) NULL
);
1374 notify("SUP: Upgrade of %s aborted at %s",
1375 collrelname
, ctime(&tloc
) + 4);
1379 /* if we've not been blown off, make sure he is! */
1382 (void) requestend();
1385 if (thisC
->Cnogood
) {
1386 notify("SUP: Upgrade of %s completed with errors at %s",
1387 collrelname
, ctime(&tloc
) + 4);
1388 notify("SUP: Upgrade time will not be updated\n");
1392 done(FDONEUSRERROR
, "Completed with errors");
1393 (void) requestend();
1397 (void) chdir(thisC
->Cbase
);
1398 vnotify("SUP Upgrade of %s completed at %s",
1399 collrelname
, ctime(&tloc
) + 4);
1400 if (thisC
->Cflags
& CFLIST
) {
1404 done(FDONEDONTLOG
, "List only");
1405 (void) requestend();
1408 (void) sprintf(fname
, FILEWHEN
, collname
, relsufix
);
1409 if (establishdir(fname
)) {
1413 done(FDONEUSRERROR
, "Couldn't timestamp");
1414 (void) requestend();
1417 if (!putwhen(fname
, scantime
)) {
1418 notify("SUP: Can't record current time in %s (%s)\n",
1419 fname
, strerror(errno
));
1423 done(FDONEUSRERROR
, "Couldn't timestamp");
1424 (void) requestend();
1428 /* At this point we have let the server go */
1429 /* "I'm sorry, we've had to let you go" */
1430 done(FDONESUCCESS
, "Success");
1431 (void) requestend();
1433 (void) sprintf(tname
, FILELASTTEMP
, collname
, relsufix
);
1434 finishfile
= fopen(tname
, "w");
1435 if (finishfile
== NULL
) {
1436 notify("SUP: Can't record list of all files in %s\n", tname
);
1440 (void) Tprocess(lastT
, finishone
, finishfile
);
1441 (void) fclose(finishfile
);
1442 (void) sprintf(fname
, FILELAST
, collname
, relsufix
);
1443 if (rename(tname
, fname
) < 0)
1444 notify("SUP: Can't change %s to %s (%s)\n", tname
, fname
,
1446 (void) unlink(tname
);
1451 finishone(TREE
* t
, void *fv
)
1453 FILE *finishfile
= fv
;
1454 if ((thisC
->Cflags
& CFDELETE
) == 0 || (t
->Tflags
& FUPDATE
))
1455 fprintf(finishfile
, "%s\n", t
->Tname
);
1460 done(int value
, const char *fmt
, ...)
1462 char buf
[STRINGLENGTH
];
1466 (void) netcrypt((char *) NULL
);
1469 vsnprintf(buf
, sizeof(buf
), fmt
, ap
);
1474 goawayreason
= (fmt
) ? estrdup(buf
) : (char *) NULL
;
1478 donereason
= (fmt
) ? buf
: (char *) NULL
;
1482 longjmp(sjbuf
, TRUE
);
1486 goaway(const char *fmt
, ...)
1488 char buf
[STRINGLENGTH
];
1493 (void) netcrypt((char *) NULL
);
1495 vsnprintf(buf
, sizeof(buf
), fmt
, ap
);
1498 goawayreason
= NULL
;
1503 notify("SUP: %s\n", buf
);
1505 printf("SUP: %s\n", buf
);
1508 longjmp(sjbuf
, TRUE
);