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 /*
22 ** dtach is a quick hack, since I wanted the detach feature of screen without
23 ** all the other crud. It'll work best with full-screen applications, as it
24 ** does not keep track of the screen or anything like that.
25 */
26 
27 /* Make sure the binary has a copyright. */
28 const char copyright[] = "dtach - version " PACKAGE_VERSION "(C)Copyright 2004-2016 Ned T. Crigler";
29 
30 /* argv[0] from the program */
31 char *progname;
32 /* The name of the passed in socket. */
33 char *sockname;
34 /* The character used for detaching. Defaults to '^\' */
35 int detach_char = '\\' - 64;
36 /* 1 if we should not interpret the suspend character. */
37 int no_suspend;
38 /* The default redraw method. Initially set to unspecified. */
39 int redraw_method = REDRAW_UNSPEC;
40 
41 /*
42 ** The original terminal settings. Shared between the master and attach
43 ** processes. The master uses it to initialize the pty, and the attacher uses
44 ** it to restore the original settings.
45 */
46 struct termios orig_term;
47 int dont_have_tty;
48 
49 static void
usage()50 usage()
51 {
52 	printf(
53 		"dtach - version %s, compiled on %s at %s.\n"
54 		"Usage: dtach -a <socket> <options>\n"
55 		"       dtach -A <socket> <options> <command...>\n"
56 		"       dtach -c <socket> <options> <command...>\n"
57 		"       dtach -n <socket> <options> <command...>\n"
58 		"       dtach -N <socket> <options> <command...>\n"
59 		"       dtach -p <socket>\n"
60 		"Modes:\n"
61 		"  -a\t\tAttach to the specified socket.\n"
62 		"  -A\t\tAttach to the specified socket, or create it if it\n"
63 		"\t\t  does not exist, running the specified command.\n"
64 		"  -c\t\tCreate a new socket and run the specified command.\n"
65 		"  -n\t\tCreate a new socket and run the specified command "
66 		"detached.\n"
67 		"  -N\t\tCreate a new socket and run the specified command "
68 		"detached,\n"
69 		"\t\t  and have dtach run in the foreground.\n"
70 		"  -p\t\tCopy the contents of standard input to the specified\n"
71 		"\t\t  socket.\n"
72 		"Options:\n"
73 		"  -e <char>\tSet the detach character to <char>, defaults "
74 		"to ^\\.\n"
75 		"  -E\t\tDisable the detach character.\n"
76 		"  -r <method>\tSet the redraw method to <method>. The "
77 		"valid methods are:\n"
78 		"\t\t     none: Don't redraw at all.\n"
79 		"\t\t   ctrl_l: Send a Ctrl L character to the program.\n"
80 		"\t\t    winch: Send a WINCH signal to the program.\n"
81 		"  -z\t\tDisable processing of the suspend key.\n"
82 		"\nReport any bugs to <" PACKAGE_BUGREPORT ">.\n",
83 		PACKAGE_VERSION, __DATE__, __TIME__);
84 	exit(0);
85 }
86 
87 int
main(int argc,char ** argv)88 main(int argc, char **argv)
89 {
90 	int mode = 0;
91 
92 	/* Save the program name */
93 	progname = argv[0];
94 	++argv; --argc;
95 
96 	/* Parse the arguments */
97 	if (argc >= 1 && **argv == '-')
98 	{
99 		if (strncmp(*argv, "--help", strlen(*argv)) == 0)
100 			usage();
101 		else if (strncmp(*argv, "--version", strlen(*argv)) == 0)
102 		{
103 			printf("dtach - version %s, compiled on %s at %s.\n",
104 				PACKAGE_VERSION, __DATE__, __TIME__);
105 			return 0;
106 		}
107 
108 		mode = argv[0][1];
109 		if (mode == '?')
110 			usage();
111 		else if (mode != 'a' && mode != 'c' && mode != 'n' &&
112 			 mode != 'A' && mode != 'N' && mode != 'p')
113 		{
114 			printf("%s: Invalid mode '-%c'\n", progname, mode);
115 			printf("Try '%s --help' for more information.\n",
116 				progname);
117 			return 1;
118 		}
119 	}
120 	if (!mode)
121 	{
122 		printf("%s: No mode was specified.\n", progname);
123 		printf("Try '%s --help' for more information.\n",
124 			progname);
125 		return 1;
126 	}
127 	++argv; --argc;
128 
129 	if (argc < 1)
130 	{
131 		printf("%s: No socket was specified.\n", progname);
132 		printf("Try '%s --help' for more information.\n",
133 			progname);
134 		return 1;
135 	}
136 	sockname = *argv;
137 	++argv; --argc;
138 
139 	if (mode == 'p')
140 	{
141 		if (argc > 0)
142 		{
143 			printf("%s: Invalid number of arguments.\n",
144 				progname);
145 			printf("Try '%s --help' for more information.\n",
146 				progname);
147 			return 1;
148 		}
149 		return push_main();
150 	}
151 
152 	while (argc >= 1 && **argv == '-')
153 	{
154 		char *p;
155 
156 		for (p = argv[0] + 1; *p; ++p)
157 		{
158 			if (*p == 'E')
159 				detach_char = -1;
160 			else if (*p == 'z')
161 				no_suspend = 1;
162 			else if (*p == 'e')
163 			{
164 				++argv; --argc;
165 				if (argc < 1)
166 				{
167 					printf("%s: No escape character "
168 						"specified.\n", progname);
169 					printf("Try '%s --help' for more "
170 						"information.\n", progname);
171 					return 1;
172 				}
173 				if (argv[0][0] == '^' && argv[0][1])
174 				{
175 					if (argv[0][1] == '?')
176 						detach_char = '\177';
177 					else
178 						detach_char = argv[0][1] & 037;
179 				}
180 				else
181 					detach_char = argv[0][0];
182 				break;
183 			}
184 			else if (*p == 'r')
185 			{
186 				++argv; --argc;
187 				if (argc < 1)
188 				{
189 					printf("%s: No redraw method "
190 						"specified.\n", progname);
191 					printf("Try '%s --help' for more "
192 						"information.\n", progname);
193 					return 1;
194 				}
195 				if (strcmp(argv[0], "none") == 0)
196 					redraw_method = REDRAW_NONE;
197 				else if (strcmp(argv[0], "ctrl_l") == 0)
198 					redraw_method = REDRAW_CTRL_L;
199 				else if (strcmp(argv[0], "winch") == 0)
200 					redraw_method = REDRAW_WINCH;
201 				else
202 				{
203 					printf("%s: Invalid redraw method "
204 						"specified.\n", progname);
205 					printf("Try '%s --help' for more "
206 						"information.\n", progname);
207 					return 1;
208 				}
209 				break;
210 			}
211 			else
212 			{
213 				printf("%s: Invalid option '-%c'\n",
214 					progname, *p);
215 				printf("Try '%s --help' for more information.\n",
216 					progname);
217 				return 1;
218 			}
219 		}
220 		++argv; --argc;
221 	}
222 
223 	if (mode != 'a' && argc < 1)
224 	{
225 		printf("%s: No command was specified.\n", progname);
226 		printf("Try '%s --help' for more information.\n",
227 			progname);
228 		return 1;
229 	}
230 
231 	/* Save the original terminal settings. */
232 	if (tcgetattr(0, &orig_term) < 0)
233 	{
234 		memset(&orig_term, 0, sizeof(struct termios));
235 		dont_have_tty = 1;
236 	}
237 
238 	if (dont_have_tty && mode != 'n' && mode != 'N')
239 	{
240 		printf("%s: Attaching to a session requires a terminal.\n",
241 			progname);
242 		return 1;
243 	}
244 
245 	if (mode == 'a')
246 	{
247 		if (argc > 0)
248 		{
249 			printf("%s: Invalid number of arguments.\n",
250 				progname);
251 			printf("Try '%s --help' for more information.\n",
252 				progname);
253 			return 1;
254 		}
255 		return attach_main(0);
256 	}
257 	else if (mode == 'n')
258 		return master_main(argv, 0, 0);
259 	else if (mode == 'N')
260 		return master_main(argv, 0, 1);
261 	else if (mode == 'c')
262 	{
263 		if (master_main(argv, 1, 0) != 0)
264 			return 1;
265 		return attach_main(0);
266 	}
267 	else if (mode == 'A')
268 	{
269 		/* Try to attach first. If that doesn't work, create a new
270 		** socket. */
271 		if (attach_main(1) != 0)
272 		{
273 			if (errno == ECONNREFUSED || errno == ENOENT)
274 			{
275 				if (errno == ECONNREFUSED)
276 					unlink(sockname);
277 				if (master_main(argv, 1, 0) != 0)
278 					return 1;
279 			}
280 			return attach_main(0);
281 		}
282 	}
283 	return 0;
284 }
285