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