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