From 152a15652d1c22f790d63ffc99951e02f2ea62cb Mon Sep 17 00:00:00 2001 From: Thomas Cort Date: Wed, 30 Oct 2013 08:24:41 -0400 Subject: [PATCH] Importing usr.bin/shlock No Minix-specific changes needed. Change-Id: I34fb2911de21f91b0c02387e57c3d6fd4502cd43 --- distrib/sets/lists/minix/mi | 2 + releasetools/nbsd_ports | 1 + usr.bin/Makefile | 4 +- usr.bin/shlock/Makefile | 5 + usr.bin/shlock/shlock.1 | 147 +++++++++++++++ usr.bin/shlock/shlock.c | 363 ++++++++++++++++++++++++++++++++++++ 6 files changed, 520 insertions(+), 2 deletions(-) create mode 100644 usr.bin/shlock/Makefile create mode 100644 usr.bin/shlock/shlock.1 create mode 100644 usr.bin/shlock/shlock.c diff --git a/distrib/sets/lists/minix/mi b/distrib/sets/lists/minix/mi index c93748113..123099ab8 100644 --- a/distrib/sets/lists/minix/mi +++ b/distrib/sets/lists/minix/mi @@ -474,6 +474,7 @@ ./usr/bin/seq minix-sys ./usr/bin/sha1 minix-sys ./usr/bin/shar minix-sys +./usr/bin/shlock minix-sys ./usr/bin/shuffle minix-sys ./usr/bin/shutdown minix-sys ./usr/bin/size minix-sys binutils @@ -2054,6 +2055,7 @@ ./usr/man/man1/sha1.1 minix-sys ./usr/man/man1/shar.1 minix-sys ./usr/man/man1/shift.1 minix-sys +./usr/man/man1/shlock.1 minix-sys ./usr/man/man1/shuffle.1 minix-sys ./usr/man/man1/size.1 minix-sys binutils ./usr/man/man1/sleep.1 minix-sys diff --git a/releasetools/nbsd_ports b/releasetools/nbsd_ports index 67274b92b..f7f898f4a 100644 --- a/releasetools/nbsd_ports +++ b/releasetools/nbsd_ports @@ -208,6 +208,7 @@ 2013/09/28 12:00:00,usr.bin/rev 2010/02/19 16:35:27,usr.bin/sed 2010/05/27 08:40:19,usr.bin/seq +2012/10/17 12:00:00,usr.bin/shlock 2013/06/02 12:00:00,usr.bin/shuffle 2012/10/17 12:00:00,usr.bin/sort 2012/10/17 12:00:00,usr.bin/split diff --git a/usr.bin/Makefile b/usr.bin/Makefile index 179cd9528..6f9b2663d 100644 --- a/usr.bin/Makefile +++ b/usr.bin/Makefile @@ -23,8 +23,8 @@ SUBDIR= asa \ printenv printf pwhash \ renice rev \ \ - shuffle sed seq \ - sort split stat su \ + sed seq shlock \ + shuffle sort split stat su \ tee tic tput \ tr tsort tty unexpand unifdef \ toproto \ diff --git a/usr.bin/shlock/Makefile b/usr.bin/shlock/Makefile new file mode 100644 index 000000000..cb3e21c28 --- /dev/null +++ b/usr.bin/shlock/Makefile @@ -0,0 +1,5 @@ +# $NetBSD: Makefile,v 1.4 2009/04/14 22:15:26 lukem Exp $ + +PROG= shlock + +.include diff --git a/usr.bin/shlock/shlock.1 b/usr.bin/shlock/shlock.1 new file mode 100644 index 000000000..9b1667dc7 --- /dev/null +++ b/usr.bin/shlock/shlock.1 @@ -0,0 +1,147 @@ +.\" $NetBSD: shlock.1,v 1.11 2008/04/30 13:11:01 martin Exp $ +.\" +.\" Copyright (c) 2006 The NetBSD Foundation, Inc. +.\" All rights reserved. +.\" +.\" This code is derived from software contributed to The NetBSD Foundation +.\" by Erik E. Fair. +.\" +.\" Redistribution and use in source and binary forms, with or without +.\" modification, are permitted provided that the following conditions +.\" are met: +.\" 1. Redistributions of source code must retain the above copyright +.\" notice, this list of conditions and the following disclaimer. +.\" 2. Redistributions in binary form must reproduce the above copyright +.\" notice, this list of conditions and the following disclaimer in the +.\" documentation and/or other materials provided with the distribution. +.\" +.\" THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS +.\" ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED +.\" TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +.\" PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS +.\" BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +.\" CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +.\" SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +.\" INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +.\" CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +.\" ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +.\" POSSIBILITY OF SUCH DAMAGE. +.\" +.Dd June 29, 1997 +.Dt SHLOCK 1 +.Os +.Sh NAME +.Nm shlock +.Nd create or verify a lock file for shell scripts +.Sh SYNOPSIS +.Nm +.Op Fl du +.Op Fl p Ar PID +.Fl f +.Ar lockfile +.Sh DESCRIPTION +The +.Nm +command can create or verify a lock file on behalf of a shell or +other script program. +When it attempts to create a lock file, if one already exists, +.Nm +verifies that it is or is not valid. +If valid, +.Nm +will exit with a non-zero exit code. +If invalid, +.Nm +will remove the lock file, and +create a new one. +.Pp +.Nm +uses the +.Xr link 2 +system call to make the final target lock file, which is an atomic +operation (i.e. "dot locking", so named for this mechanism's original +use for locking system mailboxes). +It puts the process ID ("PID") from the command line into the +requested lock file. +.Pp +.Nm +verifies that an extant lock file is still valid by +using +.Xr kill 2 +with a zero signal to check for the existence of the process that +holds the lock. +.Pp +The +.Fl d +option causes +.Nm +to be verbose about what it is doing. +.Pp +The +.Fl f +argument with +.Ar lockfile +is always required. +.Pp +The +.Fl p +option with +.Ar PID +is given when the program is to create a lock file; when absent, +.Nm +will simply check for the validity of the lock file. +.Pp +The +.Fl u +option causes +.Nm +to read and write the PID as a binary pid_t, instead of as ASCII, +to be compatible with the locks created by UUCP. +.Sh EXIT STATUS +A zero exit code indicates a valid lock file. +.Sh EXAMPLES +.Ss BOURNE SHELL +.Bd -literal +#!/bin/sh +lckfile=/tmp/foo.lock +if shlock -f ${lckfile} -p $$ +then +# do what required the lock + rm ${lckfile} +else + echo Lock ${lckfile} already held by `cat ${lckfile}` +fi +.Ed +.Ss C SHELL +.Bd -literal +#!/bin/csh -f +set lckfile=/tmp/foo.lock +shlock -f ${lckfile} -p $$ +if ($status == 0) then +# do what required the lock + rm ${lckfile} +else + echo Lock ${lckfile} already held by `cat ${lckfile}` +endif +.Ed +.Pp +The examples assume that the file system where the lock file is to +be created is writable by the user, and has space available. +.Sh HISTORY +.Nm +was written for the first Network News Transfer Protocol (NNTP) +software distribution, released in March 1986. +The algorithm was suggested by Peter Honeyman, +from work he did on HoneyDanBer UUCP. +.Sh AUTHORS +.An Erik E. Fair Aq fair@clock.org +.Sh BUGS +Does not work on NFS or other network file system on different +systems because the disparate systems have disjoint PID spaces. +.Pp +Cannot handle the case where a lock file was not deleted, the +process that created it has exited, and the system has created a +new process with the same PID as in the dead lock file. +The lock file will appear to be valid even though the process is +unrelated to the one that created the lock in the first place. +Always remove your lock files after you're done. diff --git a/usr.bin/shlock/shlock.c b/usr.bin/shlock/shlock.c new file mode 100644 index 000000000..51231000c --- /dev/null +++ b/usr.bin/shlock/shlock.c @@ -0,0 +1,363 @@ +/* $NetBSD: shlock.c,v 1.12 2011/09/06 18:30:38 joerg Exp $ */ + +/*- + * Copyright (c) 2006 The NetBSD Foundation, Inc. + * All rights reserved. + * + * This code is derived from software contributed to The NetBSD Foundation + * by Erik E. Fair. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +/* +** Program to produce reliable locks for shell scripts. +** Algorithm suggested by Peter Honeyman, January 1984, +** in connection with HoneyDanBer UUCP. +** +** I tried extending this to handle shared locks in November 1987, +** and ran into to some fundamental problems: +** +** Neither 4.3 BSD nor System V have an open(2) with locking, +** so that you can open a file and have it locked as soon as +** it's real; you have to make two system calls, and there's +** a race... +** +** When removing dead process id's from a list in a file, +** you need to truncate the file (you don't want to create a +** new one; see above); unfortunately for the portability of +** this program, only 4.3 BSD has ftruncate(2). +** +** Erik E. Fair , November 8, 1987 +** +** Extensions for UUCP style locks (i.e. pid is an int in the file, +** rather than an ASCII string). Also fix long standing bug with +** full file systems and temporary files. +** +** Erik E. Fair , November 12, 1989 +** +** ANSIfy the code somewhat to make gcc -Wall happy with the code. +** Submit to NetBSD +** +** Erik E. Fair , May 20, 1997 +*/ + +#include + +#ifndef lint +__RCSID("$NetBSD: shlock.c,v 1.12 2011/09/06 18:30:38 joerg Exp $"); +#endif + +#include +#include +#include /* Needed on hpux */ +#include +#include +#include +#include +#include +#include + +#define LOCK_SET 0 +#define LOCK_FAIL 1 + +#define LOCK_GOOD 0 +#define LOCK_BAD 1 + +#define FAIL (-1) + +#define TRUE 1 +#define FALSE 0 + +static int Debug = FALSE; +static char *Pname; +static const char USAGE[] = "%s: USAGE: %s [-du] [-p PID] -f file\n"; +static const char E_unlk[] = "%s: unlink(%s): %s\n"; +static const char E_open[] = "%s: open(%s): %s\n"; + +#define dprintf if (Debug) printf + +/* +** Prototypes to make the ANSI compilers happy +** Didn't lint used to do type and argument checking? +** (and wasn't that sufficient?) +*/ + +/* the following is in case you need to make the prototypes go away. */ +static char *xtmpfile(char *, pid_t, int); +static int p_exists(pid_t); +static int cklock(char *, int); +static int mklock(char *, pid_t, int); +__dead static void bad_usage(void); + +/* +** Create a temporary file, all ready to lock with. +** The file arg is so we get the filename right, if he +** gave us a full path, instead of using the current directory +** which might not be in the same filesystem. +*/ +static char * +xtmpfile(char *file, pid_t pid, int uucpstyle) +{ + int fd; + int len; + char *cp, buf[BUFSIZ]; + static char tempname[BUFSIZ]; + + sprintf(buf, "shlock%ld", (u_long)getpid()); + if ((cp = strrchr(strcpy(tempname, file), '/')) != NULL) { + *++cp = '\0'; + (void) strcat(tempname, buf); + } else + (void) strcpy(tempname, buf); + dprintf("%s: temporary filename: %s\n", Pname, tempname); + + sprintf(buf, "%ld\n", (u_long)pid); + len = strlen(buf); +openloop: + if ((fd = open(tempname, O_RDWR|O_CREAT|O_EXCL, 0644)) < 0) { + switch(errno) { + case EEXIST: + dprintf("%s: file %s exists already.\n", + Pname, tempname); + if (unlink(tempname) < 0) { + fprintf(stderr, E_unlk, + Pname, tempname, strerror(errno)); + return (NULL); + } + /* + ** Further profanity + */ + goto openloop; + default: + fprintf(stderr, E_open, + Pname, tempname, strerror(errno)); + return (NULL); + } + } + + /* + ** Write the PID into the temporary file before attempting to link + ** to the actual lock file. That way we have a valid lock the instant + ** the link succeeds. + */ + if (uucpstyle ? + (write(fd, &pid, sizeof(pid)) != sizeof(pid)) : + (write(fd, buf, len) < 0)) + { + fprintf(stderr, "%s: write(%s,%ld): %s\n", + Pname, tempname, (u_long)pid, strerror(errno)); + (void) close(fd); + if (unlink(tempname) < 0) { + fprintf(stderr, E_unlk, + Pname, tempname, strerror(errno)); + } + return (NULL); + } + (void) close(fd); + return(tempname); +} + +/* +** Does the PID exist? +** Send null signal to find out. +*/ +static int +p_exists(pid_t pid) +{ + dprintf("%s: process %ld is ", Pname, (u_long)pid); + if (pid <= 0) { + dprintf("invalid\n"); + return(FALSE); + } + if (kill(pid, 0) < 0) { + switch(errno) { + case ESRCH: + dprintf("dead\n"); + return(FALSE); /* pid does not exist */ + case EPERM: + dprintf("alive\n"); + return(TRUE); /* pid exists */ + default: + dprintf("state unknown: %s\n", strerror(errno)); + return(TRUE); /* be conservative */ + } + } + dprintf("alive\n"); + return(TRUE); /* pid exists */ +} + +/* +** Check the validity of an existing lock file. +** +** Read the PID out of the lock +** Send a null signal to determine whether that PID still exists +** Existence (or not) determines the validity of the lock. +** +** Two bigs wins to this algorithm: +** +** o Locks do not survive crashes of either the system or the +** application by any appreciable period of time. +** +** o No clean up to do if the system or application crashes. +** +*/ +static int +cklock(char *file, int uucpstyle) +{ + int fd = open(file, O_RDONLY); + ssize_t len; + pid_t pid; + char buf[BUFSIZ]; + + dprintf("%s: checking extant lock <%s>\n", Pname, file); + if (fd < 0) { + if (errno != ENOENT) + fprintf(stderr, E_open, Pname, file, strerror(errno)); + return(TRUE); /* might or might not; conservatism */ + } + + if (uucpstyle ? + ((len = read(fd, &pid, sizeof(pid))) != sizeof(pid)) : + ((len = read(fd, buf, sizeof(buf))) <= 0)) + { + close(fd); + dprintf("%s: lock file format error\n", Pname); + return(FALSE); + } + close(fd); + buf[len + 1] = '\0'; + return(p_exists(uucpstyle ? pid : atoi(buf))); +} + +static int +mklock(char *file, pid_t pid, int uucpstyle) +{ + char *tmp; + int retcode = FALSE; + + dprintf("%s: trying lock <%s> for process %ld\n", Pname, file, + (u_long)pid); + if ((tmp = xtmpfile(file, pid, uucpstyle)) == NULL) + return(FALSE); + +linkloop: + if (link(tmp, file) < 0) { + switch(errno) { + case EEXIST: + dprintf("%s: lock <%s> already exists\n", Pname, file); + if (cklock(file, uucpstyle)) { + dprintf("%s: extant lock is valid\n", Pname); + break; + } else { + dprintf("%s: lock is invalid, removing\n", + Pname); + if (unlink(file) < 0) { + fprintf(stderr, E_unlk, + Pname, file, strerror(errno)); + break; + } + } + /* + ** I hereby profane the god of structured programming, + ** Edsgar Dijkstra + */ + goto linkloop; + default: + fprintf(stderr, "%s: link(%s, %s): %s\n", + Pname, tmp, file, strerror(errno)); + break; + } + } else { + dprintf("%s: got lock <%s>\n", Pname, file); + retcode = TRUE; + } + if (unlink(tmp) < 0) { + fprintf(stderr, E_unlk, Pname, tmp, strerror(errno)); + } + return(retcode); +} + +static void +bad_usage(void) +{ + fprintf(stderr, USAGE, Pname, Pname); + exit(LOCK_FAIL); +} + +int +main(int ac, char **av) +{ + int x; + char *file = NULL; + pid_t pid = 0; + int uucpstyle = FALSE; /* indicating UUCP style locks */ + int only_check = TRUE; /* don't make a lock */ + + Pname = ((Pname = strrchr(av[0], '/')) ? Pname + 1 : av[0]); + + for(x = 1; x < ac; x++) { + if (av[x][0] == '-') { + switch(av[x][1]) { + case 'u': + uucpstyle = TRUE; + break; + case 'd': + Debug = TRUE; + break; + case 'p': + if (strlen(av[x]) > 2) { + pid = atoi(&av[x][2]); + } else { + if (++x >= ac) { + bad_usage(); + } + pid = atoi(av[x]); + } + only_check = FALSE; /* wants one */ + break; + case 'f': + if (strlen(av[x]) > 2) { + file = &av[x][2]; + } else { + if (++x >= ac) { + bad_usage(); + } + file = av[x]; + } + break; + default: + bad_usage(); + } + } + } + + if (file == NULL || (!only_check && pid <= 0)) { + bad_usage(); + } + + if (only_check) { + exit(cklock(file, uucpstyle) ? LOCK_GOOD : LOCK_BAD); + } + + exit(mklock(file, pid, uucpstyle) ? LOCK_SET : LOCK_FAIL); +}