/*
* Copyright (c) 2000-2001, 2005, 2008 Sendmail, Inc. and its suppliers.
* All rights reserved.
*
* By using this file, you agree to the terms and conditions set
* forth in the LICENSE file which can be found at the top level of
* the sendmail distribution.
*/
#include <sm/gen.h>
SM_RCSID("@(#)$Id: sem.c,v 1.14 2008/05/30 16:26:38 ca Exp $")
#if SM_CONF_SEM
# include <stdlib.h>
# include <unistd.h>
# include <sm/string.h>
# include <sm/sem.h>
# include <sm/heap.h>
# include <errno.h>
/*
** SM_SEM_START -- initialize semaphores
**
** Parameters:
** key -- key for semaphores.
** nsem -- number of semaphores.
** semflg -- flag for semget(), if 0, use a default.
** owner -- create semaphores.
**
** Returns:
** id for semaphores.
** < 0 on failure.
*/
int
sm_sem_start(key, nsem, semflg, owner)
key_t key;
int nsem;
int semflg;
bool owner;
{
int semid, i, err;
unsigned short *semvals;
semvals = NULL;
if (semflg == 0)
semflg = (SEM_A|SEM_R)|((SEM_A|SEM_R) >> 3);
if (owner)
semflg |= IPC_CREAT|IPC_EXCL;
semid = semget(key, nsem, semflg);
if (semid < 0)
goto error;
if (owner)
{
union semun semarg;
semvals = (unsigned short *) sm_malloc(nsem * sizeof semvals);
if (semvals == NULL)
goto error;
semarg.array = semvals;
/* initialize semaphore values to be available */
for (i = 0; i < nsem; i++)
semvals[i] = 1;
if (semctl(semid, 0, SETALL, semarg) < 0)
goto error;
}
return semid;
error:
err = errno;
if (semvals != NULL)
sm_free(semvals);
if (semid >= 0)
sm_sem_stop(semid);
return (err > 0) ? (0 - err) : -1;
}
/*
** SM_SEM_STOP -- stop using semaphores.
**
** Parameters:
** semid -- id for semaphores.
**
** Returns:
** 0 on success.
** < 0 on failure.
*/
int
sm_sem_stop(semid)
int semid;
{
return semctl(semid, 0, IPC_RMID, NULL);
}
/*
** SM_SEM_ACQ -- acquire semaphore.
**
** Parameters:
** semid -- id for semaphores.
** semnum -- number of semaphore.
** timeout -- how long to wait for operation to succeed.
**
** Returns:
** 0 on success.
** < 0 on failure.
*/
int
sm_sem_acq(semid, semnum, timeout)
int semid;
int semnum;
int timeout;
{
int r;
struct sembuf semops[1];
semops[0].sem_num = semnum;
semops[0].sem_op = -1;
semops[0].sem_flg = SEM_UNDO |
(timeout != SM_TIME_FOREVER ? 0 : IPC_NOWAIT);
if (timeout == SM_TIME_IMMEDIATE || timeout == SM_TIME_FOREVER)
return semop(semid, semops, 1);
do
{
r = semop(semid, semops, 1);
if (r == 0)
return r;
sleep(1);
--timeout;
} while (timeout > 0);
return r;
}
/*
** SM_SEM_REL -- release semaphore.
**
** Parameters:
** semid -- id for semaphores.
** semnum -- number of semaphore.
** timeout -- how long to wait for operation to succeed.
**
** Returns:
** 0 on success.
** < 0 on failure.
*/
int
sm_sem_rel(semid, semnum, timeout)
int semid;
int semnum;
int timeout;
{
int r;
struct sembuf semops[1];
#if PARANOID
/* XXX should we check whether the value is already 0 ? */
SM_REQUIRE(sm_get_sem(semid, semnum) > 0);
#endif /* PARANOID */
semops[0].sem_num = semnum;
semops[0].sem_op = 1;
semops[0].sem_flg = SEM_UNDO |
(timeout != SM_TIME_FOREVER ? 0 : IPC_NOWAIT);
if (timeout == SM_TIME_IMMEDIATE || timeout == SM_TIME_FOREVER)
return semop(semid, semops, 1);
do
{
r = semop(semid, semops, 1);
if (r == 0)
return r;
sleep(1);
--timeout;
} while (timeout > 0);
return r;
}
/*
** SM_SEM_GET -- get semaphore value.
**
** Parameters:
** semid -- id for semaphores.
** semnum -- number of semaphore.
**
** Returns:
** value of semaphore on success.
** < 0 on failure.
*/
int
sm_sem_get(semid, semnum)
int semid;
int semnum;
{
int semval;
if ((semval = semctl(semid, semnum, GETVAL, NULL)) < 0)
return -1;
return semval;
}
/*
** SM_SEMSETOWNER -- set owner/group/mode of semaphores.
**
** Parameters:
** semid -- id for semaphores.
** uid -- uid to use
** gid -- gid to use
** mode -- mode to use
**
** Returns:
** 0 on success.
** < 0 on failure.
*/
int
sm_semsetowner(semid, uid, gid, mode)
int semid;
uid_t uid;
gid_t gid;
mode_t mode;
{
int r;
struct semid_ds semidds;
union semun {
int val;
struct semid_ds *buf;
ushort *array;
} arg;
memset(&semidds, 0, sizeof(semidds));
arg.buf = &semidds;
if ((r = semctl(semid, 1, IPC_STAT, arg)) < 0)
return r;
semidds.sem_perm.uid = uid;
semidds.sem_perm.gid = gid;
semidds.sem_perm.mode = mode;
if ((r = semctl(semid, 1, IPC_SET, arg)) < 0)
return r;
return 0;
}
#endif /* SM_CONF_SEM */