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