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