Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP
Newer
Older
100644 285 lines (257 sloc) 8.143 kB
20b3b40 @nelhage Add the license to all of the source files.
authored
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 */
e222527 @nelhage Initial import. Mostly able to run barnowl.
authored
22 #include <fcntl.h>
23 #include <unistd.h>
24 #include <sys/types.h>
25 #include <sys/select.h>
26 #include <sys/ioctl.h>
27 #include <stdio.h>
28 #include <stdlib.h>
29 #include <errno.h>
30 #include <string.h>
31 #include <stdarg.h>
32 #include <termios.h>
33 #include <signal.h>
34
18ebcd0 @nelhage Break out prototypes into a header file.
authored
35 #include "reptyr.h"
36
eeb8c68 @nelhage Check for Linux in an #ifdef for clarity.
authored
37 #ifndef __linux__
38 #error reptyr is currently Linux-only.
39 #endif
40
d5f8060 @rwmacleod Add a [-V]erbose flag; default is off.
rwmacleod authored
41 static int verbose = 0;
42
18ebcd0 @nelhage Break out prototypes into a header file.
authored
43 void _debug(const char *pfx, const char *msg, va_list ap) {
44
45 if (pfx)
46 fprintf(stderr, "%s", pfx);
47 vfprintf(stderr, msg, ap);
48 fprintf(stderr, "\n");
49 }
f5d1d89 @nelhage Checkpoint attach work-in-progress.
authored
50
e222527 @nelhage Initial import. Mostly able to run barnowl.
authored
51 void die(const char *msg, ...) {
52 va_list ap;
53 va_start(ap, msg);
18ebcd0 @nelhage Break out prototypes into a header file.
authored
54 _debug("[!] ", msg, ap);
e222527 @nelhage Initial import. Mostly able to run barnowl.
authored
55 va_end(ap);
56
57 exit(1);
58 }
59
f5d1d89 @nelhage Checkpoint attach work-in-progress.
authored
60 void debug(const char *msg, ...) {
d5f8060 @rwmacleod Add a [-V]erbose flag; default is off.
rwmacleod authored
61
f5d1d89 @nelhage Checkpoint attach work-in-progress.
authored
62 va_list ap;
d5f8060 @rwmacleod Add a [-V]erbose flag; default is off.
rwmacleod authored
63
64 if (!verbose)
65 return;
66
f5d1d89 @nelhage Checkpoint attach work-in-progress.
authored
67 va_start(ap, msg);
18ebcd0 @nelhage Break out prototypes into a header file.
authored
68 _debug("[+] ", msg, ap);
f5d1d89 @nelhage Checkpoint attach work-in-progress.
authored
69 va_end(ap);
18ebcd0 @nelhage Break out prototypes into a header file.
authored
70 }
f5d1d89 @nelhage Checkpoint attach work-in-progress.
authored
71
18ebcd0 @nelhage Break out prototypes into a header file.
authored
72 void error(const char *msg, ...) {
73 va_list ap;
74 va_start(ap, msg);
75 _debug("[-] ", msg, ap);
76 va_end(ap);
f5d1d89 @nelhage Checkpoint attach work-in-progress.
authored
77 }
78
e222527 @nelhage Initial import. Mostly able to run barnowl.
authored
79 void setup_raw(struct termios *save) {
80 struct termios set;
81 if (tcgetattr(0, save) < 0)
82 die("Unable to read terminal attributes: %m");
83 set = *save;
692b079 @nelhage Use cfmakeraw instead of messing with termios flags ourselves.
authored
84 cfmakeraw(&set);
e222527 @nelhage Initial import. Mostly able to run barnowl.
authored
85 if (tcsetattr(0, TCSANOW, &set) < 0)
86 die("Unable to set terminal attributes: %m");
87 }
88
89 void resize_pty(int pty) {
90 struct winsize sz;
91 if (ioctl(0, TIOCGWINSZ, &sz) < 0)
92 return;
93 ioctl(pty, TIOCSWINSZ, &sz);
94 }
95
96 int writeall(int fd, const void *buf, ssize_t count) {
97 ssize_t rv;
98 while (count > 0) {
99 rv = write(fd, buf, count);
0c34e73 @robryk Add a check for EINTR in write_all()
robryk authored
100 if (rv < 0) {
101 if (errno == EINTR)
102 continue;
e222527 @nelhage Initial import. Mostly able to run barnowl.
authored
103 return rv;
0c34e73 @robryk Add a check for EINTR in write_all()
robryk authored
104 }
e222527 @nelhage Initial import. Mostly able to run barnowl.
authored
105 count -= rv;
106 buf += rv;
107 }
108 return 0;
109 }
110
022d0ad @robryk Removed a SIGWINCH race.
robryk authored
111 volatile sig_atomic_t winch_happened = 0;
e222527 @nelhage Initial import. Mostly able to run barnowl.
authored
112
113 void do_winch(int signal) {
114 winch_happened = 1;
115 }
116
117 void do_proxy(int pty) {
118 char buf[4096];
119 ssize_t count;
120 fd_set set;
121 while (1) {
122 if (winch_happened) {
123 winch_happened = 0;
022d0ad @robryk Removed a SIGWINCH race.
robryk authored
124 /*
125 * FIXME: If a signal comes in after this point but before
126 * select(), the resize will be delayed until we get more
127 * input. signalfd() is probably the cleanest solution.
128 */
129 resize_pty(pty);
e222527 @nelhage Initial import. Mostly able to run barnowl.
authored
130 }
131 FD_ZERO(&set);
132 FD_SET(0, &set);
133 FD_SET(pty, &set);
134 if (select(pty+1, &set, NULL, NULL, NULL) < 0) {
135 if (errno == EINTR)
136 continue;
137 fprintf(stderr, "select: %m");
138 return;
139 }
140 if (FD_ISSET(0, &set)) {
141 count = read(0, buf, sizeof buf);
142 if (count < 0)
143 return;
144 writeall(pty, buf, count);
145 }
146 if (FD_ISSET(pty, &set)) {
147 count = read(pty, buf, sizeof buf);
148 if (count < 0)
149 return;
150 writeall(1, buf, count);
151 }
152 }
153 }
154
e2d8d67 @nelhage Add a minimal usage message.
authored
155 void usage(char *me) {
9ce2526 @nelhage Add a -s option to force attaching stdio.
authored
156 fprintf(stderr, "Usage: %s [-s] PID\n", me);
0b783ca @vi Improve, document and refactor calling scripts from reptyr.
vi authored
157 fprintf(stderr, " %s -l|-L [COMMAND [ARGS]]\n", me);
9ce2526 @nelhage Add a -s option to force attaching stdio.
authored
158 fprintf(stderr, " -l Create a new pty pair and print the name of the slave.\n");
0b783ca @vi Improve, document and refactor calling scripts from reptyr.
vi authored
159 fprintf(stderr, " if there are command-line arguments after -l\n");
160 fprintf(stderr, " they are executed with REPTYR_PTY set to path of pty.\n");
161 fprintf(stderr, " -L Like '-l', but also redirect the child's stdio to the slave.\n");
9ce2526 @nelhage Add a -s option to force attaching stdio.
authored
162 fprintf(stderr, " -s Attach fds 0-2 on the target, even if it is not attached to a tty.\n");
66888f4 @nelhage Make the man page and -h both mention all available options.
authored
163 fprintf(stderr, " -h Print this help message and exit.\n");
164 fprintf(stderr, " -v Print the version number and exit.\n");
3fa5657 @nelhage Document the -V option in usage and man pages.
authored
165 fprintf(stderr, " -V Print verbose debug output.\n");
e2d8d67 @nelhage Add a minimal usage message.
authored
166 }
167
ff11f6d @nelhage Try to diagnose errors due to Ubuntu's ptrace_scope sysctl.
authored
168 void check_yama_ptrace_scope(void) {
169 int fd = open("/proc/sys/kernel/yama/ptrace_scope", O_RDONLY);
170 if (fd >= 0) {
171 char buf[256];
172 int n;
173 n = read(fd, buf, sizeof buf);
ce2f3eb @nelhage Don't leak an fd in check_yama_ptrace_scope.
authored
174 close(fd);
ff11f6d @nelhage Try to diagnose errors due to Ubuntu's ptrace_scope sysctl.
authored
175 if (n > 0) {
176 if (!atoi(buf)) {
177 return;
178 }
179 }
6c5d231 @nelhage Consistently use a space in 'if (...)' and 'while (...)'.
authored
180 } else if (errno == ENOENT)
ff11f6d @nelhage Try to diagnose errors due to Ubuntu's ptrace_scope sysctl.
authored
181 return;
182 fprintf(stderr, "The kernel denied permission while attaching. If your uid matches\n");
183 fprintf(stderr, "the target's, check the value of /proc/sys/kernel/yama/ptrace_scope.\n");
184 fprintf(stderr, "For more information, see /etc/sysctl.d/10-ptrace.conf\n");
185 }
186
e222527 @nelhage Initial import. Mostly able to run barnowl.
authored
187 int main(int argc, char **argv) {
188 struct termios saved_termios;
189 struct sigaction act;
190 int pty;
9ce2526 @nelhage Add a -s option to force attaching stdio.
authored
191 int arg = 1;
e2d8d67 @nelhage Add a minimal usage message.
authored
192 int do_attach = 1;
9ce2526 @nelhage Add a -s option to force attaching stdio.
authored
193 int force_stdio = 0;
0b783ca @vi Improve, document and refactor calling scripts from reptyr.
vi authored
194 int unattached_script_redirection = 0;
e2d8d67 @nelhage Add a minimal usage message.
authored
195
196 if (argc < 2) {
197 usage(argv[0]);
198 return 2;
199 }
6c5d231 @nelhage Consistently use a space in 'if (...)' and 'while (...)'.
authored
200 if (argv[arg][0] == '-') {
9ce2526 @nelhage Add a -s option to force attaching stdio.
authored
201 switch(argv[arg][1]) {
e2d8d67 @nelhage Add a minimal usage message.
authored
202 case 'h':
203 usage(argv[0]);
204 return 0;
205 case 'l':
206 do_attach = 0;
207 break;
0b783ca @vi Improve, document and refactor calling scripts from reptyr.
vi authored
208 case 'L':
209 do_attach = 0;
210 unattached_script_redirection = 1;
211 break;
9ce2526 @nelhage Add a -s option to force attaching stdio.
authored
212 case 's':
213 arg++;
214 force_stdio = 1;
215 break;
c5c9e51 @nelhage Add a version number.
authored
216 case 'v':
217 printf("This is reptyr version %s.\n", REPTYR_VERSION);
218 printf(" by Nelson Elhage <nelhage@nelhage.com>\n");
219 printf("http://github.com/nelhage/reptyr/\n");
220 return 0;
d5f8060 @rwmacleod Add a [-V]erbose flag; default is off.
rwmacleod authored
221 case 'V':
222 arg++;
223 verbose = 1;
224 break;
e2d8d67 @nelhage Add a minimal usage message.
authored
225 default:
226 usage(argv[0]);
227 return 1;
228 }
229 }
e222527 @nelhage Initial import. Mostly able to run barnowl.
authored
230
919fff7 @nelhage Don't crash if the user provides -s without a pid.
authored
231 if (do_attach && arg >= argc) {
232 fprintf(stderr, "%s: No pid specified to attach\n", argv[0]);
233 usage(argv[0]);
234 return 1;
235 }
236
e222527 @nelhage Initial import. Mostly able to run barnowl.
authored
237 if ((pty = open("/dev/ptmx", O_RDWR|O_NOCTTY)) < 0)
238 die("Unable to open /dev/ptmx: %m");
239 if (unlockpt(pty) < 0)
240 die("Unable to unlockpt: %m");
241 if (grantpt(pty) < 0)
0274231 @nelhage Fix the text of an error message.
authored
242 die("Unable to grantpt: %m");
f5d1d89 @nelhage Checkpoint attach work-in-progress.
authored
243
e2d8d67 @nelhage Add a minimal usage message.
authored
244 if (do_attach) {
9ce2526 @nelhage Add a -s option to force attaching stdio.
authored
245 pid_t child = atoi(argv[arg]);
b3eb80f @nelhage Improve error-reporting on attach failures somewhat.
authored
246 int err;
9ce2526 @nelhage Add a -s option to force attaching stdio.
authored
247 if ((err = attach_child(child, ptsname(pty), force_stdio))) {
61ea2b9 @nelhage Better error reporting if attaching to a child fails.
authored
248 fprintf(stderr, "Unable to attach to pid %d: %s\n", child, strerror(err));
ff11f6d @nelhage Try to diagnose errors due to Ubuntu's ptrace_scope sysctl.
authored
249 if (err == EPERM) {
250 check_yama_ptrace_scope();
251 }
f5d1d89 @nelhage Checkpoint attach work-in-progress.
authored
252 return 1;
253 }
254 } else {
255 printf("Opened a new pty: %s\n", ptsname(pty));
12513df @vi Flush stdout after printing "Opened a new pty:" message
vi authored
256 fflush(stdout);
10930a9 @vi Enable of calling other programs from reptyr
vi authored
257 if (argc > 2) {
258 if(!fork()) {
259 setenv("REPTYR_PTY", ptsname(pty), 1);
0b783ca @vi Improve, document and refactor calling scripts from reptyr.
vi authored
260 if (unattached_script_redirection) {
10930a9 @vi Enable of calling other programs from reptyr
vi authored
261 int f;
cc0c3f1 @nelhage Make -L also set the controlling terminal.
authored
262 setpgid(0, getppid());
263 setsid();
10930a9 @vi Enable of calling other programs from reptyr
vi authored
264 f = open(ptsname(pty), O_RDONLY, 0); dup2(f, 0); close(f);
265 f = open(ptsname(pty), O_WRONLY, 0); dup2(f, 1); dup2(f,2); close(f);
266 }
267 close(pty);
268 execvp(argv[2], argv+2);
269 exit(1);
270 }
271 }
f5d1d89 @nelhage Checkpoint attach work-in-progress.
authored
272 }
e222527 @nelhage Initial import. Mostly able to run barnowl.
authored
273
274 setup_raw(&saved_termios);
275 memset(&act, 0, sizeof act);
276 act.sa_handler = do_winch;
277 act.sa_flags = 0;
278 sigaction(SIGWINCH, &act, NULL);
022d0ad @robryk Removed a SIGWINCH race.
robryk authored
279 resize_pty(pty);
e222527 @nelhage Initial import. Mostly able to run barnowl.
authored
280 do_proxy(pty);
281 tcsetattr(0, TCSANOW, &saved_termios);
282
283 return 0;
284 }
Something went wrong with that request. Please try again.