/************************************************************************ * Whatever is needed for (un)locking files in various ways * * * * Copyright (c) 1990-1997, S.R. van den Berg, The Netherlands * * Copyright (c) 1998-2001, Philip Guenther, The United States * * of America * * #include "../README" * ************************************************************************/ #ifdef RCS static /*const*/char rcsid[]= "$Id: locking.c,v 1.63 2001/08/04 07:12:17 guenther Exp $"; #endif #include "procmail.h" #include "robust.h" #include "shell.h" #include "misc.h" #include "pipes.h" #include "foldinfo.h" #include "exopen.h" #include "locking.h" #include "lastdirsep.h" char*globlock; int lockit(name,lockp)char*name;char**const lockp; { int permanent=nfsTRY,triedforce=0,locktype=doLOCK;struct stat stbuf;time_t t; zombiecollect(); if(*lockp) { if(!strcmp(name,*lockp)) /* compare the previous lockfile to this one */ { free(name);return 1; /* they're equal, save yourself some effort */ } unlock(lockp); /* unlock any previous lockfile FIRST */ } /* to prevent deadlocks (I hate deadlocks) */ if(!*name) { free(name);return 1; } if(!strcmp(name,defdeflock)) /* is it the system mailbox lockfile? */ { locktype=doCHECK|doLOCK; if(sgid!=gid&&setegid(sgid)) /* try and get some extra permissions */ #ifndef fdlock if(!accspooldir) { yell("Bypassed locking",name); free(name);return 0; } #endif ; } for(lcking|=lck_DELAYSIG;;) { yell("Locking",name); /* in order to cater for clock skew: get */ if(!xcreat(name,LOCKperm,&t,locktype)) /* time t from the filesystem */ { *lockp=name; /* lock acquired, hurray! */ break; } switch(errno) { case EEXIST: /* check if it's time for a lock override */ if(!lstat(name,&stbuf)&&stbuf.st_size<=MAX_locksize&&locktimeout &&!lstat(name,&stbuf)&&locktimeout #endif #define REITflock 1 #else #define REITflock 0 #endif /* USEflock */ static int oldfdlock= -1; /* the fd we locked last */ #ifndef NOfcntl_lock static struct flock flck; /* why can't it be a local variable? */ #define REITfcntl 1 #else #define REITfcntl 0 #endif /* NOfcntl_lock */ #ifdef USElockf static off_t oldlockoffset; #define REITlockf 1 #else #define REITlockf 0 #endif /* USElockf */ int fdlock(fd)int fd; { int ret; if(verbose) nlog("Acquiring kernel-lock\n"); #if REITfcntl+REITflock+REITlockf>1 for(;!toutflag;verbose&&(nlog("Reiterating kernel-lock\n"),0), ssleep((unsigned)locksleep)) #endif { zombiecollect(); #ifdef USElockf oldlockoffset=tell(fd); #endif #ifndef NOfcntl_lock flck.l_type=F_WRLCK;flck.l_whence=SEEK_SET;flck.l_len=0; #ifdef USElockf flck.l_start=oldlockoffset; #else flck.l_start=tell(fd); #endif #endif lcking|=lck_KERNEL; #ifndef NOfcntl_lock ret=fcntl(fd,F_SETLKW,&flck); #ifdef USElockf if((ret|=lockf(fd,F_TLOCK,(off_t)0))&&(errno==EAGAIN||errno==EACCES|| errno==EWOULDBLOCK)) ufcntl: { flck.l_type=F_UNLCK;fcntl(fd,F_SETLK,&flck); continue; } #ifdef USEflock if((ret|=flock(fd,LOCK_EX|LOCK_NB))&&(errno==EAGAIN||errno==EACCES|| errno==EWOULDBLOCK)) { lockf(fd,F_ULOCK,(off_t)0); goto ufcntl; } #endif /* USEflock */ #else /* USElockf */ #ifdef USEflock if((ret|=flock(fd,LOCK_EX|LOCK_NB))&&(errno==EAGAIN||errno==EACCES|| errno==EWOULDBLOCK)) { flck.l_type=F_UNLCK;fcntl(fd,F_SETLK,&flck); continue; } #endif /* USEflock */ #endif /* USElockf */ #else /* NOfcntl_lock */ #ifdef USElockf ret=lockf(fd,F_LOCK,(off_t)0); #ifdef USEflock if((ret|=flock(fd,LOCK_EX|LOCK_NB))&&(errno==EAGAIN||errno==EACCES|| errno==EWOULDBLOCK)) { lockf(fd,F_ULOCK,(off_t)0); continue; } #endif /* USEflock */ #else /* USElockf */ #ifdef USEflock ret=flock(fd,LOCK_EX); #endif /* USEflock */ #endif /* USElockf */ #endif /* NOfcntl_lock */ oldfdlock=fd;lcking&=~lck_KERNEL; return ret; } return 1; /* timed out */ } int fdunlock P((void)) { int i; if(oldfdlock<0) return -1; i=0; #ifdef USEflock i|=flock(oldfdlock,LOCK_UN); #endif #ifdef USElockf ;{ off_t curp=tell(oldfdlock); /* restore the position later */ lseek(oldfdlock,oldlockoffset,SEEK_SET); i|=lockf(oldfdlock,F_ULOCK,(off_t)0);lseek(oldfdlock,curp,SEEK_SET); } #endif #ifndef NOfcntl_lock flck.l_type=F_UNLCK;i|=fcntl(oldfdlock,F_SETLK,&flck); #endif oldfdlock= -1; return i; } #endif /* fdlock */