/* Copyright (c) 2003 Michael B. Allen <mba2000 ioplex.com>
*
* The MIT License
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
* and/or sell copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included
* in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR
* OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
* ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
* OTHER DEALINGS IN THE SOFTWARE.
*/
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <stdarg.h>
#include <errno.h>
#include <limits.h>
#include <fcntl.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/ipc.h>
#include <sys/sem.h>
#include <sys/mman.h>
#include <sys/wait.h>
#define FLAGS_SEMUNDO 1
#define IS_SEMUNDO(cmd) (((cmd)->flags & FLAGS_SEMUNDO) != 0)
#define MAX_TRIES 3
#define MAX_SEMNUM 255
#define CMD_OPEN 1
#define CMD_REMOVE 2
#define CMD_POST 3
#define CMD_POSTMULT 4
#define CMD_WAIT 5
#define CMD_TRYWAIT 6
#define CMD_SETVAL 7
#define CMD_GETVAL 8
#define ERR(e,s) fprintf(stderr, "%d: %s: %s\n", __LINE__, (s), strerror(e))
static const char *cmdnames[] = {
NULL, "CMD_OPEN", "CMD_REMOVE", "CMD_POST", "CMD_POSTMULT",
"CMD_WAIT", "CMD_TRYWAIT", "CMD_SETVAL", "CMD_GETVAL"
};
static const char *cmdflags = " orpmwtsg";
struct cmd {
int cmd;
int semid;
int semnum;
const char *path;
int flags;
int oflags;
mode_t mode;
int numsems;
int value;
};
void
cmd_print(struct cmd *cmd)
{
char oflags[255], *o = oflags;
*o = '\0';
if ((cmd->oflags & O_CREAT))
o += sprintf(o, "O_CREAT");
if ((cmd->oflags & O_EXCL))
o += sprintf(o, "|O_EXCL");
fprintf(stderr,
"%s: semid: %d semnum: %d path: %s flags: %s oflags: %s mode: %04o numsems: %d value: %d\n",
cmdnames[cmd->cmd], cmd->semid, cmd->semnum, cmd->path ? cmd->path : "",
IS_SEMUNDO(cmd) ? "FLAGS_SEMUNDO" : "", oflags, cmd->mode, cmd->numsems, cmd->value);
}
#if defined(_SEM_SEMUN_UNDEFINED) || \
(defined(__digital__) && defined(__unix__)) || \
defined(_HPUX_SOURCE)
union semun {
int val; /* value for SETVAL */
struct semid_ds *buf; /* buffer for IPC_STAT, IPC_SET */
unsigned short *array; /* array for GETALL, SETALL */
/* Linux specific part: */
struct seminfo *__buf; /* buffer for IPC_INFO */
};
#endif
/* Open or create a semaphore initializing it as necessary.
*/
static int
semid_get(const char *name, int nsems, int oflags, mode_t mode, int value)
{
key_t key;
int max;
if (nsems > MAX_SEMNUM) {
ERR(errno = ERANGE, "semid_get");
return -1;
}
if ((key = ftok((char *)name, 1)) == (key_t)-1) {
ERR(errno, "ftok");
return -1;
}
/* This following loop ensures that we know if the semaphore was created
* as opposed to just opened so that it can be initialized properly. We
* do this by alternating between oflags 0 and IPC_CREATE|IPC_EXCL until
* one succeeds.
*/
for (max = MAX_TRIES; max; max--) {
int semid;
union semun arg;
if ((oflags & O_EXCL) == 0) {
if ((semid = semget(key, nsems, 0)) != -1) {
struct semid_ds buf;
/* This inner try-loop ensures that the semaphore is initialized before
* we return even if the semaphore has been created with semget but not
* yet initialized with semctl. See Stevens' UNPv2 p274.
*/
arg.buf = &buf;
for (max = MAX_TRIES; max; max--) {
if (semctl(semid, 0, IPC_STAT, arg) == -1) {
ERR(errno, "semctl");
return -1;
}
if (buf.sem_otime != 0) {
return semid;
}
sleep(1);
}
ERR(errno = ETIMEDOUT, "semid_get");
return -1;
} else if (errno != ENOENT) {
ERR(errno, "semget");
return -1;
}
}
if ((semid = semget(key, nsems, IPC_CREAT | IPC_EXCL | (mode & 0777))) != -1) {
struct sembuf initop;
if (nsems > 1) {
unsigned short array[MAX_SEMNUM * sizeof(unsigned short)];
int i;
arg.array = array;
arg.array[0] = 0; /* leave the first one 0 to be set with semop */
for (i = 1; i < nsems; i++) {
arg.array[i] = value;
}
if (semctl(semid, 0, SETALL, arg) == -1) {
ERR(errno, "semctl");
semctl(semid, 0, IPC_RMID);
return -1;
}
} else {
arg.val = 0;
if (semctl(semid, 0, SETVAL, arg) == -1) {
ERR(errno, "semctl");
semctl(semid, 0, IPC_RMID);
return -1;
}
}
/* increment by value to set sem_otime nonzero */
initop.sem_num = 0;
initop.sem_op = value;
initop.sem_flg = 0;
if (semop(semid, &initop, 1) == -1) {
ERR(errno, "semop");
semctl(semid, 0, IPC_RMID);
return -1;
}
return semid;
} else if ((oflags & O_EXCL) || errno != EEXIST) {
ERR(errno, "semget");
return -1;
}
}
ERR(errno = ETIMEDOUT, "semid_get");
return -1;
}
static int
cmd_open(struct cmd *cmd)
{
int semid, fd = 0;
char oflags[255], *o = oflags;
if ((cmd->oflags & O_CREAT) && (fd = open(cmd->path, O_CREAT, cmd->mode)) == -1) {
ERR(errno, "open");
return -1;
}
if ((semid = semid_get(cmd->path, cmd->numsems, cmd->oflags, cmd->mode, cmd->value)) == -1) {
return -1;
}
*o = '\0';
if ((cmd->oflags & O_CREAT))
o += sprintf(o, "O_CREAT");
if ((cmd->oflags & O_EXCL))
o += sprintf(o, "|O_EXCL");
fprintf(stderr, "%d = cmd_open(\"%s\", %d, %s, %04o, %d)\n",
semid, cmd->path, cmd->numsems, oflags, cmd->mode, cmd->value);
if (fd) close(fd);
printf("%d", semid);
return 0;
}
static int
cmd_remove(struct cmd *cmd)
{
fprintf(stderr, "cmd_remove(%d)\n", cmd->semid);
if (semctl(cmd->semid, 0, IPC_RMID) == -1) {
ERR(errno, "semctl");
return -1;
}
return 0;
}
static int
cmd_wait(struct cmd *cmd)
{
struct sembuf wait;
wait.sem_num = cmd->semnum;
wait.sem_op = -1;
wait.sem_flg = IS_SEMUNDO(cmd) ? SEM_UNDO : 0;
fprintf(stderr, "cmd_wait(%d, %d, %s)\n",
cmd->semid, cmd->semnum, IS_SEMUNDO(cmd) ? "SEM_UNDO" : "0");
if (semop(cmd->semid, &wait, 1) == -1) {
ERR(errno, "semop");
return -1;
}
return 0;
}
static int
cmd_trywait(struct cmd *cmd)
{
struct sembuf wait;
wait.sem_num = cmd->semnum;
wait.sem_op = -1;
wait.sem_flg = IPC_NOWAIT | (IS_SEMUNDO(cmd) ? SEM_UNDO : 0);
fprintf(stderr, "cmd_wait(%d, %d, %s)\n",
cmd->semid, cmd->semnum, IS_SEMUNDO(cmd) ? "IPC_NOWAIT|SEM_UNDO" : "IPC_NOWAIT");
if (semop(cmd->semid, &wait, 1) == -1) {
ERR(errno, "semop");
return errno == EAGAIN ? 0 : -1;
}
return 0;
}
static int
cmd_post(struct cmd *cmd)
{
struct sembuf post;
post.sem_num = cmd->semnum;
post.sem_op = 1;
post.sem_flg = IS_SEMUNDO(cmd) ? SEM_UNDO : 0;
fprintf(stderr, "cmd_post(%d, %d, %s)\n",
cmd->semid, cmd->semnum, IS_SEMUNDO(cmd) ? "SEM_UNDO" : "0");
if (semop(cmd->semid, &post, 1) == -1) {
ERR(errno, "semop");
return -1;
}
return 0;
}
static int
cmd_post_multiple(struct cmd *cmd)
{
struct sembuf post;
int count = cmd->value;
post.sem_num = cmd->semnum;
post.sem_op = 1;
post.sem_flg = IS_SEMUNDO(cmd) ? SEM_UNDO : 0;
fprintf(stderr, "cmd_post_multiple(%d, %d, %s, %d)\n",
cmd->semid, cmd->semnum, IS_SEMUNDO(cmd) ? "SEM_UNDO" : "0", count);
while (count--) {
if (semop(cmd->semid, &post, 1) == -1) {
ERR(errno, "semop");
return -1;
}
}
return 0;
}
static int
cmd_getvalue(struct cmd *cmd)
{
int v;
if ((v = semctl(cmd->semid, cmd->semnum, GETVAL, 0)) == -1) {
ERR(errno, "semctl");
return -1;
}
fprintf(stderr, "%d = cmd_getvalue(%d, %d, GETVAL)\n", v, cmd->semid, cmd->semnum);
return 0;
}
static int
cmd_setvalue(struct cmd *cmd)
{
union semun arg;
arg.val = cmd->value;
if (semctl(cmd->semid, cmd->semnum, SETVAL, arg) == -1) {
ERR(errno,"semctl");
return -1;
}
fprintf(stderr, "cmd_setvalue(%d, %d, SETVAL)\n", cmd->semid, cmd->semnum);
return 0;
}
typedef int (*cmd_fn)(struct cmd *cmd);
cmd_fn cmd_functions[] = {
NULL,
cmd_open,
cmd_remove,
cmd_post,
cmd_post_multiple,
cmd_wait,
cmd_trywait,
cmd_setvalue,
cmd_getvalue
};
int
main(int argc, char *argv[])
{
struct cmd cmd;
if (argc < 4) {
usage:
fprintf(stderr, "usage: semc <path|semid and semnum> -o|r|p|m|w|t|g|s <cmdargs>\n"
" EXAMPLES:\n"
" open: semc /tmp/foo -o 1 1 777 250 99\n"
/* argv: 0 1 2 3 4 5 6 7 */
" remove: semc 5441 I -r\n"
" post: semc 5441 3 -p\n"
"post multiple: semc 5441 3 -m 10\n"
" wait: semc 5441 3 -w\n"
" try wait: semc 5441 3 -t\n"
" get: semc 5441 3 -g\n"
" set: semc 5441 3 -s 99\n");
fputs("The -o command takes a path and command arguments O_CREAT and/or "
"O_EXCL and/or 0, mode_t in octal, numsems, and the value with which "
"the semaphore(s) should be initialize. All other commands take a semid "
"and semnum but -r igores semnum.\n", stderr);
return EXIT_FAILURE;
}
memset(&cmd, 0, sizeof cmd);
/* Determine Command */
if (strcmp(argv[2], "-o") == 0) {
cmd.cmd = CMD_OPEN;
} else if (*argv[3] == '-') {
char *ch = strchr(cmdflags, argv[3][1]);
if (!ch) {
fprintf(stderr, "Invalid option: %s\n", argv[2]);
goto usage;
}
cmd.cmd = ch - cmdflags;
} else {
fprintf(stderr, "Invalid options\n");
goto usage;
}
/* Populate struct cmd with Arguments */
switch (cmd.cmd) {
case CMD_OPEN:
if (argc < 8) {
fprintf(stderr, "Too few arguments for command\n");
goto usage;
}
cmd.path = argv[1];
if (*argv[3] == '1') cmd.oflags |= O_CREAT;
if (*argv[4] == '1') cmd.oflags |= O_EXCL;
{
unsigned long mode;
if ((mode = strtoul(argv[5], NULL, 8)) == ULONG_MAX) {
ERR(errno, "strtoul");
goto usage;
}
cmd.mode = mode;
}
if ((unsigned long)(cmd.numsems = strtoul(argv[6], NULL, 0)) == ULONG_MAX) {
ERR(errno, "strtoul");
goto usage;
}
if ((unsigned long)(cmd.value = strtoul(argv[7], NULL, 0)) == ULONG_MAX) {
ERR(errno, "strtoul");
goto usage;
}
break;
case CMD_POSTMULT:
case CMD_SETVAL:
if ((unsigned long)(cmd.value = strtoul(argv[4], NULL, 0)) == ULONG_MAX) {
ERR(errno, "strtoul");
goto usage;
}
case CMD_REMOVE:
case CMD_POST:
case CMD_WAIT:
case CMD_TRYWAIT:
case CMD_GETVAL:
if ((unsigned long)(cmd.semid = strtoul(argv[1], NULL, 0)) == ULONG_MAX ||
(unsigned long)(cmd.semnum = strtoul(argv[2], NULL, 0)) == ULONG_MAX) {
ERR(errno, "strtoul");
goto usage;
}
}
cmd_print(&cmd);
/* Run the Command */
if (cmd_functions[cmd.cmd](&cmd) == -1) {
fprintf(stderr, "Command failed\n");
return EXIT_FAILURE;
}
return EXIT_SUCCESS;
}
syntax highlighted by Code2HTML, v. 0.9.1