1 /*
2  * Copyright (c) 2000-2001, 2005, 2008 Sendmail, Inc. and its suppliers.
3  *      All rights reserved.
4  *
5  * By using this file, you agree to the terms and conditions set
6  * forth in the LICENSE file which can be found at the top level of
7  * the sendmail distribution.
8  */
9 
10 #include <sm/gen.h>
11 SM_RCSID("@(#)$Id: sem.c,v 1.14 2008/05/30 16:26:38 ca Exp $")
12 
13 #if SM_CONF_SEM
14 # include <stdlib.h>
15 # include <unistd.h>
16 # include <sm/string.h>
17 # include <sm/sem.h>
18 # include <sm/heap.h>
19 # include <errno.h>
20 
21 /*
22 **  SM_SEM_START -- initialize semaphores
23 **
24 **	Parameters:
25 **		key -- key for semaphores.
26 **		nsem -- number of semaphores.
27 **		semflg -- flag for semget(), if 0, use a default.
28 **		owner -- create semaphores.
29 **
30 **	Returns:
31 **		id for semaphores.
32 **		< 0 on failure.
33 */
34 
35 int
36 sm_sem_start(key, nsem, semflg, owner)
37 	key_t key;
38 	int nsem;
39 	int semflg;
40 	bool owner;
41 {
42 	int semid, i, err;
43 	unsigned short *semvals;
44 
45 	semvals = NULL;
46 	if (semflg == 0)
47 		semflg = (SEM_A|SEM_R)|((SEM_A|SEM_R) >> 3);
48 	if (owner)
49 		semflg |= IPC_CREAT|IPC_EXCL;
50 	semid = semget(key, nsem, semflg);
51 	if (semid < 0)
52 		goto error;
53 
54 	if (owner)
55 	{
56 		union semun semarg;
57 
58 		semvals = (unsigned short *) sm_malloc(nsem * sizeof semvals);
59 		if (semvals == NULL)
60 			goto error;
61 		semarg.array = semvals;
62 
63 		/* initialize semaphore values to be available */
64 		for (i = 0; i < nsem; i++)
65 			semvals[i] = 1;
66 		if (semctl(semid, 0, SETALL, semarg) < 0)
67 			goto error;
68 	}
69 	return semid;
70 
71 error:
72 	err = errno;
73 	if (semvals != NULL)
74 		sm_free(semvals);
75 	if (semid >= 0)
76 		sm_sem_stop(semid);
77 	return (err > 0) ? (0 - err) : -1;
78 }
79 
80 /*
81 **  SM_SEM_STOP -- stop using semaphores.
82 **
83 **	Parameters:
84 **		semid -- id for semaphores.
85 **
86 **	Returns:
87 **		0 on success.
88 **		< 0 on failure.
89 */
90 
91 int
sm_sem_stop(semid)92 sm_sem_stop(semid)
93 	int semid;
94 {
95 	return semctl(semid, 0, IPC_RMID, NULL);
96 }
97 
98 /*
99 **  SM_SEM_ACQ -- acquire semaphore.
100 **
101 **	Parameters:
102 **		semid -- id for semaphores.
103 **		semnum -- number of semaphore.
104 **		timeout -- how long to wait for operation to succeed.
105 **
106 **	Returns:
107 **		0 on success.
108 **		< 0 on failure.
109 */
110 
111 int
sm_sem_acq(semid,semnum,timeout)112 sm_sem_acq(semid, semnum, timeout)
113 	int semid;
114 	int semnum;
115 	int timeout;
116 {
117 	int r;
118 	struct sembuf semops[1];
119 
120 	semops[0].sem_num = semnum;
121 	semops[0].sem_op = -1;
122 	semops[0].sem_flg = SEM_UNDO |
123 			    (timeout != SM_TIME_FOREVER ? 0 : IPC_NOWAIT);
124 	if (timeout == SM_TIME_IMMEDIATE || timeout == SM_TIME_FOREVER)
125 		return semop(semid, semops, 1);
126 	do
127 	{
128 		r = semop(semid, semops, 1);
129 		if (r == 0)
130 			return r;
131 		sleep(1);
132 		--timeout;
133 	} while (timeout > 0);
134 	return r;
135 }
136 
137 /*
138 **  SM_SEM_REL -- release semaphore.
139 **
140 **	Parameters:
141 **		semid -- id for semaphores.
142 **		semnum -- number of semaphore.
143 **		timeout -- how long to wait for operation to succeed.
144 **
145 **	Returns:
146 **		0 on success.
147 **		< 0 on failure.
148 */
149 
150 int
sm_sem_rel(semid,semnum,timeout)151 sm_sem_rel(semid, semnum, timeout)
152 	int semid;
153 	int semnum;
154 	int timeout;
155 {
156 	int r;
157 	struct sembuf semops[1];
158 
159 #if PARANOID
160 	/* XXX should we check whether the value is already 0 ? */
161 	SM_REQUIRE(sm_get_sem(semid, semnum) > 0);
162 #endif /* PARANOID */
163 
164 	semops[0].sem_num = semnum;
165 	semops[0].sem_op = 1;
166 	semops[0].sem_flg = SEM_UNDO |
167 			    (timeout != SM_TIME_FOREVER ? 0 : IPC_NOWAIT);
168 	if (timeout == SM_TIME_IMMEDIATE || timeout == SM_TIME_FOREVER)
169 		return semop(semid, semops, 1);
170 	do
171 	{
172 		r = semop(semid, semops, 1);
173 		if (r == 0)
174 			return r;
175 		sleep(1);
176 		--timeout;
177 	} while (timeout > 0);
178 	return r;
179 }
180 
181 /*
182 **  SM_SEM_GET -- get semaphore value.
183 **
184 **	Parameters:
185 **		semid -- id for semaphores.
186 **		semnum -- number of semaphore.
187 **
188 **	Returns:
189 **		value of semaphore on success.
190 **		< 0 on failure.
191 */
192 
193 int
sm_sem_get(semid,semnum)194 sm_sem_get(semid, semnum)
195 	int semid;
196 	int semnum;
197 {
198 	int semval;
199 
200 	if ((semval = semctl(semid, semnum, GETVAL, NULL)) < 0)
201 		return -1;
202 	return semval;
203 }
204 
205 /*
206 **  SM_SEMSETOWNER -- set owner/group/mode of semaphores.
207 **
208 **	Parameters:
209 **		semid -- id for semaphores.
210 **		uid -- uid to use
211 **		gid -- gid to use
212 **		mode -- mode to use
213 **
214 **	Returns:
215 **		0 on success.
216 **		< 0 on failure.
217 */
218 
219 int
sm_semsetowner(semid,uid,gid,mode)220 sm_semsetowner(semid, uid, gid, mode)
221 	int semid;
222 	uid_t uid;
223 	gid_t gid;
224 	mode_t mode;
225 {
226 # ifndef WIN32
227 	int r;
228 	struct semid_ds	semidds;
229 	union semun {
230 		int		val;
231 		struct semid_ds	*buf;
232 		ushort		*array;
233 	} arg;
234 
235 	memset(&semidds, 0, sizeof(semidds));
236 	arg.buf = &semidds;
237 	if ((r = semctl(semid, 1, IPC_STAT, arg)) < 0)
238 		return r;
239 	semidds.sem_perm.uid = uid;
240 	semidds.sem_perm.gid = gid;
241 	semidds.sem_perm.mode = mode;
242 	if ((r = semctl(semid, 1, IPC_SET, arg)) < 0)
243 		return r;
244 # endif /* WIN32 */
245 	return 0;
246 }
247 #endif /* SM_CONF_SEM */
248