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 #endif
27
28 #ifndef _WINDOWS
29 # if !HAVE_SEMUN
30 union semun
31 {
32 int val; /* value for SETVAL */
33 struct semid_ds *buf; /* buffer for IPC_STAT & IPC_SET */
34 unsigned short int *array; /* array for GETALL & SETALL */
35 struct seminfo *__buf; /* buffer for IPC_INFO */
36 };
37
38 # undef HAVE_SEMUN
39 # define HAVE_SEMUN 1
40 # endif /* HAVE_SEMUN */
41
42 # include "cfg.h"
43 # include "threads.h"
44
45 static int ZBX_SEM_LIST_ID = -1;
46 static unsigned char mutexes = 0;
47 #endif
48
49 /******************************************************************************
50 * *
51 * Function: zbx_mutex_create_ext *
52 * *
53 * Purpose: Create the mutex *
54 * *
55 * Parameters: mutex - handle of mutex *
56 * name - name of mutex (index for nix system) *
57 * forced - remove mutex if exists (only for nix) *
58 * *
59 * Return value: If the function succeeds, then return SUCCEED, *
60 * FAIL on an error *
61 * *
62 * Author: Eugene Grigorjev *
63 * *
64 * Comments: use alias 'zbx_mutex_create' and 'zbx_mutex_create_force' *
65 * *
66 ******************************************************************************/
zbx_mutex_create_ext(ZBX_MUTEX * mutex,ZBX_MUTEX_NAME name,unsigned char forced)67 int zbx_mutex_create_ext(ZBX_MUTEX *mutex, ZBX_MUTEX_NAME name, unsigned char forced)
68 {
69 #ifdef _WINDOWS
70
71 if (NULL == (*mutex = CreateMutex(NULL, FALSE, name)))
72 {
73 zbx_error("error on mutex creating: %s", strerror_from_system(GetLastError()));
74 return FAIL;
75 }
76
77 #else
78
79 #define ZBX_MAX_ATTEMPTS 10
80 int attempts = 0, i;
81 key_t sem_key;
82 union semun semopts;
83 struct semid_ds seminfo;
84
85 if (-1 == (sem_key = ftok(CONFIG_FILE, (int)'z')))
86 {
87 zbx_error("cannot create IPC key for path '%s', try to create for path '.': %s",
88 CONFIG_FILE, zbx_strerror(errno));
89
90 if (-1 == (sem_key = ftok(".", (int)'z')))
91 {
92 zbx_error("cannot create IPC key for path '.': %s", zbx_strerror(errno));
93 return FAIL;
94 }
95 }
96 lbl_create:
97 if (-1 != ZBX_SEM_LIST_ID || -1 != (ZBX_SEM_LIST_ID = semget(sem_key, ZBX_MUTEX_COUNT, IPC_CREAT | IPC_EXCL | 0600 /* 0022 */)))
98 {
99 /* set default semaphore value */
100
101 semopts.val = 1;
102 for (i = 0; ZBX_MUTEX_COUNT > i; i++)
103 {
104 if (-1 == semctl(ZBX_SEM_LIST_ID, i, SETVAL, semopts))
105 {
106 zbx_error("semaphore [%i] error in semctl(SETVAL): %s", name, zbx_strerror(errno));
107 return FAIL;
108
109 }
110
111 zbx_mutex_lock(&i); /* call semop to update sem_otime */
112 zbx_mutex_unlock(&i); /* release semaphore */
113 }
114 }
115 else if (EEXIST == errno)
116 {
117 ZBX_SEM_LIST_ID = semget(sem_key, 0 /* get reference */, 0600 /* 0022 */);
118
119 if (1 == forced)
120 {
121 if (0 != semctl(ZBX_SEM_LIST_ID, 0, IPC_RMID, 0))
122 {
123 zbx_error("cannot recreate Zabbix semaphores for IPC key 0x%lx Semaphore ID %ld: %s",
124 sem_key, ZBX_SEM_LIST_ID, zbx_strerror(errno));
125 exit(EXIT_FAILURE);
126 }
127
128 /* semaphore is successfully removed */
129 ZBX_SEM_LIST_ID = -1;
130
131 if (ZBX_MAX_ATTEMPTS < ++attempts)
132 {
133 zbx_error("cannot recreate Zabbix semaphores for IPC key 0x%lx: too many attempts",
134 sem_key);
135 exit(EXIT_FAILURE);
136 }
137
138 if ((ZBX_MAX_ATTEMPTS / 2) < attempts)
139 zbx_sleep(1);
140
141 goto lbl_create;
142 }
143
144 semopts.buf = &seminfo;
145 /* wait for initialization */
146 for (i = 0; ZBX_MUTEX_MAX_TRIES > i; i++)
147 {
148 if (-1 == semctl(ZBX_SEM_LIST_ID, 0, IPC_STAT, semopts))
149 {
150 zbx_error("semaphore [%i] error in semctl(IPC_STAT): %s",
151 name, zbx_strerror(errno));
152 break;
153 }
154
155 if (0 != semopts.buf->sem_otime)
156 goto lbl_return;
157
158 zbx_sleep(1);
159 }
160
161 zbx_error("semaphore [%i] not initialized", name);
162 return FAIL;
163 }
164 else
165 {
166 zbx_error("cannot create Semaphore: %s", zbx_strerror(errno));
167 return FAIL;
168 }
169 lbl_return:
170 *mutex = name;
171 mutexes++;
172
173 #endif /* _WINDOWS */
174
175 return SUCCEED;
176 }
177
178 /******************************************************************************
179 * *
180 * Function: zbx_mutex_lock *
181 * *
182 * Purpose: Waits until the mutex is in the signalled state *
183 * *
184 * Parameters: mutex - handle of mutex *
185 * *
186 * Author: Eugene Grigorjev, Alexander Vladishev *
187 * *
188 ******************************************************************************/
__zbx_mutex_lock(const char * filename,int line,ZBX_MUTEX * mutex)189 void __zbx_mutex_lock(const char *filename, int line, ZBX_MUTEX *mutex)
190 {
191 #ifndef _WINDOWS
192 struct sembuf sem_lock;
193 #else
194 DWORD dwWaitResult;
195 int pass = 0;
196 #endif
197
198 if (ZBX_MUTEX_NULL == *mutex)
199 return;
200
201 #ifdef _WINDOWS
202 #ifdef ZABBIX_AGENT
203 if (0 != (ZBX_MUTEX_THREAD_DENIED & get_thread_global_mutex_flag()))
204 {
205 zbx_error("[file:'%s',line:%d] lock failed: ZBX_MUTEX_THREAD_DENIED is set for thread with id = %d",
206 filename, line, zbx_get_thread_id());
207 exit(EXIT_FAILURE);
208 }
209 #endif
210 dwWaitResult = WaitForSingleObject(*mutex, INFINITE);
211
212 switch (dwWaitResult)
213 {
214 case WAIT_OBJECT_0:
215 break;
216 case WAIT_ABANDONED:
217 THIS_SHOULD_NEVER_HAPPEN;
218 exit(EXIT_FAILURE);
219 default:
220 zbx_error("[file:'%s',line:%d] lock failed: %s",
221 filename, line, strerror_from_system(GetLastError()));
222 exit(EXIT_FAILURE);
223 }
224 #else
225 sem_lock.sem_num = *mutex;
226 sem_lock.sem_op = -1;
227 sem_lock.sem_flg = SEM_UNDO;
228
229 while (-1 == semop(ZBX_SEM_LIST_ID, &sem_lock, 1))
230 {
231 if (EINTR != errno)
232 {
233 zbx_error("[file:'%s',line:%d] lock failed: %s", filename, line, zbx_strerror(errno));
234 exit(EXIT_FAILURE);
235 }
236 }
237 #endif
238 }
239
240 /******************************************************************************
241 * *
242 * Function: zbx_mutex_unlock *
243 * *
244 * Purpose: Unlock the mutex *
245 * *
246 * Parameters: mutex - handle of mutex *
247 * *
248 * Author: Eugene Grigorjev, Alexander Vladishev *
249 * *
250 ******************************************************************************/
__zbx_mutex_unlock(const char * filename,int line,ZBX_MUTEX * mutex)251 void __zbx_mutex_unlock(const char *filename, int line, ZBX_MUTEX *mutex)
252 {
253 #ifndef _WINDOWS
254 struct sembuf sem_unlock;
255 #endif
256
257 if (ZBX_MUTEX_NULL == *mutex)
258 return;
259
260 #ifdef _WINDOWS
261 if (0 == ReleaseMutex(*mutex))
262 {
263 zbx_error("[file:'%s',line:%d] unlock failed: %s",
264 filename, line, strerror_from_system(GetLastError()));
265 exit(EXIT_FAILURE);
266 }
267 #else
268 sem_unlock.sem_num = *mutex;
269 sem_unlock.sem_op = 1;
270 sem_unlock.sem_flg = SEM_UNDO;
271
272 while (-1 == semop(ZBX_SEM_LIST_ID, &sem_unlock, 1))
273 {
274 if (EINTR != errno)
275 {
276 zbx_error("[file:'%s',line:%d] unlock failed: %s", filename, line, zbx_strerror(errno));
277 exit(EXIT_FAILURE);
278 }
279 }
280 #endif
281 }
282
283 /******************************************************************************
284 * *
285 * Function: zbx_mutex_destroy *
286 * *
287 * Purpose: Destroy the mutex *
288 * *
289 * Parameters: mutex - handle of mutex *
290 * *
291 * Return value: If the function succeeds, then return 1, 0 on an error *
292 * *
293 * Author: Eugene Grigorjev *
294 * *
295 ******************************************************************************/
zbx_mutex_destroy(ZBX_MUTEX * mutex)296 int zbx_mutex_destroy(ZBX_MUTEX *mutex)
297 {
298 #ifdef _WINDOWS
299 if (ZBX_MUTEX_NULL == *mutex)
300 return SUCCEED;
301
302 if (0 == CloseHandle(*mutex))
303 {
304 zbx_error("error on mutex destroying: %s", strerror_from_system(GetLastError()));
305 return FAIL;
306 }
307 #else
308 if (0 == --mutexes)
309 semctl(ZBX_SEM_LIST_ID, 0, IPC_RMID, 0);
310 #endif
311
312 *mutex = ZBX_MUTEX_NULL;
313
314 return SUCCEED;
315 }
316
317 #ifdef _WINDOWS
318 /******************************************************************************
319 * *
320 * Function: zbx_mutex_create_per_process_name *
321 * *
322 * Purpose: Appends PID to the prefix of the mutex *
323 * *
324 * Parameters: prefix - mutex type *
325 * *
326 * Return value: Dynamically allocated, NUL terminated name of the mutex *
327 * *
328 * Comments: The mutex name must be shorter than MAX_PATH characters, *
329 * otherwise the function calls exit() *
330 * *
331 ******************************************************************************/
zbx_mutex_create_per_process_name(const ZBX_MUTEX_NAME prefix)332 ZBX_MUTEX_NAME zbx_mutex_create_per_process_name(const ZBX_MUTEX_NAME prefix)
333 {
334 ZBX_MUTEX_NAME name = ZBX_MUTEX_NULL;
335 int size;
336 wchar_t *format = L"%s_PID_%lx";
337 DWORD pid = GetCurrentProcessId();
338
339 /* exit if the mutex name length exceed the maximum allowed */
340 size = _scwprintf(format, prefix, pid);
341 if (MAX_PATH < size)
342 {
343 THIS_SHOULD_NEVER_HAPPEN;
344 exit(EXIT_FAILURE);
345 }
346
347 size = size + 1; /* for terminating '\0' */
348
349 name = zbx_malloc(NULL, sizeof(wchar_t) * size);
350 (void)_snwprintf_s(name, size, size - 1, format, prefix, pid);
351 name[size - 1] = L'\0';
352
353 return name;
354 }
355 #endif
356