├── .gitattributes ├── .gitignore ├── .tag_name ├── COPYING ├── ChangeLog ├── Makefile ├── README.md ├── arch ├── amd64.h ├── arm.h ├── default-syscalls.h ├── i386.h └── x86_common.h ├── attach.c ├── ptrace.c ├── ptrace.h ├── relink ├── reredirect.1 ├── reredirect.c └── reredirect.h /.gitattributes: -------------------------------------------------------------------------------- 1 | .tag_name export-subst 2 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | reptyr 2 | ptrace 3 | *.o 4 | -------------------------------------------------------------------------------- /.tag_name: -------------------------------------------------------------------------------- 1 | v0.4 2 | -------------------------------------------------------------------------------- /COPYING: -------------------------------------------------------------------------------- 1 | Copyright (C) 2011 by Nelson Elhage 2 | Copyright (C) 2014 by Jérôme Pouiller 3 | 4 | Permission is hereby granted, free of charge, to any person obtaining a copy 5 | of this software and associated documentation files (the "Software"), to deal 6 | in the Software without restriction, including without limitation the rights 7 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | copies of the Software, and to permit persons to whom the Software is 9 | furnished to do so, subject to the following conditions: 10 | 11 | The above copyright notice and this permission notice shall be included in 12 | all copies or substantial portions of the Software. 13 | 14 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 20 | THE SOFTWARE. 21 | -------------------------------------------------------------------------------- /ChangeLog: -------------------------------------------------------------------------------- 1 | * 0.4 (Apr 27, 2025) 2 | - Fix compatibility with alternative libc 3 | - Prefer the more modern openat() rather than open() 4 | - Add -i/-I in addtion of -e/E and -o/-O 5 | - Fix possible hang while attaching multi-thread application 6 | - Add support for -V 7 | - Rename the main branch in "main" (was "master") 8 | 9 | * 0.3 (Aug 16, 2021) 10 | - Update install instructions in README 11 | - Update ChangeLog 12 | - Fix license of relink 13 | 14 | * 0.2 (Nov 28, 2017) 15 | - Add script 'relink' 16 | - Fix compilation for amd64 17 | 18 | * 0.1 (Aug 26, 2014) 19 | - Initial release. Fork from reptyr 20 | 21 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | override CFLAGS+=-Wall -g 2 | OBJS=reredirect.o ptrace.o attach.o 3 | 4 | # Note that because of how Make works, this can be overriden from the 5 | # command-line. 6 | # 7 | # e.g. install to /usr with `make PREFIX=/usr` 8 | PREFIX=/usr/local 9 | 10 | all: reredirect 11 | 12 | .PHONY: .force 13 | # Get version from git 14 | GIT_VERSION := $(shell git describe --abbrev=4 --dirty --always --tags 2>/dev/null || cat .tag_name) 15 | 16 | version.h: .force 17 | @VERSION_CONTENT='#define REREDIRECT_VERSION "$(GIT_VERSION)"'; \ 18 | EXISTING_CONTENT="$$(cat $@)"; \ 19 | if [ "$$EXISTING_CONTENT" != "$$VERSION_CONTENT" ]; then \ 20 | echo "$$VERSION_CONTENT" > $@; \ 21 | echo "Version updated to $(GIT_VERSION)"; \ 22 | fi 23 | 24 | reredirect: $(OBJS) 25 | 26 | attach.o: reredirect.h ptrace.h 27 | reredirect.o: reredirect.h version.h 28 | ptrace.o: ptrace.h $(wildcard arch/*.h) 29 | 30 | clean: 31 | rm -f reredirect $(OBJS) 32 | 33 | install: reredirect relink 34 | install -d -m 755 $(DESTDIR)$(PREFIX)/bin/ 35 | install -m 755 reredirect $(DESTDIR)$(PREFIX)/bin/reredirect 36 | install -m 755 relink $(DESTDIR)$(PREFIX)/bin/relink 37 | install -d -m 755 $(DESTDIR)$(PREFIX)/share/man/man1 38 | install -m 644 reredirect.1 $(DESTDIR)$(PREFIX)/share/man/man1/reredirect.1 39 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | `reredirect` - A tool to dynamically redirect outputs of a running program 2 | ========================================================================== 3 | 4 | `reredirect` is a utility for taking an existing running program and 5 | attaching its output (standard output and error output) to files or 6 | to another process. 7 | 8 | Using `reredirect`, you can log output of an already launched process, redirect 9 | debug output of a background process to `/dev/null` or to a pager as if you 10 | launched it with `>` or `|`. 11 | 12 | Installation 13 | ------------ 14 | 15 | Installation uses common rules for C projects. Compile with: 16 | 17 | make 18 | 19 | Then, install with: 20 | 21 | sudo make install 22 | 23 | Usage 24 | ----- 25 | 26 | Simple usage is: 27 | 28 | reredirect -m FILE PID 29 | 30 | It will redirect output of PID to FILE. It is also possible to redirect standard 31 | output and error output in different files: 32 | 33 | reredirect -o FILE1 -e FILE2 PID 34 | 35 | `-m` option is just a shortcut to `-o FILE -e FILE`. 36 | 37 | After being launched, reredirect give you the ability to restore state of PID. 38 | It will look something like this : 39 | 40 | reredirect -N -O 5 -E 3 5453 41 | 42 | `-O` and `-E` act as `-o` and `-e` but with already opened file descriptors in 43 | PID. They only used to restore previous state of PID. 44 | 45 | Without `-N`, `reredirect` keep previous output opened which allow you to 46 | restore them. This will produce file descriptor leak if you call 47 | `reredirect` multiple times. You should use `-N` to close and forget previous 48 | output. 49 | 50 | Redirect to your terminal or a command 51 | -------------------------------------- 52 | 53 | This package also provide an utility called `relink` that allows to redirect 54 | output to current terminal. When `relink` exits (with Ctrl+C for exemple) 55 | original state is restored and command is detached. 56 | 57 | For exemple: 58 | 59 | relink 5453 60 | relink 5453 | grep useful_line 61 | 62 | `relink` maintains stderr from original command to stderr. So you can do things 63 | like: 64 | 65 | relink 5453 > /dev/null 66 | 67 | Internally, `relink` is just a small shell script that creates necessary context 68 | and call `reredirect` as necessary. It uses "named pipes". Using "named pipes", 69 | you can redirect output of your target to another command (as a normal pipe): 70 | 71 | First create a named pipe: 72 | 73 | mkfifo /tmp/myfifo 74 | 75 | Run `reredirect` to redirect your target to /tmp/myfifo: 76 | 77 | reredirect -m /tmp/myfifo PID 78 | 79 | Launch a command on this named pipe: 80 | 81 | less < /tmp/myfifo 82 | tee my_log < /tmp/myfifo 83 | cat -n < /tmp/myfifo 84 | 85 | Note that `relink` only redirects output. The target process keep its original 86 | terminal. So if you type Ctrl+Z or CTRL+C, they are not sent to target process. 87 | If you want to do that, you should check the [`reptyr`](https://github.com/nelhage/reptyr) command from Nelson Elhage. 88 | 89 | Trick with Makefile 90 | --------------------- 91 | 92 | Sometime, I work with complex projects and I want to log subparts of compilation 93 | output in different files. I use this trick: 94 | 95 | target: 96 | @FIFO=$$(mktemp -u); mkfifo $$FIFO; tee my_file.log < $$FIFO & ./redirect -m $$FIFO $$PPID > ./restore_$$PPID.cmd 97 | @echo Call sub makefile here 98 | @sh ./restore_$$PPID.cmd 99 | @echo No more in log file 100 | 101 | Portability 102 | ----------- 103 | 104 | reredirect is Linux-only. It uses ptrace to attach to the target and control it at 105 | the syscall level, so it is highly dependent on Linux's particular syscall API, 106 | syscalls, and terminal ioctl()s. A port to Solaris or BSD may be technically 107 | feasible, but would probably require significant re-architecting to abstract out 108 | the platform-specific bits. 109 | 110 | reredirect works on i386, x86_64, and ARM. Ports to other architectures should be 111 | straightforward, and should in most cases be as simple as adding an arch/ARCH.h 112 | file and adding a clause to the ifdef ladder in ptrace.c. 113 | 114 | ptrace_scope on Ubuntu Maverick and up 115 | -------------------------------------- 116 | 117 | `redirect` depends on the `ptrace` system call to attach to the remote program. On 118 | Ubuntu Maverick and higher, this ability is disabled by default for security 119 | reasons. You can enable it temporarily by doing 120 | 121 | # echo 0 > /proc/sys/kernel/yama/ptrace_scope 122 | 123 | as root, or permanently by editing the file /etc/sysctl.d/10-ptrace.conf, which 124 | also contains more information about exactly what this setting accomplishes. 125 | 126 | How does it work? 127 | ----------------- 128 | 129 | Reredirect acts as a debugger to take control of a running process (it uses ptrace 130 | syscall). Once it takes control of a running process, it uses classical calls to 131 | `dup`, and `dup2` to change targets of file descriptors 1 and 2. 132 | 133 | Basicly, to redirect to file, this pseudo code is executed: 134 | 135 | orig_fd = 1; 136 | save_fd = dup(1); 137 | new_fd = open(file, O_WRONLY | O_CREAT, 0666); 138 | dup2(new_fd, orig_fd); 139 | close(new_fd); 140 | 141 | and to restore state: 142 | 143 | ret = dup2(save_fd, orig_fd); 144 | close(save_fd); 145 | 146 | 147 | Credits 148 | ------- 149 | 150 | reredirect was mainly written by Jérôme Pouiller . You can 151 | contact him for any questions or bug reports. 152 | 153 | reredirect (and especially all ptrace layer) is based on reptyr programm. reptyr 154 | was written by Nelson Elhage . 155 | 156 | URL 157 | --- 158 | [https://github.com/jerome-pouiller/reredirect]() 159 | -------------------------------------------------------------------------------- /arch/amd64.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2011 by Nelson Elhage 3 | * 4 | * Permission is hereby granted, free of charge, to any person obtaining a copy 5 | * of this software and associated documentation files (the "Software"), to deal 6 | * in the Software without restriction, including without limitation the rights 7 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | * copies of the Software, and to permit persons to whom the Software is 9 | * furnished to do so, subject to the following conditions: 10 | * 11 | * The above copyright notice and this permission notice shall be included in 12 | * all copies or substantial portions of the Software. 13 | * 14 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 20 | * THE SOFTWARE. 21 | */ 22 | #include "x86_common.h" 23 | 24 | #define ARCH_HAVE_MULTIPLE_PERSONALITIES 25 | 26 | static struct ptrace_personality arch_personality[2] = { 27 | { 28 | offsetof(struct user, regs.rax), 29 | offsetof(struct user, regs.rdi), 30 | offsetof(struct user, regs.rsi), 31 | offsetof(struct user, regs.rdx), 32 | offsetof(struct user, regs.r10), 33 | offsetof(struct user, regs.r8), 34 | offsetof(struct user, regs.r9), 35 | offsetof(struct user, regs.rip), 36 | }, 37 | { 38 | offsetof(struct user, regs.rax), 39 | offsetof(struct user, regs.rbx), 40 | offsetof(struct user, regs.rcx), 41 | offsetof(struct user, regs.rdx), 42 | offsetof(struct user, regs.rsi), 43 | offsetof(struct user, regs.rdi), 44 | offsetof(struct user, regs.rbp), 45 | offsetof(struct user, regs.rip), 46 | }, 47 | }; 48 | 49 | struct x86_personality x86_personality[2] = { 50 | { 51 | offsetof(struct user, regs.orig_rax), 52 | offsetof(struct user, regs.rax), 53 | }, 54 | { 55 | offsetof(struct user, regs.orig_rax), 56 | offsetof(struct user, regs.rax), 57 | }, 58 | }; 59 | 60 | struct syscall_numbers arch_syscall_numbers[2] = { 61 | #include "default-syscalls.h" 62 | { 63 | /* 64 | * These don't seem to be available in any convenient header. We could 65 | * include unistd_32.h, but those definitions would conflict with the 66 | * standard ones. So, let's just hardcode the values for now. Probably 67 | * we should generate this from unistd_32.h during the build process or 68 | * soemthing. 69 | */ 70 | .nr_mmap = 90, 71 | .nr_mmap2 = 192, 72 | .nr_munmap = 91, 73 | .nr_getsid = 147, 74 | .nr_setsid = 66, 75 | .nr_setpgid = 57, 76 | .nr_fork = 2, 77 | .nr_wait4 = 114, 78 | .nr_signal = 48, 79 | .nr_rt_sigaction = 173, 80 | .nr_openat = 295, 81 | .nr_close = 6, 82 | .nr_ioctl = 54, 83 | .nr_dup2 = 63, 84 | .nr_dup = 41 85 | } 86 | }; 87 | 88 | int arch_get_personality(struct ptrace_child *child) { 89 | unsigned long cs; 90 | 91 | cs = ptrace_command(child, PTRACE_PEEKUSER, 92 | offsetof(struct user, regs.cs)); 93 | if (child->error) 94 | return -1; 95 | if (cs == 0x23) 96 | child->personality = 1; 97 | return 0; 98 | } 99 | -------------------------------------------------------------------------------- /arch/arm.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2011 by Nelson Elhage 3 | * 4 | * Permission is hereby granted, free of charge, to any person obtaining a copy 5 | * of this software and associated documentation files (the "Software"), to deal 6 | * in the Software without restriction, including without limitation the rights 7 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | * copies of the Software, and to permit persons to whom the Software is 9 | * furnished to do so, subject to the following conditions: 10 | * 11 | * The above copyright notice and this permission notice shall be included in 12 | * all copies or substantial portions of the Software. 13 | * 14 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 20 | * THE SOFTWARE. 21 | */ 22 | static struct ptrace_personality arch_personality[1] = { 23 | { 24 | offsetof(struct user, regs.uregs[0]), 25 | offsetof(struct user, regs.uregs[0]), 26 | offsetof(struct user, regs.uregs[1]), 27 | offsetof(struct user, regs.uregs[2]), 28 | offsetof(struct user, regs.uregs[3]), 29 | offsetof(struct user, regs.uregs[4]), 30 | offsetof(struct user, regs.uregs[5]), 31 | offsetof(struct user, regs.ARM_pc), 32 | } 33 | }; 34 | 35 | static inline void arch_fixup_regs(struct ptrace_child *child) { 36 | child->user.regs.ARM_pc -= 4; 37 | } 38 | 39 | static inline int arch_set_syscall(struct ptrace_child *child, 40 | unsigned long sysno) { 41 | return ptrace_command(child, PTRACE_SET_SYSCALL, 0, sysno); 42 | } 43 | 44 | static inline int arch_save_syscall(struct ptrace_child *child) { 45 | unsigned long swi; 46 | swi = ptrace_command(child, PTRACE_PEEKTEXT, child->user.regs.ARM_pc); 47 | if (child->error) 48 | return -1; 49 | if (swi == 0xef000000) 50 | child->saved_syscall = child->user.regs.uregs[7]; 51 | else 52 | child->saved_syscall = (swi & 0x000fffff); 53 | return 0; 54 | } 55 | 56 | static inline int arch_restore_syscall(struct ptrace_child *child) { 57 | return arch_set_syscall(child, child->saved_syscall); 58 | } 59 | -------------------------------------------------------------------------------- /arch/default-syscalls.h: -------------------------------------------------------------------------------- 1 | #define SC(name) .nr_##name = __NR_##name 2 | 3 | { 4 | #ifdef __NR_mmap 5 | SC(mmap), 6 | #else 7 | .nr_mmap = -1, 8 | #endif 9 | #ifdef __NR_mmap2 10 | SC(mmap2), 11 | #else 12 | .nr_mmap2 = -1, 13 | #endif 14 | SC(munmap), 15 | SC(getsid), 16 | SC(setsid), 17 | SC(setpgid), 18 | SC(fork), 19 | SC(wait4), 20 | #ifdef __NR_signal 21 | SC(signal), 22 | #else 23 | .nr_signal = -1, 24 | #endif 25 | SC(rt_sigaction), 26 | SC(openat), 27 | SC(close), 28 | SC(ioctl), 29 | SC(dup2), 30 | SC(dup), 31 | }, 32 | 33 | #undef SC 34 | -------------------------------------------------------------------------------- /arch/i386.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2011 by Nelson Elhage 3 | * 4 | * Permission is hereby granted, free of charge, to any person obtaining a copy 5 | * of this software and associated documentation files (the "Software"), to deal 6 | * in the Software without restriction, including without limitation the rights 7 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | * copies of the Software, and to permit persons to whom the Software is 9 | * furnished to do so, subject to the following conditions: 10 | * 11 | * The above copyright notice and this permission notice shall be included in 12 | * all copies or substantial portions of the Software. 13 | * 14 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 20 | * THE SOFTWARE. 21 | */ 22 | #include "x86_common.h" 23 | 24 | static struct ptrace_personality arch_personality[1] = { 25 | { 26 | offsetof(struct user, regs.eax), 27 | offsetof(struct user, regs.ebx), 28 | offsetof(struct user, regs.ecx), 29 | offsetof(struct user, regs.edx), 30 | offsetof(struct user, regs.esi), 31 | offsetof(struct user, regs.edi), 32 | offsetof(struct user, regs.ebp), 33 | offsetof(struct user, regs.eip), 34 | } 35 | }; 36 | 37 | struct x86_personality x86_personality[1] = { 38 | { 39 | offsetof(struct user, regs.orig_eax), 40 | offsetof(struct user, regs.eax), 41 | } 42 | }; 43 | -------------------------------------------------------------------------------- /arch/x86_common.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2011 by Nelson Elhage 3 | * 4 | * Permission is hereby granted, free of charge, to any person obtaining a copy 5 | * of this software and associated documentation files (the "Software"), to deal 6 | * in the Software without restriction, including without limitation the rights 7 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | * copies of the Software, and to permit persons to whom the Software is 9 | * furnished to do so, subject to the following conditions: 10 | * 11 | * The above copyright notice and this permission notice shall be included in 12 | * all copies or substantial portions of the Software. 13 | * 14 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 20 | * THE SOFTWARE. 21 | */ 22 | 23 | struct x86_personality { 24 | size_t orig_ax; 25 | size_t ax; 26 | }; 27 | 28 | struct x86_personality x86_personality[]; 29 | 30 | static inline struct x86_personality *x86_pers(struct ptrace_child *child) { 31 | return &x86_personality[child->personality]; 32 | } 33 | 34 | static inline void arch_fixup_regs(struct ptrace_child *child) { 35 | struct x86_personality *x86pers = x86_pers(child); 36 | struct ptrace_personality *pers = personality(child); 37 | struct user *user = &child->user; 38 | #define ptr(user, off) ((unsigned long*)((void*)(user)+(off))) 39 | *ptr(user, pers->reg_ip) -= 2; 40 | *ptr(user, x86pers->ax) = *ptr(user, x86pers->orig_ax); 41 | } 42 | 43 | static inline int arch_set_syscall(struct ptrace_child *child, 44 | unsigned long sysno) { 45 | return ptrace_command(child, PTRACE_POKEUSER, 46 | x86_pers(child)->orig_ax, 47 | sysno); 48 | } 49 | 50 | static inline int arch_save_syscall(struct ptrace_child *child) { 51 | child->saved_syscall = *ptr(&child->user, x86_pers(child)->orig_ax); 52 | return 0; 53 | } 54 | 55 | static inline int arch_restore_syscall(struct ptrace_child *child) { 56 | return 0; 57 | } 58 | 59 | #undef ptr 60 | -------------------------------------------------------------------------------- /attach.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2011 by Nelson Elhage 3 | * Copyright (C) 2014 by Jérôme Pouiller 4 | * 5 | * Permission is hereby granted, free of charge, to any person obtaining a copy 6 | * of this software and associated documentation files (the "Software"), to deal 7 | * in the Software without restriction, including without limitation the rights 8 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | * copies of the Software, and to permit persons to whom the Software is 10 | * furnished to do so, subject to the following conditions: 11 | * 12 | * The above copyright notice and this permission notice shall be included in 13 | * all copies or substantial portions of the Software. 14 | * 15 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | * THE SOFTWARE. 22 | */ 23 | #include 24 | #include 25 | #include 26 | #include 27 | #include 28 | 29 | #include "ptrace.h" 30 | #include "reredirect.h" 31 | 32 | #define PAGE_SZ sysconf(_SC_PAGE_SIZE) 33 | 34 | #define do_syscall(child, name, a0, a1, a2, a3, a4, a5) \ 35 | ptrace_remote_syscall((child), ptrace_syscall_numbers((child))->nr_##name, \ 36 | a0, a1, a2, a3, a4, a5) 37 | 38 | static int do_mmap(struct ptrace_child *child, child_addr_t *arg_addr, unsigned long len) { 39 | int mmap_syscall = ptrace_syscall_numbers(child)->nr_mmap2; 40 | child_addr_t addr; 41 | if (mmap_syscall == -1) 42 | mmap_syscall = ptrace_syscall_numbers(child)->nr_mmap; 43 | addr = ptrace_remote_syscall(child, mmap_syscall, 0, 44 | PAGE_SZ, PROT_READ|PROT_WRITE, 45 | MAP_ANONYMOUS|MAP_PRIVATE, 0, 0); 46 | if (addr > (unsigned long) -1000) 47 | return -(signed long)addr; 48 | *arg_addr = addr; 49 | return 0; 50 | } 51 | 52 | static void do_unmap(struct ptrace_child *child, child_addr_t addr, unsigned long len) { 53 | if (addr == (unsigned long)-1) 54 | return; 55 | do_syscall(child, munmap, addr, len, 0, 0, 0, 0); 56 | } 57 | 58 | int child_attach(pid_t pid, struct ptrace_child *child, child_addr_t *scratch_page) { 59 | int err = 0; 60 | 61 | if (ptrace_attach_child(child, pid)) 62 | return child->error; 63 | 64 | if (ptrace_advance_to_state(child, ptrace_at_syscall)) 65 | return child->error; 66 | 67 | if (ptrace_save_regs(child)) 68 | return child->error; 69 | 70 | err = do_mmap(child, scratch_page, PAGE_SZ); 71 | if (err) 72 | return err; 73 | 74 | debug("Allocated scratch page: %lx", *scratch_page); 75 | return 0; 76 | } 77 | 78 | int child_detach(struct ptrace_child *child, child_addr_t scratch_page) { 79 | do_unmap(child, scratch_page, PAGE_SZ); 80 | debug("Freed scratch page: %lx", scratch_page); 81 | 82 | ptrace_restore_regs(child); 83 | ptrace_detach_child(child); 84 | return 0; 85 | } 86 | 87 | int child_open(struct ptrace_child *child, child_addr_t scratch_page, const char *file) { 88 | int child_fd; 89 | char buf[PATH_MAX + 1]; 90 | 91 | if (file[0] == '/') { 92 | snprintf(buf, sizeof(buf), "%s", file); 93 | } else { 94 | getcwd(buf, sizeof(buf)); 95 | snprintf(buf + strlen(buf), sizeof(buf), "/%s", file); 96 | } 97 | 98 | if (ptrace_memcpy_to_child(child, scratch_page, buf, strlen(buf) + 1)) { 99 | error("Unable to memcpy the pty path to child."); 100 | return child->error; 101 | } 102 | 103 | child_fd = do_syscall(child, openat, AT_FDCWD, scratch_page, 104 | O_RDWR | O_CREAT, 0666, 0, 0); 105 | if (child_fd < 0) { 106 | error("Unable to open the file in the child."); 107 | return child_fd; 108 | } 109 | 110 | debug("Opened the new fd in the child: %d (%s)", child_fd, file); 111 | return child_fd; 112 | } 113 | 114 | int child_dup(struct ptrace_child *child, int file_fd, int orig_fd, int save_orig) { 115 | int save_fd = -1; 116 | int err; 117 | 118 | if (save_orig) 119 | save_fd = do_syscall(child, dup, orig_fd, 0, 0, 0, 0, 0); 120 | debug("Saved fd %d to %d in the child", orig_fd, save_fd); 121 | 122 | err = do_syscall(child, dup2, file_fd, orig_fd, 0, 0, 0, 0); 123 | if (err < 0) { 124 | error("Unable to dup2 in the child."); 125 | return save_fd; 126 | } 127 | debug("Duplicated fd %d to %d", file_fd, orig_fd); 128 | 129 | err = do_syscall(child, close, file_fd, 0, 0, 0, 0, 0); 130 | if (err < 0) { 131 | error("Unable to close in the child."); 132 | return save_fd; 133 | } 134 | 135 | debug("Closed fd %d", file_fd); 136 | return save_fd; 137 | } 138 | 139 | -------------------------------------------------------------------------------- /ptrace.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2011 by Nelson Elhage 3 | * 4 | * Permission is hereby granted, free of charge, to any person obtaining a copy 5 | * of this software and associated documentation files (the "Software"), to deal 6 | * in the Software without restriction, including without limitation the rights 7 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | * copies of the Software, and to permit persons to whom the Software is 9 | * furnished to do so, subject to the following conditions: 10 | * 11 | * The above copyright notice and this permission notice shall be included in 12 | * all copies or substantial portions of the Software. 13 | * 14 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 20 | * THE SOFTWARE. 21 | */ 22 | #include 23 | #include 24 | #include 25 | #include 26 | #include 27 | #include 28 | #include 29 | #include 30 | #include 31 | #include 32 | #include 33 | #include 34 | #include 35 | #include 36 | 37 | #include "ptrace.h" 38 | 39 | #if defined(__GLIBC__) 40 | # define PTRACE_REQUEST_TYPE enum __ptrace_request 41 | #else 42 | # define PTRACE_REQUEST_TYPE int 43 | #endif 44 | 45 | /* 46 | * RHEL 5's kernel supports these flags, but their libc doesn't ship a ptrace.h 47 | * that defines them. Define them here, and if our kernel doesn't support them, 48 | * we'll find out when PTRACE_SETOPTIONS fails. 49 | */ 50 | #ifndef PTRACE_O_TRACESYSGOOD 51 | #define PTRACE_O_TRACESYSGOOD 0x00000001 52 | #endif 53 | 54 | #ifndef PTRACE_O_TRACEFORK 55 | #define PTRACE_O_TRACEFORK 0x00000002 56 | #endif 57 | 58 | #ifndef PTRACE_EVENT_FORK 59 | #define PTRACE_EVENT_FORK 1 60 | #endif 61 | 62 | #define min(x, y) ({ \ 63 | typeof(x) _min1 = (x); \ 64 | typeof(y) _min2 = (y); \ 65 | _min1 < _min2 ? _min1 : _min2; }) 66 | 67 | static long __ptrace_command(struct ptrace_child *child, PTRACE_REQUEST_TYPE req, 68 | void *, void*); 69 | 70 | #define ptrace_command(cld, req, ...) _ptrace_command(cld, req, ## __VA_ARGS__, NULL, NULL) 71 | #define _ptrace_command(cld, req, addr, data, ...) __ptrace_command((cld), (req), (void*)(addr), (void*)(data)) 72 | 73 | 74 | struct ptrace_personality { 75 | size_t syscall_rv; 76 | size_t syscall_arg0; 77 | size_t syscall_arg1; 78 | size_t syscall_arg2; 79 | size_t syscall_arg3; 80 | size_t syscall_arg4; 81 | size_t syscall_arg5; 82 | size_t reg_ip; 83 | }; 84 | 85 | static struct ptrace_personality *personality(struct ptrace_child *child); 86 | 87 | #if defined(__amd64__) 88 | #include "arch/amd64.h" 89 | #elif defined(__i386__) 90 | #include "arch/i386.h" 91 | #elif defined(__arm__) 92 | #include "arch/arm.h" 93 | #else 94 | #error Unsupported architecture. 95 | #endif 96 | 97 | #ifndef ARCH_HAVE_MULTIPLE_PERSONALITIES 98 | int arch_get_personality(struct ptrace_child *child) { 99 | return 0; 100 | } 101 | 102 | struct syscall_numbers arch_syscall_numbers[] = { 103 | #include "arch/default-syscalls.h" 104 | }; 105 | #endif 106 | 107 | static struct ptrace_personality *personality(struct ptrace_child *child) { 108 | return &arch_personality[child->personality]; 109 | } 110 | 111 | struct syscall_numbers *ptrace_syscall_numbers(struct ptrace_child *child) { 112 | return &arch_syscall_numbers[child->personality]; 113 | } 114 | 115 | int ptrace_attach_child(struct ptrace_child *child, pid_t pid) { 116 | memset(child, 0, sizeof *child); 117 | child->pid = pid; 118 | if (ptrace_command(child, PTRACE_ATTACH) < 0) 119 | return -1; 120 | 121 | return ptrace_finish_attach(child, pid); 122 | } 123 | 124 | int ptrace_finish_attach(struct ptrace_child *child, pid_t pid) { 125 | memset(child, 0, sizeof *child); 126 | child->pid = pid; 127 | 128 | if (ptrace_wait(child) < 0) 129 | goto detach; 130 | 131 | if (arch_get_personality(child)) 132 | goto detach; 133 | 134 | if (ptrace_command(child, PTRACE_SETOPTIONS, 0, 135 | PTRACE_O_TRACESYSGOOD|PTRACE_O_TRACEFORK) < 0) 136 | goto detach; 137 | 138 | return 0; 139 | 140 | detach: 141 | /* Don't clobber child->error */ 142 | ptrace(PTRACE_DETACH, child->pid, 0, 0); 143 | return -1; 144 | } 145 | 146 | int ptrace_detach_child(struct ptrace_child *child) { 147 | if (ptrace_command(child, PTRACE_DETACH, 0, 0) < 0) 148 | return -1; 149 | child->state = ptrace_detached; 150 | return 0; 151 | } 152 | 153 | int ptrace_wait(struct ptrace_child *child) { 154 | if (waitpid(child->pid, &child->status, 0) < 0) { 155 | child->error = errno; 156 | return -1; 157 | } 158 | if (WIFEXITED(child->status) || WIFSIGNALED(child->status)) { 159 | child->state = ptrace_exited; 160 | } else if (WIFSTOPPED(child->status)) { 161 | int sig = WSTOPSIG(child->status); 162 | if (sig & 0x80) { 163 | child->state = (child->state == ptrace_at_syscall) ? 164 | ptrace_after_syscall : ptrace_at_syscall; 165 | } else { 166 | if (sig == SIGTRAP && (((child->status >> 8) & PTRACE_EVENT_FORK) == PTRACE_EVENT_FORK)) 167 | ptrace_command(child, PTRACE_GETEVENTMSG, 0, &child->forked_pid); 168 | if (child->state != ptrace_at_syscall) 169 | child->state = ptrace_stopped; 170 | } 171 | } else { 172 | child->error = EINVAL; 173 | return -1; 174 | } 175 | return 0; 176 | } 177 | 178 | int ptrace_advance_to_state(struct ptrace_child *child, 179 | enum child_state desired) { 180 | int err; 181 | while (child->state != desired) { 182 | switch(desired) { 183 | case ptrace_after_syscall: 184 | case ptrace_at_syscall: 185 | if (WIFSTOPPED(child->status) && WSTOPSIG(child->status) == SIGSEGV) { 186 | child->error = EAGAIN; 187 | return -1; 188 | } 189 | err = ptrace_command(child, PTRACE_SYSCALL, 0, 0); 190 | break; 191 | case ptrace_running: 192 | return ptrace_command(child, PTRACE_CONT, 0, 0); 193 | case ptrace_stopped: 194 | err = kill(child->pid, SIGSTOP); 195 | if (err < 0) 196 | child->error = errno; 197 | break; 198 | default: 199 | child->error = EINVAL; 200 | return -1; 201 | } 202 | if (err < 0) 203 | return err; 204 | if (ptrace_wait(child) < 0) 205 | return -1; 206 | } 207 | return 0; 208 | } 209 | 210 | 211 | int ptrace_save_regs(struct ptrace_child *child) { 212 | if (ptrace_advance_to_state(child, ptrace_at_syscall) < 0) 213 | return -1; 214 | if (ptrace_command(child, PTRACE_GETREGS, 0, &child->user) < 0) 215 | return -1; 216 | arch_fixup_regs(child); 217 | if (arch_save_syscall(child) < 0) 218 | return -1; 219 | return 0; 220 | } 221 | 222 | int ptrace_restore_regs(struct ptrace_child *child) { 223 | int err; 224 | err = ptrace_command(child, PTRACE_SETREGS, 0, &child->user); 225 | if (err < 0) 226 | return err; 227 | return arch_restore_syscall(child); 228 | } 229 | 230 | unsigned long ptrace_remote_syscall(struct ptrace_child *child, 231 | unsigned long sysno, 232 | unsigned long p0, unsigned long p1, 233 | unsigned long p2, unsigned long p3, 234 | unsigned long p4, unsigned long p5) { 235 | unsigned long rv; 236 | if (ptrace_advance_to_state(child, ptrace_at_syscall) < 0) 237 | return -1; 238 | 239 | #define setreg(r, v) do { \ 240 | if (ptrace_command(child, PTRACE_POKEUSER, \ 241 | personality(child)->r, \ 242 | (v)) < 0) \ 243 | return -1; \ 244 | } while (0) 245 | 246 | if (arch_set_syscall(child, sysno) < 0) 247 | return -1; 248 | setreg(syscall_arg0, p0); 249 | setreg(syscall_arg1, p1); 250 | setreg(syscall_arg2, p2); 251 | setreg(syscall_arg3, p3); 252 | setreg(syscall_arg4, p4); 253 | setreg(syscall_arg5, p5); 254 | 255 | if (ptrace_advance_to_state(child, ptrace_after_syscall) < 0) 256 | return -1; 257 | 258 | rv = ptrace_command(child, PTRACE_PEEKUSER, 259 | personality(child)->syscall_rv); 260 | if (child->error) 261 | return -1; 262 | 263 | setreg(reg_ip, *(unsigned long*)((void*)&child->user + 264 | personality(child)->reg_ip)); 265 | 266 | #undef setreg 267 | 268 | return rv; 269 | } 270 | 271 | int ptrace_memcpy_to_child(struct ptrace_child *child, child_addr_t dst, const void *src, size_t n) { 272 | unsigned long scratch; 273 | 274 | while (n >= sizeof(unsigned long)) { 275 | if (ptrace_command(child, PTRACE_POKEDATA, dst, *((unsigned long*)src)) < 0) 276 | return -1; 277 | dst += sizeof(unsigned long); 278 | src += sizeof(unsigned long); 279 | n -= sizeof(unsigned long); 280 | } 281 | 282 | if (n) { 283 | scratch = ptrace_command(child, PTRACE_PEEKDATA, dst); 284 | if (child->error) 285 | return -1; 286 | memcpy(&scratch, src, n); 287 | if (ptrace_command(child, PTRACE_POKEDATA, dst, scratch) < 0) 288 | return -1; 289 | } 290 | 291 | return 0; 292 | } 293 | 294 | int ptrace_memcpy_from_child(struct ptrace_child *child, void *dst, child_addr_t src, size_t n) { 295 | unsigned long scratch; 296 | 297 | while (n) { 298 | scratch = ptrace_command(child, PTRACE_PEEKDATA, src); 299 | if (child->error) return -1; 300 | memcpy(dst, &scratch, min(n, sizeof(unsigned long))); 301 | 302 | dst += sizeof(unsigned long); 303 | src += sizeof(unsigned long); 304 | if (n >= sizeof(unsigned long)) 305 | n -= sizeof(unsigned long); 306 | else 307 | n = 0; 308 | } 309 | return 0; 310 | } 311 | 312 | static long __ptrace_command(struct ptrace_child *child, PTRACE_REQUEST_TYPE req, 313 | void *addr, void *data) { 314 | long rv; 315 | errno = 0; 316 | rv = ptrace(req, child->pid, addr, data); 317 | child->error = errno; 318 | return rv; 319 | } 320 | 321 | 322 | #ifdef BUILD_PTRACE_MAIN 323 | int main(int argc, char **argv) { 324 | struct ptrace_child child; 325 | pid_t pid; 326 | 327 | if (argc < 2) { 328 | printf("Usage: %s pid\n", argv[0]); 329 | return 1; 330 | } 331 | pid = atoi(argv[1]); 332 | 333 | assert(!ptrace_attach_child(&child, pid)); 334 | assert(!ptrace_save_regs(&child)); 335 | 336 | printf("mmap = %lx\n", ptrace_remote_syscall(&child, mmap_syscall, 0, 337 | 4096, PROT_READ|PROT_WRITE, 338 | MAP_ANONYMOUS|MAP_PRIVATE, 0, 0)); 339 | 340 | reset_user_struct(&child.user); 341 | assert(!ptrace_restore_regs(&child)); 342 | assert(!ptrace_detach_child(&child)); 343 | 344 | return 0; 345 | } 346 | #endif 347 | -------------------------------------------------------------------------------- /ptrace.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2011 by Nelson Elhage 3 | * 4 | * Permission is hereby granted, free of charge, to any person obtaining a copy 5 | * of this software and associated documentation files (the "Software"), to deal 6 | * in the Software without restriction, including without limitation the rights 7 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | * copies of the Software, and to permit persons to whom the Software is 9 | * furnished to do so, subject to the following conditions: 10 | * 11 | * The above copyright notice and this permission notice shall be included in 12 | * all copies or substantial portions of the Software. 13 | * 14 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 20 | * THE SOFTWARE. 21 | */ 22 | #ifndef _PTRACE_H_ 23 | #define _PTRACE_H_ 24 | #include 25 | #include 26 | #include 27 | 28 | /* 29 | * See https://github.com/nelhage/reptyr/issues/25 and 30 | * https://github.com/nelhage/reptyr/issues/26. 31 | * 32 | * Older glibc's don't define PTRACE_{SETOPTIONS,GETEVENTMSG}, (but do 33 | * in linux/ptrace.h), but on newer systems sys/ptrace.h and 34 | * linux/ptrace.h conflict. If we were using autoconf or something, we 35 | * could potentially detect the right headers at configure-time, but 36 | * I'd like to avoid adding autoconf. These numbers can't ever change 37 | * for ABI-compatibility reasons, at least. 38 | */ 39 | #ifndef PTRACE_SETOPTIONS 40 | #define PTRACE_SETOPTIONS 0x4200 41 | #endif 42 | #ifndef PTRACE_GETEVENTMSG 43 | #define PTRACE_GETEVENTMSG 0x4201 44 | #endif 45 | 46 | enum child_state { 47 | ptrace_detached = 0, 48 | ptrace_at_syscall, 49 | ptrace_after_syscall, 50 | ptrace_running, 51 | ptrace_stopped, 52 | ptrace_exited 53 | }; 54 | 55 | struct ptrace_child { 56 | pid_t pid; 57 | enum child_state state; 58 | int personality; 59 | int status; 60 | int error; 61 | unsigned long forked_pid; 62 | struct user user; 63 | unsigned long saved_syscall; 64 | }; 65 | 66 | struct syscall_numbers { 67 | long nr_mmap; 68 | long nr_mmap2; 69 | long nr_munmap; 70 | long nr_getsid; 71 | long nr_setsid; 72 | long nr_setpgid; 73 | long nr_fork; 74 | long nr_wait4; 75 | long nr_signal; 76 | long nr_rt_sigaction; 77 | long nr_openat; 78 | long nr_close; 79 | long nr_ioctl; 80 | long nr_dup; 81 | long nr_dup2; 82 | }; 83 | 84 | typedef unsigned long child_addr_t; 85 | 86 | int ptrace_wait(struct ptrace_child *child); 87 | int ptrace_attach_child(struct ptrace_child *child, pid_t pid); 88 | int ptrace_finish_attach(struct ptrace_child *child, pid_t pid); 89 | int ptrace_detach_child(struct ptrace_child *child); 90 | int ptrace_wait(struct ptrace_child *child); 91 | int ptrace_advance_to_state(struct ptrace_child *child, 92 | enum child_state desired); 93 | int ptrace_save_regs(struct ptrace_child *child); 94 | int ptrace_restore_regs(struct ptrace_child *child); 95 | unsigned long ptrace_remote_syscall(struct ptrace_child *child, 96 | unsigned long sysno, 97 | unsigned long p0, unsigned long p1, 98 | unsigned long p2, unsigned long p3, 99 | unsigned long p4, unsigned long p5); 100 | 101 | int ptrace_memcpy_to_child(struct ptrace_child *, child_addr_t, const void*, size_t); 102 | int ptrace_memcpy_from_child(struct ptrace_child *, void*, child_addr_t, size_t); 103 | struct syscall_numbers *ptrace_syscall_numbers(struct ptrace_child *child); 104 | #endif /* _PTRACE_H_ */ 105 | 106 | -------------------------------------------------------------------------------- /relink: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | # vim: set sw=4 expandtab: 3 | # 4 | # SPDX-License-Identifier: GPL-2.0-or-later 5 | # Copyright 2017, Jérôme Pouiller 6 | # 7 | 8 | usage() { 9 | echo 'Usage: relink PID' 10 | echo 'Redirect outputs of a running program as if it was output of this command.' 11 | echo 'On exit, this script automatically detach from PID, original output of PID' 12 | echo 'is restored, and PID continue to run normaly' 13 | echo 14 | echo 'Exemples:' 15 | echo ' relink $(pidof dhclient)' 16 | echo ' relink $(pidof dhclient) | grep useful_line' 17 | } 18 | 19 | # detach 20 | atexit() { 21 | sh $RESTORE_SCRIPT 22 | rm $RESTORE_SCRIPT 23 | # Note: no need to kill 'cat' 24 | rm $FIFO_OUT 25 | rm $FIFO_ERR 26 | exit 0 27 | } 28 | trap atexit INT 29 | 30 | if [ "$#" -ne 1 ]; then 31 | usage 32 | exit 1 33 | fi 34 | 35 | PID=$1 36 | FIFO_OUT=$(mktemp -u) 37 | FIFO_ERR=$(mktemp -u) 38 | RESTORE_SCRIPT=$(mktemp -u) 39 | mkfifo $FIFO_OUT 40 | mkfifo $FIFO_ERR 41 | cat $FIFO_ERR >&2 & 42 | cat $FIFO_OUT & 43 | CAT_PID=$! 44 | reredirect -o $FIFO_OUT -e $FIFO_ERR $PID > $RESTORE_SCRIPT || atexit 45 | wait $CAT_PID 46 | 47 | # $PID has stopped 48 | rm $RESTORE_SCRIPT 49 | rm $FIFO_OUT 50 | rm $FIFO_ERR 51 | exit 0 52 | 53 | -------------------------------------------------------------------------------- /reredirect.1: -------------------------------------------------------------------------------- 1 | .mso www.tmac 2 | .TH reredirect 1 "26 Aug 2014" 3 | .SH NAME 4 | reredirect \- Tool to dynamically redirect outputs of a running process 5 | .SH SYNOPSIS 6 | .B reredirect [-i 7 | .I FILE 8 | .B |-i 9 | .I FD 10 | .B ] [-m 11 | .I FILE 12 | .B |-o 13 | .I FILE 14 | .B |-e 15 | .I FILE 16 | .B |-I 17 | .I FD 18 | .B |-O 19 | .I FD 20 | .B |-E 21 | .I FD 22 | .B ] [-N] [-d] 23 | .I PID 24 | 25 | .SH DESCRIPTION 26 | 27 | .B reredirect 28 | is a utility for taking an existing running program and 29 | attaching its input/outputs (standard output and error output) to files or 30 | another process. 31 | 32 | Simple usage is: 33 | 34 | reredirect -m FILE PID 35 | 36 | It will redirect outputs of 37 | .I PID 38 | to 39 | .I FILE. 40 | It is also possible to redirect standard 41 | input, output and error output in different files: 42 | 43 | reredirect -i FILE1 -o FILE2 -e FILE3 PID 44 | 45 | .B \-m 46 | option is just a shortcut to 47 | .B \-o FILE \-e FILE 48 | 49 | After launched, reredirect, give you command to restore state of 50 | .I PID. 51 | It will looks like: 52 | 53 | reredirect -N -I 4 -O 5 -E 3 5453 54 | 55 | .B \-I 56 | , 57 | .B \-O 58 | and 59 | .B \-E 60 | act as 61 | .B \-i 62 | , 63 | .B \-o 64 | and 65 | .B \-e 66 | but with already opened file descriptors in 67 | .I PID. 68 | They only used to restore previous state of 69 | .I PID. 70 | 71 | 72 | 73 | .SH OPTIONS 74 | 75 | .B \-i FILE 76 | .IP 77 | File to redirect stdin 78 | .LP 79 | 80 | .B \-o FILE 81 | .IP 82 | File to redirect stdout 83 | .LP 84 | 85 | .B \-e FILE 86 | .IP 87 | File to redirect stderr 88 | .LP 89 | 90 | .B \-m FILE 91 | .IP 92 | Same than 93 | .B \-o FILE \-e FILE 94 | .LP 95 | 96 | .B \-I FD 97 | .IP 98 | Redirect stdin to this file descriptor. Mainly used to restore process input 99 | .LP 100 | 101 | .B \-O FD 102 | .IP 103 | Redirect stdout to this file descriptor. Mainly used to restore process outputs 104 | .LP 105 | 106 | .B \-E FD 107 | .IP 108 | Redirect stderr to this file descriptor. Mainly used to restore process outputs 109 | .LP 110 | 111 | .B \-N 112 | .IP 113 | Do not save previous stream 114 | .LP 115 | 116 | .B \-V 117 | .IP 118 | Print the version of 119 | .B reredirect 120 | and exit. 121 | .LP 122 | 123 | .B \-h 124 | .IP 125 | Print a usage message and exit. 126 | .LP 127 | 128 | .B \-v 129 | .IP 130 | Print verbose debug output while running. 131 | .LP 132 | 133 | .SH NOTES 134 | 135 | .B reredirect 136 | depends on the 137 | .BR ptrace (2) 138 | system call to attach to the remote program. On Ubuntu Maverick and higher, this 139 | ability is disabled by default for security reasons. You can enable it 140 | temporarily by doing 141 | .IP 142 | # echo 0 > /proc/sys/kernel/yama/ptrace_scope 143 | .LP 144 | as root, or permanently by editing the file 145 | .IR /etc/sysctl.d/10-ptrace.conf , 146 | which also contains more information about this setting. 147 | 148 | .SH BUGS 149 | 150 | Bugs should be reported to the author (see below) or via the issue tracker on 151 | GitHub. 152 | 153 | .SH AUTHORS 154 | 155 | reredirect was mainly written by Jérôme Pouiller . You can 156 | contact hom for any questions or bug reports. 157 | 158 | reredirect (and especially all ptrace layer) is based on reptyr programm. reptyr 159 | was written by Nelson Elhage . 160 | 161 | .SH HOMEPAGE 162 | 163 | .URL https://github.com/jerome-pouiller/reredirect 164 | 165 | -------------------------------------------------------------------------------- /reredirect.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2011 by Nelson Elhage 3 | * Copyright (C) 2014 by Jérôme Pouiller 4 | * 5 | * Permission is hereby granted, free of charge, to any person obtaining a copy 6 | * of this software and associated documentation files (the "Software"), to deal 7 | * in the Software without restriction, including without limitation the rights 8 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | * copies of the Software, and to permit persons to whom the Software is 10 | * furnished to do so, subject to the following conditions: 11 | * 12 | * The above copyright notice and this permission notice shall be included in 13 | * all copies or substantial portions of the Software. 14 | * 15 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | * THE SOFTWARE. 22 | */ 23 | #define _GNU_SOURCE 24 | #include 25 | #include 26 | #include 27 | #include 28 | #include 29 | #include 30 | #include 31 | #include "reredirect.h" 32 | 33 | static int verbose = 0; 34 | 35 | static void usage() { 36 | char *me = program_invocation_short_name; 37 | fprintf(stderr, "Usage: %s [-m FILE|-o FILE|-e FILE|-O FD|-E FD] [-N] [-d] PID\n", me); 38 | fprintf(stderr, "%s redirect outputs of a running process to a file.\n", me); 39 | fprintf(stderr, " PID Process to reattach\n"); 40 | fprintf(stderr, " -o FILE File to redirect stdout. \n"); 41 | fprintf(stderr, " -e FILE File to redirect stderr.\n"); 42 | fprintf(stderr, " -m FILE Same than -o FILE -e FILE.\n"); 43 | fprintf(stderr, " -O FD Redirect stdout to this file descriptor. Mainly used to restore\n"); 44 | fprintf(stderr, " process outputs.\n"); 45 | fprintf(stderr, " -E FD Redirect stderr to this file descriptor. Mainly used to restore\n"); 46 | fprintf(stderr, " process outputs.\n"); 47 | fprintf(stderr, " -N Do not save previous stream.\n"); 48 | fprintf(stderr, "\n"); 49 | fprintf(stderr, "Notice you can redirect to another program using name pipe. For example:\n"); 50 | fprintf(stderr, " mkfifo /tmp/fifo\n"); 51 | fprintf(stderr, " tee /tmp/log < /tmp/fifo\n"); 52 | fprintf(stderr, " %s PID -m /tmp/fifo\n", me); 53 | } 54 | 55 | static void _debug(const char *pfx, const char *msg, va_list ap) { 56 | if (pfx) 57 | fprintf(stderr, "%s", pfx); 58 | vfprintf(stderr, msg, ap); 59 | fprintf(stderr, "\n"); 60 | } 61 | 62 | void debug(const char *msg, ...) { 63 | va_list ap; 64 | 65 | if (!verbose) 66 | return; 67 | 68 | va_start(ap, msg); 69 | _debug("[+] ", msg, ap); 70 | va_end(ap); 71 | } 72 | 73 | void error(const char *msg, ...) { 74 | va_list ap; 75 | va_start(ap, msg); 76 | _debug("[-] ", msg, ap); 77 | va_end(ap); 78 | } 79 | 80 | void die(const char *msg, ...) { 81 | va_list ap; 82 | va_start(ap, msg); 83 | _debug("[!] ", msg, ap); 84 | va_end(ap); 85 | 86 | exit(1); 87 | } 88 | 89 | void usage_die(const char *msg, ...) { 90 | va_list ap; 91 | va_start(ap, msg); 92 | _debug("[!] ", msg, ap); 93 | va_end(ap); 94 | usage(); 95 | 96 | exit(1); 97 | } 98 | 99 | static void check_yama_ptrace_scope(void) { 100 | int fd = open("/proc/sys/kernel/yama/ptrace_scope", O_RDONLY); 101 | if (fd >= 0) { 102 | char buf[256]; 103 | int n; 104 | n = read(fd, buf, sizeof buf); 105 | close(fd); 106 | if (n > 0) { 107 | if (!atoi(buf)) { 108 | return; 109 | } 110 | } 111 | } else if (errno == ENOENT) 112 | return; 113 | fprintf(stderr, "The kernel denied permission while attaching. If your uid matches\n"); 114 | fprintf(stderr, "the target's, check the value of /proc/sys/kernel/yama/ptrace_scope.\n"); 115 | fprintf(stderr, "For more information, see /etc/sysctl.d/10-ptrace.conf\n"); 116 | } 117 | 118 | int main(int argc, char **argv) { 119 | int no_restore = 0; 120 | int fde = -1; 121 | int fdo = -1; 122 | int fdi = -1; 123 | int fde_orig = -1; 124 | int fdo_orig = -1; 125 | int fdi_orig = -1; 126 | const char *filei = NULL; 127 | const char *fileo = NULL; 128 | const char *filee = NULL; 129 | pid_t pid; 130 | int opt; 131 | int err; 132 | unsigned long scratch_page = (unsigned long) -1; 133 | struct ptrace_child child; 134 | 135 | while ((opt = getopt(argc, argv, "m:i:o:e:I:O:E:s:dNvVh")) != -1) { 136 | switch (opt) { 137 | case 'I': 138 | if (filei || fdi >= 0) 139 | usage_die("-i and -I are exclusive\n"); 140 | fdi = atoi(optarg); 141 | break; 142 | case 'O': 143 | if (fileo || fdo >= 0) 144 | usage_die("-m, -o and -O are exclusive\n"); 145 | fdo = atoi(optarg); 146 | break; 147 | case 'E': 148 | if (filee || fde >= 0) 149 | usage_die("-m, -e and -E are exclusive\n"); 150 | fde = atoi(optarg); 151 | break; 152 | case 'i': 153 | if (filei || fdi >= 0) 154 | usage_die("-i and -I are exclusive\n"); 155 | filei = optarg; 156 | break; 157 | case 'o': 158 | if (fileo || fdo >= 0) 159 | usage_die("-m, -o and -O are exclusive\n"); 160 | fileo = optarg; 161 | break; 162 | case 'e': 163 | if (filee || fde >= 0) 164 | usage_die("-m, -e and -E are exclusive\n"); 165 | filee = optarg; 166 | break; 167 | case 'm': 168 | if (filee || fde >= 0 || fileo || fdo >= 0) 169 | usage_die("-m is exclusive with -o, -e, -O and -E\n"); 170 | fileo = filee = optarg; 171 | break; 172 | case 'N': 173 | no_restore = 1; 174 | break; 175 | case 'h': 176 | usage(argv[0]); 177 | exit(0); 178 | break; 179 | case 'v': 180 | verbose = 1; 181 | break; 182 | case 'V': 183 | printf("This is reredirect version %s.\n", REREDIRECT_VERSION); 184 | printf("https://github.com/jerome-pouiller/reredirect/\n"); 185 | exit(0); 186 | default: /* '?' */ 187 | usage_die("Unknown option\n"); 188 | break; 189 | } 190 | } 191 | 192 | if (optind >= argc) 193 | usage_die("No pid specified to attach\n"); 194 | 195 | pid = atoi(argv[optind]); 196 | err = child_attach(pid, &child, &scratch_page); 197 | if (err) { 198 | fprintf(stderr, "Unable to attach to pid %d: %s\n", pid, strerror(err)); 199 | if (err == EPERM) 200 | check_yama_ptrace_scope(); 201 | exit(1); 202 | } 203 | if (filei) 204 | fdi = child_open(&child, scratch_page, filei); 205 | if (fileo) 206 | fdo = child_open(&child, scratch_page, fileo); 207 | if (filee) 208 | fde = child_open(&child, scratch_page, filee); 209 | if (fdi >= 0) 210 | fdi_orig = child_dup(&child, fdi, 0, !no_restore); 211 | if (fdo >= 0) 212 | fdo_orig = child_dup(&child, fdo, 1, !no_restore); 213 | if (fde >= 0) 214 | fde_orig = child_dup(&child, fde, 2, !no_restore); 215 | child_detach(&child, scratch_page); 216 | 217 | if (!no_restore) { 218 | printf("# Previous state saved. To restore, use:\n"); 219 | printf("%s -N -I %d -O %d -E %d %d\n", program_invocation_name, fdi_orig, fdo_orig, fde_orig, pid); 220 | } 221 | 222 | return 0; 223 | } 224 | -------------------------------------------------------------------------------- /reredirect.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2011 by Nelson Elhage 3 | * Copyright (C) 2014 by Jérôme Pouiller 4 | * 5 | * Permission is hereby granted, free of charge, to any person obtaining a copy 6 | * of this software and associated documentation files (the "Software"), to deal 7 | * in the Software without restriction, including without limitation the rights 8 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | * copies of the Software, and to permit persons to whom the Software is 10 | * furnished to do so, subject to the following conditions: 11 | * 12 | * The above copyright notice and this permission notice shall be included in 13 | * all copies or substantial portions of the Software. 14 | * 15 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | * THE SOFTWARE. 22 | */ 23 | #include "ptrace.h" 24 | #include "version.h" 25 | 26 | int child_attach(pid_t pid, struct ptrace_child *child, child_addr_t *scratch_page); 27 | int child_detach(struct ptrace_child *child, child_addr_t scratch_page); 28 | int child_open(struct ptrace_child *child, child_addr_t scratch_page, const char *file); 29 | int child_dup(struct ptrace_child *child, int file_fd, int orig_fd, int save_orig); 30 | 31 | #define __printf __attribute__((format(printf, 1, 2))) 32 | void __printf die(const char *msg, ...) __attribute__((noreturn)); 33 | void __printf debug(const char *msg, ...); 34 | void __printf error(const char *msg, ...); 35 | --------------------------------------------------------------------------------