1 
2 /*
3  * Copyright Colten Edwards (July 1/1998).
4  * This code is for the purpose of re-attaching to a detached BitchX session.
5  * The idea for this program was by kasper@efnet after I mentioned the trouble
6  * I was having reconnecting to a detached terminal.
7  */
8 
9  /*
10   * Version 1.0 released with BitchX 75
11   * $Id: scr-bx.c 462 2013-11-13 11:43:57Z keaston $
12   */
13 
14 #include "irc.h"
15 #include "struct.h"
16 
17 #include <sys/types.h>
18 #include <sys/stat.h>
19 #include <pwd.h>
20 #ifdef USING_CURSES
21 #include <curses.h>
22 #endif
23 #include <stdarg.h>
24 #include <string.h>
25 #include "ircterm.h"
26 #include "screen.h"
27 #include "ircaux.h"
28 
29 #include <sys/ioctl.h>
30 
31 #ifdef HAVE_SYS_UN_H
32 #include <sys/un.h>
33 #endif
34 
35 #ifdef MEM_DEBUG
36 #include <dmalloc.h>
37 #endif
38 
39 #ifdef TRANSLATE
40 char translation = 0;
41 unsigned char   transToClient[256];    /* Server to client translation. */
42 #endif
43 
44 int dumb_mode = 0;
45 int already_detached = 1;
46 int do_check_pid = 0;
47 char socket_path[500];
48 char attach_ttyname[500];
49 
50 
51 struct param
52 {
53 	pid_t	pgrp,
54 		pid;
55 	uid_t	uid;
56 	int	cols;
57 	int	rows;
58 	char	tty[80];
59 	char	cookie[30];
60 	char	password[80];
61 	char	termid[81];
62 };
63 
64 struct param parm;
65 char *old_pass = NULL;
66 Screen *output_screen = NULL, *last_input_screen = NULL, *main_screen = NULL;
67 char empty_string[] = "";
68 int foreground = 0;
69 int use_input = 1;
70 int use_flow_control = 0;
71 static int displays = 0;
72 
73 #define SOCKMODE (S_IWRITE | S_IREAD | (displays ? S_IEXEC : 0))
74 
75 #ifdef CLOAKED
76 extern char **Argv;             /* pointer to argument vector */
77 extern char *LastArgv;          /* end of argv */
78 #endif
79 
80 
n_m_strdup(const char * str,const char * module,const char * file,const int line)81 char	*n_m_strdup (const char *str, const char *module, const char *file, const int line)
82 {
83 	char *ptr;
84 	if (!str)
85 		str = empty_string;
86 	ptr = (char *)malloc(strlen(str) + 1);
87 	return strcpy(ptr, str);
88 }
89 
ircpanic(char * string,...)90 void ircpanic(char *string, ...)
91 {
92 	return;
93 }
94 
get_int_var(int var)95 int get_int_var(int var)
96 {
97 	return 1;
98 }
99 
ltoa(long foo)100 char *ltoa (long foo)
101 {
102 	static char buffer[BIG_BUFFER_SIZE/8+1];
103 	char *pos = buffer + BIG_BUFFER_SIZE/8-1;
104 	unsigned long absv;
105 	int negative;
106 
107 	absv = (foo < 0) ? (unsigned long)-foo : (unsigned long)foo;
108 	negative = (foo < 0) ? 1 : 0;
109 
110 	buffer[BIG_BUFFER_SIZE/8] = 0;
111 	for (; absv > 9; absv /= 10)
112 		*pos-- = (absv % 10) + '0';
113 	*pos = (absv) + '0';
114 
115 	if (negative)
116 		*--pos = '-';
117 
118 	return pos;
119 }
120 
lower(char * str)121 char *lower(char *str)
122 {
123 register char   *ptr = NULL;
124 
125 	if (str)
126 	{
127 		ptr = str;
128 		for (; *str; str++)
129 		{
130 			if (isupper(*str))
131 				*str = tolower(*str);
132 		}
133 	}
134 	return (ptr);
135 }
136 
137 #ifndef HAVE_GETPASS
138 char *getpass(char *);
get_string_var(int var)139 char *get_string_var(int var)
140 {
141 	return NULL;
142 }
143 #endif
144 
145 #ifdef WINNT
refresh_screen(int i,char * u)146 void refresh_screen(int i, char *u)
147 {
148 	return;
149 }
150 #endif
151 
find_tty_name(char * name)152 char *find_tty_name(char *name)
153 {
154 static char tty[20];
155 char *q, *s;
156 	*tty = 0;
157 	if ((q = strrchr(name, '/')))
158 	{
159 		q++;
160 		if ((q = strchr(q, '.')))
161 		{
162 			q++;
163 			if ((s = strchr(q, '.')))
164 				strncpy(tty, q, s-q);
165 		}
166 	}
167 	return tty;
168 }
169 
find_tty_path(char * name)170 char *find_tty_path(char *name)
171 {
172 static char ttypath[200];
173 char *q;
174 	*ttypath = 0;
175 	if ((q = strrchr(name, '/')))
176 		strncpy(ttypath, name, q - name);
177 	return ttypath;
178 }
179 
display_socket_list(char * path,int unl,char * arg)180 void display_socket_list(char *path, int unl, char *arg)
181 {
182 DIR	*dptr;
183 struct	dirent	*dir;
184 struct	stat	st;
185 char buffer[2000];
186 char *new_path, *p;
187 int count = 0;
188 int doit = 0;
189 
190 	new_path = LOCAL_COPY(path);
191 	if ((p = strrchr(new_path, '/')))
192 		*p = 0;
193 	if (!(dptr = opendir(new_path)))
194 	{
195 		fprintf(stderr, "No such directory %s\r\n ", new_path);
196 		exit(1);
197 	}
198 	while ((dir = readdir(dptr)))
199 	{
200 		doit = 0;
201 		if (!dir->d_ino)
202 			continue;
203 		if (dir->d_name[0] == '.')
204 			continue;
205 		sprintf(buffer, "%s/%s", new_path, dir->d_name);
206 		if ((stat(buffer, &st) == -1))
207 			continue;
208 		if (arg && strstr(dir->d_name, arg))
209 			doit++;
210 		if (!count && !unl)
211 			fprintf(stderr, "There is more than one sockets available - \r\n");
212 		else if (!count)
213 			fprintf(stderr, "unlinking the following\r\n");
214 		count++;
215 		if (unl)
216 		{
217 			if (!((st.st_mode & 0700) == 0600) || doit)
218 			{
219 				fprintf(stderr, "%30s\r\n", dir->d_name);
220 				unlink(buffer);
221 			}
222 		}
223 		else if ((!doit && !arg) || (doit && arg))
224 			fprintf(stderr, "%30s %s\r\n", dir->d_name, ((st.st_mode & 0700) == 0600) ? "detached":"Attached or dead");
225 	}
226 	if (!count)
227 		fprintf(stderr, "No sockets to attach to.\r\n");
228 	closedir(dptr);
229 	exit(1);
230 }
231 
find_detach_socket(char * path,char * name)232 char *find_detach_socket(char *path, char *name)
233 {
234 char	*new_path;
235 DIR	*dptr;
236 struct	dirent	*dir;
237 struct	stat	st;
238 char *ret = NULL, *p;
239 int count = 0;
240 	new_path = LOCAL_COPY(path);
241 	if ((p = strrchr(new_path, '/')))
242 		*p = 0;
243 	else
244 		return NULL;
245 	if (!(dptr = opendir(new_path)))
246 		return NULL;
247 	ret = malloc(2000);
248 	*ret = 0;
249 	while ((dir = readdir(dptr)))
250 	{
251 		*ret = 0;
252 		if (!dir->d_ino)
253 			continue;
254 		if (dir->d_name[0] == '.')
255 			continue;
256 		sprintf(ret, "%s/%s", new_path, dir->d_name);
257 		p = strrchr(ret, '/'); p++;
258 		if ((stat(ret, &st) == -1) || (st.st_uid != getuid()) || S_ISDIR(st.st_mode))
259 		{
260 			*ret = 0;
261 			continue;
262 		}
263 		if (name)
264 		{
265 			char *pid, *n_tty, *h_name;
266 			pid = LOCAL_COPY(p);
267 			n_tty = strchr(pid, '.'); *n_tty++ = 0;
268 			h_name = strchr(n_tty, '.'); *h_name++ = 0;
269 			if (strcmp(name, pid))
270 			{
271 				if (strcmp(n_tty, name))
272 				{
273 					if (strcmp(h_name, name))
274 					{
275 						if (strcmp(p, name))
276 						{
277 							if (!strstr(p, name))
278 							{
279 								*ret = 0;
280 								continue;
281 							}
282 						}
283 					}
284 				}
285 			}
286 		}
287 		if ((st.st_mode & 0700) == 0600)
288 			break;
289 		count++;
290 		*ret = 0;
291 	}
292 	closedir(dptr);
293 	if (ret && !*ret)
294 	{
295 		free(ret);
296 		ret = NULL;
297 	}
298 	switch (count)
299 	{
300 		case 0:
301 			break;
302 		case 1:
303 			break;
304 		default:
305 			display_socket_list(path, 0, name);
306 			if (ret) free(ret);
307 			ret = NULL;
308 	}
309 	return ret;
310 }
311 
charset_ibmpc(void)312 void charset_ibmpc (void)
313 {
314 	fwrite("\033(U", 3, 1, stdout);	/* switch to IBM code page 437 */
315 }
316 
SIGNAL_HANDLER(handle_pipe)317 SIGNAL_HANDLER(handle_pipe)
318 {
319 
320 	term_cr();
321 	term_clear_to_eol();
322 	term_reset2();
323 	fprintf(stdout, "\rdetached from %s. To re-attach type scr-bx %s\n\r", attach_ttyname, old_pass? "password":"");
324 	fflush(stdout);
325 	exit(0);
326 }
327 
SIGNAL_HANDLER(handle_hup)328 SIGNAL_HANDLER(handle_hup)
329 {
330 	term_cr();
331 	term_clear_to_eol();
332 	term_reset2();
333 	fprintf(stdout, "\r");
334 	fflush(stdout);
335 	exit(0);
336 }
337 
338 volatile int ctrl_c = 0;
339 
SIGNAL_HANDLER(handle_ctrlc)340 SIGNAL_HANDLER(handle_ctrlc)
341 {
342 	ctrl_c++;
343 }
344 
345 /* set's socket options */
set_socket_options(int s)346 void set_socket_options (int s)
347 {
348 	int	opt = 1;
349 	int	optlen = sizeof(opt);
350 #ifndef NO_STRUCT_LINGER
351 	struct linger	lin;
352 
353 	lin.l_onoff = lin.l_linger = 0;
354 	setsockopt(s, SOL_SOCKET, SO_LINGER, (char *)&lin, optlen);
355 #endif
356 
357 	setsockopt(s, SOL_SOCKET, SO_REUSEADDR, (char *)&opt, optlen);
358 	opt = 1;
359 	setsockopt(s, SOL_SOCKET, SO_KEEPALIVE, (char *)&opt, optlen);
360 }
361 
362 
get_cookie(char * name)363 char *get_cookie(char *name)
364 {
365 	static char cookie[80];
366 	FILE *fp = NULL;
367 	*cookie = 0;
368 	if ((fp = fopen(name, "r")))
369 	{
370 		fread(cookie, 40, 1, fp);
371 		fclose(fp);
372 		if (*cookie)
373 			cookie[strlen(cookie)-1] = 0;
374 	}
375 	return cookie;
376 }
377 
reattach_tty(char * tty,char * password)378 void reattach_tty(char *tty, char *password)
379 {
380 int s = -1;
381 char *name;
382 struct sockaddr_in addr;
383 struct hostent *hp;
384 int len = 0;
385 fd_set rd_fd;
386 struct timeval tm = {0};
387 char chr_c[] = "\003";
388 
389 /*
390  * this buffer has to be big enough to handle a full screen of
391  * information from the detached process.
392  */
393 unsigned char buffer[6 * BIG_BUFFER_SIZE+1];
394 char *p;
395 int port = 0;
396 #if defined (TIOCGWINSZ)
397 struct winsize window;
398 #endif
399 	memset(&parm, 0, sizeof(struct param));
400 
401 	if (!(name = find_detach_socket(socket_path, tty)))
402 	{
403 		fprintf(stderr, "No detached process to attach to.\r\n");
404 		_exit(1);
405 	}
406 
407 	strcpy(parm.cookie, get_cookie(name));
408 	if (!*parm.cookie)
409 		_exit(1);
410 	if ((p = strrchr(name, '/')))
411 		p++;
412 	sscanf(p, "%d.%*s.%*s", &port);
413 	displays = 1;
414 	if ((s = socket(AF_INET, SOCK_STREAM, 0)) < 0)
415 	{
416 		displays = 0;
417 		_exit(1);
418 	}
419 
420 	chmod(name, SOCKMODE);
421 	set_socket_options(s);
422 	memset(&addr, 0, sizeof(struct sockaddr_in));
423 	addr.sin_port = htons(port);
424 	addr.sin_family = AF_INET;
425 	if((hp = gethostbyname("localhost")))
426 		memcpy(&addr.sin_addr, hp->h_addr, hp->h_length);
427 	else
428 		inet_aton("127.0.0.1", (struct in_addr *)&addr.sin_addr);
429 	if (connect(s, (struct sockaddr *)&addr, sizeof(addr)) < 0)
430 	{
431 		fprintf(stderr, "connection refused for %s\r\n", name);
432 		_exit(1);
433 	}
434 
435 	parm.pid = getpid();
436 	parm.pgrp = getpgrp();
437 	parm.uid = getuid();
438 	strcpy(parm.tty, ttyname(0));
439 	strncpy(parm.termid, getenv("TERM"), 80);
440 	if (password)
441 		strncpy(parm.password, password, 60);
442 	fprintf(stderr, "attempting to wakeup %s\r\n", find_tty_name(name));
443 #if defined (TIOCGWINSZ)
444 	if (ioctl(0, TIOCGWINSZ, &window) > -1)
445 	{
446 		parm.cols = window.ws_col;
447 		parm.rows = window.ws_row;
448 	}
449 	else
450 #endif
451 	{
452 		parm.cols = 79;
453 		parm.rows = 25;
454 	}
455 	write(s, &parm, sizeof(struct param));
456 	sleep(2);
457 	alarm(15);
458 	len = read(s, &parm, sizeof(struct param));
459 	alarm(0);
460 	if (len <= 0)
461 	{
462 		fprintf(stderr, "error reconnecting to %s\r\n", find_tty_name(name));
463 		displays = 0;
464 		chmod(name, SOCKMODE);
465 		exit(1);
466 	}
467 	unlink(name);
468 
469 	term_init(parm.termid);
470 	set_term_eight_bit(1);
471 	charset_ibmpc();
472 	term_clear_screen();
473 	term_resize();
474 	term_move_cursor(0,0);
475 
476 	my_signal(SIGPIPE, handle_pipe, 0);
477 	my_signal(SIGINT,  handle_ctrlc, 0);
478 	my_signal(SIGHUP,  handle_hup, 0);
479 
480 	/*
481 	 * according to MHacker we need to set errno to 0 under BSD.
482 	 * for some reason we get a address in use from a socket
483 	 *
484 	 */
485 	errno = 0;
486 	while (1)
487 	{
488 		FD_ZERO(&rd_fd);
489 		FD_SET(0, &rd_fd);
490 		FD_SET(s, &rd_fd);
491 		tm.tv_sec = 2;
492 
493 		switch(select(s+1, &rd_fd, NULL, NULL, &tm))
494 		{
495 			case -1:
496 				if (ctrl_c)
497 				{
498 					write(s, chr_c, 1);
499 					ctrl_c = 0;
500 				}
501 				else if (errno != EINTR)
502 				{
503 					close(s);
504 					_exit(1);
505 				}
506 				break;
507 			case 0:
508 				break;
509 			default:
510 			{
511 				if (FD_ISSET(0, &rd_fd))
512 				{
513 					len = read(0, buffer, sizeof(buffer)-1);
514 					write(s, buffer, len);
515 				}
516 				if (FD_ISSET(s, &rd_fd))
517 				{
518 					len = read(s, buffer, sizeof(buffer)-1);
519 					write(1, buffer, len);
520 				}
521 			}
522 		}
523 	}
524 	close(s);
525 	fprintf(stderr, "Never should have got here");
526 	_exit(1);
527 
528 	return; /* error return */
529 }
530 
stripdev(char * ttynam)531 char *stripdev(char *ttynam)
532 {
533 	if (ttynam == NULL)
534 		return NULL;
535 #ifdef SVR4
536   /* unixware has /dev/pts012 as synonym for /dev/pts/12 */
537 	if (!strncmp(ttynam, "/dev/pts", 8) && ttynam[8] >= '0' && ttynam[8] <= '9')
538 	{
539 		static char b[13];
540 		sprintf(b, "pts/%d", atoi(ttynam + 8));
541 		return b;
542 	}
543 #endif /* SVR4 */
544 	if (!strncmp(ttynam, "/dev/", 5))
545 		return ttynam + 5;
546 	return ttynam;
547 }
548 
549 
init_socketpath(void)550 void init_socketpath(void)
551 {
552 #if !defined(__EMX__) && !defined(WINNT)
553 struct stat st;
554 extern char socket_path[], attach_ttyname[];
555 
556 	sprintf(socket_path, "%s/.BitchX/screens", getenv("HOME"));
557 	if (access(socket_path, F_OK))
558 		return;
559 	if (stat(socket_path, &st) != -1)
560 	{
561 		char host[BIG_BUFFER_SIZE+1];
562 		char *ap;
563 		if (!S_ISDIR(st.st_mode))
564 			return;
565 		gethostname(host, BIG_BUFFER_SIZE);
566 		if ((ap = strchr(host, '.')))
567 			*ap = 0;
568 		ap = &socket_path[strlen(socket_path)];
569 		sprintf(ap, "/%%d.%s.%s", stripdev(attach_ttyname), host);
570 		ap++;
571 		for ( ; *ap; ap++)
572 			if (*ap == '/')
573 				*ap = '-';
574 	}
575 #endif
576 }
577 
578 
579 char *old_tty = NULL;
parse_args(int argc,char ** argv)580 void parse_args(int argc, char **argv)
581 {
582 int ac = 1;
583 int disp_sock = 0;
584 
585 	for (; ac < argc; ac++)
586 	{
587 
588 		if (!strncasecmp(argv[ac], "tty", 3))
589 		{
590 			old_tty = malloc(strlen(argv[ac])+1);
591 			strcpy(old_tty, argv[ac]);
592 		}
593 		else if (argv[ac][0] == '-' && argv[ac][1] == 'p')
594 		{
595 			char *pass;
596 			pass = getpass("Enter password : ");
597 			old_pass = malloc(strlen(pass)+1);
598 			strcpy(old_pass, pass);
599 		}
600 		else if (argv[ac][0] == '-' && argv[ac][1] == 'h')
601 		{
602 			char *p;
603 			if ((p = strrchr(argv[0], '/')))
604 				p++;
605 			else
606 				p = argv[0];
607 			fprintf(stderr, "Usage %s: [tty] [-p] [-h] [-l]\r\n\t\ttty is the name of a tty\r\n\t\t-p to specify a password\r\n\t\t-l to list available sockets\r\n\t\t-w to wipe out dead sockets\r\n", p);
608 			exit(0);
609 		}
610 		else if (argv[ac][0] == '-' && argv[ac][1] == 'l')
611 			disp_sock = 1;
612 		else if (argv[ac][0] == '-' && argv[ac][1] == 'w')
613 			disp_sock = 2;
614 		else if (!old_tty)
615 		{
616 			old_tty = malloc(strlen(argv[ac])+1);
617 			strcpy(old_tty, argv[ac]);
618 		}
619 	}
620 	if (disp_sock)
621 		display_socket_list(socket_path, disp_sock - 1, old_tty);
622 }
623 
624 
main(int argc,char ** argv)625 int main(int argc, char **argv)
626 {
627 #ifdef MEM_DEBUG
628 	dmalloc_debug(0x1df47dfb);
629 #endif
630 	*socket_path = 0;
631 	strcpy(attach_ttyname, ttyname(0));
632 	init_socketpath();
633 	parse_args(argc, argv);
634         chdir(getenv("HOME"));
635 	reattach_tty(old_tty, old_pass);
636 	return 0;
637 }
638