run_as: adapt run_as implementation to support complex payloads
[lttng-tools.git] / src / common / runas.c
... / ...
CommitLineData
1/*
2 * Copyright (C) 2011 - David Goulet <david.goulet@polymtl.ca>
3 * Mathieu Desnoyers <mathieu.desnoyers@efficios.com>
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, version 2 only,
7 * as published by the Free Software Foundation.
8 *
9 * This program is distributed in the hope that it will be useful, but WITHOUT
10 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
11 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
12 * more details.
13 *
14 * You should have received a copy of the GNU General Public License along
15 * with this program; if not, write to the Free Software Foundation, Inc.,
16 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
17 */
18
19#define _LGPL_SOURCE
20#include <errno.h>
21#include <limits.h>
22#include <stdio.h>
23#include <stdlib.h>
24#include <string.h>
25#include <sys/wait.h>
26#include <sys/types.h>
27#include <sys/stat.h>
28#include <unistd.h>
29#include <fcntl.h>
30#include <sched.h>
31#include <signal.h>
32#include <assert.h>
33#include <signal.h>
34
35#include <common/common.h>
36#include <common/utils.h>
37#include <common/compat/getenv.h>
38#include <common/compat/prctl.h>
39#include <common/unix.h>
40#include <common/defaults.h>
41
42#include "runas.h"
43
44struct run_as_data;
45struct run_as_ret;
46typedef int (*run_as_fct)(struct run_as_data *data, struct run_as_ret *ret_value);
47
48struct run_as_mkdir_data {
49 char path[PATH_MAX];
50 mode_t mode;
51};
52
53struct run_as_open_data {
54 char path[PATH_MAX];
55 int flags;
56 mode_t mode;
57};
58
59struct run_as_unlink_data {
60 char path[PATH_MAX];
61};
62
63struct run_as_rmdir_recursive_data {
64 char path[PATH_MAX];
65};
66
67struct run_as_mkdir_ret {
68 int ret;
69};
70
71struct run_as_open_ret {
72 int ret;
73};
74
75struct run_as_unlink_ret {
76 int ret;
77};
78
79struct run_as_rmdir_recursive_ret {
80 int ret;
81};
82
83enum run_as_cmd {
84 RUN_AS_MKDIR,
85 RUN_AS_OPEN,
86 RUN_AS_UNLINK,
87 RUN_AS_RMDIR_RECURSIVE,
88 RUN_AS_MKDIR_RECURSIVE,
89};
90
91struct run_as_data {
92 enum run_as_cmd cmd;
93 int fd;
94 union {
95 struct run_as_mkdir_data mkdir;
96 struct run_as_open_data open;
97 struct run_as_unlink_data unlink;
98 struct run_as_rmdir_recursive_data rmdir_recursive;
99 } u;
100 uid_t uid;
101 gid_t gid;
102};
103
104/*
105 * The run_as_ret structure holds the returned value and status of the command.
106 *
107 * The `u` union field holds the return value of the command; in most cases it
108 * represents the success or the failure of the command. In more complex
109 * commands, it holds a computed value.
110 *
111 * The _errno field is the errno recorded after the execution of the command.
112 *
113 * The _error fields is used the signify that return status of the command. For
114 * simple commands returning `int` the _error field will be the same as the
115 * ret_int field. In complex commands, it signify the success or failure of the
116 * command.
117 *
118 */
119struct run_as_ret {
120 int fd;
121 union {
122 struct run_as_mkdir_ret mkdir;
123 struct run_as_open_ret open;
124 struct run_as_unlink_ret unlink;
125 struct run_as_rmdir_recursive_ret rmdir_recursive;
126 } u;
127 int _errno;
128 bool _error;
129};
130
131struct run_as_worker {
132 pid_t pid; /* Worker PID. */
133 int sockpair[2];
134 char *procname;
135};
136
137/* Single global worker per process (for now). */
138static struct run_as_worker *global_worker;
139/* Lock protecting the worker. */
140static pthread_mutex_t worker_lock = PTHREAD_MUTEX_INITIALIZER;
141
142#ifdef VALGRIND
143static
144int use_clone(void)
145{
146 return 0;
147}
148#else
149static
150int use_clone(void)
151{
152 return !lttng_secure_getenv("LTTNG_DEBUG_NOCLONE");
153}
154#endif
155
156LTTNG_HIDDEN
157int _utils_mkdir_recursive_unsafe(const char *path, mode_t mode);
158
159/*
160 * Create recursively directory using the FULL path.
161 */
162static
163int _mkdir_recursive(struct run_as_data *data, struct run_as_ret *ret_value)
164{
165 const char *path;
166 mode_t mode;
167
168 path = data->u.mkdir.path;
169 mode = data->u.mkdir.mode;
170
171 /* Safe to call as we have transitioned to the requested uid/gid. */
172 ret_value->u.mkdir.ret = _utils_mkdir_recursive_unsafe(path, mode);
173 ret_value->_errno = errno;
174 ret_value->_error = (ret_value->u.mkdir.ret) ? true : false;
175 return ret_value->u.mkdir.ret;
176}
177
178static
179int _mkdir(struct run_as_data *data, struct run_as_ret *ret_value)
180{
181 ret_value->u.mkdir.ret = mkdir(data->u.mkdir.path, data->u.mkdir.mode);
182 ret_value->_errno = errno;
183 ret_value->_error = (ret_value->u.mkdir.ret) ? true : false;
184 return ret_value->u.mkdir.ret;
185}
186
187static
188int _open(struct run_as_data *data, struct run_as_ret *ret_value)
189{
190 ret_value->u.open.ret = open(data->u.open.path, data->u.open.flags, data->u.open.mode);
191 ret_value->fd = ret_value->u.open.ret;
192 ret_value->_errno = errno;
193 ret_value->_error = (ret_value->u.open.ret) ? true : false;
194 return ret_value->u.open.ret;
195}
196
197static
198int _unlink(struct run_as_data *data, struct run_as_ret *ret_value)
199{
200 ret_value->u.unlink.ret = unlink(data->u.unlink.path);
201 ret_value->_errno = errno;
202 ret_value->_error = (ret_value->u.unlink.ret) ? true : false;
203 return ret_value->u.unlink.ret;
204}
205
206static
207int _rmdir_recursive(struct run_as_data *data, struct run_as_ret *ret_value)
208{
209 ret_value->u.rmdir_recursive.ret = utils_recursive_rmdir(data->u.rmdir_recursive.path);
210 ret_value->_errno = errno;
211 ret_value->_error = (ret_value->u.rmdir_recursive.ret) ? true : false;
212 return ret_value->u.rmdir_recursive.ret;
213}
214
215static
216run_as_fct run_as_enum_to_fct(enum run_as_cmd cmd)
217{
218 switch (cmd) {
219 case RUN_AS_MKDIR:
220 return _mkdir;
221 case RUN_AS_OPEN:
222 return _open;
223 case RUN_AS_UNLINK:
224 return _unlink;
225 case RUN_AS_RMDIR_RECURSIVE:
226 return _rmdir_recursive;
227 case RUN_AS_MKDIR_RECURSIVE:
228 return _mkdir_recursive;
229 default:
230 ERR("Unknown command %d", (int) cmd);
231 return NULL;
232 }
233}
234
235static
236int do_send_fd(int sock, int fd)
237{
238 ssize_t len;
239
240 if (fd < 0) {
241 ERR("Invalid file description");
242 return 0;
243 }
244
245 len = lttcomm_send_fds_unix_sock(sock, &fd, 1);
246 if (len < 0) {
247 PERROR("lttcomm_send_fds_unix_sock");
248 return -1;
249 }
250 return 0;
251}
252
253static
254int do_recv_fd(int sock, int *fd)
255{
256 ssize_t len;
257
258 if (*fd < 0) {
259 ERR("Invalid file description");
260 return 0;
261 }
262
263 len = lttcomm_recv_fds_unix_sock(sock, fd, 1);
264
265 if (!len) {
266 return -1;
267 } else if (len < 0) {
268 PERROR("lttcomm_recv_fds_unix_sock");
269 return -1;
270 }
271 return 0;
272}
273
274static
275int send_fd_to_worker(struct run_as_worker *worker, enum run_as_cmd cmd, int fd)
276{
277 int ret = 0;
278
279 switch (cmd) {
280 default:
281 return 0;
282 }
283
284 ret = do_send_fd(worker->sockpair[0], fd);
285 if (ret < 0) {
286 PERROR("do_send_fd");
287 ret = -1;
288 }
289
290 return ret;
291}
292
293static
294int send_fd_to_master(struct run_as_worker *worker, enum run_as_cmd cmd, int fd)
295{
296 int ret = 0, ret_close = 0;
297
298 switch (cmd) {
299 case RUN_AS_OPEN:
300 break;
301 default:
302 return 0;
303 }
304
305 ret = do_send_fd(worker->sockpair[1], fd);
306 if (ret < 0) {
307 PERROR("do_send_fd error");
308 ret = -1;
309 }
310
311 ret_close = close(fd);
312 if (ret_close < 0) {
313 PERROR("close");
314 }
315
316 return ret;
317}
318
319static
320int recv_fd_from_worker(struct run_as_worker *worker, enum run_as_cmd cmd, int *fd)
321{
322 int ret = 0;
323
324 switch (cmd) {
325 case RUN_AS_OPEN:
326 break;
327 default:
328 return 0;
329 }
330
331 ret = do_recv_fd(worker->sockpair[0], fd);
332 if (ret < 0) {
333 PERROR("do_recv_fd error");
334 ret = -1;
335 }
336
337 return ret;
338}
339
340static
341int recv_fd_from_master(struct run_as_worker *worker, enum run_as_cmd cmd, int *fd)
342{
343 int ret = 0;
344
345 switch (cmd) {
346 default:
347 return 0;
348 }
349
350 ret = do_recv_fd(worker->sockpair[1], fd);
351 if (ret < 0) {
352 PERROR("do_recv_fd error");
353 ret = -1;
354 }
355
356 return ret;
357}
358
359static
360int cleanup_received_fd(enum run_as_cmd cmd, int fd)
361{
362 int ret = 0;
363
364 switch (cmd) {
365 default:
366 return 0;
367 }
368
369 ret = close(fd);
370 if (ret < 0) {
371 PERROR("close error");
372 ret = -1;
373 }
374
375 return ret;
376}
377/*
378 * Return < 0 on error, 0 if OK, 1 on hangup.
379 */
380static
381int handle_one_cmd(struct run_as_worker *worker)
382{
383 int ret = 0;
384 struct run_as_data data;
385 ssize_t readlen, writelen;
386 struct run_as_ret sendret;
387 run_as_fct cmd;
388 uid_t prev_euid;
389
390 /*
391 * Stage 1: Receive run_as_data struct from the master.
392 * The structure contains the command type and all the parameters needed for
393 * its execution
394 */
395 readlen = lttcomm_recv_unix_sock(worker->sockpair[1], &data,
396 sizeof(data));
397 if (readlen == 0) {
398 /* hang up */
399 ret = 1;
400 goto end;
401 }
402 if (readlen < sizeof(data)) {
403 PERROR("lttcomm_recv_unix_sock error");
404 ret = -1;
405 goto end;
406 }
407
408 cmd = run_as_enum_to_fct(data.cmd);
409 if (!cmd) {
410 ret = -1;
411 goto end;
412 }
413
414 /*
415 * Stage 2: Receive file descriptor from master.
416 * Some commands need a file descriptor as input so if it's needed we
417 * receive the fd using the Unix socket.
418 */
419 ret = recv_fd_from_master(worker, data.cmd, &data.fd);
420 if (ret < 0) {
421 PERROR("recv_fd_from_master error");
422 ret = -1;
423 goto end;
424 }
425
426 prev_euid = getuid();
427 if (data.gid != getegid()) {
428 ret = setegid(data.gid);
429 if (ret < 0) {
430 PERROR("setegid");
431 goto write_return;
432 }
433 }
434 if (data.uid != prev_euid) {
435 ret = seteuid(data.uid);
436 if (ret < 0) {
437 PERROR("seteuid");
438 goto write_return;
439 }
440 }
441
442 /*
443 * Also set umask to 0 for mkdir executable bit.
444 */
445 umask(0);
446
447 /*
448 * Stage 3: Execute the command
449 */
450 ret = (*cmd)(&data, &sendret);
451 if (ret < 0) {
452 DBG("Execution of command returned an error");
453 }
454
455write_return:
456 ret = cleanup_received_fd(data.cmd, data.fd);
457 if (ret < 0) {
458 ERR("Error cleaning up FD");
459 goto end;
460 }
461
462 /*
463 * Stage 4: Send run_as_ret structure to the master.
464 * This structure contain the return value of the command and the errno.
465 */
466 writelen = lttcomm_send_unix_sock(worker->sockpair[1], &sendret,
467 sizeof(sendret));
468 if (writelen < sizeof(sendret)) {
469 PERROR("lttcomm_send_unix_sock error");
470 ret = -1;
471 goto end;
472 }
473
474 /*
475 * Stage 5: Send file descriptor to the master
476 * Some commands return a file descriptor so if it's needed we pass it back
477 * to the master using the Unix socket.
478 */
479 ret = send_fd_to_master(worker, data.cmd, sendret.fd);
480 if (ret < 0) {
481 DBG("Sending FD to master returned an error");
482 goto end;
483 }
484
485 if (seteuid(prev_euid) < 0) {
486 PERROR("seteuid");
487 ret = -1;
488 goto end;
489 }
490 ret = 0;
491end:
492 return ret;
493}
494
495static
496int run_as_worker(struct run_as_worker *worker)
497{
498 int ret;
499 ssize_t writelen;
500 struct run_as_ret sendret;
501 size_t proc_orig_len;
502
503 /*
504 * Initialize worker. Set a different process cmdline.
505 */
506 proc_orig_len = strlen(worker->procname);
507 memset(worker->procname, 0, proc_orig_len);
508 strncpy(worker->procname, DEFAULT_RUN_AS_WORKER_NAME, proc_orig_len);
509
510 ret = lttng_prctl(PR_SET_NAME,
511 (unsigned long) DEFAULT_RUN_AS_WORKER_NAME, 0, 0, 0);
512 if (ret && ret != -ENOSYS) {
513 /* Don't fail as this is not essential. */
514 PERROR("prctl PR_SET_NAME");
515 }
516
517 memset(&sendret, 0, sizeof(sendret));
518
519 writelen = lttcomm_send_unix_sock(worker->sockpair[1], &sendret,
520 sizeof(sendret));
521 if (writelen < sizeof(sendret)) {
522 PERROR("lttcomm_send_unix_sock error");
523 ret = EXIT_FAILURE;
524 goto end;
525 }
526
527 for (;;) {
528 ret = handle_one_cmd(worker);
529 if (ret < 0) {
530 ret = EXIT_FAILURE;
531 goto end;
532 } else if (ret > 0) {
533 break;
534 } else {
535 continue; /* Next command. */
536 }
537 }
538 ret = EXIT_SUCCESS;
539end:
540 return ret;
541}
542
543static
544int run_as_cmd(struct run_as_worker *worker,
545 enum run_as_cmd cmd,
546 struct run_as_data *data,
547 struct run_as_ret *ret_value,
548 uid_t uid, gid_t gid)
549{
550 int ret = 0;
551 ssize_t readlen, writelen;
552
553 /*
554 * If we are non-root, we can only deal with our own uid.
555 */
556 if (geteuid() != 0) {
557 if (uid != geteuid()) {
558 ret = -1;
559 ret_value->_errno = EPERM;
560 ERR("Client (%d)/Server (%d) UID mismatch (and sessiond is not root)",
561 (int) uid, (int) geteuid());
562 goto end;
563 }
564 }
565
566 data->cmd = cmd;
567 data->uid = uid;
568 data->gid = gid;
569
570 /*
571 * Stage 1: Send the run_as_data struct to the worker process
572 */
573 writelen = lttcomm_send_unix_sock(worker->sockpair[0], data,
574 sizeof(*data));
575 if (writelen < sizeof(*data)) {
576 PERROR("Error writing message to run_as");
577 ret = -1;
578 ret_value->_errno = EIO;
579 goto end;
580 }
581
582 /*
583 * Stage 2: Send file descriptor to the worker process if needed
584 */
585 ret = send_fd_to_worker(worker, data->cmd, data->fd);
586 if (ret) {
587 PERROR("do_send_fd error");
588 ret = -1;
589 ret_value->_errno = EIO;
590 goto end;
591 }
592
593 /*
594 * Stage 3: Wait for the execution of the command
595 */
596
597 /*
598 * Stage 4: Receive the run_as_ret struct containing the return value and
599 * errno
600 */
601 readlen = lttcomm_recv_unix_sock(worker->sockpair[0], ret_value,
602 sizeof(*ret_value));
603 if (!readlen) {
604 ERR("Run-as worker has hung-up during run_as_cmd");
605 ret = -1;
606 ret_value->_errno = EIO;
607 goto end;
608 } else if (readlen < sizeof(*ret_value)) {
609 PERROR("Error reading response from run_as");
610 ret = -1;
611 ret_value->_errno = errno;
612 }
613
614 /*
615 * Stage 5: Receive file descriptor if needed
616 */
617 ret = recv_fd_from_worker(worker, data->cmd, &ret_value->fd);
618 if (ret < 0) {
619 ERR("Error receiving fd");
620 ret = -1;
621 ret_value->_errno = EIO;
622 }
623
624end:
625 return ret;
626}
627
628/*
629 * This is for debugging ONLY and should not be considered secure.
630 */
631static
632int run_as_noworker(enum run_as_cmd cmd,
633 struct run_as_data *data, struct run_as_ret *ret_value,
634 uid_t uid, gid_t gid)
635{
636 int ret, saved_errno;
637 mode_t old_mask;
638 run_as_fct fct;
639
640 fct = run_as_enum_to_fct(cmd);
641 if (!fct) {
642 errno = -ENOSYS;
643 ret = -1;
644 goto end;
645 }
646 old_mask = umask(0);
647 ret = fct(data, ret_value);
648 saved_errno = ret_value->_errno;
649 umask(old_mask);
650 errno = saved_errno;
651end:
652 return ret;
653}
654
655static
656int run_as_restart_worker(struct run_as_worker *worker)
657{
658 int ret = 0;
659 char *procname = NULL;
660
661 procname = worker->procname;
662
663 /* Close socket to run_as worker process and clean up the zombie process */
664 run_as_destroy_worker();
665
666 /* Create a new run_as worker process*/
667 ret = run_as_create_worker(procname);
668 if (ret < 0 ) {
669 ERR("Restarting the worker process failed");
670 ret = -1;
671 goto err;
672 }
673err:
674 return ret;
675}
676
677static
678int run_as(enum run_as_cmd cmd, struct run_as_data *data,
679 struct run_as_ret *ret_value, uid_t uid, gid_t gid)
680{
681 int ret, saved_errno;
682
683 if (use_clone()) {
684 DBG("Using run_as worker");
685 pthread_mutex_lock(&worker_lock);
686 assert(global_worker);
687
688 ret = run_as_cmd(global_worker, cmd, data, ret_value, uid, gid);
689 saved_errno = ret_value->_errno;
690
691 pthread_mutex_unlock(&worker_lock);
692 /*
693 * If the worker thread crashed the errno is set to EIO. we log
694 * the error and start a new worker process.
695 */
696 if (ret == -1 && saved_errno == EIO) {
697 DBG("Socket closed unexpectedly... "
698 "Restarting the worker process");
699 ret = run_as_restart_worker(global_worker);
700
701 if (ret == -1) {
702 ERR("Failed to restart worker process.");
703 goto err;
704 }
705 }
706 } else {
707 DBG("Using run_as without worker");
708 ret = run_as_noworker(cmd, data, ret_value, uid, gid);
709 }
710err:
711 return ret;
712}
713
714LTTNG_HIDDEN
715int run_as_mkdir_recursive(const char *path, mode_t mode, uid_t uid, gid_t gid)
716{
717 struct run_as_data data;
718 struct run_as_ret ret;
719
720 memset(&data, 0, sizeof(data));
721 memset(&ret, 0, sizeof(ret));
722 DBG3("mkdir() recursive %s with mode %d for uid %d and gid %d",
723 path, (int) mode, (int) uid, (int) gid);
724 strncpy(data.u.mkdir.path, path, PATH_MAX - 1);
725 data.u.mkdir.path[PATH_MAX - 1] = '\0';
726 data.u.mkdir.mode = mode;
727
728 run_as(RUN_AS_MKDIR_RECURSIVE, &data, &ret, uid, gid);
729 errno = ret._errno;
730 return ret.u.mkdir.ret;
731}
732
733LTTNG_HIDDEN
734int run_as_mkdir(const char *path, mode_t mode, uid_t uid, gid_t gid)
735{
736 struct run_as_data data;
737 struct run_as_ret ret;
738
739 memset(&data, 0, sizeof(data));
740 memset(&ret, 0, sizeof(ret));
741
742 DBG3("mkdir() %s with mode %d for uid %d and gid %d",
743 path, (int) mode, (int) uid, (int) gid);
744 strncpy(data.u.mkdir.path, path, PATH_MAX - 1);
745 data.u.mkdir.path[PATH_MAX - 1] = '\0';
746 data.u.mkdir.mode = mode;
747 run_as(RUN_AS_MKDIR, &data, &ret, uid, gid);
748 errno = ret._errno;
749 return ret.u.mkdir.ret;
750}
751
752LTTNG_HIDDEN
753int run_as_open(const char *path, int flags, mode_t mode, uid_t uid, gid_t gid)
754{
755 struct run_as_data data;
756 struct run_as_ret ret;
757
758 memset(&data, 0, sizeof(data));
759 memset(&ret, 0, sizeof(ret));
760
761 DBG3("open() %s with flags %X mode %d for uid %d and gid %d",
762 path, flags, (int) mode, (int) uid, (int) gid);
763 strncpy(data.u.open.path, path, PATH_MAX - 1);
764 data.u.open.path[PATH_MAX - 1] = '\0';
765 data.u.open.flags = flags;
766 data.u.open.mode = mode;
767 run_as(RUN_AS_OPEN, &data, &ret, uid, gid);
768 errno = ret._errno;
769 ret.u.open.ret = ret.fd;
770 return ret.u.open.ret;
771}
772
773LTTNG_HIDDEN
774int run_as_unlink(const char *path, uid_t uid, gid_t gid)
775{
776 struct run_as_data data;
777 struct run_as_ret ret;
778
779 memset(&data, 0, sizeof(data));
780 memset(&ret, 0, sizeof(ret));
781
782 DBG3("unlink() %s with for uid %d and gid %d",
783 path, (int) uid, (int) gid);
784 strncpy(data.u.unlink.path, path, PATH_MAX - 1);
785 data.u.unlink.path[PATH_MAX - 1] = '\0';
786 run_as(RUN_AS_UNLINK, &data, &ret, uid, gid);
787 errno = ret._errno;
788 return ret.u.unlink.ret;
789}
790
791LTTNG_HIDDEN
792int run_as_rmdir_recursive(const char *path, uid_t uid, gid_t gid)
793{
794 struct run_as_data data;
795 struct run_as_ret ret;
796
797 memset(&data, 0, sizeof(data));
798 memset(&ret, 0, sizeof(ret));
799
800 DBG3("rmdir_recursive() %s with for uid %d and gid %d",
801 path, (int) uid, (int) gid);
802 strncpy(data.u.rmdir_recursive.path, path, PATH_MAX - 1);
803 data.u.rmdir_recursive.path[PATH_MAX - 1] = '\0';
804 run_as(RUN_AS_RMDIR_RECURSIVE, &data, &ret, uid, gid);
805 errno = ret._errno;
806 return ret.u.rmdir_recursive.ret;
807}
808
809static
810int reset_sighandler(void)
811{
812 int sig;
813
814 DBG("Resetting run_as worker signal handlers to default");
815 for (sig = 1; sig <= 31; sig++) {
816 (void) signal(sig, SIG_DFL);
817 }
818 return 0;
819}
820
821static
822void worker_sighandler(int sig)
823{
824 const char *signame;
825
826 /*
827 * The worker will inherit its parent's signals since they are part of
828 * the same process group. However, in the case of SIGINT and SIGTERM,
829 * we want to give the worker a chance to teardown gracefully when its
830 * parent closes the command socket.
831 */
832 switch (sig) {
833 case SIGINT:
834 signame = "SIGINT";
835 break;
836 case SIGTERM:
837 signame = "SIGTERM";
838 break;
839 default:
840 signame = NULL;
841 }
842
843 if (signame) {
844 DBG("run_as worker received signal %s", signame);
845 } else {
846 DBG("run_as_worker received signal %d", sig);
847 }
848}
849
850static
851int set_worker_sighandlers(void)
852{
853 int ret = 0;
854 sigset_t sigset;
855 struct sigaction sa;
856
857 if ((ret = sigemptyset(&sigset)) < 0) {
858 PERROR("sigemptyset");
859 goto end;
860 }
861
862 sa.sa_handler = worker_sighandler;
863 sa.sa_mask = sigset;
864 sa.sa_flags = 0;
865 if ((ret = sigaction(SIGINT, &sa, NULL)) < 0) {
866 PERROR("sigaction SIGINT");
867 goto end;
868 }
869
870 if ((ret = sigaction(SIGTERM, &sa, NULL)) < 0) {
871 PERROR("sigaction SIGTERM");
872 goto end;
873 }
874
875 DBG("run_as signal handler set for SIGTERM and SIGINT");
876end:
877 return ret;
878}
879
880LTTNG_HIDDEN
881int run_as_create_worker(char *procname)
882{
883 pid_t pid;
884 int i, ret = 0;
885 ssize_t readlen;
886 struct run_as_ret recvret;
887 struct run_as_worker *worker;
888
889 pthread_mutex_lock(&worker_lock);
890 assert(!global_worker);
891 if (!use_clone()) {
892 /*
893 * Don't initialize a worker, all run_as tasks will be performed
894 * in the current process.
895 */
896 ret = 0;
897 goto end;
898 }
899 worker = zmalloc(sizeof(*worker));
900 if (!worker) {
901 ret = -ENOMEM;
902 goto end;
903 }
904 worker->procname = procname;
905 /* Create unix socket. */
906 if (lttcomm_create_anon_unix_socketpair(worker->sockpair) < 0) {
907 ret = -1;
908 goto error_sock;
909 }
910
911 /* Fork worker. */
912 pid = fork();
913 if (pid < 0) {
914 PERROR("fork");
915 ret = -1;
916 goto error_fork;
917 } else if (pid == 0) {
918 /* Child */
919
920 reset_sighandler();
921
922 set_worker_sighandlers();
923
924 /* The child has no use for this lock. */
925 pthread_mutex_unlock(&worker_lock);
926 /* Just close, no shutdown. */
927 if (close(worker->sockpair[0])) {
928 PERROR("close");
929 exit(EXIT_FAILURE);
930 }
931
932 /*
933 * Close all FDs aside from STDIN, STDOUT, STDERR and sockpair[1]
934 * Sockpair[1] is used as a control channel with the master
935 */
936 for (i = 3; i < sysconf(_SC_OPEN_MAX); i++) {
937 if (i != worker->sockpair[1]) {
938 (void) close(i);
939 }
940 }
941
942 worker->sockpair[0] = -1;
943 ret = run_as_worker(worker);
944 if (lttcomm_close_unix_sock(worker->sockpair[1])) {
945 PERROR("close");
946 ret = -1;
947 }
948 worker->sockpair[1] = -1;
949 LOG(ret ? PRINT_ERR : PRINT_DBG, "run_as worker exiting (ret = %d)", ret);
950 exit(ret ? EXIT_FAILURE : EXIT_SUCCESS);
951 } else {
952 /* Parent */
953
954 /* Just close, no shutdown. */
955 if (close(worker->sockpair[1])) {
956 PERROR("close");
957 ret = -1;
958 goto error_fork;
959 }
960 worker->sockpair[1] = -1;
961 worker->pid = pid;
962 /* Wait for worker to become ready. */
963 readlen = lttcomm_recv_unix_sock(worker->sockpair[0],
964 &recvret, sizeof(recvret));
965 if (readlen < sizeof(recvret)) {
966 ERR("readlen: %zd", readlen);
967 PERROR("Error reading response from run_as at creation");
968 ret = -1;
969 goto error_fork;
970 }
971 global_worker = worker;
972 }
973end:
974 pthread_mutex_unlock(&worker_lock);
975 return ret;
976
977 /* Error handling. */
978error_fork:
979 for (i = 0; i < 2; i++) {
980 if (worker->sockpair[i] < 0) {
981 continue;
982 }
983 if (lttcomm_close_unix_sock(worker->sockpair[i])) {
984 PERROR("close");
985 }
986 worker->sockpair[i] = -1;
987 }
988error_sock:
989 free(worker);
990 pthread_mutex_unlock(&worker_lock);
991 return ret;
992}
993
994LTTNG_HIDDEN
995void run_as_destroy_worker(void)
996{
997 struct run_as_worker *worker = global_worker;
998
999 DBG("Destroying run_as worker");
1000 pthread_mutex_lock(&worker_lock);
1001 if (!worker) {
1002 goto end;
1003 }
1004 /* Close unix socket */
1005 DBG("Closing run_as worker socket");
1006 if (lttcomm_close_unix_sock(worker->sockpair[0])) {
1007 PERROR("close");
1008 }
1009 worker->sockpair[0] = -1;
1010 /* Wait for worker. */
1011 for (;;) {
1012 int status;
1013 pid_t wait_ret;
1014
1015 wait_ret = waitpid(worker->pid, &status, 0);
1016 if (wait_ret < 0) {
1017 if (errno == EINTR) {
1018 continue;
1019 }
1020 PERROR("waitpid");
1021 break;
1022 }
1023
1024 if (WIFEXITED(status)) {
1025 LOG(WEXITSTATUS(status) == 0 ? PRINT_DBG : PRINT_ERR,
1026 DEFAULT_RUN_AS_WORKER_NAME " terminated with status code %d",
1027 WEXITSTATUS(status));
1028 break;
1029 } else if (WIFSIGNALED(status)) {
1030 ERR(DEFAULT_RUN_AS_WORKER_NAME " was killed by signal %d",
1031 WTERMSIG(status));
1032 break;
1033 }
1034 }
1035 free(worker);
1036 global_worker = NULL;
1037end:
1038 pthread_mutex_unlock(&worker_lock);
1039}
This page took 0.025865 seconds and 4 git commands to generate.