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
12 #define _GNU_SOURCE // grantpt & family
14 #include <sys/ioctl.h>
20 #include <sys/ptrace.h>
22 #include <sys/types.h>
24 #include <sys/select.h>
29 #define VERSION "1/sqrt(2)"
34 static int oldin
, oldout
, olderr
, die
, intr
;
37 struct termios t_orig
;
40 /* Write NLONG 4 byte words from BUF into PID starting
41 at address POS. Calling process must be attached to PID. */
43 write_mem(pid_t pid
, unsigned long *buf
, int nlong
, unsigned long pos
)
48 for (p
= buf
, i
= 0; i
< nlong
; p
++, i
++)
49 if (0 > ptrace(PTRACE_POKEDATA
, pid
, pos
+(i
*4), *p
))
56 inject_attach(pid_t pid
, int n
, char ptsname
[])
58 struct user_regs_struct regs
;
59 unsigned long codeaddr
, ptsnameaddr
;
62 static char attach_code
[] = {
63 #include "bc-attach.i"
67 if (0 > ptrace(PTRACE_ATTACH
, pid
, 0, 0)) {
68 fprintf(stderr
, "cannot attach to %d\n", pid
);
71 waitpid(pid
, NULL
, 0);
72 ptrace(PTRACE_GETREGS
, pid
, 0, ®s
);
79 ptrace(PTRACE_POKEDATA
, pid
, regs
.esp
, regs
.eip
);
81 /* finish code and push it */
82 regs
.esp
-= sizeof(attach_code
);
84 printf("codesize: %x codeaddr: %lx\n", sizeof(attach_code
), codeaddr
);
85 *((int*)&attach_code
[sizeof(attach_code
)-5]) = sizeof(attach_code
) + n
*4 + 4;
86 if (0 > write_mem(pid
, (unsigned long*)&attach_code
, sizeof(attach_code
)/sizeof(long), regs
.esp
)) {
87 fprintf(stderr
, "cannot write attach_code\n");
93 ptsnameaddr
= regs
.esp
;
94 if (0 > write_mem(pid
, (unsigned long*)ptsname
, n
, regs
.esp
)) {
95 fprintf(stderr
, "cannot write bla argument (%s)\n",
101 /* FIXME: This is superfluous now, change bytecode to use lea */
103 ptrace(PTRACE_POKEDATA
, pid
, regs
.esp
, ptsnameaddr
);
105 regs
.eip
= codeaddr
+8;
106 printf("stack: %lx eip: %lx sub:%x\n", regs
.esp
, regs
.eip
, (int) attach_code
[sizeof(attach_code
)-5]);
109 /* Run the bytecode */
110 ptrace(PTRACE_SETREGS
, pid
, 0, ®s
);
111 sigwinch(0); // bytecode will raise another SIGWINCH later so it will get sync'd thru
112 // interrupt any syscall with the WINCH (typically read() ;)
114 ptrace(PTRACE_CONT
, pid
, 0, (void*) SIGWINCH
);
116 if (!WIFSTOPPED(waitst
)) {
117 fprintf(stderr
, "attached task not stopped\n");
120 } while (WSTOPSIG(waitst
) != SIGWINCH
);
122 /* Grab backed up fds from stack */
123 ptrace(PTRACE_GETREGS
, pid
, 0, ®s
);
124 oldin
= ptrace(PTRACE_PEEKDATA
, pid
, regs
.esp
+ 0x8, NULL
);
125 oldout
= ptrace(PTRACE_PEEKDATA
, pid
, regs
.esp
+ 0x4, NULL
);
126 olderr
= ptrace(PTRACE_PEEKDATA
, pid
, regs
.esp
+ 0x0, NULL
);
127 printf("oldfds (esp: %lx): %d, %d, %d\n", regs
.esp
, oldin
, oldout
, olderr
);
130 ptrace(PTRACE_DETACH
, pid
, 0, (void*) SIGWINCH
);
135 static int detached
= 0;
136 if (detached
> 0) return 0;
137 if (0 > ptrace(PTRACE_ATTACH
, pid
, 0, 0)) {
145 inject_detach(pid_t pid
, int fd0
, int fd1
, int fd2
)
147 struct user_regs_struct regs
;
148 unsigned long codeaddr
;
150 static char detach_code
[] = {
151 #include "bc-detach.i"
156 waitpid(pid
, NULL
, 0);
157 ptrace(PTRACE_GETREGS
, pid
, 0, ®s
);
164 ptrace(PTRACE_POKEDATA
, pid
, regs
.esp
, regs
.eip
);
166 /* finish code and push it */
167 regs
.esp
-= sizeof(detach_code
);
169 printf("codesize: %x codeaddr: %lx\n", sizeof(detach_code
), codeaddr
);
170 *((int*)&detach_code
[sizeof(detach_code
)-5]) = sizeof(detach_code
) + 4 + 4 + 4;
171 if (0 > write_mem(pid
, (unsigned long*)&detach_code
, sizeof(detach_code
)/sizeof(long), regs
.esp
)) {
172 fprintf(stderr
, "cannot write detach_code\n");
178 ptrace(PTRACE_POKEDATA
, pid
, regs
.esp
, fd0
);
180 ptrace(PTRACE_POKEDATA
, pid
, regs
.esp
, fd1
);
182 ptrace(PTRACE_POKEDATA
, pid
, regs
.esp
, fd2
);
184 regs
.eip
= codeaddr
+8;
185 printf("stack: %lx eip: %lx sub:%x\n", regs
.esp
, regs
.eip
, (int) detach_code
[sizeof(detach_code
)-5]);
188 /* Detach and continue */
189 ptrace(PTRACE_SETREGS
, pid
, 0, ®s
);
190 kill(pid
, SIGWINCH
); // interrupt any syscall (typically read() ;)
191 ptrace(PTRACE_DETACH
, pid
, 0, 0);
201 ioctl(1, TIOCGWINSZ
, &w
);
202 ioctl(ptm
, TIOCSWINSZ
, &w
);
215 if ((x
!= 0) && try_detach()) return;
216 if (cleanups
++ > 0) return;
217 if (!try_detach()) inject_detach(pid
, oldin
, oldout
, olderr
);
218 ioctl(0, TCSETS
, &t_orig
);
223 process_escapes(char *buf
, ssize_t
*len
)
225 static enum { ST_NONE
, ST_ENTER
, ST_ESCAPE
} state
;
227 for (i
= 0; i
< *len
; i
++) {
228 //fprintf(stderr, "[state=%d %d/%d char=%x]\n", state, i, *len - 1, buf[i]);
231 if (buf
[i
] == '\n' || buf
[i
] == '\r')
237 memmove(buf
+ i
, buf
+ i
+ 1, *len
- i
- 1);
249 printf("Detach request aborted - ptrace unsuccessful\n");
250 memmove(buf
+ i
, buf
+ i
+ 1, *len
- i
- 1);
255 printf("Supported escape sequences:\n");
256 printf("`. - return the process to its original terminal\n");
257 printf("`d - return the process to its original terminal\n");
258 printf("`? - this message\n");
259 printf("`` - send the escape character by typing it twice\n");
260 printf("(Note that escapes are only recognized immediately after newline.)\n");
261 memmove(buf
+ i
, buf
+ i
+ 1, *len
- i
- 1);
267 memmove(buf
+ i
+ 1, buf
+ i
, *len
- i
);
281 printf("retty %s\n", VERSION
);
282 printf("Copyright (c) 2006 Petr Baudis, Jan Sembera\n");
283 printf("This program is licensed under GNU GPL version 2 and no later.\n");
290 printf(" %s [-h] [-v] [-d fd[,fd[..]]] [-f] [-a arch] PID \n\n", pname
);
292 printf(" %s [-h] [-v] PID \n\n", pname
);
294 printf(" -h This help\n");
295 printf(" -v Shows version of retty\n");
297 printf(" -d fd,... List of file descriptors to be attached, separated by comma\n");
298 printf(" If not specified, default is 0, 1 and 2.\n");
299 printf(" -f Use forking code instead of standard code. Beware that this might\n");
300 printf(" cause some very unexpected behaviour.\n");
301 printf(" -a arch Selects architecture on which the target process is running.\n");
302 printf(" Normally, retty will select the platform itself, but there are\n");
303 printf(" some specific cases that require manual selection\n");
305 printf(" PID PID of process that will be attached\n");
309 setpid(char *pidchar
, char *argv
) {
312 pid
= strtol(pidchar
, &x
, 0);
314 fprintf(stderr
, "PID specified incorrectly. Aborting.\n");
321 main(int argc
, char *argv
[])
330 res
= getopt(argc
, argv
, "hvd:fa:o:");
331 if (res
== -1) break;
345 fprintf(stderr
, "File descriptor alteration not yet implemented\n");
349 fprintf(stderr
, "Forking not yet implemented\n");
354 fprintf(stderr
, "Architecture selection not yet implemented\n");
368 pid
= strtol(argv
[optind
], &x
, 0);
370 fprintf(stderr
, "PID specified incorrectly. Aborting.\n");
385 tcflush(ptm
, TCIOFLUSH
);
386 //(void) ioctl(ptm, TIOCEXCL, (char *) 0);
389 n
= n
/4 + (n
%4 ? 1 : 0);
390 arg
= malloc(n
*sizeof(unsigned long));
391 memcpy(arg
, pts
, n
*4);
393 signal(SIGWINCH
, sigwinch
);
394 signal(SIGINT
, sigint
); // breaks stuff
397 inject_attach(pid
, n
, arg
);
399 ioctl(0, TCGETS
, &t_orig
);
401 signal(SIGTERM
, cleanup
);
402 //signal(SIGINT, cleanup);
403 signal(SIGQUIT
, cleanup
);
404 signal(SIGPIPE
, cleanup
);
407 static struct termios t
;
411 char ibuf
= t
.c_cc
[VINTR
];
412 write(ptm
, &ibuf
, 1);
419 if (select(ptm
+1, &fds
, NULL
, NULL
, NULL
) < 0) {
420 if (errno
== EINTR
|| errno
== EAGAIN
)
426 ioctl(ptm
, TCGETS
, &t
);
427 // we keep 0 raw and let the pts do the terminal work
428 t
.c_lflag
&= ~(ECHO
|ECHOE
|ECHOK
|ECHONL
|ICANON
);
429 ioctl(0, TCSETS
, &t
);
431 if (FD_ISSET(ptm
, &fds
)) {
433 ssize_t len
= read(ptm
, buf
, 256);
434 if (len
< 0 && errno
!= EINTR
&& errno
!= EAGAIN
) {
440 if (FD_ISSET(0, &fds
)) {
442 ssize_t len
= read(0, buf
, 256);
444 stop
= process_escapes(buf
, &len
);
446 write(ptm
, buf
, stop
-1);
449 write(ptm
, buf
, len
);