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