[comp.unix.questions] shsem

rjk@mrstve.UUCP (Richard Kuhns) (10/11/87)

I realize that this isn't a source group, but shsem isn't very big
and I can't seem to contact a moderator (my mail bounces around
Purdue for while, then comes home to roost).  Enough people have
asked for it that I think it's worth posting, so here goes...

Do whatever you want with it, but please let me know of any bugs/fixes
(preferably fixes :-)

# This is a shell archive.  Remove anything before this line,
# then unpack it by saving it in a file and typing "sh file".
#
# Wrapped by rjk on Sun Oct 11 13:21:21 EST 1987
# Contents:  shsem.1l shsem.c
 
echo x - shsem.1l
sed 's/^@//' > "shsem.1l" <<'@//E*O*F shsem.1l//'
@.TH SHSEM 1
@.tr ~
@.SH NAME
shsem \- Utilize System V semaphores from the shell
@.SH SYNOPSIS
@.B shsem
[ -Vclruvw ] [ -d debug_level ] file_name
@.SH DESCRIPTION
@.I Shsem
is a program to allow shell scripts to specify `critical'
sections of code, using semaphores.
@.P
@.I Shsem
attempts to lock a semaphore whose id (see stdipc(3)) is determined
by the file name supplied.
It's an error if the file doesn't exist.
By default, the semaphore is created if it doesn't already exist.
@.P
The options are
@.TP
@.B \-l
lock semaphore, fail if semaphore is already locked.
@.TP
@.B \-w
lock semaphore, waiting if semaphore is already locked.
@.TP
@.B \-c
fail if the semaphore doesn't already exist (don't create it).
@.TP
@.B \-u
unlock semaphore.  Remove the semaphore if no one is waiting on it.
@.TP
@.B \-r
don't remove the semaphore on last unlock.
@.TP
@.B \-V
talk about what we're doing.
@.TP
@.B \-v
print the version.
@.TP
@.B \-d #
turn on debugging output, with a larger # ==> more output.
@.SH EXAMPLE
Since
@.I shsem
returns a succeeded/failed value when run, it
could be used in a shell script as follows:
@.nf

#!/bin/sh
if shsem -w /dev/lp
then
	echo We now have exclusive access to /dev/lp
else
	# Since we said to wait for the lock, failure implies
	# that /dev/lp doesn't exist, we've got too many
	# semaphores, or something else on the system level is wrong.
	echo Shsem failed, something is seriously wrong
fi
@.fi
@.SH BUGS
Shsem sometimes thinks there's no one waiting to lock a semaphore
when there really is.
However, since shsem tries to relock the semaphore before removing it,
we don't remove semaphores that are in use.
Look at the source code.
@.SH SEE ALSO
Various Semaphore System Calls
@.br
semctl(2), semget(2), semop(2)
@.SH VERSION
It varies...
@//E*O*F shsem.1l//
chmod u=rw,g=rw,o=rw shsem.1l
 
echo x - shsem.c
sed 's/^@//' > "shsem.c" <<'@//E*O*F shsem.c//'
static char *RCSid = "$Header: shsem.c,v 1.4 87/10/10 12:26:52 rjk Exp $\n";
/*
 * $Log:	shsem.c,v $
 * Revision 1.4  87/10/10  12:26:52  rjk
 * Added the 3 lines of code necessary for no_create... (oops)
 * 
 * Revision 1.3  87/08/14  17:43:54  rjk
 * Fooled with semctl stuff, fixed a bad error message
 * 
 * Revision 1.2  87/08/10  20:58:05  buhrt
 * newest version....
 * need to checkin to install old version
 * 
 * Revision 1.1  87/08/06  17:17:56  rjk
 * Initial revision
 * 
 */

#include <stdio.h>
#include <errno.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/sem.h>

typedef	int	FLAG;

#define	USAGE()	fprintf(stderr, \
			"usage:%s -Vcdulrvw file_name_to_lock\n", \
			progname)

#define BADOPTS() fprintf(stderr, \
		"%s:bad options: -l, -u and -w are mutually exclusive\n", \
			  progname);
#define FTOKID	('s')
#define LOCKIT	(-1)
#define UNLOCKIT	(1)

#ifndef	TRUE
#define	TRUE	1
#define	FALSE	0
#endif

/* exit statuses (for shell tests) */
#define	ERRORRET	1
#define	OKRET		0

extern char	*optarg;
extern int	optind, opterr;

char	*progname;

FLAG	debug_flag,		/* obvious */
	no_create,		/* don't create it if it doesn't exist */
	wlockit,		/* lock it, waiting if necessary */
	nwlockit,		/* lock it, don't wait */
	unlockit,		/* unlock it */
	errflag,		/* was there an error? */
	verbose,		/* talk about it */
	no_remove_sem;		/* don't remove when we're the last unlock */

int	SemId;			/* returned by semget */

int	debug_level;

key_t	SemKey;			/* our identifier */

main(argc, argv)
     int	argc;
     char	*argv[];
{
    int	c;

    progname = argv[0];		/* who i am */

    no_create = wlockit = nwlockit = unlockit = FALSE;
    while ((c = getopt(argc, argv, "Vcd:lruvw")) != -1) {
	switch (c) {
	  case 'V':
	    verbose = TRUE;
	    break;
	  case 'c':
	    no_create = TRUE;
	    break;
	  case 'd':			/* turn on debugging */
	    debug_flag++;
	    debug_level = atoi(optarg);
	    break;
	  case 'l':		/* lock it, no waiting */
	    if (wlockit || unlockit) {
		BADOPTS();
		errflag = TRUE;
	    }
	    else {
		nwlockit = TRUE;
	    }
	    break;
	  case 'r':		/* don't remove sem if noone's waiting */
	    no_remove_sem = TRUE;
	    break;
	  case 'u':		/* unlock it */
	    if (wlockit || nwlockit) {
		BADOPTS();
		errflag = TRUE;
	    }
	    else {
		unlockit = TRUE;
	    }
	    break;
	  case 'v':		/* version */
	    puts(RCSid);
	    break;
	  case 'w':
	    if (unlockit || nwlockit) {
		BADOPTS();
		errflag = TRUE;
	    }
	    else {
		wlockit = TRUE;
	    }
	    break;
	  case '?':			/* bad argument */
	    errflag = TRUE;
	    break;
	  default:			/* this can't happen! */
	    fprintf(stderr, "%s: BAD RETURN FROM GETOPT (%d)!\n", c);
	    exit(ERRORRET);
	}
	if (errflag) {
	    USAGE();
	    exit(ERRORRET);
	}
    }

    if ((argc - optind) != 1) {
	fprintf(stderr, "%s: bad arg count (filename required)\n",
	       progname);
	USAGE();
	exit(ERRORRET);
    }

    if (!wlockit && !nwlockit && !unlockit) {
	fprintf(stderr, "%s: one of -l, -u or -w is required\n",
		progname);
	USAGE();
	exit(ERRORRET);
    }

    if (no_remove_sem && !unlockit) {
	fprintf(stderr, "%s: -r only useful with -u, ignored\n",
		progname);
    }

    if (debug_flag) {
	fprintf(stderr, "+++attempting to get key for %s\n",
		argv[optind]);
    }

    if ((SemKey = ftok(argv[optind], FTOKID)) == (key_t) -1) {
	fprintf(stderr, "%s: %s does not exist or is not accessible\n",
		progname, argv[optind]);
	exit(ERRORRET);
    }

    /* we have a real key -- let's see if we can attach the semaphore... */

    if ((SemId = semget(SemKey, 0, 0)) == -1) {
	if (no_create) {	/* Don't even try to create it */
	    exit(ERRORRET);
	}
	if (debug_flag) {
	    fprintf(stderr,"+++creating semaphore\n");
	}
	/* Couldn't get it because... */
	if (errno == ENOENT) {	/* it doesn't exist */
	    /* so let's create it! */
	    if ((SemId = semget(SemKey, 1, IPC_CREAT | IPC_EXCL | 0666)) ==
		-1) {
		fprintf(stderr, "%s: can't create semaphore (errno = %d)\n",
			progname, errno);
		exit(ERRORRET);
	    }
	    /* Make sure the first time thru, it's unlocked */
	    if (!semcall(SemId, UNLOCKIT, (short)0)) {
		fprintf(stderr, "%s: can't initialize semaphore\n",
			progname);
		exit(ERRORRET);
	    }
	}
	else {			/* for now, any other reason is fatal */
	    fprintf(stderr, "%s: can't get/create semaphore (errno = %d)\n",
		    progname, errno);
	    exit(ERRORRET);
	}
    }
    if (unlockit) {
	if (!semcall(SemId, UNLOCKIT, (short)0)) {
	    fprintf(stderr, "%s: can't unlock semaphore\n",
		    progname);
	    exit(ERRORRET);
	}
	if (!no_remove_sem) {	/* let's see if anyone is waiting on it */
	    int		waiting;

	    if (debug_flag && debug_level > 5) {
		fprintf(stderr, "+++current value of semaphore: %d\n",
			semctl(SemId, 0, GETVAL, (unsigned *)NULL));
	    }
	    if ((waiting = semctl(SemId, 0, GETNCNT, (unsigned *)NULL))
		== 0) { /* Nope */
		/* first, try to lock it again WITHOUT waiting (just in case */
		/* someone else snuck in while we weren't looking) */
		if (semcall(SemId, LOCKIT, IPC_NOWAIT)) {
		    if (debug_flag) {
			fprintf(stderr, "+++Locked, ready to delete\n");
		    }
		    if (semctl(SemId, 0, IPC_RMID, (unsigned *)NULL) == -1) {
			if (debug_flag) {
			    fprintf(stderr, "+++Couldn't remove semaphore\n");
			}
		    }
		}
		else {
		    if (debug_flag) {
			fprintf(stderr, "+++Couldn't sneak a lock in\n");
		    }
		}
	    }
	}
    }	    
    else {
	if (wlockit) {
	    if (!semcall(SemId, LOCKIT, 0)) { /* couldn't lock it or wait */
		fprintf(stderr, "%s: can't lock semaphore or wait on it\n",
			progname);
		exit(ERRORRET);
	    }
	}
	else {
	    if (nwlockit) {
		if (!semcall(SemId, LOCKIT, IPC_NOWAIT)) {
		    exit(ERRORRET);
		}
	    }
	}
    }
    if (verbose) {
	printf("0x%x %s\n", SemId, unlockit ? "unlocked" : "locked");
    }
    exit(OKRET);
}

static int
semcall(sid, op, sflags)
  int	sid, op;
  short	sflags;
{
    struct sembuf	sb;

    sb.sem_num = 0;
    sb.sem_op = op;
    sb.sem_flg = sflags;
    if (semop(sid, &sb, 1) == -1) {
	if (errno == EAGAIN) {
	    return(FALSE);
	}
	else {
	    fprintf(stderr, "%s: semop error (errno = %d)\n",
		    progname, errno);
	}
    }
    return(TRUE);
}
@//E*O*F shsem.c//
chmod u=r,g=r,o=r shsem.c
 
exit 0
-- 
				       !pur-ee!pur-phy!mrstve!rjk
Rich Kuhns	{ihnp4, decvax, etc...}
				       !itivax!mrstve!rjk