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_mutex_addr_get                                               *
176  *                                                                            *
177  * Purpose: acquire address of the mutex                                      *
178  *                                                                            *
179  * Parameters: mutex_name - name of the mutex to return address for           *
180  *                                                                            *
181  * Return value: address of the mutex                                         *
182  *                                                                            *
183  ******************************************************************************/
zbx_mutex_addr_get(zbx_mutex_name_t mutex_name)184 zbx_mutex_t	zbx_mutex_addr_get(zbx_mutex_name_t mutex_name)
185 {
186 #ifdef HAVE_PTHREAD_PROCESS_SHARED
187 	return &shared_lock->mutexes[mutex_name];
188 #else
189 	return mutex_name;
190 #endif
191 }
192 
193 /******************************************************************************
194  *                                                                            *
195  * Function: zbx_mutex_addr_get                                               *
196  *                                                                            *
197  * Purpose: acquire address of the rwlock                                     *
198  *                                                                            *
199  * Parameters: rwlock_name - name of the rwlock to return address for         *
200  *                                                                            *
201  * Return value: address of the rwlock                                        *
202  *                                                                            *
203  ******************************************************************************/
zbx_rwlock_addr_get(zbx_rwlock_name_t rwlock_name)204 zbx_rwlock_t	zbx_rwlock_addr_get(zbx_rwlock_name_t rwlock_name)
205 {
206 #ifdef HAVE_PTHREAD_PROCESS_SHARED
207 	return &shared_lock->rwlocks[rwlock_name];
208 #else
209 	return rwlock_name + ZBX_MUTEX_COUNT;
210 #endif
211 }
212 
213 /******************************************************************************
214  *                                                                            *
215  * Function: zbx_rwlock_create                                                *
216  *                                                                            *
217  * Purpose: read-write locks are created using zbx_locks_create() function    *
218  *          this is only to obtain handle, if read write locks are not        *
219  *          supported, then outputs numeric handle of mutex that can be used  *
220  *          with mutex handling functions                                     *
221  *                                                                            *
222  * Parameters:  rwlock - read-write lock handle if supported, otherwise mutex *
223  *              name - name of read-write lock (index for nix system)         *
224  *              error - unused                                                *
225  *                                                                            *
226  * Return value: SUCCEED if mutexes successfully created, otherwise FAIL      *
227  *                                                                            *
228  ******************************************************************************/
zbx_rwlock_create(zbx_rwlock_t * rwlock,zbx_rwlock_name_t name,char ** error)229 int	zbx_rwlock_create(zbx_rwlock_t *rwlock, zbx_rwlock_name_t name, char **error)
230 {
231 	ZBX_UNUSED(error);
232 #ifdef HAVE_PTHREAD_PROCESS_SHARED
233 	*rwlock = &shared_lock->rwlocks[name];
234 #else
235 	*rwlock = name + ZBX_MUTEX_COUNT;
236 	mutexes++;
237 #endif
238 	return SUCCEED;
239 }
240 #ifdef HAVE_PTHREAD_PROCESS_SHARED
241 /******************************************************************************
242  *                                                                            *
243  * Function: __zbx_rwlock_wrlock                                              *
244  *                                                                            *
245  * Purpose: acquire write lock for read-write lock (exclusive access)         *
246  *                                                                            *
247  * Parameters: rwlock - handle of read-write lock                             *
248  *                                                                            *
249  ******************************************************************************/
__zbx_rwlock_wrlock(const char * filename,int line,zbx_rwlock_t rwlock)250 void	__zbx_rwlock_wrlock(const char *filename, int line, zbx_rwlock_t rwlock)
251 {
252 	if (ZBX_RWLOCK_NULL == rwlock)
253 		return;
254 
255 	if (0 != locks_disabled)
256 		return;
257 
258 	if (0 != pthread_rwlock_wrlock(rwlock))
259 	{
260 		zbx_error("[file:'%s',line:%d] write lock failed: %s", filename, line, zbx_strerror(errno));
261 		exit(EXIT_FAILURE);
262 	}
263 }
264 
265 /******************************************************************************
266  *                                                                            *
267  * Function: __zbx_rwlock_rdlock                                              *
268  *                                                                            *
269  * Purpose: acquire read lock for read-write lock (there can be many readers) *
270  *                                                                            *
271  * Parameters: rwlock - handle of read-write lock                             *
272  *                                                                            *
273  ******************************************************************************/
__zbx_rwlock_rdlock(const char * filename,int line,zbx_rwlock_t rwlock)274 void	__zbx_rwlock_rdlock(const char *filename, int line, zbx_rwlock_t rwlock)
275 {
276 	if (ZBX_RWLOCK_NULL == rwlock)
277 		return;
278 
279 	if (0 != locks_disabled)
280 		return;
281 
282 	if (0 != pthread_rwlock_rdlock(rwlock))
283 	{
284 		zbx_error("[file:'%s',line:%d] read lock failed: %s", filename, line, zbx_strerror(errno));
285 		exit(EXIT_FAILURE);
286 	}
287 }
288 
289 /******************************************************************************
290  *                                                                            *
291  * Function: __zbx_rwlock_unlock                                              *
292  *                                                                            *
293  * Purpose: unlock read-write lock                                            *
294  *                                                                            *
295  * Parameters: rwlock - handle of read-write lock                             *
296  *                                                                            *
297  ******************************************************************************/
__zbx_rwlock_unlock(const char * filename,int line,zbx_rwlock_t rwlock)298 void	__zbx_rwlock_unlock(const char *filename, int line, zbx_rwlock_t rwlock)
299 {
300 	if (ZBX_RWLOCK_NULL == rwlock)
301 		return;
302 
303 	if (0 != locks_disabled)
304 		return;
305 
306 	if (0 != pthread_rwlock_unlock(rwlock))
307 	{
308 		zbx_error("[file:'%s',line:%d] read-write lock unlock failed: %s", filename, line, zbx_strerror(errno));
309 		exit(EXIT_FAILURE);
310 	}
311 }
312 
313 /******************************************************************************
314  *                                                                            *
315  * Function: zbx_rwlock_destroy                                               *
316  *                                                                            *
317  * Purpose: Destroy read-write lock                                           *
318  *                                                                            *
319  * Parameters: rwlock - handle of read-write lock                             *
320  *                                                                            *
321  *                                                                            *
322  ******************************************************************************/
323 
zbx_rwlock_destroy(zbx_rwlock_t * rwlock)324 void	zbx_rwlock_destroy(zbx_rwlock_t *rwlock)
325 {
326 	if (ZBX_RWLOCK_NULL == *rwlock)
327 		return;
328 
329 	if (0 != locks_disabled)
330 		return;
331 
332 	if (0 != pthread_rwlock_destroy(*rwlock))
333 		zbx_error("cannot remove read-write lock: %s", zbx_strerror(errno));
334 
335 	*rwlock = ZBX_RWLOCK_NULL;
336 }
337 
338 /******************************************************************************
339  *                                                                            *
340  * Function: zbx_locks_disable                                                *
341  *                                                                            *
342  * Purpose:  disable locks                                                    *
343  *                                                                            *
344  ******************************************************************************/
zbx_locks_disable(void)345 void	zbx_locks_disable(void)
346 {
347 	/* attempting to destroy a locked pthread mutex results in undefined behavior */
348 	locks_disabled = 1;
349 }
350 #endif
351 #endif	/* _WINDOWS */
352 
353 /******************************************************************************
354  *                                                                            *
355  * Function: zbx_mutex_create                                                 *
356  *                                                                            *
357  * Purpose: Create the mutex                                                  *
358  *                                                                            *
359  * Parameters:  mutex - handle of mutex                                       *
360  *              name - name of mutex (index for nix system)                   *
361  *                                                                            *
362  * Return value: If the function succeeds, then return SUCCEED,               *
363  *               FAIL on an error                                             *
364  *                                                                            *
365  * Author: Eugene Grigorjev                                                   *
366  *                                                                            *
367  ******************************************************************************/
zbx_mutex_create(zbx_mutex_t * mutex,zbx_mutex_name_t name,char ** error)368 int	zbx_mutex_create(zbx_mutex_t *mutex, zbx_mutex_name_t name, char **error)
369 {
370 #ifdef _WINDOWS
371 	if (NULL == (*mutex = CreateMutex(NULL, FALSE, name)))
372 	{
373 		*error = zbx_dsprintf(*error, "error on mutex creating: %s", strerror_from_system(GetLastError()));
374 		return FAIL;
375 	}
376 #else
377 	ZBX_UNUSED(error);
378 #ifdef	HAVE_PTHREAD_PROCESS_SHARED
379 	*mutex = &shared_lock->mutexes[name];
380 #else
381 	mutexes++;
382 	*mutex = name;
383 #endif
384 #endif
385 	return SUCCEED;
386 }
387 
388 /******************************************************************************
389  *                                                                            *
390  * Function: zbx_mutex_lock                                                   *
391  *                                                                            *
392  * Purpose: Waits until the mutex is in the signalled state                   *
393  *                                                                            *
394  * Parameters: mutex - handle of mutex                                        *
395  *                                                                            *
396  * Author: Eugene Grigorjev, Alexander Vladishev                              *
397  *                                                                            *
398  ******************************************************************************/
__zbx_mutex_lock(const char * filename,int line,zbx_mutex_t mutex)399 void	__zbx_mutex_lock(const char *filename, int line, zbx_mutex_t mutex)
400 {
401 #ifndef _WINDOWS
402 #ifndef	HAVE_PTHREAD_PROCESS_SHARED
403 	struct sembuf	sem_lock;
404 #endif
405 #else
406 	DWORD   dwWaitResult;
407 #endif
408 
409 	if (ZBX_MUTEX_NULL == mutex)
410 		return;
411 
412 #ifdef _WINDOWS
413 #ifdef ZABBIX_AGENT
414 	if (0 != (ZBX_MUTEX_THREAD_DENIED & get_thread_global_mutex_flag()))
415 	{
416 		zbx_error("[file:'%s',line:%d] lock failed: ZBX_MUTEX_THREAD_DENIED is set for thread with id = %d",
417 				filename, line, zbx_get_thread_id());
418 		exit(EXIT_FAILURE);
419 	}
420 #endif
421 	dwWaitResult = WaitForSingleObject(mutex, INFINITE);
422 
423 	switch (dwWaitResult)
424 	{
425 		case WAIT_OBJECT_0:
426 			break;
427 		case WAIT_ABANDONED:
428 			THIS_SHOULD_NEVER_HAPPEN;
429 			exit(EXIT_FAILURE);
430 		default:
431 			zbx_error("[file:'%s',line:%d] lock failed: %s",
432 				filename, line, strerror_from_system(GetLastError()));
433 			exit(EXIT_FAILURE);
434 	}
435 #else
436 #ifdef	HAVE_PTHREAD_PROCESS_SHARED
437 	if (0 != locks_disabled)
438 		return;
439 
440 	if (0 != pthread_mutex_lock(mutex))
441 	{
442 		zbx_error("[file:'%s',line:%d] lock failed: %s", filename, line, zbx_strerror(errno));
443 		exit(EXIT_FAILURE);
444 	}
445 #else
446 	sem_lock.sem_num = mutex;
447 	sem_lock.sem_op = -1;
448 	sem_lock.sem_flg = SEM_UNDO;
449 
450 	while (-1 == semop(ZBX_SEM_LIST_ID, &sem_lock, 1))
451 	{
452 		if (EINTR != errno)
453 		{
454 			zbx_error("[file:'%s',line:%d] lock failed: %s", filename, line, zbx_strerror(errno));
455 			exit(EXIT_FAILURE);
456 		}
457 	}
458 #endif
459 #endif
460 }
461 
462 /******************************************************************************
463  *                                                                            *
464  * Function: zbx_mutex_unlock                                                 *
465  *                                                                            *
466  * Purpose: Unlock the mutex                                                  *
467  *                                                                            *
468  * Parameters: mutex - handle of mutex                                        *
469  *                                                                            *
470  * Author: Eugene Grigorjev, Alexander Vladishev                              *
471  *                                                                            *
472  ******************************************************************************/
__zbx_mutex_unlock(const char * filename,int line,zbx_mutex_t mutex)473 void	__zbx_mutex_unlock(const char *filename, int line, zbx_mutex_t mutex)
474 {
475 #ifndef _WINDOWS
476 #ifndef	HAVE_PTHREAD_PROCESS_SHARED
477 	struct sembuf	sem_unlock;
478 #endif
479 #endif
480 
481 	if (ZBX_MUTEX_NULL == mutex)
482 		return;
483 
484 #ifdef _WINDOWS
485 	if (0 == ReleaseMutex(mutex))
486 	{
487 		zbx_error("[file:'%s',line:%d] unlock failed: %s",
488 				filename, line, strerror_from_system(GetLastError()));
489 		exit(EXIT_FAILURE);
490 	}
491 #else
492 #ifdef	HAVE_PTHREAD_PROCESS_SHARED
493 	if (0 != locks_disabled)
494 		return;
495 
496 	if (0 != pthread_mutex_unlock(mutex))
497 	{
498 		zbx_error("[file:'%s',line:%d] unlock failed: %s", filename, line, zbx_strerror(errno));
499 		exit(EXIT_FAILURE);
500 	}
501 #else
502 	sem_unlock.sem_num = mutex;
503 	sem_unlock.sem_op = 1;
504 	sem_unlock.sem_flg = SEM_UNDO;
505 
506 	while (-1 == semop(ZBX_SEM_LIST_ID, &sem_unlock, 1))
507 	{
508 		if (EINTR != errno)
509 		{
510 			zbx_error("[file:'%s',line:%d] unlock failed: %s", filename, line, zbx_strerror(errno));
511 			exit(EXIT_FAILURE);
512 		}
513 	}
514 #endif
515 #endif
516 }
517 
518 /******************************************************************************
519  *                                                                            *
520  * Function: zbx_mutex_destroy                                                *
521  *                                                                            *
522  * Purpose: Destroy the mutex                                                 *
523  *                                                                            *
524  * Parameters: mutex - handle of mutex                                        *
525  *                                                                            *
526  * Author: Eugene Grigorjev                                                   *
527  *                                                                            *
528  ******************************************************************************/
zbx_mutex_destroy(zbx_mutex_t * mutex)529 void	zbx_mutex_destroy(zbx_mutex_t *mutex)
530 {
531 #ifdef _WINDOWS
532 	if (ZBX_MUTEX_NULL == *mutex)
533 		return;
534 
535 	if (0 == CloseHandle(*mutex))
536 		zbx_error("error on mutex destroying: %s", strerror_from_system(GetLastError()));
537 #else
538 #ifdef	HAVE_PTHREAD_PROCESS_SHARED
539 	if (0 != locks_disabled)
540 		return;
541 
542 	if (0 != pthread_mutex_destroy(*mutex))
543 		zbx_error("cannot remove mutex %p: %s", (void *)mutex, zbx_strerror(errno));
544 #else
545 	if (0 == --mutexes && -1 == semctl(ZBX_SEM_LIST_ID, 0, IPC_RMID, 0))
546 		zbx_error("cannot remove semaphore set %d: %s", ZBX_SEM_LIST_ID, zbx_strerror(errno));
547 #endif
548 #endif
549 	*mutex = ZBX_MUTEX_NULL;
550 }
551 
552 #ifdef _WINDOWS
553 /******************************************************************************
554  *                                                                            *
555  * Function: zbx_mutex_create_per_process_name                                *
556  *                                                                            *
557  * Purpose: Appends PID to the prefix of the mutex                            *
558  *                                                                            *
559  * Parameters: prefix - mutex type                                            *
560  *                                                                            *
561  * Return value: Dynamically allocated, NUL terminated name of the mutex      *
562  *                                                                            *
563  * Comments: The mutex name must be shorter than MAX_PATH characters,         *
564  *           otherwise the function calls exit()                              *
565  *                                                                            *
566  ******************************************************************************/
zbx_mutex_create_per_process_name(const zbx_mutex_name_t prefix)567 zbx_mutex_name_t	zbx_mutex_create_per_process_name(const zbx_mutex_name_t prefix)
568 {
569 	zbx_mutex_name_t	name = ZBX_MUTEX_NULL;
570 	int			size;
571 	wchar_t			*format = L"%s_PID_%lx";
572 	DWORD			pid = GetCurrentProcessId();
573 
574 	/* exit if the mutex name length exceed the maximum allowed */
575 	size = _scwprintf(format, prefix, pid);
576 	if (MAX_PATH < size)
577 	{
578 		THIS_SHOULD_NEVER_HAPPEN;
579 		exit(EXIT_FAILURE);
580 	}
581 
582 	size = size + 1; /* for terminating '\0' */
583 
584 	name = zbx_malloc(NULL, sizeof(wchar_t) * size);
585 	(void)_snwprintf_s(name, size, size - 1, format, prefix, pid);
586 	name[size - 1] = L'\0';
587 
588 	return name;
589 }
590 #endif
591 
592