Commit | Line | Data |
---|---|---|
65931c8b | 1 | /* |
ab5be9fa | 2 | * Copyright (C) 2013 Mathieu Desnoyers <mathieu.desnoyers@efficios.com> |
65931c8b | 3 | * |
ab5be9fa | 4 | * SPDX-License-Identifier: GPL-2.0-only |
65931c8b | 5 | * |
65931c8b MD |
6 | */ |
7 | ||
6c1c0768 | 8 | #define _LGPL_SOURCE |
28ab034a JG |
9 | #include "health-relayd.hpp" |
10 | #include "lttng-relayd.hpp" | |
11 | ||
12 | #include <common/common.hpp> | |
13 | #include <common/compat/getenv.hpp> | |
14 | #include <common/compat/poll.hpp> | |
15 | #include <common/consumer/consumer-timer.hpp> | |
16 | #include <common/consumer/consumer.hpp> | |
17 | #include <common/defaults.hpp> | |
18 | #include <common/fd-tracker/utils.hpp> | |
19 | #include <common/sessiond-comm/sessiond-comm.hpp> | |
20 | #include <common/utils.hpp> | |
21 | ||
65931c8b MD |
22 | #include <fcntl.h> |
23 | #include <getopt.h> | |
24 | #include <grp.h> | |
28ab034a | 25 | #include <inttypes.h> |
65931c8b | 26 | #include <limits.h> |
28ab034a | 27 | #include <poll.h> |
65931c8b MD |
28 | #include <pthread.h> |
29 | #include <signal.h> | |
30 | #include <stdio.h> | |
31 | #include <stdlib.h> | |
32 | #include <string.h> | |
33 | #include <sys/ipc.h> | |
28ab034a | 34 | #include <sys/mman.h> |
65931c8b MD |
35 | #include <sys/resource.h> |
36 | #include <sys/shm.h> | |
37 | #include <sys/socket.h> | |
38 | #include <sys/stat.h> | |
39 | #include <sys/types.h> | |
65931c8b | 40 | #include <unistd.h> |
65931c8b | 41 | #include <urcu/compiler.h> |
28ab034a | 42 | #include <urcu/list.h> |
65931c8b MD |
43 | |
44 | /* Global health check unix path */ | |
28ab034a | 45 | static char health_unix_sock_path[PATH_MAX]; |
65931c8b | 46 | |
794e2e5f | 47 | int health_quit_pipe[2] = { -1, -1 }; |
65931c8b | 48 | |
65931c8b MD |
49 | /* |
50 | * Send data on a unix socket using the liblttsessiondcomm API. | |
51 | * | |
52 | * Return lttcomm error code. | |
53 | */ | |
54 | static int send_unix_sock(int sock, void *buf, size_t len) | |
55 | { | |
56 | /* Check valid length */ | |
57 | if (len == 0) { | |
58 | return -1; | |
59 | } | |
60 | ||
61 | return lttcomm_send_unix_sock(sock, buf, len); | |
62 | } | |
63 | ||
64 | static int create_lttng_rundir_with_perm(const char *rundir) | |
65 | { | |
66 | int ret; | |
67 | ||
68 | DBG3("Creating LTTng run directory: %s", rundir); | |
69 | ||
70 | ret = mkdir(rundir, S_IRWXU); | |
71 | if (ret < 0) { | |
72 | if (errno != EEXIST) { | |
73 | ERR("Unable to create %s", rundir); | |
74 | goto error; | |
75 | } else { | |
76 | ret = 0; | |
77 | } | |
78 | } else if (ret == 0) { | |
79 | int is_root = !getuid(); | |
80 | ||
81 | if (is_root) { | |
28ab59d0 JR |
82 | gid_t gid; |
83 | ||
84 | ret = utils_get_group_id(tracing_group_name, true, &gid); | |
85 | if (ret) { | |
86 | /* Default to root group. */ | |
87 | gid = 0; | |
88 | } | |
89 | ||
90 | ret = chown(rundir, 0, gid); | |
65931c8b MD |
91 | if (ret < 0) { |
92 | ERR("Unable to set group on %s", rundir); | |
93 | PERROR("chown"); | |
94 | ret = -1; | |
95 | goto error; | |
96 | } | |
97 | ||
98 | ret = chmod(rundir, | |
28ab034a JG |
99 | S_IRUSR | S_IWUSR | S_IXUSR | S_IRGRP | S_IXGRP | S_IROTH | |
100 | S_IXOTH); | |
65931c8b | 101 | if (ret < 0) { |
f7b09ec2 | 102 | ERR("Unable to set permissions on %s", rundir); |
65931c8b MD |
103 | PERROR("chmod"); |
104 | ret = -1; | |
105 | goto error; | |
106 | } | |
107 | } | |
108 | } | |
109 | ||
110 | error: | |
111 | return ret; | |
112 | } | |
113 | ||
cd9adb8b | 114 | static int parse_health_env() |
094fe907 MD |
115 | { |
116 | const char *health_path; | |
117 | ||
e8fa9fb0 | 118 | health_path = lttng_secure_getenv(LTTNG_RELAYD_HEALTH_ENV); |
094fe907 | 119 | if (health_path) { |
28ab034a | 120 | strncpy(health_unix_sock_path, health_path, PATH_MAX); |
094fe907 MD |
121 | health_unix_sock_path[PATH_MAX - 1] = '\0'; |
122 | } | |
123 | ||
124 | return 0; | |
125 | } | |
126 | ||
cd9adb8b | 127 | static int setup_health_path() |
65931c8b MD |
128 | { |
129 | int is_root, ret = 0; | |
cd9adb8b JG |
130 | const char *home_path = nullptr; |
131 | char *rundir = nullptr, *relayd_path = nullptr; | |
65931c8b | 132 | |
094fe907 MD |
133 | ret = parse_health_env(); |
134 | if (ret) { | |
135 | return ret; | |
136 | } | |
137 | ||
65931c8b MD |
138 | is_root = !getuid(); |
139 | ||
140 | if (is_root) { | |
c3844e39 | 141 | rundir = strdup(DEFAULT_LTTNG_RUNDIR); |
b6ab01aa MD |
142 | if (!rundir) { |
143 | ret = -ENOMEM; | |
144 | goto end; | |
145 | } | |
65931c8b MD |
146 | } else { |
147 | /* | |
148 | * Create rundir from home path. This will create something like | |
149 | * $HOME/.lttng | |
150 | */ | |
151 | home_path = utils_get_home_dir(); | |
152 | ||
cd9adb8b | 153 | if (home_path == nullptr) { |
65931c8b MD |
154 | /* TODO: Add --socket PATH option */ |
155 | ERR("Can't get HOME directory for sockets creation."); | |
156 | ret = -EPERM; | |
157 | goto end; | |
158 | } | |
159 | ||
c3844e39 | 160 | ret = asprintf(&rundir, DEFAULT_LTTNG_HOME_RUNDIR, home_path); |
65931c8b MD |
161 | if (ret < 0) { |
162 | ret = -ENOMEM; | |
163 | goto end; | |
164 | } | |
165 | } | |
166 | ||
c3844e39 | 167 | ret = asprintf(&relayd_path, DEFAULT_RELAYD_PATH, rundir); |
65931c8b MD |
168 | if (ret < 0) { |
169 | ret = -ENOMEM; | |
170 | goto end; | |
171 | } | |
172 | ||
c3844e39 | 173 | ret = create_lttng_rundir_with_perm(rundir); |
65931c8b MD |
174 | if (ret < 0) { |
175 | goto end; | |
176 | } | |
177 | ||
178 | ret = create_lttng_rundir_with_perm(relayd_path); | |
179 | if (ret < 0) { | |
180 | goto end; | |
181 | } | |
182 | ||
183 | if (is_root) { | |
184 | if (strlen(health_unix_sock_path) != 0) { | |
185 | goto end; | |
186 | } | |
28ab034a JG |
187 | snprintf(health_unix_sock_path, |
188 | sizeof(health_unix_sock_path), | |
189 | DEFAULT_GLOBAL_RELAY_HEALTH_UNIX_SOCK, | |
190 | (int) getpid()); | |
65931c8b MD |
191 | } else { |
192 | /* Set health check Unix path */ | |
193 | if (strlen(health_unix_sock_path) != 0) { | |
194 | goto end; | |
195 | } | |
196 | ||
28ab034a JG |
197 | snprintf(health_unix_sock_path, |
198 | sizeof(health_unix_sock_path), | |
199 | DEFAULT_HOME_RELAY_HEALTH_UNIX_SOCK, | |
200 | home_path, | |
201 | (int) getpid()); | |
65931c8b MD |
202 | } |
203 | ||
204 | end: | |
c3844e39 | 205 | free(rundir); |
edd94901 | 206 | free(relayd_path); |
65931c8b MD |
207 | return ret; |
208 | } | |
209 | ||
28ab034a | 210 | static int accept_unix_socket(void *data, int *out_fd) |
1c9bd75b JG |
211 | { |
212 | int ret; | |
213 | int accepting_sock = *((int *) data); | |
214 | ||
215 | ret = lttcomm_accept_unix_sock(accepting_sock); | |
216 | if (ret < 0) { | |
217 | goto end; | |
218 | } | |
219 | ||
220 | *out_fd = ret; | |
221 | ret = 0; | |
222 | end: | |
223 | return ret; | |
224 | } | |
225 | ||
28ab034a | 226 | static int open_unix_socket(void *data, int *out_fd) |
d7eddab9 JG |
227 | { |
228 | int ret; | |
ac497a37 | 229 | const char *path = (const char *) data; |
d7eddab9 JG |
230 | |
231 | ret = lttcomm_create_unix_sock(path); | |
232 | if (ret < 0) { | |
233 | goto end; | |
234 | } | |
235 | ||
236 | *out_fd = ret; | |
237 | ret = 0; | |
238 | end: | |
239 | return ret; | |
240 | } | |
241 | ||
65931c8b MD |
242 | /* |
243 | * Thread managing health check socket. | |
244 | */ | |
f46376a1 | 245 | void *thread_manage_health_relayd(void *data __attribute__((unused))) |
65931c8b | 246 | { |
8a00688e MJ |
247 | int sock = -1, new_sock = -1, ret, i, err = -1; |
248 | uint32_t nb_fd; | |
65931c8b MD |
249 | struct lttng_poll_event events; |
250 | struct health_comm_msg msg; | |
251 | struct health_comm_reply reply; | |
252 | int is_root; | |
d7eddab9 | 253 | char *sock_name; |
65931c8b MD |
254 | |
255 | DBG("[thread] Manage health check started"); | |
256 | ||
257 | setup_health_path(); | |
258 | ||
259 | rcu_register_thread(); | |
260 | ||
261 | /* We might hit an error path before this is created. */ | |
262 | lttng_poll_init(&events); | |
263 | ||
264 | /* Create unix socket */ | |
d7eddab9 JG |
265 | ret = asprintf(&sock_name, "Unix socket @ %s", health_unix_sock_path); |
266 | if (ret == -1) { | |
267 | PERROR("Failed to allocate unix socket name"); | |
268 | err = -1; | |
269 | goto error; | |
270 | } | |
28ab034a JG |
271 | ret = fd_tracker_open_unsuspendable_fd(the_fd_tracker, |
272 | &sock, | |
273 | (const char **) &sock_name, | |
274 | 1, | |
275 | open_unix_socket, | |
276 | health_unix_sock_path); | |
d7eddab9 JG |
277 | free(sock_name); |
278 | if (ret < 0) { | |
65931c8b | 279 | ERR("Unable to create health check Unix socket"); |
7568ddbf | 280 | err = -1; |
65931c8b MD |
281 | goto error; |
282 | } | |
283 | ||
284 | is_root = !getuid(); | |
285 | if (is_root) { | |
286 | /* lttng health client socket path permissions */ | |
28ab59d0 JR |
287 | gid_t gid; |
288 | ||
289 | ret = utils_get_group_id(tracing_group_name, true, &gid); | |
290 | if (ret) { | |
291 | /* Default to root group. */ | |
292 | gid = 0; | |
293 | } | |
294 | ||
295 | ret = chown(health_unix_sock_path, 0, gid); | |
65931c8b MD |
296 | if (ret < 0) { |
297 | ERR("Unable to set group on %s", health_unix_sock_path); | |
298 | PERROR("chown"); | |
7568ddbf | 299 | err = -1; |
65931c8b MD |
300 | goto error; |
301 | } | |
302 | ||
28ab034a | 303 | ret = chmod(health_unix_sock_path, S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP); |
65931c8b MD |
304 | if (ret < 0) { |
305 | ERR("Unable to set permissions on %s", health_unix_sock_path); | |
306 | PERROR("chmod"); | |
7568ddbf | 307 | err = -1; |
65931c8b MD |
308 | goto error; |
309 | } | |
310 | } | |
311 | ||
312 | /* | |
313 | * Set the CLOEXEC flag. Return code is useless because either way, the | |
314 | * show must go on. | |
315 | */ | |
316 | (void) utils_set_fd_cloexec(sock); | |
317 | ||
318 | ret = lttcomm_listen_unix_sock(sock); | |
319 | if (ret < 0) { | |
320 | goto error; | |
321 | } | |
322 | ||
aa91fbc5 | 323 | /* Size is set to 2 for the unix socket and quit pipe. */ |
28ab034a JG |
324 | ret = fd_tracker_util_poll_create( |
325 | the_fd_tracker, "Health management thread epoll", &events, 2, LTTNG_CLOEXEC); | |
65931c8b MD |
326 | if (ret < 0) { |
327 | ERR("Poll set creation failed"); | |
328 | goto error; | |
329 | } | |
330 | ||
331 | ret = lttng_poll_add(&events, health_quit_pipe[0], LPOLLIN); | |
332 | if (ret < 0) { | |
333 | goto error; | |
334 | } | |
335 | ||
336 | /* Add the application registration socket */ | |
337 | ret = lttng_poll_add(&events, sock, LPOLLIN | LPOLLPRI); | |
338 | if (ret < 0) { | |
339 | goto error; | |
340 | } | |
341 | ||
3fd27398 MD |
342 | lttng_relay_notify_ready(); |
343 | ||
cd9adb8b | 344 | while (true) { |
1c9bd75b JG |
345 | char *accepted_socket_name; |
346 | ||
65931c8b MD |
347 | DBG("Health check ready"); |
348 | ||
349 | /* Inifinite blocking call, waiting for transmission */ | |
28ab034a | 350 | restart: |
65931c8b MD |
351 | ret = lttng_poll_wait(&events, -1); |
352 | if (ret < 0) { | |
353 | /* | |
354 | * Restart interrupted system call. | |
355 | */ | |
356 | if (errno == EINTR) { | |
357 | goto restart; | |
358 | } | |
359 | goto error; | |
360 | } | |
361 | ||
362 | nb_fd = ret; | |
363 | ||
364 | for (i = 0; i < nb_fd; i++) { | |
365 | /* Fetch once the poll data */ | |
8a00688e MJ |
366 | const auto revents = LTTNG_POLL_GETEV(&events, i); |
367 | const auto pollfd = LTTNG_POLL_GETFD(&events, i); | |
65931c8b | 368 | |
8a00688e MJ |
369 | /* Activity on thread quit pipe, exiting. */ |
370 | if (pollfd == health_quit_pipe[0]) { | |
371 | DBG("Activity on thread quit pipe"); | |
65931c8b MD |
372 | err = 0; |
373 | goto exit; | |
374 | } | |
375 | ||
376 | /* Event on the registration socket */ | |
377 | if (pollfd == sock) { | |
03e43155 MD |
378 | if (revents & LPOLLIN) { |
379 | continue; | |
380 | } else if (revents & (LPOLLERR | LPOLLHUP | LPOLLRDHUP)) { | |
65931c8b MD |
381 | ERR("Health socket poll error"); |
382 | goto error; | |
03e43155 | 383 | } else { |
28ab034a JG |
384 | ERR("Unexpected poll events %u for sock %d", |
385 | revents, | |
386 | pollfd); | |
03e43155 | 387 | goto error; |
65931c8b MD |
388 | } |
389 | } | |
390 | } | |
391 | ||
28ab034a JG |
392 | ret = asprintf(&accepted_socket_name, |
393 | "Socket accepted from unix socket @ %s", | |
394 | health_unix_sock_path); | |
1c9bd75b JG |
395 | if (ret == -1) { |
396 | PERROR("Failed to allocate name of accepted socket from unix socket @ %s", | |
28ab034a | 397 | health_unix_sock_path); |
1c9bd75b JG |
398 | goto error; |
399 | } | |
28ab034a JG |
400 | ret = fd_tracker_open_unsuspendable_fd(the_fd_tracker, |
401 | &new_sock, | |
402 | (const char **) &accepted_socket_name, | |
403 | 1, | |
404 | accept_unix_socket, | |
405 | &sock); | |
1c9bd75b JG |
406 | free(accepted_socket_name); |
407 | if (ret < 0) { | |
65931c8b MD |
408 | goto error; |
409 | } | |
410 | ||
411 | /* | |
412 | * Set the CLOEXEC flag. Return code is useless because either way, the | |
413 | * show must go on. | |
414 | */ | |
415 | (void) utils_set_fd_cloexec(new_sock); | |
416 | ||
417 | DBG("Receiving data from client for health..."); | |
28ab034a | 418 | ret = lttcomm_recv_unix_sock(new_sock, (void *) &msg, sizeof(msg)); |
65931c8b MD |
419 | if (ret <= 0) { |
420 | DBG("Nothing recv() from client... continuing"); | |
28ab034a | 421 | ret = fd_tracker_close_unsuspendable_fd( |
cd9adb8b | 422 | the_fd_tracker, &new_sock, 1, fd_tracker_util_close_fd, nullptr); |
65931c8b MD |
423 | if (ret) { |
424 | PERROR("close"); | |
425 | } | |
426 | new_sock = -1; | |
427 | continue; | |
428 | } | |
429 | ||
430 | rcu_thread_online(); | |
431 | ||
a0377dfe | 432 | LTTNG_ASSERT(msg.cmd == HEALTH_CMD_CHECK); |
65931c8b | 433 | |
53efb85a | 434 | memset(&reply, 0, sizeof(reply)); |
65931c8b MD |
435 | for (i = 0; i < NR_HEALTH_RELAYD_TYPES; i++) { |
436 | /* | |
437 | * health_check_state return 0 if thread is in | |
438 | * error. | |
439 | */ | |
440 | if (!health_check_state(health_relayd, i)) { | |
441 | reply.ret_code |= 1ULL << i; | |
442 | } | |
443 | } | |
444 | ||
445 | DBG2("Health check return value %" PRIx64, reply.ret_code); | |
446 | ||
447 | ret = send_unix_sock(new_sock, (void *) &reply, sizeof(reply)); | |
448 | if (ret < 0) { | |
449 | ERR("Failed to send health data back to client"); | |
450 | } | |
451 | ||
452 | /* End of transmission */ | |
28ab034a | 453 | ret = fd_tracker_close_unsuspendable_fd( |
cd9adb8b | 454 | the_fd_tracker, &new_sock, 1, fd_tracker_util_close_fd, nullptr); |
65931c8b MD |
455 | if (ret) { |
456 | PERROR("close"); | |
457 | } | |
458 | new_sock = -1; | |
459 | } | |
460 | ||
65931c8b | 461 | error: |
81714439 JG |
462 | lttng_relay_stop_threads(); |
463 | exit: | |
65931c8b MD |
464 | if (err) { |
465 | ERR("Health error occurred in %s", __func__); | |
466 | } | |
467 | DBG("Health check thread dying"); | |
468 | unlink(health_unix_sock_path); | |
469 | if (sock >= 0) { | |
28ab034a | 470 | ret = fd_tracker_close_unsuspendable_fd( |
cd9adb8b | 471 | the_fd_tracker, &sock, 1, fd_tracker_util_close_fd, nullptr); |
65931c8b MD |
472 | if (ret) { |
473 | PERROR("close"); | |
474 | } | |
475 | } | |
476 | ||
dcbcae3e MD |
477 | /* |
478 | * We do NOT rmdir rundir nor the relayd path because there are | |
479 | * other processes using them. | |
480 | */ | |
481 | ||
aa91fbc5 | 482 | (void) fd_tracker_util_poll_clean(the_fd_tracker, &events); |
65931c8b MD |
483 | |
484 | rcu_unregister_thread(); | |
cd9adb8b | 485 | return nullptr; |
65931c8b | 486 | } |