1 /* retty.c - attach process to current terminal
5 * PID is the pid of a running process.
7 * retty works on x86 Linux.
9 * Copyright (c) 2006 Petr Baudis, Jan Sembera
21 * 'So!' cried Denethor. 'Thou hadst already stolen half my son's love. Now
22 * thou stealest the hearts of my knights also, so that they rob me wholly of
23 * my son at the last. But in this at least thou shalt not defy my will: to
27 #define _GNU_SOURCE // grantpt & family
29 #include <sys/ioctl.h>
36 #include <sys/ptrace.h>
38 #include <sys/types.h>
40 #include <sys/select.h>
50 static int oldin
, oldout
, olderr
, die
, intr
;
51 int stin
= 0, sout
= 1, serr
= 2;
54 struct termios t_orig
;
57 /* Write NLONG 4 byte words from BUF into PID starting
58 at address POS. Calling process must be attached to PID. */
60 write_mem(pid_t pid
, unsigned long *buf
, int nlong
, unsigned long pos
)
65 for (p
= buf
, i
= 0; i
< nlong
; p
++, i
++)
66 if (0 > ptrace(PTRACE_POKEDATA
, pid
, pos
+(i
*4), *p
))
73 poke_32(unsigned char *data
, off_t offset
, uint32_t val
)
75 *((uint32_t *)(&data
[offset
])) = val
;
80 dump_code(unsigned char *code
, size_t size
)
83 for (i
= 0; i
< size
; i
++) {
87 printf("0x%02x, ", code
[i
]);
94 inject_attach(pid_t pid
, int n
, char ptsname
[])
96 struct user_regs_struct regs
;
97 unsigned long codeaddr
, ptsnameaddr
;
100 int fd_cervena
= stin
, fd_zelena
= sout
, fd_modra
= serr
;
101 int fd_fialova
= stin
, fd_oranzova
= sout
, fd_bezova
= serr
;
102 int fd_zluta
= stin
, fd_bila
= sout
, fd_cerna
= serr
;
103 int fd_hnusna
= stin
, fd_cokoladova
= sout
, fd_vanilkova
= serr
;
105 static unsigned char attach_code
[] = {
106 // this is not how it looks like *hint* *hint*
107 #include "bc-attach.i"
111 dump_code(attach_code
, sizeof(attach_code
));
115 if (0 > ptrace(PTRACE_ATTACH
, pid
, 0, 0)) {
116 fprintf(stderr
, "cannot attach to %d\n", pid
);
119 waitpid(pid
, NULL
, 0);
120 ptrace(PTRACE_GETREGS
, pid
, 0, ®s
);
127 ptrace(PTRACE_POKEDATA
, pid
, regs
.esp
, regs
.eip
);
129 /* finish code and push it */
130 regs
.esp
-= sizeof(attach_code
);
132 printf("codesize: %x codeaddr: %lx\n", sizeof(attach_code
), codeaddr
);
133 *((int*)&attach_code
[sizeof(attach_code
)-5]) = sizeof(attach_code
) + n
*4 + 4;
134 if (0 > write_mem(pid
, (unsigned long*)&attach_code
, sizeof(attach_code
)/sizeof(long), regs
.esp
)) {
135 fprintf(stderr
, "cannot write attach_code\n");
141 ptsnameaddr
= regs
.esp
;
142 if (0 > write_mem(pid
, (unsigned long*)ptsname
, n
, regs
.esp
)) {
143 fprintf(stderr
, "cannot write bla argument (%s)\n",
149 /* FIXME: This is superfluous now, change bytecode to use lea */
151 ptrace(PTRACE_POKEDATA
, pid
, regs
.esp
, ptsnameaddr
);
153 regs
.eip
= codeaddr
+8;
154 printf("stack: %lx eip: %lx sub:%x\n", regs
.esp
, regs
.eip
, (int) attach_code
[sizeof(attach_code
)-5]);
157 /* Run the bytecode */
158 ptrace(PTRACE_SETREGS
, pid
, 0, ®s
);
159 sigwinch(0); // bytecode will raise another SIGWINCH later so it will get sync'd thru
160 // interrupt any syscall with the WINCH (typically read() ;)
162 ptrace(PTRACE_CONT
, pid
, 0, (void*) SIGWINCH
);
164 if (!WIFSTOPPED(waitst
)) {
165 fprintf(stderr
, "attached task not stopped\n");
168 } while (WSTOPSIG(waitst
) != SIGWINCH
);
170 /* Grab backed up fds from stack */
171 ptrace(PTRACE_GETREGS
, pid
, 0, ®s
);
172 oldin
= ptrace(PTRACE_PEEKDATA
, pid
, regs
.esp
+ 0x8, NULL
);
173 oldout
= ptrace(PTRACE_PEEKDATA
, pid
, regs
.esp
+ 0x4, NULL
);
174 olderr
= ptrace(PTRACE_PEEKDATA
, pid
, regs
.esp
+ 0x0, NULL
);
175 printf("oldfds (esp: %lx): %d, %d, %d\n", regs
.esp
, oldin
, oldout
, olderr
);
178 ptrace(PTRACE_DETACH
, pid
, 0, (void*) SIGWINCH
);
183 static int detached
= 0;
184 if (detached
> 0) return 0;
185 if (0 > ptrace(PTRACE_ATTACH
, pid
, 0, 0)) {
193 inject_detach(pid_t pid
, int fd0
, int fd1
, int fd2
)
195 struct user_regs_struct regs
;
196 unsigned long codeaddr
;
198 int fd_zelena
= stin
, fd_cervena
= sout
, fd_vyblita
= serr
;
199 int fd_modra
= stin
, fd_smoulova
= sout
, fd_hneda
= serr
;
201 static unsigned char detach_code
[] = {
202 // this is not how it looks like either *hint* *hint*
203 #include "bc-detach.i"
208 waitpid(pid
, NULL
, 0);
209 ptrace(PTRACE_GETREGS
, pid
, 0, ®s
);
216 ptrace(PTRACE_POKEDATA
, pid
, regs
.esp
, regs
.eip
);
218 /* finish code and push it */
219 regs
.esp
-= sizeof(detach_code
);
221 printf("codesize: %x codeaddr: %lx\n", sizeof(detach_code
), codeaddr
);
222 *((int*)&detach_code
[sizeof(detach_code
)-5]) = sizeof(detach_code
) + 4 + 4 + 4;
223 if (0 > write_mem(pid
, (unsigned long*)&detach_code
, sizeof(detach_code
)/sizeof(long), regs
.esp
)) {
224 fprintf(stderr
, "cannot write detach_code\n");
230 ptrace(PTRACE_POKEDATA
, pid
, regs
.esp
, fd0
);
232 ptrace(PTRACE_POKEDATA
, pid
, regs
.esp
, fd1
);
234 ptrace(PTRACE_POKEDATA
, pid
, regs
.esp
, fd2
);
236 regs
.eip
= codeaddr
+8;
237 printf("stack: %lx eip: %lx sub:%x\n", regs
.esp
, regs
.eip
, (int) detach_code
[sizeof(detach_code
)-5]);
240 /* Detach and continue */
241 ptrace(PTRACE_SETREGS
, pid
, 0, ®s
);
242 kill(pid
, SIGWINCH
); // interrupt any syscall (typically read() ;)
243 ptrace(PTRACE_DETACH
, pid
, 0, 0);
253 ioctl(1, TIOCGWINSZ
, &w
);
254 ioctl(ptm
, TIOCSWINSZ
, &w
);
267 if ((x
!= 0) && try_detach()) return;
268 if (cleanups
++ > 0) return;
269 if (!try_detach()) inject_detach(pid
, oldin
, oldout
, olderr
);
270 ioctl(0, TCSETS
, &t_orig
);
275 process_escapes(char *buf
, ssize_t
*len
)
277 static enum { ST_NONE
, ST_ENTER
, ST_ESCAPE
} state
;
279 for (i
= 0; i
< *len
; i
++) {
280 //fprintf(stderr, "[state=%d %d/%d char=%x]\n", state, i, *len - 1, buf[i]);
283 if (buf
[i
] == '\n' || buf
[i
] == '\r')
289 memmove(buf
+ i
, buf
+ i
+ 1, *len
- i
- 1);
301 printf("Detach request aborted - ptrace unsuccessful\n");
302 memmove(buf
+ i
, buf
+ i
+ 1, *len
- i
- 1);
307 printf("Supported escape sequences:\n");
308 printf("`. - return the process to its original terminal\n");
309 printf("`d - return the process to its original terminal\n");
310 printf("`? - this message\n");
311 printf("`` - send the escape character by typing it twice\n");
312 printf("(Note that escapes are only recognized immediately after newline.)\n");
313 memmove(buf
+ i
, buf
+ i
+ 1, *len
- i
- 1);
319 memmove(buf
+ i
+ 1, buf
+ i
, *len
- i
);
333 printf("retty %s\n", VERSION
);
334 printf("Copyright (c) 2006 Petr Baudis, Jan Sembera\n");
335 printf("This program is licensed under GNU GPL version 2 and no later.\n");
341 printf(" %s [-h] [-v] [-0 fd] [-1 fd] [-2 fd] PID \n\n", pname
);
343 printf(" -h This help\n");
344 printf(" -v Shows version of retty\n\n");
346 printf(" -0 fd Specify input file descriptor of target process (default 0)\n");
347 printf(" -1 fd Specify output file descriptor of target process (default 1)\n");
348 printf(" -2 fd Specify error file descriptor of target process (default 2)\n\n");
350 printf(" PID PID of process that will be attached (required)\n");
354 main(int argc
, char *argv
[])
364 res
= getopt(argc
, argv
, "hv0:1:2:");
365 if (res
== -1) break;
379 stin
= strtol(optarg
, &c
, 10);
380 if ((*optarg
== '\0') || (*c
!= '\0')) {
381 fprintf(stderr
, "Wrong stdin specification\n");
387 sout
= strtol(optarg
, &c
, 10);
388 if ((*optarg
== '\0') || (*c
!= '\0')) {
389 fprintf(stderr
, "Wrong stdout specification\n");
395 serr
= strtol(optarg
, &c
, 10);
396 if ((*optarg
== '\0') || (*c
!= '\0')) {
397 fprintf(stderr
, "Wrong stderr specification\n");
413 pid
= strtol(argv
[optind
], &x
, 0);
415 fprintf(stderr
, "PID specified incorrectly. Aborting.\n");
430 tcflush(ptm
, TCIOFLUSH
);
431 //(void) ioctl(ptm, TIOCEXCL, (char *) 0);
434 n
= n
/4 + (n
%4 ? 1 : 0);
435 arg
= malloc(n
*sizeof(unsigned long));
436 memcpy(arg
, pts
, n
*4);
438 signal(SIGWINCH
, sigwinch
);
439 signal(SIGINT
, sigint
); // breaks stuff
442 inject_attach(pid
, n
, arg
);
444 ioctl(0, TCGETS
, &t_orig
);
446 signal(SIGTERM
, cleanup
);
447 //signal(SIGINT, cleanup);
448 signal(SIGQUIT
, cleanup
);
449 signal(SIGPIPE
, cleanup
);
452 static struct termios t
;
456 char ibuf
= t
.c_cc
[VINTR
];
457 write(ptm
, &ibuf
, 1);
464 if (select(ptm
+1, &fds
, NULL
, NULL
, NULL
) < 0) {
465 if (errno
== EINTR
|| errno
== EAGAIN
)
471 ioctl(ptm
, TCGETS
, &t
);
472 // we keep 0 raw and let the pts do the terminal work
473 t
.c_lflag
&= ~(ECHO
|ECHOE
|ECHOK
|ECHONL
|ICANON
);
474 ioctl(0, TCSETS
, &t
);
476 if (FD_ISSET(ptm
, &fds
)) {
478 ssize_t len
= read(ptm
, buf
, 256);
479 if (len
< 0 && errno
!= EINTR
&& errno
!= EAGAIN
) {
485 if (FD_ISSET(0, &fds
)) {
487 ssize_t len
= read(0, buf
, 256);
489 stop
= process_escapes(buf
, &len
);
491 write(ptm
, buf
, stop
-1);
494 write(ptm
, buf
, len
);