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 "threads.h"
23 
24 #if !defined(_WINDOWS)
25 /******************************************************************************
26  *                                                                            *
27  * Function: zbx_fork                                                         *
28  *                                                                            *
29  * Purpose: Flush stdout and stderr before forking                            *
30  *                                                                            *
31  * Return value: same as system fork() function                               *
32  *                                                                            *
33  * Author: Eugene Grigorjev                                                   *
34  *                                                                            *
35  ******************************************************************************/
zbx_fork(void)36 int	zbx_fork(void)
37 {
38 	fflush(stdout);
39 	fflush(stderr);
40 	return fork();
41 }
42 
43 /******************************************************************************
44  *                                                                            *
45  * Function: zbx_child_fork                                                   *
46  *                                                                            *
47  * Purpose: fork from master process and set SIGCHLD handler                  *
48  *                                                                            *
49  * Return value: same as system fork() function                               *
50  *                                                                            *
51  * Author: Rudolfs Kreicbergs                                                 *
52  *                                                                            *
53  * Comments: use this function only for forks from the main process           *
54  *                                                                            *
55  ******************************************************************************/
zbx_child_fork(pid_t * pid)56 void	zbx_child_fork(pid_t *pid)
57 {
58 	sigset_t	mask, orig_mask;
59 
60 	/* block signals during fork to avoid deadlock (we've seen one in __unregister_atfork()) */
61 	sigemptyset(&mask);
62 	sigaddset(&mask, SIGTERM);
63 	sigaddset(&mask, SIGUSR2);
64 	sigaddset(&mask, SIGHUP);
65 	sigaddset(&mask, SIGINT);
66 	sigaddset(&mask, SIGQUIT);
67 	sigaddset(&mask, SIGCHLD);
68 
69 	sigprocmask(SIG_BLOCK, &mask, &orig_mask);
70 
71 	/* set process id instead of returning, this is to avoid race condition when signal arrives before return */
72 	*pid = zbx_fork();
73 
74 	sigprocmask(SIG_SETMASK, &orig_mask, NULL);
75 
76 	/* ignore SIGCHLD to avoid problems with exiting scripts in zbx_execute() and other cases */
77 	if (0 == *pid)
78 		signal(SIGCHLD, SIG_DFL);
79 }
80 #else
81 int	zbx_win_exception_filter(unsigned int code, struct _EXCEPTION_POINTERS *ep);
82 
ZBX_THREAD_ENTRY(zbx_win_thread_entry,args)83 static ZBX_THREAD_ENTRY(zbx_win_thread_entry, args)
84 {
85 	__try
86 	{
87 		zbx_thread_args_t	*thread_args = (zbx_thread_args_t *)args;
88 
89 		return thread_args->entry(thread_args);
90 	}
91 	__except(zbx_win_exception_filter(GetExceptionCode(), GetExceptionInformation()))
92 	{
93 		zbx_thread_exit(EXIT_SUCCESS);
94 	}
95 }
96 
ZBXEndThread(ULONG_PTR dwParam)97 void CALLBACK	ZBXEndThread(ULONG_PTR dwParam)
98 {
99 	_endthreadex(SUCCEED);
100 }
101 #endif
102 
103 /******************************************************************************
104  *                                                                            *
105  * Function: zbx_thread_start                                                 *
106  *                                                                            *
107  * Purpose: Start the handled function as "thread"                            *
108  *                                                                            *
109  * Parameters: handler     - [IN] new thread starts execution from this       *
110  *                                handler function                            *
111  *             thread_args - [IN] arguments for thread function               *
112  *             thread      - [OUT] handle to a newly created thread           *
113  *                                                                            *
114  * Author: Eugene Grigorjev                                                   *
115  *                                                                            *
116  * Comments: The zbx_thread_exit must be called from the handler!             *
117  *                                                                            *
118  ******************************************************************************/
zbx_thread_start(ZBX_THREAD_ENTRY_POINTER (handler),zbx_thread_args_t * thread_args,ZBX_THREAD_HANDLE * thread)119 void	zbx_thread_start(ZBX_THREAD_ENTRY_POINTER(handler), zbx_thread_args_t *thread_args, ZBX_THREAD_HANDLE *thread)
120 {
121 #ifdef _WINDOWS
122 	unsigned		thrdaddr;
123 
124 	thread_args->entry = handler;
125 	/* NOTE: _beginthreadex returns 0 on failure, rather than 1 */
126 	if (0 == (*thread = (ZBX_THREAD_HANDLE)_beginthreadex(NULL, 0, zbx_win_thread_entry, thread_args, 0, &thrdaddr)))
127 	{
128 		zabbix_log(LOG_LEVEL_CRIT, "failed to create a thread: %s", strerror_from_system(GetLastError()));
129 		*thread = (ZBX_THREAD_HANDLE)ZBX_THREAD_ERROR;
130 	}
131 #else
132 	zbx_child_fork(thread);
133 
134 	if (0 == *thread)	/* child process */
135 	{
136 		(*handler)(thread_args);
137 
138 		/* The zbx_thread_exit must be called from the handler. */
139 		/* And in normal case the program will never reach this point. */
140 		THIS_SHOULD_NEVER_HAPPEN;
141 		/* program will never reach this point */
142 	}
143 	else if (-1 == *thread)
144 	{
145 		zbx_error("failed to fork: %s", zbx_strerror(errno));
146 		*thread = (ZBX_THREAD_HANDLE)ZBX_THREAD_ERROR;
147 	}
148 #endif
149 }
150 
151 /******************************************************************************
152  *                                                                            *
153  * Function: zbx_thread_wait                                                  *
154  *                                                                            *
155  * Purpose: Waits until the "thread" is in the signalled state                *
156  *                                                                            *
157  * Parameters: "thread" handle                                                *
158  *                                                                            *
159  * Return value: process or thread exit code                                  *
160  *                                                                            *
161  * Author: Eugene Grigorjev                                                   *
162  *                                                                            *
163  ******************************************************************************/
zbx_thread_wait(ZBX_THREAD_HANDLE thread)164 int	zbx_thread_wait(ZBX_THREAD_HANDLE thread)
165 {
166 	int	status = 0;	/* significant 8 bits of the status */
167 
168 #ifdef _WINDOWS
169 
170 	if (WAIT_OBJECT_0 != WaitForSingleObject(thread, INFINITE))
171 	{
172 		zbx_error("Error on thread waiting. [%s]", strerror_from_system(GetLastError()));
173 		return ZBX_THREAD_ERROR;
174 	}
175 
176 	if (0 == GetExitCodeThread(thread, &status))
177 	{
178 		zbx_error("Error on thread exit code receiving. [%s]", strerror_from_system(GetLastError()));
179 		return ZBX_THREAD_ERROR;
180 	}
181 
182 	if (0 == CloseHandle(thread))
183 	{
184 		zbx_error("Error on thread closing. [%s]", strerror_from_system(GetLastError()));
185 		return ZBX_THREAD_ERROR;
186 	}
187 
188 #else	/* not _WINDOWS */
189 
190 	if (0 >= waitpid(thread, &status, 0))
191 	{
192 		zbx_error("Error waiting for process with PID %d: %s", (int)thread, zbx_strerror(errno));
193 		return ZBX_THREAD_ERROR;
194 	}
195 
196 	status = WEXITSTATUS(status);
197 
198 #endif	/* _WINDOWS */
199 
200 	return status;
201 }
202 
203 /******************************************************************************
204  *                                                                            *
205  * Function: threads_kill                                                     *
206  *                                                                            *
207  * Purpose: sends termination signal to "threads"                             *
208  *                                                                            *
209  * Parameters: threads     - [IN] handles to threads or processes             *
210  *             threads_num - [IN] number of handles                           *
211  *             ret         - [IN] terminate thread politely on SUCCEED or ask *
212  *                                threads to exit immediately on FAIL         *
213  *                                                                            *
214  ******************************************************************************/
threads_kill(ZBX_THREAD_HANDLE * threads,int threads_num,int ret)215 static void	threads_kill(ZBX_THREAD_HANDLE *threads, int threads_num, int ret)
216 {
217 	int	i;
218 
219 	for (i = 0; i < threads_num; i++)
220 	{
221 		if (!threads[i])
222 			continue;
223 
224 		if (SUCCEED != ret)
225 			zbx_thread_kill_fatal(threads[i]);
226 		else
227 			zbx_thread_kill(threads[i]);
228 	}
229 }
230 
231 /******************************************************************************
232  *                                                                            *
233  * Function: zbx_threads_wait                                                 *
234  *                                                                            *
235  * Purpose: Waits until the "threads" are in the signalled state              *
236  *                                                                            *
237  * Parameters: "threads" handles                                              *
238  *                                                                            *
239  *                                                                            *
240  ******************************************************************************/
zbx_threads_wait(ZBX_THREAD_HANDLE * threads,const int * threads_flags,int threads_num,int ret)241 void	zbx_threads_wait(ZBX_THREAD_HANDLE *threads, const int *threads_flags, int threads_num, int ret)
242 {
243 	int		i;
244 #if !defined(_WINDOWS)
245 	sigset_t	set;
246 
247 	/* ignore SIGCHLD signals in order for zbx_sleep() to work */
248 	sigemptyset(&set);
249 	sigaddset(&set, SIGCHLD);
250 	sigprocmask(SIG_BLOCK, &set, NULL);
251 
252 	/* signal all threads to go into idle state and wait for flagged threads to exit */
253 	threads_kill(threads, threads_num, ret);
254 
255 	for (i = 0; i < threads_num; i++)
256 	{
257 		if (!threads[i] || ZBX_THREAD_WAIT_EXIT != threads_flags[i])
258 			continue;
259 
260 		zbx_thread_wait(threads[i]);
261 
262 		threads[i] = ZBX_THREAD_HANDLE_NULL;
263 	}
264 
265 	/* signal idle threads to exit */
266 	threads_kill(threads, threads_num, FAIL);
267 #else
268 	/* wait for threads to finish first. although listener threads will never end */
269 	WaitForMultipleObjectsEx(threads_num, threads, TRUE, 1000, FALSE);
270 	threads_kill(threads, threads_num, ret);
271 #endif
272 
273 	for (i = 0; i < threads_num; i++)
274 	{
275 		if (!threads[i])
276 			continue;
277 
278 		zbx_thread_wait(threads[i]);
279 
280 		threads[i] = ZBX_THREAD_HANDLE_NULL;
281 	}
282 }
283 
zbx_get_thread_id(void)284 long int	zbx_get_thread_id(void)
285 {
286 #ifdef _WINDOWS
287 	return (long int)GetCurrentThreadId();
288 #else
289 	return (long int)getpid();
290 #endif
291 }
292