1 /*
2 *
3 * Copyright (C) 2001-2003 FhG Fokus
4 *
5 * Permission to use, copy, modify, and distribute this software for any
6 * purpose with or without fee is hereby granted, provided that the above
7 * copyright notice and this permission notice appear in all copies.
8 *
9 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
10 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
11 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
12 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
13 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
14 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
15 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
16 */
17
18 /*!
19 * \file
20 * \brief Kamailio core :: Kamailio locking library
21 * \ingroup core
22 * \author andrei
23 * Module: \ref core
24 *
25 * WARNING: do not include this file directly, use instead locking.h
26 * (unless you don't need to alloc/dealloc locks)
27 *
28 *
29 Implements:
30
31 simple locks:
32 -------------
33 gen_lock_t* lock_init(gen_lock_t* lock); - inits the lock
34 void lock_destroy(gen_lock_t* lock); - removes the lock (e.g sysv rmid)
35 void lock_get(gen_lock_t* lock); - lock (mutex down)
36 void lock_release(gen_lock_t* lock); - unlock (mutex up)
37 int lock_try(gen_lock_t* lock); - tries to get the lock, returns
38 0 on success and !=0 on failure
39
40 lock sets:
41 ----------
42 gen_lock_set_t* lock_set_init(gen_lock_set_t* set); - inits the lock set
43 void lock_set_destroy(gen_lock_set_t* s); - removes the lock set
44 void lock_set_get(gen_lock_set_t* s, int i); - locks sem i from the set
45 void lock_set_release(gen_lock_set_t* s, int i) - unlocks sem i from the
46 set
47 int lock_set_try(gen_lock_set_t* s, int i); - tries to lock the sem i,
48 returns 0 on success and
49 !=0 on failure
50
51 defines:
52 --------
53 GEN_LOCK_T_PREFERRED - defined if using arrays of gen_lock_t is as good as
54 using a lock set (gen_lock_set_t).
55 In general is better to have the locks "close" or
56 inside the protected data structure rather then
57 having a separate array or lock set. However in some
58 case (e.g. SYSV_LOCKS) is better to use lock sets,
59 either due to lock number limitations, excesive
60 performance or memory overhead. In this cases
61 GEN_LOCK_T_PREFERRED will not be defined.
62 GEN_LOCK_T_UNLIMITED - defined if there is no system imposed limit on
63 the number of locks (other then the memory).
64 GEN_LOCK_SET_T_UNLIMITED
65 - like above but for the size of a lock set.
66
67 WARNING: - lock_set_init may fail for large number of sems (e.g. sysv).
68 - signals are not treated! (some locks are "awakened" by the signals)
69 */
70
71 #ifndef _lock_ops_h
72 #define _lock_ops_h
73
74 #ifdef USE_FUTEX
75 #include "futexlock.h"
76 /* if no native atomic ops support => USE_FUTEX will be undefined */
77 #endif
78
79
80 #ifdef USE_FUTEX
81
82 typedef futex_lock_t gen_lock_t;
83
84 #define lock_destroy(lock) /* do nothing */
85 #define lock_init(lock) futex_init(lock)
86 #define lock_try(lock) futex_try(lock)
87 #define lock_get(lock) futex_get(lock)
88 #define lock_release(lock) futex_release(lock)
89
90
91 #elif defined FAST_LOCK
92 #include "fastlock.h"
93
94 typedef fl_lock_t gen_lock_t;
95
96
97 #define lock_destroy(lock) /* do nothing */
98
lock_init(gen_lock_t * lock)99 inline static gen_lock_t* lock_init(gen_lock_t* lock)
100 {
101 init_lock(*lock);
102 return lock;
103 }
104
105 #define lock_try(lock) try_lock(lock)
106 #define lock_get(lock) get_lock(lock)
107 #define lock_release(lock) release_lock(lock)
108
109
110 #elif defined USE_PTHREAD_MUTEX
111 #include <pthread.h>
112
113 typedef pthread_mutex_t gen_lock_t;
114
115 #define lock_destroy(lock) /* do nothing */
116
lock_init(gen_lock_t * lock)117 inline static gen_lock_t* lock_init(gen_lock_t* lock)
118 {
119 if (pthread_mutex_init(lock, 0)==0) return lock;
120 else return 0;
121 }
122
123 #define lock_try(lock) pthread_mutex_trylock(lock)
124 #define lock_get(lock) pthread_mutex_lock(lock)
125 #define lock_release(lock) pthread_mutex_unlock(lock)
126
127
128
129 #elif defined USE_POSIX_SEM
130 #include <semaphore.h>
131
132 typedef sem_t gen_lock_t;
133
134 #define lock_destroy(lock) /* do nothing */
135
lock_init(gen_lock_t * lock)136 inline static gen_lock_t* lock_init(gen_lock_t* lock)
137 {
138 if (sem_init(lock, 1, 1)<0) return 0;
139 return lock;
140 }
141
142 #define lock_try(lock) sem_trywait(lock)
143 #define lock_get(lock) sem_wait(lock)
144 #define lock_release(lock) sem_post(lock)
145
146
147 #elif defined USE_SYSV_SEM
148 #include <sys/ipc.h>
149 #include <sys/sem.h>
150
151 #include <errno.h>
152 #include <string.h>
153 #include <sys/types.h>
154 #include <unistd.h>
155 #include "dprint.h"
156 #include "globals.h" /* uid */
157
158 #if ((defined(HAVE_UNION_SEMUN) || defined(__GNU_LIBRARY__) )&& !defined(_SEM_SEMUN_UNDEFINED))
159
160 /* union semun is defined by including sem.h */
161 #else
162 /* according to X/OPEN we have to define it ourselves */
163 union semun {
164 int val; /* value for SETVAL */
165 struct semid_ds *buf; /* buffer for IPC_STAT, IPC_SET */
166 unsigned short int *array; /* array for GETALL, SETALL */
167 struct seminfo *__buf; /* buffer for IPC_INFO */
168 };
169 #endif
170
171 typedef int gen_lock_t;
172
173
174
175
lock_init(gen_lock_t * lock)176 inline static gen_lock_t* lock_init(gen_lock_t* lock)
177 {
178 union semun su;
179 int euid;
180
181 euid=geteuid();
182 if (uid && uid!=euid)
183 seteuid(uid); /* set euid to the cfg. requested one */
184 *lock=semget(IPC_PRIVATE, 1, 0700);
185 if (uid && uid!=euid)
186 seteuid(euid); /* restore it */
187 if (*lock==-1) return 0;
188 su.val=1;
189 if (semctl(*lock, 0, SETVAL, su)==-1){
190 /* init error*/
191 return 0;
192 }
193 return lock;
194 }
195
lock_destroy(gen_lock_t * lock)196 inline static void lock_destroy(gen_lock_t* lock)
197 {
198 semctl(*lock, 0, IPC_RMID, (union semun)(int)0);
199 }
200
201
202 /* returns 0 if it got the lock, -1 otherwise */
lock_try(gen_lock_t * lock)203 inline static int lock_try(gen_lock_t* lock)
204 {
205 struct sembuf sop;
206
207 sop.sem_num=0;
208 sop.sem_op=-1; /* down */
209 sop.sem_flg=IPC_NOWAIT;
210 tryagain:
211 if (semop(*lock, &sop, 1)==-1){
212 if (errno==EAGAIN){
213 return -1;
214 }else if (errno==EINTR){
215 DBG("lock_get: signal received while waiting for on a mutex\n");
216 goto tryagain;
217 }else{
218 LM_CRIT("sysv: %s (%d)\n", strerror(errno), errno);
219 return -1;
220 }
221 }
222 return 0;
223 }
224
lock_get(gen_lock_t * lock)225 inline static void lock_get(gen_lock_t* lock)
226 {
227 struct sembuf sop;
228
229 sop.sem_num=0;
230 sop.sem_op=-1; /* down */
231 sop.sem_flg=0;
232 tryagain:
233 if (semop(*lock, &sop, 1)==-1){
234 if (errno==EINTR){
235 DBG("lock_get: signal received while waiting for on a mutex\n");
236 goto tryagain;
237 }else{
238 LM_CRIT("sysv: %s (%d)\n", strerror(errno), errno);
239 }
240 }
241 }
242
lock_release(gen_lock_t * lock)243 inline static void lock_release(gen_lock_t* lock)
244 {
245 struct sembuf sop;
246
247 sop.sem_num=0;
248 sop.sem_op=1; /* up */
249 sop.sem_flg=0;
250 tryagain:
251 if (semop(*lock, &sop, 1)==-1){
252 if (errno==EINTR){
253 /* very improbable*/
254 DBG("lock_release: signal received while releasing a mutex\n");
255 goto tryagain;
256 }else{
257 LM_CRIT("sysv: %s (%d)\n", strerror(errno), errno);
258 }
259 }
260 }
261
262
263 #else
264 #error "no locking method selected"
265 #endif
266
267
268 /* lock sets */
269
270 #if defined(FAST_LOCK) || defined(USE_PTHREAD_MUTEX) || \
271 defined(USE_POSIX_SEM) || defined(USE_FUTEX)
272 #define GEN_LOCK_T_PREFERRED
273 #define GEN_LOCK_T_PREFERED /* backwards compat. */
274 #define GEN_LOCK_T_UNLIMITED
275 #define GEN_LOCK_SET_T_UNLIMITED
276
277 struct gen_lock_set_t_ {
278 long size;
279 gen_lock_t* locks;
280 }; /* must be aligned (32 bits or 64 depending on the arch)*/
281 typedef struct gen_lock_set_t_ gen_lock_set_t;
282
283
284 #define lock_set_destroy(lock_set) /* do nothing */
285
lock_set_init(gen_lock_set_t * s)286 inline static gen_lock_set_t* lock_set_init(gen_lock_set_t* s)
287 {
288 int r;
289 for (r=0; r<s->size; r++) if (lock_init(&s->locks[r])==0) return 0;
290 return s;
291 }
292
293 /* WARNING: no boundary checks!*/
294 #define lock_set_try(set, i) lock_try(&set->locks[i])
295 #define lock_set_get(set, i) lock_get(&set->locks[i])
296 #define lock_set_release(set, i) lock_release(&set->locks[i])
297
298 #elif defined(USE_SYSV_SEM)
299 #undef GEN_LOCK_T_PREFERRED
300 #undef GEN_LOCK_T_PREFERED /* backwards compat. */
301 #undef GEN_LOCK_T_UNLIMITED
302 #undef GEN_LOCK_SET_T_UNLIMITED
303 #define GEN_LOCK_T_LIMITED
304 #define GEN_LOCK_SET_T_LIMITED
305
306 struct gen_lock_set_t_ {
307 int size;
308 int semid;
309 };
310
311
312 typedef struct gen_lock_set_t_ gen_lock_set_t;
lock_set_init(gen_lock_set_t * s)313 inline static gen_lock_set_t* lock_set_init(gen_lock_set_t* s)
314 {
315 union semun su;
316 int r;
317 int euid;
318
319 euid=geteuid();
320 if (uid && uid!=euid)
321 seteuid(uid); /* set euid to the cfg. requested one */
322 s->semid=semget(IPC_PRIVATE, s->size, 0700);
323 if (uid && uid!=euid)
324 seteuid(euid); /* restore euid */
325 if (s->semid==-1){
326 LM_CRIT("(SYSV): semget (..., %d, 0700) failed: %s\n",
327 s->size, strerror(errno));
328 return 0;
329 }
330 su.val=1;
331 for (r=0; r<s->size; r++){
332 if (semctl(s->semid, r, SETVAL, su)==-1){
333 LM_CRIT("(SYSV): semctl failed on sem %d: %s\n", r, strerror(errno));
334 semctl(s->semid, 0, IPC_RMID, (union semun)(int)0);
335 return 0;
336 }
337 }
338 return s;
339 }
340
lock_set_destroy(gen_lock_set_t * s)341 inline static void lock_set_destroy(gen_lock_set_t* s)
342 {
343 semctl(s->semid, 0, IPC_RMID, (union semun)(int)0);
344 }
345
346
347 /* returns 0 if it "gets" the lock, -1 otherwise */
lock_set_try(gen_lock_set_t * s,int n)348 inline static int lock_set_try(gen_lock_set_t* s, int n)
349 {
350 struct sembuf sop;
351
352 sop.sem_num=n;
353 sop.sem_op=-1; /* down */
354 sop.sem_flg=IPC_NOWAIT;
355 tryagain:
356 if (semop(s->semid, &sop, 1)==-1){
357 if (errno==EAGAIN){
358 return -1;
359 }else if (errno==EINTR){
360 DBG("lock_get: signal received while waiting for on a mutex\n");
361 goto tryagain;
362 }else{
363 LM_CRIT("sysv: %s (%d)\n", strerror(errno), errno);
364 return -1;
365 }
366 }
367 return 0;
368 }
369
370
lock_set_get(gen_lock_set_t * s,int n)371 inline static void lock_set_get(gen_lock_set_t* s, int n)
372 {
373 struct sembuf sop;
374 sop.sem_num=n;
375 sop.sem_op=-1; /* down */
376 sop.sem_flg=0;
377 tryagain:
378 if (semop(s->semid, &sop, 1)==-1){
379 if (errno==EINTR){
380 DBG("lock_set_get: signal received while waiting on a mutex\n");
381 goto tryagain;
382 }else{
383 LM_CRIT("sysv: %s (%d)\n", strerror(errno), errno);
384 }
385 }
386 }
387
lock_set_release(gen_lock_set_t * s,int n)388 inline static void lock_set_release(gen_lock_set_t* s, int n)
389 {
390 struct sembuf sop;
391 sop.sem_num=n;
392 sop.sem_op=1; /* up */
393 sop.sem_flg=0;
394 tryagain:
395 if (semop(s->semid, &sop, 1)==-1){
396 if (errno==EINTR){
397 /* very improbable */
398 DBG("lock_set_release: signal received while releasing mutex\n");
399 goto tryagain;
400 }else{
401 LM_CRIT("sysv: %s (%d)\n", strerror(errno), errno);
402 }
403 }
404 }
405 #else
406 #error "no lock set method selected"
407 #endif
408
409
410 #endif
411