Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Newer
Older
100644 257 lines (229 sloc) 6.899 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
111 int winch_happened = 0;
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 resize_pty(pty);
124 /* FIXME: racy against a second resize */
125 winch_happened = 0;
126 }
127 FD_ZERO(&set);
128 FD_SET(0, &set);
129 FD_SET(pty, &set);
130 if (select(pty+1, &set, NULL, NULL, NULL) < 0) {
131 if (errno == EINTR)
132 continue;
133 fprintf(stderr, "select: %m");
134 return;
135 }
136 if (FD_ISSET(0, &set)) {
137 count = read(0, buf, sizeof buf);
138 if (count < 0)
139 return;
140 writeall(pty, buf, count);
141 }
142 if (FD_ISSET(pty, &set)) {
143 count = read(pty, buf, sizeof buf);
144 if (count < 0)
145 return;
146 writeall(1, buf, count);
147 }
148 }
149 }
150
e2d8d67 @nelhage Add a minimal usage message.
authored
151 void usage(char *me) {
9ce2526 @nelhage Add a -s option to force attaching stdio.
authored
152 fprintf(stderr, "Usage: %s [-s] PID\n", me);
153 fprintf(stderr, " %s -l\n", me);
154 fprintf(stderr, " -l Create a new pty pair and print the name of the slave.\n");
155 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
156 fprintf(stderr, " -h Print this help message and exit.\n");
157 fprintf(stderr, " -v Print the version number and exit.\n");
3fa5657 @nelhage Document the -V option in usage and man pages.
authored
158 fprintf(stderr, " -V Print verbose debug output.\n");
e2d8d67 @nelhage Add a minimal usage message.
authored
159 }
160
ff11f6d @nelhage Try to diagnose errors due to Ubuntu's ptrace_scope sysctl.
authored
161 void check_yama_ptrace_scope(void) {
162 int fd = open("/proc/sys/kernel/yama/ptrace_scope", O_RDONLY);
163 if (fd >= 0) {
164 char buf[256];
165 int n;
166 n = read(fd, buf, sizeof buf);
ce2f3eb @nelhage Don't leak an fd in check_yama_ptrace_scope.
authored
167 close(fd);
ff11f6d @nelhage Try to diagnose errors due to Ubuntu's ptrace_scope sysctl.
authored
168 if (n > 0) {
169 if (!atoi(buf)) {
170 return;
171 }
172 }
6c5d231 @nelhage Consistently use a space in 'if (...)' and 'while (...)'.
authored
173 } else if (errno == ENOENT)
ff11f6d @nelhage Try to diagnose errors due to Ubuntu's ptrace_scope sysctl.
authored
174 return;
175 fprintf(stderr, "The kernel denied permission while attaching. If your uid matches\n");
176 fprintf(stderr, "the target's, check the value of /proc/sys/kernel/yama/ptrace_scope.\n");
177 fprintf(stderr, "For more information, see /etc/sysctl.d/10-ptrace.conf\n");
178 }
179
e222527 @nelhage Initial import. Mostly able to run barnowl.
authored
180 int main(int argc, char **argv) {
181 struct termios saved_termios;
182 struct sigaction act;
183 int pty;
9ce2526 @nelhage Add a -s option to force attaching stdio.
authored
184 int arg = 1;
e2d8d67 @nelhage Add a minimal usage message.
authored
185 int do_attach = 1;
9ce2526 @nelhage Add a -s option to force attaching stdio.
authored
186 int force_stdio = 0;
e2d8d67 @nelhage Add a minimal usage message.
authored
187
188 if (argc < 2) {
189 usage(argv[0]);
190 return 2;
191 }
6c5d231 @nelhage Consistently use a space in 'if (...)' and 'while (...)'.
authored
192 if (argv[arg][0] == '-') {
9ce2526 @nelhage Add a -s option to force attaching stdio.
authored
193 switch(argv[arg][1]) {
e2d8d67 @nelhage Add a minimal usage message.
authored
194 case 'h':
195 usage(argv[0]);
196 return 0;
197 case 'l':
198 do_attach = 0;
199 break;
9ce2526 @nelhage Add a -s option to force attaching stdio.
authored
200 case 's':
201 arg++;
202 force_stdio = 1;
203 break;
c5c9e51 @nelhage Add a version number.
authored
204 case 'v':
205 printf("This is reptyr version %s.\n", REPTYR_VERSION);
206 printf(" by Nelson Elhage <nelhage@nelhage.com>\n");
207 printf("http://github.com/nelhage/reptyr/\n");
208 return 0;
d5f8060 @rwmacleod Add a [-V]erbose flag; default is off.
rwmacleod authored
209 case 'V':
210 arg++;
211 verbose = 1;
212 break;
e2d8d67 @nelhage Add a minimal usage message.
authored
213 default:
214 usage(argv[0]);
215 return 1;
216 }
217 }
e222527 @nelhage Initial import. Mostly able to run barnowl.
authored
218
919fff7 @nelhage Don't crash if the user provides -s without a pid.
authored
219 if (do_attach && arg >= argc) {
220 fprintf(stderr, "%s: No pid specified to attach\n", argv[0]);
221 usage(argv[0]);
222 return 1;
223 }
224
e222527 @nelhage Initial import. Mostly able to run barnowl.
authored
225 if ((pty = open("/dev/ptmx", O_RDWR|O_NOCTTY)) < 0)
226 die("Unable to open /dev/ptmx: %m");
227 if (unlockpt(pty) < 0)
228 die("Unable to unlockpt: %m");
229 if (grantpt(pty) < 0)
0274231 @nelhage Fix the text of an error message.
authored
230 die("Unable to grantpt: %m");
f5d1d89 @nelhage Checkpoint attach work-in-progress.
authored
231
e2d8d67 @nelhage Add a minimal usage message.
authored
232 if (do_attach) {
9ce2526 @nelhage Add a -s option to force attaching stdio.
authored
233 pid_t child = atoi(argv[arg]);
b3eb80f @nelhage Improve error-reporting on attach failures somewhat.
authored
234 int err;
9ce2526 @nelhage Add a -s option to force attaching stdio.
authored
235 if ((err = attach_child(child, ptsname(pty), force_stdio))) {
61ea2b9 @nelhage Better error reporting if attaching to a child fails.
authored
236 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
237 if (err == EPERM) {
238 check_yama_ptrace_scope();
239 }
f5d1d89 @nelhage Checkpoint attach work-in-progress.
authored
240 return 1;
241 }
242 } else {
243 printf("Opened a new pty: %s\n", ptsname(pty));
244 }
e222527 @nelhage Initial import. Mostly able to run barnowl.
authored
245
246 setup_raw(&saved_termios);
247 resize_pty(pty);
248 memset(&act, 0, sizeof act);
249 act.sa_handler = do_winch;
250 act.sa_flags = 0;
251 sigaction(SIGWINCH, &act, NULL);
252 do_proxy(pty);
253 tcsetattr(0, TCSANOW, &saved_termios);
254
255 return 0;
256 }
Something went wrong with that request. Please try again.