/*
* File: lock.c
*
* Author: Ulli Horlacher (framstag@belwue.de)
*
* History:
*
* 1995-11-02 Framstag initial version
* 1998-09-29 Framstag print also PID on testing
*
* This program sets or tests advisory locks conforming to POSIX fcntl() call.
* You may specify optionaly a program to start it within the lock context.
*
* Copyright © 1997 Ulli Horlacher
* This file is covered by the GNU General Public License
*/
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/wait.h>
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <fcntl.h>
#include <stdlib.h>
#include <limits.h>
#include <errno.h>
/* some systems are missing the getopt declarations */
int getopt(int, char * const *, const char *);
extern int opterr;
extern int optind;
extern int optopt;
extern char *optarg;
/* write-lock a file (POSIX conform) */
int wlock_file(int,int,int);
/* test if a file is write-lock blocked (POSIX conform) */
int tlock_file(int,int,int);
/* print short usage message */
void usage();
char *prg; /* name of the game */
int main(int argc, char *argv[]) {
int i, /* simple loop counter */
status, /* return status */
lockf, /* lock file descriptor */
opt, /* getopt return value */
verbose, /* flag for verbose mode */
begin, /* begin of locking area */
length, /* length of locking area */
seconds, /* seconds to sleep */
pid; /* process ID */
char type; /* locl-type: testing or seting */
char cmd[32768]; /* program and arguments to start */
char *cp, /* simple string pointer */
*file, /* file to lock */
*nprg; /* next program to start */
verbose=0;
begin=0;
length=0;
seconds=86400;
strcpy(cmd,"");
type='t'; /* default: test lock */
/* what's my name? */
prg=argv[0];
if ((cp=strrchr(prg,'/'))) prg=cp+1;
while ((opt=getopt(argc, argv, "vtsb:l:a:")) > 0) {
switch (opt) {
case ':':
case 'h':
case '?': usage();
case 't': type='t'; break;
case 's': type='s'; break;
case 'v': verbose=1; break;
case 'b': begin=atoi(optarg); break;
case 'l': length=atoi(optarg); break;
case 'a': seconds=atoi(optarg); break;
}
}
if (argc-optind==0) usage();
file=argv[optind];
nprg=argv[optind+1];
/* try to open file */
if ((lockf=open(file,O_WRONLY|O_APPEND,S_IRUSR|S_IWUSR))<0) {
fprintf(stderr,"%s: cannot open %s : %s\n",prg,file,strerror(errno));
exit(1);
}
/* set lock mode? */
if (type=='s') {
/* try to lock it */
status=wlock_file(lockf,begin,length);
/* lock failed */
if (status<0) {
if (verbose) fprintf(stderr,"%s: lock failed for %s\n",prg,file);
exit(2);
}
/* next program specified? */
if (nprg) {
/* build command string */
for (i=optind+1;argv[i];i++) {
strcat(cmd," ");
strcat(cmd,argv[i]);
}
/* create subprocess */
pid=fork();
if (pid<0) {
fprintf(stderr,"%s: cannot create subprocess : %s\n",prg,strerror(errno));
exit(2);
}
/* in subprocess start next program */
if (pid==0) {
if (verbose) fprintf(stderr,"%s: executing %s\n",prg,cmd);
execvp(nprg,&argv[optind+1]);
fprintf(stderr,"%s: cannot execute %s : %s\n",prg,nprg,strerror(errno));
exit(2);
}
/* that's it */
wait(NULL);
exit(0);
}
/* sleep for some time to keep lock alive */
if (verbose) fprintf(stderr,"%s: lock ok for %s, "
"going into sleep for %d seconds\n",
prg,file,seconds);
sleep(seconds);
exit(0);
/* only test the file for locks */
} else {
status=tlock_file(lockf,begin,length);
if (status<0) {
if (verbose) fprintf(stderr,"%s: testing lock for %s failed!\n",prg,file);
exit(2);
}
if (status==0) {
if (verbose) fprintf(stderr,"%s: no lock for %s\n",prg,file);
exit(1);
}
if (status>0) {
if (verbose) fprintf(stderr,"%s: %s is locked by PID %d\n",
prg,file,status);
exit(0);
}
}
exit(0);
}
/*
* wlock_file - write-lock a file (POSIX conform)
*
* INPUT: file descriptor
*
* RETURN: >= 0 if ok, -1 if error
*/
int wlock_file(int fd, int begin, int length) {
struct flock lock; /* file locking structure */
/* fill out the file locking structure */
lock.l_type=F_WRLCK;
lock.l_start=begin;
lock.l_whence=SEEK_SET;
lock.l_len=length;
/* try to lock the file and return the status */
return(fcntl(fd,F_SETLK,&lock));
}
/*
* tlock_file - test if a file is write-lock blocked (POSIX conform)
*
* INPUT: fd - file descriptor
*
* RETURN: 0 if no lock, lock-PID if locked, -1 on error
*/
int tlock_file(int fd, int begin, int length) {
int status;
struct flock lock; /* file locking structure */
/* fill out the file locking structure */
lock.l_type=F_WRLCK;
lock.l_start=begin;
lock.l_whence=SEEK_SET;
lock.l_len=length;
/* test the lock status */
status=fcntl(fd,F_GETLK,&lock);
if (status>=0) status=(lock.l_type!=F_UNLCK);
if (status>0) status=lock.l_pid;
return(status);
}
void usage() {
printf("purpose: \"%s\" tests or sets a file with POSIX-fcntl() write-locks\n",prg);
printf("usage: %s [-s [-a seconds]] [-t] [-v] [-b begin] [-l length] file\n",prg);
printf(" or: %s -s [-v] [-b begin] [-l length] file program [arguments]\n",prg);
printf("options: -t test lock (default)\n");
printf(" -s set lock\n");
printf(" -v verbose output\n");
printf(" -b lock starts at byte #begin\n");
printf(" -l lock is #length byte long\n");
printf(" -a lock is #seconds active (default=86400)\n");
printf(" program ... start this program within the lock context\n");
printf("Exit status: test: 0 = locked, 1 = not locked, 2 = error\n");
printf(" set: 0 = lock was successful, 2 = error\n");
printf("Examples: %s -s -v log_file\n",prg);
printf(" %s -s -b 10 log_file vi log_file\n",prg);
printf(" %s -vt log_file\n",prg);
exit(0);
}
syntax highlighted by Code2HTML, v. 0.9.1