1 /*
2 ** Zabbix
3 ** Copyright (C) 2001-2021 Zabbix SIA
4 **
5 ** This program is free software; you can redistribute it and/or modify
6 ** it under the terms of the GNU General Public License as published by
7 ** the Free Software Foundation; either version 2 of the License, or
8 ** (at your option) any later version.
9 **
10 ** This program is distributed in the hope that it will be useful,
11 ** but WITHOUT ANY WARRANTY; without even the implied warranty of
12 ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 ** GNU General Public License for more details.
14 **
15 ** You should have received a copy of the GNU General Public License
16 ** along with this program; if not, write to the Free Software
17 ** Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
18 **/
19 
20 #include "common.h"
21 #include "log.h"
22 #include "mutexs.h"
23 
24 #ifdef _WINDOWS
25 #	include "sysinfo.h"
26 #else
27 #ifdef HAVE_PTHREAD_PROCESS_SHARED
28 typedef struct
29 {
30 	pthread_mutex_t		mutexes[ZBX_MUTEX_COUNT];
31 	pthread_rwlock_t	rwlocks[ZBX_RWLOCK_COUNT];
32 }
33 zbx_shared_lock_t;
34 
35 static zbx_shared_lock_t	*shared_lock;
36 static int			shm_id, locks_disabled;
37 #else
38 #	if !HAVE_SEMUN
39 		union semun
40 		{
41 			int			val;	/* value for SETVAL */
42 			struct semid_ds		*buf;	/* buffer for IPC_STAT & IPC_SET */
43 			unsigned short int	*array;	/* array for GETALL & SETALL */
44 			struct seminfo		*__buf;	/* buffer for IPC_INFO */
45 		};
46 
47 #		undef HAVE_SEMUN
48 #		define HAVE_SEMUN 1
49 #	endif	/* HAVE_SEMUN */
50 
51 #	include "cfg.h"
52 #	include "threads.h"
53 
54 	static int		ZBX_SEM_LIST_ID;
55 	static unsigned char	mutexes;
56 #endif
57 
58 /******************************************************************************
59  *                                                                            *
60  * Function: zbx_locks_create                                                 *
61  *                                                                            *
62  * Purpose: if pthread mutexes and read-write locks can be shared between     *
63  *          processes then create them, otherwise fallback to System V        *
64  *          semaphore operations                                              *
65  *                                                                            *
66  * Parameters: error - dynamically allocated memory with error message.       *
67  *                                                                            *
68  * Return value: SUCCEED if mutexes successfully created, otherwise FAIL      *
69  *                                                                            *
70  ******************************************************************************/
zbx_locks_create(char ** error)71 int	zbx_locks_create(char **error)
72 {
73 #ifdef HAVE_PTHREAD_PROCESS_SHARED
74 	int			i;
75 	pthread_mutexattr_t	mta;
76 	pthread_rwlockattr_t	rwa;
77 
78 	if (-1 == (shm_id = shmget(IPC_PRIVATE, ZBX_SIZE_T_ALIGN8(sizeof(zbx_shared_lock_t)),
79 			IPC_CREAT | IPC_EXCL | 0600)))
80 	{
81 		*error = zbx_dsprintf(*error, "cannot allocate shared memory for locks");
82 		return FAIL;
83 	}
84 
85 	if ((void *)(-1) == (shared_lock = (zbx_shared_lock_t *)shmat(shm_id, NULL, 0)))
86 	{
87 		*error = zbx_dsprintf(*error, "cannot attach shared memory for locks: %s", zbx_strerror(errno));
88 		return FAIL;
89 	}
90 
91 	memset(shared_lock, 0, sizeof(zbx_shared_lock_t));
92 
93 	/* immediately mark the new shared memory for destruction after attaching to it */
94 	if (-1 == shmctl(shm_id, IPC_RMID, 0))
95 	{
96 		*error = zbx_dsprintf(*error, "cannot mark the new shared memory for destruction: %s",
97 				zbx_strerror(errno));
98 		return FAIL;
99 	}
100 
101 	if (0 != pthread_mutexattr_init(&mta))
102 	{
103 		*error = zbx_dsprintf(*error, "cannot initialize mutex attribute: %s", zbx_strerror(errno));
104 		return FAIL;
105 	}
106 
107 	if (0 != pthread_mutexattr_setpshared(&mta, PTHREAD_PROCESS_SHARED))
108 	{
109 		*error = zbx_dsprintf(*error, "cannot set shared mutex attribute: %s", zbx_strerror(errno));
110 		return FAIL;
111 	}
112 
113 	for (i = 0; i < ZBX_MUTEX_COUNT; i++)
114 	{
115 		if (0 != pthread_mutex_init(&shared_lock->mutexes[i], &mta))
116 		{
117 			*error = zbx_dsprintf(*error, "cannot create mutex: %s", zbx_strerror(errno));
118 			return FAIL;
119 		}
120 	}
121 
122 	if (0 != pthread_rwlockattr_init(&rwa))
123 	{
124 		*error = zbx_dsprintf(*error, "cannot initialize read write lock attribute: %s", zbx_strerror(errno));
125 		return FAIL;
126 	}
127 
128 	if (0 != pthread_rwlockattr_setpshared(&rwa, PTHREAD_PROCESS_SHARED))
129 	{
130 		*error = zbx_dsprintf(*error, "cannot set shared read write lock attribute: %s", zbx_strerror(errno));
131 		return FAIL;
132 	}
133 
134 	for (i = 0; i < ZBX_RWLOCK_COUNT; i++)
135 	{
136 		if (0 != pthread_rwlock_init(&shared_lock->rwlocks[i], &rwa))
137 		{
138 			*error = zbx_dsprintf(*error, "cannot create rwlock: %s", zbx_strerror(errno));
139 			return FAIL;
140 		}
141 	}
142 #else
143 	union semun	semopts;
144 	int		i;
145 
146 	if (-1 == (ZBX_SEM_LIST_ID = semget(IPC_PRIVATE, ZBX_MUTEX_COUNT + ZBX_RWLOCK_COUNT, 0600)))
147 	{
148 		*error = zbx_dsprintf(*error, "cannot create semaphore set: %s", zbx_strerror(errno));
149 		return FAIL;
150 	}
151 
152 	/* set default semaphore value */
153 
154 	semopts.val = 1;
155 	for (i = 0; ZBX_MUTEX_COUNT + ZBX_RWLOCK_COUNT > i; i++)
156 	{
157 		if (-1 != semctl(ZBX_SEM_LIST_ID, i, SETVAL, semopts))
158 			continue;
159 
160 		*error = zbx_dsprintf(*error, "cannot initialize semaphore: %s", zbx_strerror(errno));
161 
162 		if (-1 == semctl(ZBX_SEM_LIST_ID, 0, IPC_RMID, 0))
163 			zbx_error("cannot remove semaphore set %d: %s", ZBX_SEM_LIST_ID, zbx_strerror(errno));
164 
165 		ZBX_SEM_LIST_ID = -1;
166 
167 		return FAIL;
168 	}
169 #endif
170 	return SUCCEED;
171 }
172 
173 /******************************************************************************
174  *                                                                            *
175  * Function: zbx_rwlock_create                                                *
176  *                                                                            *
177  * Purpose: read-write locks are created using zbx_locks_create() function    *
178  *          this is only to obtain handle, if read write locks are not        *
179  *          supported, then outputs numeric handle of mutex that can be used  *
180  *          with mutex handling functions                                     *
181  *                                                                            *
182  * Parameters:  rwlock - read-write lock handle if supported, otherwise mutex *
183  *              name - name of read-write lock (index for nix system)         *
184  *              error - unused                                                *
185  *                                                                            *
186  * Return value: SUCCEED if mutexes successfully created, otherwise FAIL      *
187  *                                                                            *
188  ******************************************************************************/
zbx_rwlock_create(zbx_rwlock_t * rwlock,zbx_rwlock_name_t name,char ** error)189 int	zbx_rwlock_create(zbx_rwlock_t *rwlock, zbx_rwlock_name_t name, char **error)
190 {
191 	ZBX_UNUSED(error);
192 #ifdef HAVE_PTHREAD_PROCESS_SHARED
193 	*rwlock = &shared_lock->rwlocks[name];
194 #else
195 	*rwlock = name + ZBX_MUTEX_COUNT;
196 	mutexes++;
197 #endif
198 	return SUCCEED;
199 }
200 #ifdef HAVE_PTHREAD_PROCESS_SHARED
201 /******************************************************************************
202  *                                                                            *
203  * Function: __zbx_rwlock_wrlock                                              *
204  *                                                                            *
205  * Purpose: acquire write lock for read-write lock (exclusive access)         *
206  *                                                                            *
207  * Parameters: rwlock - handle of read-write lock                             *
208  *                                                                            *
209  ******************************************************************************/
__zbx_rwlock_wrlock(const char * filename,int line,zbx_rwlock_t rwlock)210 void	__zbx_rwlock_wrlock(const char *filename, int line, zbx_rwlock_t rwlock)
211 {
212 	if (ZBX_RWLOCK_NULL == rwlock)
213 		return;
214 
215 	if (0 != locks_disabled)
216 		return;
217 
218 	if (0 != pthread_rwlock_wrlock(rwlock))
219 	{
220 		zbx_error("[file:'%s',line:%d] write lock failed: %s", filename, line, zbx_strerror(errno));
221 		exit(EXIT_FAILURE);
222 	}
223 }
224 
225 /******************************************************************************
226  *                                                                            *
227  * Function: __zbx_rwlock_rdlock                                              *
228  *                                                                            *
229  * Purpose: acquire read lock for read-write lock (there can be many readers) *
230  *                                                                            *
231  * Parameters: rwlock - handle of read-write lock                             *
232  *                                                                            *
233  ******************************************************************************/
__zbx_rwlock_rdlock(const char * filename,int line,zbx_rwlock_t rwlock)234 void	__zbx_rwlock_rdlock(const char *filename, int line, zbx_rwlock_t rwlock)
235 {
236 	if (ZBX_RWLOCK_NULL == rwlock)
237 		return;
238 
239 	if (0 != locks_disabled)
240 		return;
241 
242 	if (0 != pthread_rwlock_rdlock(rwlock))
243 	{
244 		zbx_error("[file:'%s',line:%d] read lock failed: %s", filename, line, zbx_strerror(errno));
245 		exit(EXIT_FAILURE);
246 	}
247 }
248 
249 /******************************************************************************
250  *                                                                            *
251  * Function: __zbx_rwlock_unlock                                              *
252  *                                                                            *
253  * Purpose: unlock read-write lock                                            *
254  *                                                                            *
255  * Parameters: rwlock - handle of read-write lock                             *
256  *                                                                            *
257  ******************************************************************************/
__zbx_rwlock_unlock(const char * filename,int line,zbx_rwlock_t rwlock)258 void	__zbx_rwlock_unlock(const char *filename, int line, zbx_rwlock_t rwlock)
259 {
260 	if (ZBX_RWLOCK_NULL == rwlock)
261 		return;
262 
263 	if (0 != locks_disabled)
264 		return;
265 
266 	if (0 != pthread_rwlock_unlock(rwlock))
267 	{
268 		zbx_error("[file:'%s',line:%d] read-write lock unlock failed: %s", filename, line, zbx_strerror(errno));
269 		exit(EXIT_FAILURE);
270 	}
271 }
272 
273 /******************************************************************************
274  *                                                                            *
275  * Function: zbx_rwlock_destroy                                               *
276  *                                                                            *
277  * Purpose: Destroy read-write lock                                           *
278  *                                                                            *
279  * Parameters: rwlock - handle of read-write lock                             *
280  *                                                                            *
281  *                                                                            *
282  ******************************************************************************/
283 
zbx_rwlock_destroy(zbx_rwlock_t * rwlock)284 void	zbx_rwlock_destroy(zbx_rwlock_t *rwlock)
285 {
286 	if (ZBX_RWLOCK_NULL == *rwlock)
287 		return;
288 
289 	if (0 != locks_disabled)
290 		return;
291 
292 	if (0 != pthread_rwlock_destroy(*rwlock))
293 		zbx_error("cannot remove read-write lock: %s", zbx_strerror(errno));
294 
295 	*rwlock = ZBX_RWLOCK_NULL;
296 }
297 
298 /******************************************************************************
299  *                                                                            *
300  * Function: zbx_locks_disable                                                *
301  *                                                                            *
302  * Purpose:  disable locks                                                    *
303  *                                                                            *
304  ******************************************************************************/
zbx_locks_disable(void)305 void	zbx_locks_disable(void)
306 {
307 	/* attempting to destroy a locked pthread mutex results in undefined behavior */
308 	locks_disabled = 1;
309 }
310 #endif
311 #endif	/* _WINDOWS */
312 
313 /******************************************************************************
314  *                                                                            *
315  * Function: zbx_mutex_create                                                 *
316  *                                                                            *
317  * Purpose: Create the mutex                                                  *
318  *                                                                            *
319  * Parameters:  mutex - handle of mutex                                       *
320  *              name - name of mutex (index for nix system)                   *
321  *                                                                            *
322  * Return value: If the function succeeds, then return SUCCEED,               *
323  *               FAIL on an error                                             *
324  *                                                                            *
325  * Author: Eugene Grigorjev                                                   *
326  *                                                                            *
327  ******************************************************************************/
zbx_mutex_create(zbx_mutex_t * mutex,zbx_mutex_name_t name,char ** error)328 int	zbx_mutex_create(zbx_mutex_t *mutex, zbx_mutex_name_t name, char **error)
329 {
330 #ifdef _WINDOWS
331 	if (NULL == (*mutex = CreateMutex(NULL, FALSE, name)))
332 	{
333 		*error = zbx_dsprintf(*error, "error on mutex creating: %s", strerror_from_system(GetLastError()));
334 		return FAIL;
335 	}
336 #else
337 	ZBX_UNUSED(error);
338 #ifdef	HAVE_PTHREAD_PROCESS_SHARED
339 	*mutex = &shared_lock->mutexes[name];
340 #else
341 	mutexes++;
342 	*mutex = name;
343 #endif
344 #endif
345 	return SUCCEED;
346 }
347 
348 /******************************************************************************
349  *                                                                            *
350  * Function: zbx_mutex_lock                                                   *
351  *                                                                            *
352  * Purpose: Waits until the mutex is in the signalled state                   *
353  *                                                                            *
354  * Parameters: mutex - handle of mutex                                        *
355  *                                                                            *
356  * Author: Eugene Grigorjev, Alexander Vladishev                              *
357  *                                                                            *
358  ******************************************************************************/
__zbx_mutex_lock(const char * filename,int line,zbx_mutex_t mutex)359 void	__zbx_mutex_lock(const char *filename, int line, zbx_mutex_t mutex)
360 {
361 #ifndef _WINDOWS
362 #ifndef	HAVE_PTHREAD_PROCESS_SHARED
363 	struct sembuf	sem_lock;
364 #endif
365 #else
366 	DWORD   dwWaitResult;
367 #endif
368 
369 	if (ZBX_MUTEX_NULL == mutex)
370 		return;
371 
372 #ifdef _WINDOWS
373 #ifdef ZABBIX_AGENT
374 	if (0 != (ZBX_MUTEX_THREAD_DENIED & get_thread_global_mutex_flag()))
375 	{
376 		zbx_error("[file:'%s',line:%d] lock failed: ZBX_MUTEX_THREAD_DENIED is set for thread with id = %d",
377 				filename, line, zbx_get_thread_id());
378 		exit(EXIT_FAILURE);
379 	}
380 #endif
381 	dwWaitResult = WaitForSingleObject(mutex, INFINITE);
382 
383 	switch (dwWaitResult)
384 	{
385 		case WAIT_OBJECT_0:
386 			break;
387 		case WAIT_ABANDONED:
388 			THIS_SHOULD_NEVER_HAPPEN;
389 			exit(EXIT_FAILURE);
390 		default:
391 			zbx_error("[file:'%s',line:%d] lock failed: %s",
392 				filename, line, strerror_from_system(GetLastError()));
393 			exit(EXIT_FAILURE);
394 	}
395 #else
396 #ifdef	HAVE_PTHREAD_PROCESS_SHARED
397 	if (0 != locks_disabled)
398 		return;
399 
400 	if (0 != pthread_mutex_lock(mutex))
401 	{
402 		zbx_error("[file:'%s',line:%d] lock failed: %s", filename, line, zbx_strerror(errno));
403 		exit(EXIT_FAILURE);
404 	}
405 #else
406 	sem_lock.sem_num = mutex;
407 	sem_lock.sem_op = -1;
408 	sem_lock.sem_flg = SEM_UNDO;
409 
410 	while (-1 == semop(ZBX_SEM_LIST_ID, &sem_lock, 1))
411 	{
412 		if (EINTR != errno)
413 		{
414 			zbx_error("[file:'%s',line:%d] lock failed: %s", filename, line, zbx_strerror(errno));
415 			exit(EXIT_FAILURE);
416 		}
417 	}
418 #endif
419 #endif
420 }
421 
422 /******************************************************************************
423  *                                                                            *
424  * Function: zbx_mutex_unlock                                                 *
425  *                                                                            *
426  * Purpose: Unlock the mutex                                                  *
427  *                                                                            *
428  * Parameters: mutex - handle of mutex                                        *
429  *                                                                            *
430  * Author: Eugene Grigorjev, Alexander Vladishev                              *
431  *                                                                            *
432  ******************************************************************************/
__zbx_mutex_unlock(const char * filename,int line,zbx_mutex_t mutex)433 void	__zbx_mutex_unlock(const char *filename, int line, zbx_mutex_t mutex)
434 {
435 #ifndef _WINDOWS
436 #ifndef	HAVE_PTHREAD_PROCESS_SHARED
437 	struct sembuf	sem_unlock;
438 #endif
439 #endif
440 
441 	if (ZBX_MUTEX_NULL == mutex)
442 		return;
443 
444 #ifdef _WINDOWS
445 	if (0 == ReleaseMutex(mutex))
446 	{
447 		zbx_error("[file:'%s',line:%d] unlock failed: %s",
448 				filename, line, strerror_from_system(GetLastError()));
449 		exit(EXIT_FAILURE);
450 	}
451 #else
452 #ifdef	HAVE_PTHREAD_PROCESS_SHARED
453 	if (0 != locks_disabled)
454 		return;
455 
456 	if (0 != pthread_mutex_unlock(mutex))
457 	{
458 		zbx_error("[file:'%s',line:%d] unlock failed: %s", filename, line, zbx_strerror(errno));
459 		exit(EXIT_FAILURE);
460 	}
461 #else
462 	sem_unlock.sem_num = mutex;
463 	sem_unlock.sem_op = 1;
464 	sem_unlock.sem_flg = SEM_UNDO;
465 
466 	while (-1 == semop(ZBX_SEM_LIST_ID, &sem_unlock, 1))
467 	{
468 		if (EINTR != errno)
469 		{
470 			zbx_error("[file:'%s',line:%d] unlock failed: %s", filename, line, zbx_strerror(errno));
471 			exit(EXIT_FAILURE);
472 		}
473 	}
474 #endif
475 #endif
476 }
477 
478 /******************************************************************************
479  *                                                                            *
480  * Function: zbx_mutex_destroy                                                *
481  *                                                                            *
482  * Purpose: Destroy the mutex                                                 *
483  *                                                                            *
484  * Parameters: mutex - handle of mutex                                        *
485  *                                                                            *
486  * Author: Eugene Grigorjev                                                   *
487  *                                                                            *
488  ******************************************************************************/
zbx_mutex_destroy(zbx_mutex_t * mutex)489 void	zbx_mutex_destroy(zbx_mutex_t *mutex)
490 {
491 #ifdef _WINDOWS
492 	if (ZBX_MUTEX_NULL == *mutex)
493 		return;
494 
495 	if (0 == CloseHandle(*mutex))
496 		zbx_error("error on mutex destroying: %s", strerror_from_system(GetLastError()));
497 #else
498 #ifdef	HAVE_PTHREAD_PROCESS_SHARED
499 	if (0 != locks_disabled)
500 		return;
501 
502 	if (0 != pthread_mutex_destroy(*mutex))
503 		zbx_error("cannot remove mutex %p: %s", (void *)mutex, zbx_strerror(errno));
504 #else
505 	if (0 == --mutexes && -1 == semctl(ZBX_SEM_LIST_ID, 0, IPC_RMID, 0))
506 		zbx_error("cannot remove semaphore set %d: %s", ZBX_SEM_LIST_ID, zbx_strerror(errno));
507 #endif
508 #endif
509 	*mutex = ZBX_MUTEX_NULL;
510 }
511 
512 #ifdef _WINDOWS
513 /******************************************************************************
514  *                                                                            *
515  * Function: zbx_mutex_create_per_process_name                                *
516  *                                                                            *
517  * Purpose: Appends PID to the prefix of the mutex                            *
518  *                                                                            *
519  * Parameters: prefix - mutex type                                            *
520  *                                                                            *
521  * Return value: Dynamically allocated, NUL terminated name of the mutex      *
522  *                                                                            *
523  * Comments: The mutex name must be shorter than MAX_PATH characters,         *
524  *           otherwise the function calls exit()                              *
525  *                                                                            *
526  ******************************************************************************/
zbx_mutex_create_per_process_name(const zbx_mutex_name_t prefix)527 zbx_mutex_name_t	zbx_mutex_create_per_process_name(const zbx_mutex_name_t prefix)
528 {
529 	zbx_mutex_name_t	name = ZBX_MUTEX_NULL;
530 	int			size;
531 	wchar_t			*format = L"%s_PID_%lx";
532 	DWORD			pid = GetCurrentProcessId();
533 
534 	/* exit if the mutex name length exceed the maximum allowed */
535 	size = _scwprintf(format, prefix, pid);
536 	if (MAX_PATH < size)
537 	{
538 		THIS_SHOULD_NEVER_HAPPEN;
539 		exit(EXIT_FAILURE);
540 	}
541 
542 	size = size + 1; /* for terminating '\0' */
543 
544 	name = zbx_malloc(NULL, sizeof(wchar_t) * size);
545 	(void)_snwprintf_s(name, size, size - 1, format, prefix, pid);
546 	name[size - 1] = L'\0';
547 
548 	return name;
549 }
550 #endif
551 
552