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