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