1 /*
2 dtach - A simple program that emulates the detach feature of screen.
3 Copyright (C) 2004-2016 Ned T. Crigler
4
5 This program is free software; you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published by
7 the Free Software Foundation; either version 2 of the License, or
8 (at your option) any later version.
9
10 This program is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 GNU General Public License for more details.
14
15 You should have received a copy of the GNU General Public License
16 along with this program; if not, write to the Free Software
17 Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
18 */
19 #include "dtach.h"
20
21 #ifndef VDISABLE
22 #ifdef _POSIX_VDISABLE
23 #define VDISABLE _POSIX_VDISABLE
24 #else
25 #define VDISABLE 0377
26 #endif
27 #endif
28
29 /*
30 ** The current terminal settings. After coming back from a suspend, we
31 ** restore this.
32 */
33 static struct termios cur_term;
34 /* 1 if the window size changed */
35 static int win_changed;
36
37 /* Restores the original terminal settings. */
38 static void
restore_term(void)39 restore_term(void)
40 {
41 tcsetattr(0, TCSADRAIN, &orig_term);
42
43 /* Make cursor visible. Assumes VT100. */
44 printf("\033[?25h");
45 fflush(stdout);
46 }
47
48 /* Connects to a unix domain socket */
49 static int
connect_socket(char * name)50 connect_socket(char *name)
51 {
52 int s;
53 struct sockaddr_un sockun;
54
55 if (strlen(name) > sizeof(sockun.sun_path) - 1)
56 {
57 errno = ENAMETOOLONG;
58 return -1;
59 }
60
61 s = socket(PF_UNIX, SOCK_STREAM, 0);
62 if (s < 0)
63 return -1;
64 sockun.sun_family = AF_UNIX;
65 strcpy(sockun.sun_path, name);
66 if (connect(s, (struct sockaddr*)&sockun, sizeof(sockun)) < 0)
67 {
68 close(s);
69
70 /* ECONNREFUSED is also returned for regular files, so make
71 ** sure we are trying to connect to a socket. */
72 if (errno == ECONNREFUSED)
73 {
74 struct stat st;
75
76 if (stat(name, &st) < 0)
77 return -1;
78 else if (!S_ISSOCK(st.st_mode) || S_ISREG(st.st_mode))
79 errno = ENOTSOCK;
80 }
81 return -1;
82 }
83 return s;
84 }
85
86 /* Signal */
87 static RETSIGTYPE
die(int sig)88 die(int sig)
89 {
90 /* Print a nice pretty message for some things. */
91 if (sig == SIGHUP || sig == SIGINT)
92 printf(EOS "\r\n[detached]\r\n");
93 else
94 printf(EOS "\r\n[got signal %d - dying]\r\n", sig);
95 exit(1);
96 }
97
98 /* Window size change. */
99 static RETSIGTYPE
win_change()100 win_change()
101 {
102 signal(SIGWINCH, win_change);
103 win_changed = 1;
104 }
105
106 /* Handles input from the keyboard. */
107 static void
process_kbd(int s,struct packet * pkt)108 process_kbd(int s, struct packet *pkt)
109 {
110 /* Suspend? */
111 if (!no_suspend && (pkt->u.buf[0] == cur_term.c_cc[VSUSP]))
112 {
113 /* Tell the master that we are suspending. */
114 pkt->type = MSG_DETACH;
115 write(s, pkt, sizeof(struct packet));
116
117 /* And suspend... */
118 tcsetattr(0, TCSADRAIN, &orig_term);
119 printf(EOS "\r\n");
120 kill(getpid(), SIGTSTP);
121 tcsetattr(0, TCSADRAIN, &cur_term);
122
123 /* Tell the master that we are returning. */
124 pkt->type = MSG_ATTACH;
125 write(s, pkt, sizeof(struct packet));
126
127 /* We would like a redraw, too. */
128 pkt->type = MSG_REDRAW;
129 pkt->len = redraw_method;
130 ioctl(0, TIOCGWINSZ, &pkt->u.ws);
131 write(s, pkt, sizeof(struct packet));
132 return;
133 }
134 /* Detach char? */
135 else if (pkt->u.buf[0] == detach_char)
136 {
137 printf(EOS "\r\n[detached]\r\n");
138 exit(0);
139 }
140 /* Just in case something pukes out. */
141 else if (pkt->u.buf[0] == '\f')
142 win_changed = 1;
143
144 /* Push it out */
145 write(s, pkt, sizeof(struct packet));
146 }
147
148 int
attach_main(int noerror)149 attach_main(int noerror)
150 {
151 struct packet pkt;
152 unsigned char buf[BUFSIZE];
153 fd_set readfds;
154 int s;
155
156 /* Attempt to open the socket. Don't display an error if noerror is
157 ** set. */
158 s = connect_socket(sockname);
159 if (s < 0 && errno == ENAMETOOLONG)
160 {
161 char *slash = strrchr(sockname, '/');
162
163 /* Try to shorten the socket's path name by using chdir. */
164 if (slash)
165 {
166 int dirfd = open(".", O_RDONLY);
167
168 if (dirfd >= 0)
169 {
170 *slash = '\0';
171 if (chdir(sockname) >= 0)
172 {
173 s = connect_socket(slash + 1);
174 fchdir(dirfd);
175 }
176 *slash = '/';
177 close(dirfd);
178 }
179 }
180 }
181 if (s < 0)
182 {
183 if (!noerror)
184 printf("%s: %s: %s\n", progname, sockname,
185 strerror(errno));
186 return 1;
187 }
188
189 /* The current terminal settings are equal to the original terminal
190 ** settings at this point. */
191 cur_term = orig_term;
192
193 /* Set a trap to restore the terminal when we die. */
194 atexit(restore_term);
195
196 /* Set some signals. */
197 signal(SIGPIPE, SIG_IGN);
198 signal(SIGXFSZ, SIG_IGN);
199 signal(SIGHUP, die);
200 signal(SIGTERM, die);
201 signal(SIGINT, die);
202 signal(SIGQUIT, die);
203 signal(SIGWINCH, win_change);
204
205 /* Set raw mode. */
206 cur_term.c_iflag &= ~(IGNBRK|BRKINT|PARMRK|ISTRIP|INLCR|IGNCR|ICRNL);
207 cur_term.c_iflag &= ~(IXON|IXOFF);
208 cur_term.c_oflag &= ~(OPOST);
209 cur_term.c_lflag &= ~(ECHO|ECHONL|ICANON|ISIG|IEXTEN);
210 cur_term.c_cflag &= ~(CSIZE|PARENB);
211 cur_term.c_cflag |= CS8;
212 cur_term.c_cc[VLNEXT] = VDISABLE;
213 cur_term.c_cc[VMIN] = 1;
214 cur_term.c_cc[VTIME] = 0;
215 tcsetattr(0, TCSADRAIN, &cur_term);
216
217 /* Clear the screen. This assumes VT100. */
218 write(1, "\33[H\33[J", 6);
219
220 /* Tell the master that we want to attach. */
221 memset(&pkt, 0, sizeof(struct packet));
222 pkt.type = MSG_ATTACH;
223 write(s, &pkt, sizeof(struct packet));
224
225 /* We would like a redraw, too. */
226 pkt.type = MSG_REDRAW;
227 pkt.len = redraw_method;
228 ioctl(0, TIOCGWINSZ, &pkt.u.ws);
229 write(s, &pkt, sizeof(struct packet));
230
231 /* Wait for things to happen */
232 while (1)
233 {
234 int n;
235
236 FD_ZERO(&readfds);
237 FD_SET(0, &readfds);
238 FD_SET(s, &readfds);
239 n = select(s + 1, &readfds, NULL, NULL, NULL);
240 if (n < 0 && errno != EINTR && errno != EAGAIN)
241 {
242 printf(EOS "\r\n[select failed]\r\n");
243 exit(1);
244 }
245
246 /* Pty activity */
247 if (n > 0 && FD_ISSET(s, &readfds))
248 {
249 ssize_t len = read(s, buf, sizeof(buf));
250
251 if (len == 0)
252 {
253 printf(EOS "\r\n[EOF - dtach terminating]"
254 "\r\n");
255 exit(0);
256 }
257 else if (len < 0)
258 {
259 printf(EOS "\r\n[read returned an error]\r\n");
260 exit(1);
261 }
262 /* Send the data to the terminal. */
263 write(1, buf, len);
264 n--;
265 }
266 /* stdin activity */
267 if (n > 0 && FD_ISSET(0, &readfds))
268 {
269 ssize_t len;
270
271 pkt.type = MSG_PUSH;
272 memset(pkt.u.buf, 0, sizeof(pkt.u.buf));
273 len = read(0, pkt.u.buf, sizeof(pkt.u.buf));
274
275 if (len <= 0)
276 exit(1);
277
278 pkt.len = len;
279 process_kbd(s, &pkt);
280 n--;
281 }
282
283 /* Window size changed? */
284 if (win_changed)
285 {
286 win_changed = 0;
287
288 pkt.type = MSG_WINCH;
289 ioctl(0, TIOCGWINSZ, &pkt.u.ws);
290 write(s, &pkt, sizeof(pkt));
291 }
292 }
293 return 0;
294 }
295
296 int
push_main()297 push_main()
298 {
299 struct packet pkt;
300 int s;
301
302 /* Attempt to open the socket. */
303 s = connect_socket(sockname);
304 if (s < 0 && errno == ENAMETOOLONG)
305 {
306 char *slash = strrchr(sockname, '/');
307
308 /* Try to shorten the socket's path name by using chdir. */
309 if (slash)
310 {
311 int dirfd = open(".", O_RDONLY);
312
313 if (dirfd >= 0)
314 {
315 *slash = '\0';
316 if (chdir(sockname) >= 0)
317 {
318 s = connect_socket(slash + 1);
319 fchdir(dirfd);
320 }
321 *slash = '/';
322 close(dirfd);
323 }
324 }
325 }
326 if (s < 0)
327 {
328 printf("%s: %s: %s\n", progname, sockname, strerror(errno));
329 return 1;
330 }
331
332 /* Set some signals. */
333 signal(SIGPIPE, SIG_IGN);
334
335 /* Push the contents of standard input to the socket. */
336 pkt.type = MSG_PUSH;
337 for (;;)
338 {
339 ssize_t len;
340
341 memset(pkt.u.buf, 0, sizeof(pkt.u.buf));
342 len = read(0, pkt.u.buf, sizeof(pkt.u.buf));
343
344 if (len == 0)
345 return 0;
346 else if (len < 0)
347 {
348 printf("%s: %s: %s\n", progname, sockname,
349 strerror(errno));
350 return 1;
351 }
352
353 pkt.len = len;
354 if (write(s, &pkt, sizeof(struct packet)) < 0)
355 {
356 printf("%s: %s: %s\n", progname, sockname,
357 strerror(errno));
358 return 1;
359 }
360 }
361 }
362