├── .gitignore ├── COPYING ├── Makefile.in ├── README ├── attach.c ├── config.h.in ├── configure ├── configure.ac ├── dtach.1 ├── dtach.h ├── dtach.spec ├── main.c └── master.c /.gitignore: -------------------------------------------------------------------------------- 1 | # Files generated by configure 2 | 3 | /Makefile 4 | /config.h 5 | /config.log 6 | /config.status 7 | /configure.lineno 8 | 9 | # Files generated by make 10 | *.o 11 | /dtach 12 | -------------------------------------------------------------------------------- /COPYING: -------------------------------------------------------------------------------- 1 | GNU GENERAL PUBLIC LICENSE 2 | Version 2, June 1991 3 | 4 | Copyright (C) 1989, 1991 Free Software Foundation, Inc., 5 | 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA 6 | Everyone is permitted to copy and distribute verbatim copies 7 | of this license document, but changing it is not allowed. 8 | 9 | Preamble 10 | 11 | The licenses for most software are designed to take away your 12 | freedom to share and change it. By contrast, the GNU General Public 13 | License is intended to guarantee your freedom to share and change free 14 | software--to make sure the software is free for all its users. This 15 | General Public License applies to most of the Free Software 16 | Foundation's software and to any other program whose authors commit to 17 | using it. (Some other Free Software Foundation software is covered by 18 | the GNU Lesser General Public License instead.) You can apply it to 19 | your programs, too. 20 | 21 | When we speak of free software, we are referring to freedom, not 22 | price. Our General Public Licenses are designed to make sure that you 23 | have the freedom to distribute copies of free software (and charge for 24 | this service if you wish), that you receive source code or can get it 25 | if you want it, that you can change the software or use pieces of it 26 | in new free programs; and that you know you can do these things. 27 | 28 | To protect your rights, we need to make restrictions that forbid 29 | anyone to deny you these rights or to ask you to surrender the rights. 30 | These restrictions translate to certain responsibilities for you if you 31 | distribute copies of the software, or if you modify it. 32 | 33 | For example, if you distribute copies of such a program, whether 34 | gratis or for a fee, you must give the recipients all the rights that 35 | you have. You must make sure that they, too, receive or can get the 36 | source code. And you must show them these terms so they know their 37 | rights. 38 | 39 | We protect your rights with two steps: (1) copyright the software, and 40 | (2) offer you this license which gives you legal permission to copy, 41 | distribute and/or modify the software. 42 | 43 | Also, for each author's protection and ours, we want to make certain 44 | that everyone understands that there is no warranty for this free 45 | software. If the software is modified by someone else and passed on, we 46 | want its recipients to know that what they have is not the original, so 47 | that any problems introduced by others will not reflect on the original 48 | authors' reputations. 49 | 50 | Finally, any free program is threatened constantly by software 51 | patents. We wish to avoid the danger that redistributors of a free 52 | program will individually obtain patent licenses, in effect making the 53 | program proprietary. To prevent this, we have made it clear that any 54 | patent must be licensed for everyone's free use or not licensed at all. 55 | 56 | The precise terms and conditions for copying, distribution and 57 | modification follow. 58 | 59 | GNU GENERAL PUBLIC LICENSE 60 | TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION 61 | 62 | 0. This License applies to any program or other work which contains 63 | a notice placed by the copyright holder saying it may be distributed 64 | under the terms of this General Public License. The "Program", below, 65 | refers to any such program or work, and a "work based on the Program" 66 | means either the Program or any derivative work under copyright law: 67 | that is to say, a work containing the Program or a portion of it, 68 | either verbatim or with modifications and/or translated into another 69 | language. (Hereinafter, translation is included without limitation in 70 | the term "modification".) Each licensee is addressed as "you". 71 | 72 | Activities other than copying, distribution and modification are not 73 | covered by this License; they are outside its scope. The act of 74 | running the Program is not restricted, and the output from the Program 75 | is covered only if its contents constitute a work based on the 76 | Program (independent of having been made by running the Program). 77 | Whether that is true depends on what the Program does. 78 | 79 | 1. You may copy and distribute verbatim copies of the Program's 80 | source code as you receive it, in any medium, provided that you 81 | conspicuously and appropriately publish on each copy an appropriate 82 | copyright notice and disclaimer of warranty; keep intact all the 83 | notices that refer to this License and to the absence of any warranty; 84 | and give any other recipients of the Program a copy of this License 85 | along with the Program. 86 | 87 | You may charge a fee for the physical act of transferring a copy, and 88 | you may at your option offer warranty protection in exchange for a fee. 89 | 90 | 2. You may modify your copy or copies of the Program or any portion 91 | of it, thus forming a work based on the Program, and copy and 92 | distribute such modifications or work under the terms of Section 1 93 | above, provided that you also meet all of these conditions: 94 | 95 | a) You must cause the modified files to carry prominent notices 96 | stating that you changed the files and the date of any change. 97 | 98 | b) You must cause any work that you distribute or publish, that in 99 | whole or in part contains or is derived from the Program or any 100 | part thereof, to be licensed as a whole at no charge to all third 101 | parties under the terms of this License. 102 | 103 | c) If the modified program normally reads commands interactively 104 | when run, you must cause it, when started running for such 105 | interactive use in the most ordinary way, to print or display an 106 | announcement including an appropriate copyright notice and a 107 | notice that there is no warranty (or else, saying that you provide 108 | a warranty) and that users may redistribute the program under 109 | these conditions, and telling the user how to view a copy of this 110 | License. (Exception: if the Program itself is interactive but 111 | does not normally print such an announcement, your work based on 112 | the Program is not required to print an announcement.) 113 | 114 | These requirements apply to the modified work as a whole. If 115 | identifiable sections of that work are not derived from the Program, 116 | and can be reasonably considered independent and separate works in 117 | themselves, then this License, and its terms, do not apply to those 118 | sections when you distribute them as separate works. But when you 119 | distribute the same sections as part of a whole which is a work based 120 | on the Program, the distribution of the whole must be on the terms of 121 | this License, whose permissions for other licensees extend to the 122 | entire whole, and thus to each and every part regardless of who wrote it. 123 | 124 | Thus, it is not the intent of this section to claim rights or contest 125 | your rights to work written entirely by you; rather, the intent is to 126 | exercise the right to control the distribution of derivative or 127 | collective works based on the Program. 128 | 129 | In addition, mere aggregation of another work not based on the Program 130 | with the Program (or with a work based on the Program) on a volume of 131 | a storage or distribution medium does not bring the other work under 132 | the scope of this License. 133 | 134 | 3. You may copy and distribute the Program (or a work based on it, 135 | under Section 2) in object code or executable form under the terms of 136 | Sections 1 and 2 above provided that you also do one of the following: 137 | 138 | a) Accompany it with the complete corresponding machine-readable 139 | source code, which must be distributed under the terms of Sections 140 | 1 and 2 above on a medium customarily used for software interchange; or, 141 | 142 | b) Accompany it with a written offer, valid for at least three 143 | years, to give any third party, for a charge no more than your 144 | cost of physically performing source distribution, a complete 145 | machine-readable copy of the corresponding source code, to be 146 | distributed under the terms of Sections 1 and 2 above on a medium 147 | customarily used for software interchange; or, 148 | 149 | c) Accompany it with the information you received as to the offer 150 | to distribute corresponding source code. (This alternative is 151 | allowed only for noncommercial distribution and only if you 152 | received the program in object code or executable form with such 153 | an offer, in accord with Subsection b above.) 154 | 155 | The source code for a work means the preferred form of the work for 156 | making modifications to it. For an executable work, complete source 157 | code means all the source code for all modules it contains, plus any 158 | associated interface definition files, plus the scripts used to 159 | control compilation and installation of the executable. However, as a 160 | special exception, the source code distributed need not include 161 | anything that is normally distributed (in either source or binary 162 | form) with the major components (compiler, kernel, and so on) of the 163 | operating system on which the executable runs, unless that component 164 | itself accompanies the executable. 165 | 166 | If distribution of executable or object code is made by offering 167 | access to copy from a designated place, then offering equivalent 168 | access to copy the source code from the same place counts as 169 | distribution of the source code, even though third parties are not 170 | compelled to copy the source along with the object code. 171 | 172 | 4. You may not copy, modify, sublicense, or distribute the Program 173 | except as expressly provided under this License. Any attempt 174 | otherwise to copy, modify, sublicense or distribute the Program is 175 | void, and will automatically terminate your rights under this License. 176 | However, parties who have received copies, or rights, from you under 177 | this License will not have their licenses terminated so long as such 178 | parties remain in full compliance. 179 | 180 | 5. You are not required to accept this License, since you have not 181 | signed it. However, nothing else grants you permission to modify or 182 | distribute the Program or its derivative works. These actions are 183 | prohibited by law if you do not accept this License. Therefore, by 184 | modifying or distributing the Program (or any work based on the 185 | Program), you indicate your acceptance of this License to do so, and 186 | all its terms and conditions for copying, distributing or modifying 187 | the Program or works based on it. 188 | 189 | 6. Each time you redistribute the Program (or any work based on the 190 | Program), the recipient automatically receives a license from the 191 | original licensor to copy, distribute or modify the Program subject to 192 | these terms and conditions. You may not impose any further 193 | restrictions on the recipients' exercise of the rights granted herein. 194 | You are not responsible for enforcing compliance by third parties to 195 | this License. 196 | 197 | 7. If, as a consequence of a court judgment or allegation of patent 198 | infringement or for any other reason (not limited to patent issues), 199 | conditions are imposed on you (whether by court order, agreement or 200 | otherwise) that contradict the conditions of this License, they do not 201 | excuse you from the conditions of this License. If you cannot 202 | distribute so as to satisfy simultaneously your obligations under this 203 | License and any other pertinent obligations, then as a consequence you 204 | may not distribute the Program at all. For example, if a patent 205 | license would not permit royalty-free redistribution of the Program by 206 | all those who receive copies directly or indirectly through you, then 207 | the only way you could satisfy both it and this License would be to 208 | refrain entirely from distribution of the Program. 209 | 210 | If any portion of this section is held invalid or unenforceable under 211 | any particular circumstance, the balance of the section is intended to 212 | apply and the section as a whole is intended to apply in other 213 | circumstances. 214 | 215 | It is not the purpose of this section to induce you to infringe any 216 | patents or other property right claims or to contest validity of any 217 | such claims; this section has the sole purpose of protecting the 218 | integrity of the free software distribution system, which is 219 | implemented by public license practices. Many people have made 220 | generous contributions to the wide range of software distributed 221 | through that system in reliance on consistent application of that 222 | system; it is up to the author/donor to decide if he or she is willing 223 | to distribute software through any other system and a licensee cannot 224 | impose that choice. 225 | 226 | This section is intended to make thoroughly clear what is believed to 227 | be a consequence of the rest of this License. 228 | 229 | 8. If the distribution and/or use of the Program is restricted in 230 | certain countries either by patents or by copyrighted interfaces, the 231 | original copyright holder who places the Program under this License 232 | may add an explicit geographical distribution limitation excluding 233 | those countries, so that distribution is permitted only in or among 234 | countries not thus excluded. In such case, this License incorporates 235 | the limitation as if written in the body of this License. 236 | 237 | 9. The Free Software Foundation may publish revised and/or new versions 238 | of the General Public License from time to time. Such new versions will 239 | be similar in spirit to the present version, but may differ in detail to 240 | address new problems or concerns. 241 | 242 | Each version is given a distinguishing version number. If the Program 243 | specifies a version number of this License which applies to it and "any 244 | later version", you have the option of following the terms and conditions 245 | either of that version or of any later version published by the Free 246 | Software Foundation. If the Program does not specify a version number of 247 | this License, you may choose any version ever published by the Free Software 248 | Foundation. 249 | 250 | 10. If you wish to incorporate parts of the Program into other free 251 | programs whose distribution conditions are different, write to the author 252 | to ask for permission. For software which is copyrighted by the Free 253 | Software Foundation, write to the Free Software Foundation; we sometimes 254 | make exceptions for this. Our decision will be guided by the two goals 255 | of preserving the free status of all derivatives of our free software and 256 | of promoting the sharing and reuse of software generally. 257 | 258 | NO WARRANTY 259 | 260 | 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY 261 | FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN 262 | OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES 263 | PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED 264 | OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF 265 | MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS 266 | TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE 267 | PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, 268 | REPAIR OR CORRECTION. 269 | 270 | 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING 271 | WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR 272 | REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, 273 | INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING 274 | OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED 275 | TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY 276 | YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER 277 | PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE 278 | POSSIBILITY OF SUCH DAMAGES. 279 | 280 | END OF TERMS AND CONDITIONS 281 | 282 | How to Apply These Terms to Your New Programs 283 | 284 | If you develop a new program, and you want it to be of the greatest 285 | possible use to the public, the best way to achieve this is to make it 286 | free software which everyone can redistribute and change under these terms. 287 | 288 | To do so, attach the following notices to the program. It is safest 289 | to attach them to the start of each source file to most effectively 290 | convey the exclusion of warranty; and each file should have at least 291 | the "copyright" line and a pointer to where the full notice is found. 292 | 293 | 294 | Copyright (C) 295 | 296 | This program is free software; you can redistribute it and/or modify 297 | it under the terms of the GNU General Public License as published by 298 | the Free Software Foundation; either version 2 of the License, or 299 | (at your option) any later version. 300 | 301 | This program is distributed in the hope that it will be useful, 302 | but WITHOUT ANY WARRANTY; without even the implied warranty of 303 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 304 | GNU General Public License for more details. 305 | 306 | You should have received a copy of the GNU General Public License along 307 | with this program; if not, write to the Free Software Foundation, Inc., 308 | 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. 309 | 310 | Also add information on how to contact you by electronic and paper mail. 311 | 312 | If the program is interactive, make it output a short notice like this 313 | when it starts in an interactive mode: 314 | 315 | Gnomovision version 69, Copyright (C) year name of author 316 | Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. 317 | This is free software, and you are welcome to redistribute it 318 | under certain conditions; type `show c' for details. 319 | 320 | The hypothetical commands `show w' and `show c' should show the appropriate 321 | parts of the General Public License. Of course, the commands you use may 322 | be called something other than `show w' and `show c'; they could even be 323 | mouse-clicks or menu items--whatever suits your program. 324 | 325 | You should also get your employer (if you work as a programmer) or your 326 | school, if any, to sign a "copyright disclaimer" for the program, if 327 | necessary. Here is a sample; alter the names: 328 | 329 | Yoyodyne, Inc., hereby disclaims all copyright interest in the program 330 | `Gnomovision' (which makes passes at compilers) written by James Hacker. 331 | 332 | , 1 April 1989 333 | Ty Coon, President of Vice 334 | 335 | This General Public License does not permit incorporating your program into 336 | proprietary programs. If your program is a subroutine library, you may 337 | consider it more useful to permit linking proprietary applications with the 338 | library. If this is what you want to do, use the GNU Lesser General 339 | Public License instead of this License. 340 | -------------------------------------------------------------------------------- /Makefile.in: -------------------------------------------------------------------------------- 1 | srcdir = @srcdir@ 2 | CC = @CC@ 3 | CFLAGS = @CFLAGS@ -I. 4 | LDFLAGS = @LDFLAGS@ 5 | LIBS = @LIBS@ 6 | VERSION = @PACKAGE_VERSION@ 7 | VPATH = $(srcdir) 8 | 9 | OBJ = attach.o master.o main.o 10 | SRC = $(srcdir)/attach.c $(srcdir)/master.c $(srcdir)/main.c 11 | 12 | TARFILES = $(srcdir)/README $(srcdir)/COPYING $(srcdir)/Makefile.in \ 13 | $(srcdir)/config.h.in $(SRC) \ 14 | $(srcdir)/dtach.h $(srcdir)/dtach.spec $(srcdir)/configure \ 15 | $(srcdir)/configure.ac $(srcdir)/dtach.1 16 | 17 | dtach: $(OBJ) 18 | $(CC) -o $@ $(LDFLAGS) $(OBJ) $(LIBS) 19 | 20 | clean: 21 | rm -f dtach $(OBJ) dtach-$(VERSION).tar.gz 22 | 23 | distclean: clean 24 | rm -f config.h Makefile config.log config.status config.cache 25 | 26 | tar: 27 | mkdir dtach-$(VERSION) 28 | cp $(TARFILES) dtach-$(VERSION) 29 | tar -cf dtach-$(VERSION).tar dtach-$(VERSION)/ 30 | gzip -9f dtach-$(VERSION).tar 31 | rm -rf dtach-$(VERSION) 32 | 33 | attach.o: @srcdir@/attach.c @srcdir@/dtach.h config.h 34 | master.o: @srcdir@/master.c @srcdir@/dtach.h config.h 35 | main.o: @srcdir@/main.c @srcdir@/dtach.h config.h 36 | 37 | -------------------------------------------------------------------------------- /README: -------------------------------------------------------------------------------- 1 | 1. INTRODUCTION 2 | 3 | dtach is a program written in C that emulates the detach feature of 4 | screen, which allows a program to be executed in an environment that is 5 | protected from the controlling terminal. For instance, the program under 6 | the control of dtach would not be affected by the terminal being 7 | disconnected for some reason. 8 | 9 | dtach was written because screen did not adequately meet my needs; I did 10 | not need screen's extra features, such as support for multiple 11 | terminals or terminal emulation support. screen was also too big, 12 | bulky, and had source code that was difficult to understand. 13 | 14 | screen also interfered with my use of full-screen applications such as 15 | emacs and ircII, due to its excessive interpretation of the stream between 16 | the program and the attached terminals. dtach does not have a terminal 17 | emulation layer, and passes the raw output stream of the program to the 18 | attached terminals. The only input processing that dtach does perform is 19 | scanning for the detach character (which signals dtach to detach from 20 | the program) and processing the suspend key (which tells dtach to 21 | temporarily suspend itself without affecting the running program), and both 22 | of these can both be disabled if desired. 23 | 24 | Contrary to screen, dtach has minimal features, and is extremely tiny. 25 | This allows dtach to be more easily audited for bugs and security 26 | holes, and makes it accessible in environments where space is limited, 27 | such as on rescue disks. 28 | 29 | dtach has only been tested on the Linux/x86 platform, however it should 30 | be easily portable to other variants of Unix. It currently assumes that 31 | the host system uses POSIX termios, and has a working forkpty function 32 | available. 33 | 34 | dtach may need access to various devices in the filesystem depending on what 35 | forkpty does. For example, dtach on Linux usually needs access to /dev/ptmx 36 | and /dev/pts. 37 | 38 | 2. QUICK START 39 | 40 | Compiling dtach should be simple, as it uses autoconf: 41 | 42 | $ ./configure 43 | $ make 44 | 45 | If all goes well, a dtach binary should be built for your system. You can 46 | then copy it to the appropriate place on your system. 47 | 48 | dtach uses Unix-domain sockets to represent sessions; these are network 49 | sockets that are stored in the filesystem. You specify the name of the 50 | socket that dtach should use when creating or attaching to dtach sessions. 51 | 52 | For example, let's create a new session that is running ircII. We will use 53 | /tmp/foozle as the session's socket: 54 | 55 | $ dtach -A /tmp/foozle irc RuneB irc.freenode.net 56 | 57 | Here, -A tells dtach to either create a new session or attach to the 58 | existing session. If the session at /tmp/foozle does not exist yet, the 59 | program will be executed. If it does exist, then dtach will attach to 60 | the existing session. 61 | 62 | dtach has another attach mode, which is specified by using -a. The -a 63 | mode attaches to an already existing session, but will not create a new 64 | session. Each attaching process can have a separate detach character, 65 | suspend behavior, and redraw method, which are explained in the 66 | following sections. 67 | 68 | dtach is able to attach to the same session multiple times, though you 69 | will likely encounter problems if your terminals have different window 70 | sizes. Pressing ^L (Ctrl-L) will reset the window size of the program to 71 | match the current terminal. 72 | 73 | dtach also has a mode that copies the contents of standard input to a session. 74 | For example: 75 | 76 | $ echo -ne 'cd /var/log\nls -l\n' | dtach -p /tmp/foozle 77 | 78 | The contents are sent verbatim including any embedded control characters (e.g. 79 | the newline characters in the above example), and dtach will not scan the 80 | input for a detach character. 81 | 82 | 3. DETACHING FROM THE SESSION 83 | 84 | By default, dtach scans the keyboard input looking for the detach character. 85 | When the detach character is pressed, dtach will detach from the current 86 | session and exit, leaving the program running in the background. You can then 87 | re-attach to the program by running dtach again with -A or -a. 88 | 89 | The default detach character is ^\ (Ctrl-\). This can be changed by supplying 90 | the -e option to dtach when attaching. For example: 91 | 92 | $ dtach -a /tmp/foozle -e '^A' 93 | 94 | That command would attach to the existing session at /tmp/foozle and use 95 | ^A (Ctrl-A) as the detach character, instead of the default ^\. 96 | 97 | You can disable processing of the detach character by supplying the -E 98 | option to dtach when attaching. 99 | 100 | 4. SUSPENDING DTACH 101 | 102 | By default, dtach also processes the suspend key (^Z or Ctrl-Z) itself, 103 | instead of passing it to the program. Thus, pressing suspend only suspends 104 | the attaching process, instead of the running program. This can be very 105 | useful for applications such as ircII, where you may not necessarily want 106 | the program to be suspended. 107 | 108 | Processing of the suspend key can be disabled by supplying the -z option 109 | to dtach when attaching. 110 | 111 | 5. REDRAW METHOD 112 | 113 | When attaching, dtach can use one of three methods to redraw the screen 114 | (none, ctrl_l, or winch). By default, dtach uses the ctrl_l method, 115 | which simply sends a ^L (Ctrl-L) character to the program if the 116 | terminal is in character-at-a-time and no-echo mode. The winch method 117 | forces a WINCH signal to be sent to the program, and the none method 118 | disables redrawing completely. 119 | 120 | For example, this command tells dtach to attach to a session at 121 | /tmp/foozle and use the winch redraw method: 122 | 123 | $ dtach -a /tmp/foozle -r winch 124 | 125 | When creating a new session (with the -c or -A modes), the specified 126 | method is used as the default redraw method for the session. 127 | 128 | 6. CHANGES 129 | 130 | The changes in version 0.9 are: 131 | - Added AIX support. 132 | - Added dtach -N, a mode similar to dtach -n, except dtach will stay in the 133 | foreground instead of daemonizing. 134 | - Added dtach -p, which copies the contents of standard input to a session. 135 | - dtach will no longer send 255 bytes of garbage to the program when read() 136 | returns an error. 137 | - The executable bit is now set on the socket if clients are attached, and 138 | cleared when all clients have detached. 139 | - The initial state of signals such as SIGPIPE are now preserved when 140 | executing the program, instead of having the program start with some signals 141 | ignored. 142 | - A buffer overflow no longer occurs when a long socket path name is used, and 143 | dtach will now try to use chdir to get around the length limitation if 144 | necessary. 145 | 146 | The changes in version 0.8 are: 147 | - When using dtach -A or dtach -c, the master will now wait until the client 148 | attaches before trying to read from the program being executed. This avoids 149 | a race condition when the program prints something and exits before the 150 | client can attach itself. 151 | - Instead of exiting quietly, dtach will now report any errors that occur 152 | while trying to execute the program. 153 | - dtach -n can now be used without a terminal. 154 | - dtach -A will now try to detect and remove stale sockets. 155 | - Removed a Linux-specific escape sequence from the code that restores the 156 | original terminal settings. 157 | - Changed dtach.1 to use \- for the dashes in command line options, and 158 | fix an ambiguous backslash. 159 | - Use non-blocking mode in the master process, and avoid data loss by ensuring 160 | that at least one attaching client succesfully completes a write. 161 | - Fix -e ^ to work with lowercase characters. 162 | 163 | The changes in version 0.7 are: 164 | - The redraw method can now be explicitly specified on the command line 165 | (either no redraw at all, the old ^L character method, and the new WINCH 166 | signal method), since many programs only handle one or the other properly. 167 | - Changed the default redraw method back to the old ^L character method. 168 | - Changed the attach code to check the return value of select more carefully. 169 | - Changed the SIGWINCH handler to reinstall itself, to handle systems that 170 | always reset the handler. 171 | - Added more proper process group handling. 172 | 173 | The changes in version 0.6 are: 174 | - Redraws are now handled by sending the child process a WINCH signal instead 175 | of by sending a ^L character. This should help prevent line-oriented 176 | programs (such as bash) from clearing the screen excessively. 177 | - Flow control is now disabled when setting raw mode on the terminal. 178 | - Switched to using select instead of poll. 179 | - Changed some exits to exit succesfully instead of non-sucessfully. 180 | - Updated my email address. 181 | - Updated to Autoconf 2.59, renaming some files in the process. 182 | 183 | The changes in version 0.5 are: 184 | - Fix fd leakage. 185 | - Prevent atexit from being called twice on dtach -A. 186 | 187 | The changes in version 0.4 are: 188 | - Slightly improved README and dtach.1 189 | - Portability updates thanks to sourceforge's compile farm. dtach should now 190 | work on: FreeBSD, Debian/alpha, Debian/sparc, Debian/PPC, and Solaris. 191 | 192 | The changes in version 0.3 are: 193 | - Fixed a typo in dtach.1 194 | - Changed the attach code so that it tells the master when a suspend 195 | occurs. 196 | - Decreased the client <-> master packet size. 197 | - Changed the master to send a stream of text to attaching clients 198 | instead of sending a huge packet all the time. 199 | - Use getrlimit and dynamically allocate the data structures, if 200 | possible. 201 | - Added some more autoconf checks. 202 | - Initial sourceforge release. 203 | 204 | 7. AUTHOR 205 | 206 | dtach is (C)Copyright 2004-2016 Ned T. Crigler, and is under the GNU General 207 | Public License. 208 | 209 | Comments and suggestions about dtach are welcome, and can be sent to 210 | the author at: . 211 | -------------------------------------------------------------------------------- /attach.c: -------------------------------------------------------------------------------- 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, see . 17 | */ 18 | #include "dtach.h" 19 | 20 | #ifndef VDISABLE 21 | #ifdef _POSIX_VDISABLE 22 | #define VDISABLE _POSIX_VDISABLE 23 | #else 24 | #define VDISABLE 0377 25 | #endif 26 | #endif 27 | 28 | /* 29 | ** The current terminal settings. After coming back from a suspend, we 30 | ** restore this. 31 | */ 32 | static struct termios cur_term; 33 | /* 1 if the window size changed */ 34 | static int win_changed; 35 | 36 | /* Restores the original terminal settings. */ 37 | static void 38 | restore_term(void) 39 | { 40 | tcsetattr(0, TCSADRAIN, &orig_term); 41 | 42 | /* Make cursor visible. Assumes VT100. */ 43 | printf("\033[?25h"); 44 | fflush(stdout); 45 | } 46 | 47 | /* Connects to a unix domain socket */ 48 | static int 49 | connect_socket(char *name) 50 | { 51 | int s; 52 | struct sockaddr_un sockun; 53 | 54 | if (strlen(name) > sizeof(sockun.sun_path) - 1) 55 | { 56 | errno = ENAMETOOLONG; 57 | return -1; 58 | } 59 | 60 | s = socket(PF_UNIX, SOCK_STREAM, 0); 61 | if (s < 0) 62 | return -1; 63 | sockun.sun_family = AF_UNIX; 64 | strcpy(sockun.sun_path, name); 65 | if (connect(s, (struct sockaddr*)&sockun, sizeof(sockun)) < 0) 66 | { 67 | close(s); 68 | 69 | /* ECONNREFUSED is also returned for regular files, so make 70 | ** sure we are trying to connect to a socket. */ 71 | if (errno == ECONNREFUSED) 72 | { 73 | struct stat st; 74 | 75 | if (stat(name, &st) < 0) 76 | return -1; 77 | else if (!S_ISSOCK(st.st_mode) || S_ISREG(st.st_mode)) 78 | errno = ENOTSOCK; 79 | } 80 | return -1; 81 | } 82 | return s; 83 | } 84 | 85 | /* Signal */ 86 | static RETSIGTYPE 87 | die(int sig) 88 | { 89 | /* Print a nice pretty message for some things. */ 90 | if (sig == SIGHUP || sig == SIGINT) 91 | printf(EOS "\r\n[detached]\r\n"); 92 | else 93 | printf(EOS "\r\n[got signal %d - dying]\r\n", sig); 94 | exit(1); 95 | } 96 | 97 | /* Window size change. */ 98 | static RETSIGTYPE 99 | win_change() 100 | { 101 | signal(SIGWINCH, win_change); 102 | win_changed = 1; 103 | } 104 | 105 | /* Handles input from the keyboard. */ 106 | static void 107 | process_kbd(int s, struct packet *pkt) 108 | { 109 | /* Suspend? */ 110 | if (!no_suspend && (pkt->u.buf[0] == cur_term.c_cc[VSUSP])) 111 | { 112 | /* Tell the master that we are suspending. */ 113 | pkt->type = MSG_DETACH; 114 | write(s, pkt, sizeof(struct packet)); 115 | 116 | /* And suspend... */ 117 | tcsetattr(0, TCSADRAIN, &orig_term); 118 | printf(EOS "\r\n"); 119 | kill(getpid(), SIGTSTP); 120 | tcsetattr(0, TCSADRAIN, &cur_term); 121 | 122 | /* Tell the master that we are returning. */ 123 | pkt->type = MSG_ATTACH; 124 | write(s, pkt, sizeof(struct packet)); 125 | 126 | /* We would like a redraw, too. */ 127 | pkt->type = MSG_REDRAW; 128 | pkt->len = redraw_method; 129 | ioctl(0, TIOCGWINSZ, &pkt->u.ws); 130 | write(s, pkt, sizeof(struct packet)); 131 | return; 132 | } 133 | /* Detach char? */ 134 | else if (pkt->u.buf[0] == detach_char) 135 | { 136 | printf(EOS "\r\n[detached]\r\n"); 137 | exit(0); 138 | } 139 | /* Just in case something pukes out. */ 140 | else if (pkt->u.buf[0] == '\f') 141 | win_changed = 1; 142 | 143 | /* Push it out */ 144 | write(s, pkt, sizeof(struct packet)); 145 | } 146 | 147 | int 148 | attach_main(int noerror) 149 | { 150 | struct packet pkt; 151 | unsigned char buf[BUFSIZE]; 152 | fd_set readfds; 153 | int s; 154 | 155 | /* Attempt to open the socket. Don't display an error if noerror is 156 | ** set. */ 157 | s = connect_socket(sockname); 158 | if (s < 0 && errno == ENAMETOOLONG) 159 | { 160 | char *slash = strrchr(sockname, '/'); 161 | 162 | /* Try to shorten the socket's path name by using chdir. */ 163 | if (slash) 164 | { 165 | int dirfd = open(".", O_RDONLY); 166 | 167 | if (dirfd >= 0) 168 | { 169 | *slash = '\0'; 170 | if (chdir(sockname) >= 0) 171 | { 172 | s = connect_socket(slash + 1); 173 | fchdir(dirfd); 174 | } 175 | *slash = '/'; 176 | close(dirfd); 177 | } 178 | } 179 | } 180 | if (s < 0) 181 | { 182 | if (!noerror) 183 | printf("%s: %s: %s\n", progname, sockname, 184 | strerror(errno)); 185 | return 1; 186 | } 187 | 188 | /* The current terminal settings are equal to the original terminal 189 | ** settings at this point. */ 190 | cur_term = orig_term; 191 | 192 | /* Set a trap to restore the terminal when we die. */ 193 | atexit(restore_term); 194 | 195 | /* Set some signals. */ 196 | signal(SIGPIPE, SIG_IGN); 197 | signal(SIGXFSZ, SIG_IGN); 198 | signal(SIGHUP, die); 199 | signal(SIGTERM, die); 200 | signal(SIGINT, die); 201 | signal(SIGQUIT, die); 202 | signal(SIGWINCH, win_change); 203 | 204 | /* Set raw mode. */ 205 | cur_term.c_iflag &= ~(IGNBRK|BRKINT|PARMRK|ISTRIP|INLCR|IGNCR|ICRNL); 206 | cur_term.c_iflag &= ~(IXON|IXOFF); 207 | cur_term.c_oflag &= ~(OPOST); 208 | cur_term.c_lflag &= ~(ECHO|ECHONL|ICANON|ISIG|IEXTEN); 209 | cur_term.c_cflag &= ~(CSIZE|PARENB); 210 | cur_term.c_cflag |= CS8; 211 | cur_term.c_cc[VLNEXT] = VDISABLE; 212 | cur_term.c_cc[VMIN] = 1; 213 | cur_term.c_cc[VTIME] = 0; 214 | tcsetattr(0, TCSADRAIN, &cur_term); 215 | 216 | /* Clear the screen. This assumes VT100. */ 217 | write(1, "\33[H\33[J", 6); 218 | 219 | /* Tell the master that we want to attach. */ 220 | memset(&pkt, 0, sizeof(struct packet)); 221 | pkt.type = MSG_ATTACH; 222 | write(s, &pkt, sizeof(struct packet)); 223 | 224 | /* We would like a redraw, too. */ 225 | pkt.type = MSG_REDRAW; 226 | pkt.len = redraw_method; 227 | ioctl(0, TIOCGWINSZ, &pkt.u.ws); 228 | write(s, &pkt, sizeof(struct packet)); 229 | 230 | /* Wait for things to happen */ 231 | while (1) 232 | { 233 | int n; 234 | 235 | FD_ZERO(&readfds); 236 | FD_SET(0, &readfds); 237 | FD_SET(s, &readfds); 238 | n = select(s + 1, &readfds, NULL, NULL, NULL); 239 | if (n < 0 && errno != EINTR && errno != EAGAIN) 240 | { 241 | printf(EOS "\r\n[select failed]\r\n"); 242 | exit(1); 243 | } 244 | 245 | /* Pty activity */ 246 | if (n > 0 && FD_ISSET(s, &readfds)) 247 | { 248 | ssize_t len = read(s, buf, sizeof(buf)); 249 | 250 | if (len == 0) 251 | { 252 | printf(EOS "\r\n[EOF - dtach terminating]" 253 | "\r\n"); 254 | exit(0); 255 | } 256 | else if (len < 0) 257 | { 258 | printf(EOS "\r\n[read returned an error]\r\n"); 259 | exit(1); 260 | } 261 | /* Send the data to the terminal. */ 262 | write(1, buf, len); 263 | n--; 264 | } 265 | /* stdin activity */ 266 | if (n > 0 && FD_ISSET(0, &readfds)) 267 | { 268 | ssize_t len; 269 | 270 | pkt.type = MSG_PUSH; 271 | memset(pkt.u.buf, 0, sizeof(pkt.u.buf)); 272 | len = read(0, pkt.u.buf, sizeof(pkt.u.buf)); 273 | 274 | if (len <= 0) 275 | exit(1); 276 | 277 | pkt.len = len; 278 | process_kbd(s, &pkt); 279 | n--; 280 | } 281 | 282 | /* Window size changed? */ 283 | if (win_changed) 284 | { 285 | win_changed = 0; 286 | 287 | pkt.type = MSG_WINCH; 288 | ioctl(0, TIOCGWINSZ, &pkt.u.ws); 289 | write(s, &pkt, sizeof(pkt)); 290 | } 291 | } 292 | return 0; 293 | } 294 | 295 | int 296 | push_main() 297 | { 298 | struct packet pkt; 299 | int s; 300 | 301 | /* Attempt to open the socket. */ 302 | s = connect_socket(sockname); 303 | if (s < 0 && errno == ENAMETOOLONG) 304 | { 305 | char *slash = strrchr(sockname, '/'); 306 | 307 | /* Try to shorten the socket's path name by using chdir. */ 308 | if (slash) 309 | { 310 | int dirfd = open(".", O_RDONLY); 311 | 312 | if (dirfd >= 0) 313 | { 314 | *slash = '\0'; 315 | if (chdir(sockname) >= 0) 316 | { 317 | s = connect_socket(slash + 1); 318 | fchdir(dirfd); 319 | } 320 | *slash = '/'; 321 | close(dirfd); 322 | } 323 | } 324 | } 325 | if (s < 0) 326 | { 327 | printf("%s: %s: %s\n", progname, sockname, strerror(errno)); 328 | return 1; 329 | } 330 | 331 | /* Set some signals. */ 332 | signal(SIGPIPE, SIG_IGN); 333 | 334 | /* Push the contents of standard input to the socket. */ 335 | pkt.type = MSG_PUSH; 336 | for (;;) 337 | { 338 | ssize_t len; 339 | 340 | memset(pkt.u.buf, 0, sizeof(pkt.u.buf)); 341 | len = read(0, pkt.u.buf, sizeof(pkt.u.buf)); 342 | 343 | if (len == 0) 344 | return 0; 345 | else if (len < 0) 346 | { 347 | printf("%s: %s: %s\n", progname, sockname, 348 | strerror(errno)); 349 | return 1; 350 | } 351 | 352 | pkt.len = len; 353 | if (write(s, &pkt, sizeof(struct packet)) < 0) 354 | { 355 | printf("%s: %s: %s\n", progname, sockname, 356 | strerror(errno)); 357 | return 1; 358 | } 359 | } 360 | } 361 | -------------------------------------------------------------------------------- /config.h.in: -------------------------------------------------------------------------------- 1 | /* config.h.in. Generated from configure.ac by autoheader. */ 2 | 3 | /* Define to 1 if you have the `atexit' function. */ 4 | #undef HAVE_ATEXIT 5 | 6 | /* Define to 1 if you have the `dup2' function. */ 7 | #undef HAVE_DUP2 8 | 9 | /* Define to 1 if you have the header file. */ 10 | #undef HAVE_FCNTL_H 11 | 12 | /* Define to 1 if you have the `forkpty' function. */ 13 | #undef HAVE_FORKPTY 14 | 15 | /* Define to 1 if you have the `grantpt' function. */ 16 | #undef HAVE_GRANTPT 17 | 18 | /* Define to 1 if you have the header file. */ 19 | #undef HAVE_INTTYPES_H 20 | 21 | /* Define to 1 if you have the `socket' library (-lsocket). */ 22 | #undef HAVE_LIBSOCKET 23 | 24 | /* Define to 1 if you have the `util' library (-lutil). */ 25 | #undef HAVE_LIBUTIL 26 | 27 | /* Define to 1 if you have the header file. */ 28 | #undef HAVE_LIBUTIL_H 29 | 30 | /* Define to 1 if you have the header file. */ 31 | #undef HAVE_MEMORY_H 32 | 33 | /* Define to 1 if you have the `memset' function. */ 34 | #undef HAVE_MEMSET 35 | 36 | /* Define to 1 if you have the `openpty' function. */ 37 | #undef HAVE_OPENPTY 38 | 39 | /* Define to 1 if you have the `ptsname' function. */ 40 | #undef HAVE_PTSNAME 41 | 42 | /* Define to 1 if you have the header file. */ 43 | #undef HAVE_PTY_H 44 | 45 | /* Define to 1 if you have the `select' function. */ 46 | #undef HAVE_SELECT 47 | 48 | /* Define to 1 if you have the `socket' function. */ 49 | #undef HAVE_SOCKET 50 | 51 | /* Define to 1 if you have the header file. */ 52 | #undef HAVE_STDINT_H 53 | 54 | /* Define to 1 if you have the header file. */ 55 | #undef HAVE_STDLIB_H 56 | 57 | /* Define to 1 if you have the `strerror' function. */ 58 | #undef HAVE_STRERROR 59 | 60 | /* Define to 1 if you have the header file. */ 61 | #undef HAVE_STRINGS_H 62 | 63 | /* Define to 1 if you have the header file. */ 64 | #undef HAVE_STRING_H 65 | 66 | /* Define to 1 if you have the header file. */ 67 | #undef HAVE_STROPTS_H 68 | 69 | /* Define to 1 if you have the header file. */ 70 | #undef HAVE_SYS_IOCTL_H 71 | 72 | /* Define to 1 if you have the header file. */ 73 | #undef HAVE_SYS_RESOURCE_H 74 | 75 | /* Define to 1 if you have the header file. */ 76 | #undef HAVE_SYS_SELECT_H 77 | 78 | /* Define to 1 if you have the header file. */ 79 | #undef HAVE_SYS_SOCKET_H 80 | 81 | /* Define to 1 if you have the header file. */ 82 | #undef HAVE_SYS_STAT_H 83 | 84 | /* Define to 1 if you have the header file. */ 85 | #undef HAVE_SYS_TIME_H 86 | 87 | /* Define to 1 if you have the header file. */ 88 | #undef HAVE_SYS_TYPES_H 89 | 90 | /* Define to 1 if you have the header file. */ 91 | #undef HAVE_TERMIOS_H 92 | 93 | /* Define to 1 if you have the header file. */ 94 | #undef HAVE_UNISTD_H 95 | 96 | /* Define to 1 if you have the `unlockpt' function. */ 97 | #undef HAVE_UNLOCKPT 98 | 99 | /* Define to 1 if you have the header file. */ 100 | #undef HAVE_UTIL_H 101 | 102 | /* Define to the address where bug reports for this package should be sent. */ 103 | #undef PACKAGE_BUGREPORT 104 | 105 | /* Define to the full name of this package. */ 106 | #undef PACKAGE_NAME 107 | 108 | /* Define to the full name and version of this package. */ 109 | #undef PACKAGE_STRING 110 | 111 | /* Define to the one symbol short name of this package. */ 112 | #undef PACKAGE_TARNAME 113 | 114 | /* Define to the home page for this package. */ 115 | #undef PACKAGE_URL 116 | 117 | /* Define to the version of this package. */ 118 | #undef PACKAGE_VERSION 119 | 120 | /* Define as the return type of signal handlers (`int' or `void'). */ 121 | #undef RETSIGTYPE 122 | 123 | /* Define to 1 if you have the ANSI C header files. */ 124 | #undef STDC_HEADERS 125 | 126 | /* Define to 1 if you can safely include both and . */ 127 | #undef TIME_WITH_SYS_TIME 128 | 129 | /* Define to empty if `const' does not conform to ANSI C. */ 130 | #undef const 131 | 132 | /* Define to `int' if does not define. */ 133 | #undef pid_t 134 | 135 | /* Define to `int' if does not define. */ 136 | #undef ssize_t 137 | -------------------------------------------------------------------------------- /configure.ac: -------------------------------------------------------------------------------- 1 | # Process this file with autoconf to produce a configure script. 2 | AC_INIT(dtach, 0.9, crigler@users.sourceforge.net) 3 | AC_PREREQ(2.60) 4 | AC_CONFIG_SRCDIR(main.c) 5 | AC_CONFIG_HEADER(config.h) 6 | 7 | # Checks for programs. 8 | AC_PROG_CC 9 | AC_PROG_GCC_TRADITIONAL 10 | 11 | if test "$GCC" = yes; then 12 | CFLAGS="$CFLAGS -W -Wall"; 13 | fi 14 | 15 | # Checks for libraries. 16 | AC_CHECK_LIB(util, openpty) 17 | AC_CHECK_LIB(socket, socket) 18 | 19 | # Checks for header files. 20 | AC_CHECK_HEADERS(fcntl.h sys/select.h sys/socket.h sys/time.h) 21 | AC_CHECK_HEADERS(sys/ioctl.h sys/resource.h pty.h termios.h util.h) 22 | AC_CHECK_HEADERS(libutil.h stropts.h) 23 | AC_HEADER_TIME 24 | 25 | # Checks for typedefs, structures, and compiler characteristics. 26 | AC_C_CONST 27 | AC_TYPE_PID_T 28 | AC_TYPE_SSIZE_T 29 | 30 | # Checks for library functions. 31 | AC_TYPE_SIGNAL 32 | AC_CHECK_FUNCS(atexit dup2 memset) 33 | AC_CHECK_FUNCS(select socket strerror) 34 | AC_CHECK_FUNCS(openpty forkpty ptsname grantpt unlockpt) 35 | 36 | AC_CONFIG_FILES(Makefile) 37 | AC_OUTPUT 38 | -------------------------------------------------------------------------------- /dtach.1: -------------------------------------------------------------------------------- 1 | .TH dtach 1 "May 2016" "dtach 0.9" 2 | .SH NAME 3 | dtach \- simple program that emulates the detach feature of screen. 4 | .SH SYNOPSIS 5 | .B dtach \-a 6 | .I 7 | .br 8 | .B dtach \-A 9 | .I 10 | .br 11 | .B dtach \-c 12 | .I 13 | .br 14 | .B dtach \-n 15 | .I 16 | .br 17 | .B dtach \-N 18 | .I 19 | .br 20 | .B dtach \-p 21 | .I 22 | 23 | .SH DESCRIPTION 24 | .B dtach 25 | is a program that emulates the detach feature of screen. It is designed 26 | to be transparent and un-intrusive; it avoids interpreting the input and output 27 | between attached terminals and the program under its control. Consequently, it 28 | works best with full-screen applications such as emacs. 29 | 30 | .B dtach 31 | is intended for users who want the detach feature of screen without the other 32 | overhead of screen. It is tiny, does not use many libraries, and stays 33 | out of the way as much as possible. 34 | 35 | .SS SESSIONS 36 | A session in 37 | .B dtach 38 | is a single instance in which a program is running under the control of 39 | .BR dtach . 40 | The program is disassociated from the original terminal, and is thus protected 41 | from your original terminal being disconnected for some reason. 42 | .PP 43 | 44 | Other instances of 45 | .B dtach 46 | can attach themselves to a particular session. Input and output is copied 47 | between the program running in the 48 | .B dtach 49 | session, and the attached terminals. 50 | .PP 51 | 52 | .B dtach 53 | avoids interpreting the communication stream between the program and the 54 | attached terminals; it instead relies on the ability of the attached terminals 55 | to manage the screen. 56 | .PP 57 | 58 | Sessions are represented by Unix-domain sockets in the filesystem. No other 59 | permission checking other than the filesystem access checks is performed. 60 | .B dtach 61 | creates a master process that monitors the session socket, the program, and any 62 | attached terminals. 63 | 64 | .PP 65 | .SS MODES 66 | .B dtach 67 | has several modes of operation. It can create a new session in which a 68 | program is executed, or it can attach to an existing session. The first 69 | argument specifies which mode 70 | .B dtach 71 | should operate in. 72 | .TP 73 | .B \-a 74 | Attach to an existing session. 75 | .B dtach 76 | attaches itself to the session specified by 77 | .IR . 78 | After the attach is completed, the window size of the current terminal is sent 79 | to the master process, and a redraw is also requested. 80 | .TP 81 | .B \-A 82 | Attach to an existing session, or create a new one. 83 | .B dtach 84 | first tries to attach to the session specified by 85 | .I 86 | if possible. If the attempt to open the socket fails, 87 | .B dtach 88 | tries to create a new session before attaching to it. 89 | .TP 90 | .B \-c 91 | Creates a new session. A new session is created in which the specified program 92 | is executed. 93 | .B dtach 94 | then tries to attach itself to the newly created session. 95 | .TP 96 | .B \-n 97 | Creates a new session, without attaching to it. A new session is created in 98 | which the specified program is executed. 99 | .B dtach 100 | does not try to attach to the newly created session, however, and exits 101 | instead. 102 | .TP 103 | .B \-N 104 | Creates a new session, without attaching to it or daemonizing. A new session is 105 | created in which the specified program is executed. 106 | .B dtach 107 | does not try to attach to the newly created session, however, and will stay 108 | in the foreground until the program exits. 109 | .TP 110 | .B \-p 111 | Copies the contents of standard input to a session. 112 | .B dtach 113 | connects to the session specified by 114 | .IR , 115 | copies the contents of standard input to the session, and then exits. dtach 116 | will not scan the input for a detach character. 117 | 118 | .PP 119 | .SS OPTIONS 120 | .B dtach 121 | has a few options that allow you to modify its behavior. Each attaching 122 | process can have separate settings for these options, which allows for 123 | some flexibility. 124 | 125 | .TP 126 | .BI "\-e " "" 127 | Sets the detach character to 128 | .IR . 129 | When the detach character is pressed, 130 | .B dtach 131 | detaches itself from the current session and exits. The process running in 132 | the session is unaffected by the detach. By default, the detach character is 133 | set to ^\e (Ctrl-\e). 134 | 135 | .TP 136 | .B \-E 137 | Disables the detach character. 138 | .B dtach 139 | does not try to scan input from the terminal for a detach character. The only 140 | way to detach from the session is then by sending the attaching process an 141 | appropriate signal. 142 | 143 | .TP 144 | .BI "\-r " "" 145 | Sets the redraw method to 146 | .IR . 147 | The valid methods are 148 | .IR none , 149 | .IR ctrl_l , 150 | or 151 | .IR winch . 152 | 153 | .I none 154 | disables redrawing completely, 155 | .I ctrl_l 156 | sends a Ctrl L character to the program if the terminal is in 157 | character-at-a-time and no-echo mode, and 158 | .I winch 159 | forces a WINCH signal to be sent to the program. 160 | 161 | When creating a new session, the specified method is used as the default 162 | redraw method for the session. If not specified, the 163 | .I ctrl_l 164 | method is used. 165 | 166 | .TP 167 | .B \-z 168 | Disables processing of the suspend key. 169 | Normally, 170 | .B dtach 171 | will suspend itself when the suspend key is pressed. With this option, the 172 | suspend character is sent to the session instead of being handled by 173 | .BR dtach . 174 | 175 | .PP 176 | .SH EXAMPLES 177 | 178 | The following example creates a new session that has the detach character 179 | and suspend processing disabled. A socket is created in the /tmp directory 180 | for the session. 181 | 182 | .nf 183 | $ dtach \-c /tmp/foozle \-Ez bash 184 | .fi 185 | 186 | The following example attaches to the /tmp/foozle session if it exists, and if 187 | not, creates a new session using /tmp/foozle as the socket for the session. 188 | Processing of the suspend character is also disabled for the attach instance. 189 | 190 | .nf 191 | $ dtach \-A /tmp/foozle \-z bash 192 | .fi 193 | 194 | The following example attaches to the /tmp/foozle session, using the 195 | .I winch 196 | redraw method to redraw the screen. 197 | 198 | .nf 199 | $ dtach \-a /tmp/foozle \-r winch 200 | .fi 201 | 202 | The following example creates a new session and sets the default redraw method 203 | for the session to the 204 | .I winch 205 | redraw method. 206 | 207 | .nf 208 | $ dtach \-c /tmp/foozle \-r winch bash 209 | .fi 210 | 211 | .PP 212 | .SH AUTHOR 213 | Ned T. Crigler . 214 | 215 | .SH "SEE ALSO" 216 | .BR screen "(1)" 217 | -------------------------------------------------------------------------------- /dtach.h: -------------------------------------------------------------------------------- 1 | /* 2 | dtach - A simple program that emulates the detach feature of screen. 3 | Copyright (C) 2001, 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, see . 17 | */ 18 | #ifndef dtach_h 19 | #define dtach_h 20 | 21 | #include 22 | 23 | #include 24 | #include 25 | #include 26 | #include 27 | #include 28 | #include 29 | 30 | #if TIME_WITH_SYS_TIME 31 | #include 32 | #include 33 | #else 34 | #if HAVE_SYS_TIME_H 35 | #include 36 | #else 37 | #include 38 | #endif 39 | #endif 40 | 41 | #ifdef HAVE_PTY_H 42 | #include 43 | #endif 44 | 45 | #ifdef HAVE_UTIL_H 46 | #include 47 | #endif 48 | 49 | #ifdef HAVE_LIBUTIL_H 50 | #include 51 | #endif 52 | 53 | #ifdef HAVE_STROPTS_H 54 | #include 55 | #endif 56 | 57 | #ifdef HAVE_UNISTD_H 58 | #include 59 | #endif 60 | 61 | #ifdef HAVE_SYS_IOCTL_H 62 | #include 63 | #endif 64 | 65 | #ifdef HAVE_SYS_RESOURCE_H 66 | #include 67 | #endif 68 | 69 | #include 70 | #include 71 | #include 72 | #include 73 | #include 74 | 75 | #ifndef S_ISREG 76 | #define S_ISREG(m) (((m) & S_IFMT) == S_IFREG) 77 | #endif 78 | 79 | #ifndef S_ISSOCK 80 | #define S_ISSOCK(m) (((m) & S_IFMT) == S_IFSOCK) 81 | #endif 82 | 83 | extern char *progname, *sockname; 84 | extern int detach_char, no_suspend, redraw_method; 85 | extern struct termios orig_term; 86 | extern int dont_have_tty; 87 | 88 | enum 89 | { 90 | MSG_PUSH = 0, 91 | MSG_ATTACH = 1, 92 | MSG_DETACH = 2, 93 | MSG_WINCH = 3, 94 | MSG_REDRAW = 4, 95 | }; 96 | 97 | enum 98 | { 99 | REDRAW_UNSPEC = 0, 100 | REDRAW_NONE = 1, 101 | REDRAW_CTRL_L = 2, 102 | REDRAW_WINCH = 3, 103 | }; 104 | 105 | /* The client to master protocol. */ 106 | struct packet 107 | { 108 | unsigned char type; 109 | unsigned char len; 110 | union 111 | { 112 | unsigned char buf[sizeof(struct winsize)]; 113 | struct winsize ws; 114 | } u; 115 | }; 116 | 117 | /* 118 | ** The master sends a simple stream of text to the attaching clients, without 119 | ** any protocol. This might change back to the packet based protocol in the 120 | ** future. In the meantime, however, we minimize the amount of data sent back 121 | ** and forth between the client and the master. BUFSIZE is the size of the 122 | ** buffer used for the text stream. 123 | */ 124 | #define BUFSIZE 4096 125 | 126 | /* This hopefully moves to the bottom of the screen */ 127 | #define EOS "\033[999H" 128 | 129 | int attach_main(int noerror); 130 | int master_main(char **argv, int waitattach, int dontfork); 131 | int push_main(void); 132 | 133 | #ifdef sun 134 | #define BROKEN_MASTER 135 | #endif 136 | #endif 137 | -------------------------------------------------------------------------------- /dtach.spec: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/crigler/dtach/748020b2fa79fc41b81c2a8430578b65749196a6/dtach.spec -------------------------------------------------------------------------------- /main.c: -------------------------------------------------------------------------------- 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, see . 17 | */ 18 | #include "dtach.h" 19 | 20 | /* 21 | ** dtach is a quick hack, since I wanted the detach feature of screen without 22 | ** all the other crud. It'll work best with full-screen applications, as it 23 | ** does not keep track of the screen or anything like that. 24 | */ 25 | 26 | /* Make sure the binary has a copyright. */ 27 | const char copyright[] = "dtach - version " PACKAGE_VERSION "(C)Copyright 2004-2016 Ned T. Crigler"; 28 | 29 | /* argv[0] from the program */ 30 | char *progname; 31 | /* The name of the passed in socket. */ 32 | char *sockname; 33 | /* The character used for detaching. Defaults to '^\' */ 34 | int detach_char = '\\' - 64; 35 | /* 1 if we should not interpret the suspend character. */ 36 | int no_suspend; 37 | /* The default redraw method. Initially set to unspecified. */ 38 | int redraw_method = REDRAW_UNSPEC; 39 | 40 | /* 41 | ** The original terminal settings. Shared between the master and attach 42 | ** processes. The master uses it to initialize the pty, and the attacher uses 43 | ** it to restore the original settings. 44 | */ 45 | struct termios orig_term; 46 | int dont_have_tty; 47 | 48 | static void 49 | usage() 50 | { 51 | printf( 52 | "dtach - version %s, compiled on %s at %s.\n" 53 | "Usage: dtach -a \n" 54 | " dtach -A \n" 55 | " dtach -c \n" 56 | " dtach -n \n" 57 | " dtach -N \n" 58 | " dtach -p \n" 59 | "Modes:\n" 60 | " -a\t\tAttach to the specified socket.\n" 61 | " -A\t\tAttach to the specified socket, or create it if it\n" 62 | "\t\t does not exist, running the specified command.\n" 63 | " -c\t\tCreate a new socket and run the specified command.\n" 64 | " -n\t\tCreate a new socket and run the specified command " 65 | "detached.\n" 66 | " -N\t\tCreate a new socket and run the specified command " 67 | "detached,\n" 68 | "\t\t and have dtach run in the foreground.\n" 69 | " -p\t\tCopy the contents of standard input to the specified\n" 70 | "\t\t socket.\n" 71 | "Options:\n" 72 | " -e \tSet the detach character to , defaults " 73 | "to ^\\.\n" 74 | " -E\t\tDisable the detach character.\n" 75 | " -r \tSet the redraw method to . The " 76 | "valid methods are:\n" 77 | "\t\t none: Don't redraw at all.\n" 78 | "\t\t ctrl_l: Send a Ctrl L character to the program.\n" 79 | "\t\t winch: Send a WINCH signal to the program.\n" 80 | " -z\t\tDisable processing of the suspend key.\n" 81 | "\nReport any bugs to <" PACKAGE_BUGREPORT ">.\n", 82 | PACKAGE_VERSION, __DATE__, __TIME__); 83 | exit(0); 84 | } 85 | 86 | int 87 | main(int argc, char **argv) 88 | { 89 | int mode = 0; 90 | 91 | /* Save the program name */ 92 | progname = argv[0]; 93 | ++argv; --argc; 94 | 95 | /* Parse the arguments */ 96 | if (argc >= 1 && **argv == '-') 97 | { 98 | if (strncmp(*argv, "--help", strlen(*argv)) == 0) 99 | usage(); 100 | else if (strncmp(*argv, "--version", strlen(*argv)) == 0) 101 | { 102 | printf("dtach - version %s, compiled on %s at %s.\n", 103 | PACKAGE_VERSION, __DATE__, __TIME__); 104 | return 0; 105 | } 106 | 107 | mode = argv[0][1]; 108 | if (mode == '?') 109 | usage(); 110 | else if (mode != 'a' && mode != 'c' && mode != 'n' && 111 | mode != 'A' && mode != 'N' && mode != 'p') 112 | { 113 | printf("%s: Invalid mode '-%c'\n", progname, mode); 114 | printf("Try '%s --help' for more information.\n", 115 | progname); 116 | return 1; 117 | } 118 | } 119 | if (!mode) 120 | { 121 | printf("%s: No mode was specified.\n", progname); 122 | printf("Try '%s --help' for more information.\n", 123 | progname); 124 | return 1; 125 | } 126 | ++argv; --argc; 127 | 128 | if (argc < 1) 129 | { 130 | printf("%s: No socket was specified.\n", progname); 131 | printf("Try '%s --help' for more information.\n", 132 | progname); 133 | return 1; 134 | } 135 | sockname = *argv; 136 | ++argv; --argc; 137 | 138 | if (mode == 'p') 139 | { 140 | if (argc > 0) 141 | { 142 | printf("%s: Invalid number of arguments.\n", 143 | progname); 144 | printf("Try '%s --help' for more information.\n", 145 | progname); 146 | return 1; 147 | } 148 | return push_main(); 149 | } 150 | 151 | while (argc >= 1 && **argv == '-') 152 | { 153 | char *p; 154 | 155 | for (p = argv[0] + 1; *p; ++p) 156 | { 157 | if (*p == 'E') 158 | detach_char = -1; 159 | else if (*p == 'z') 160 | no_suspend = 1; 161 | else if (*p == 'e') 162 | { 163 | ++argv; --argc; 164 | if (argc < 1) 165 | { 166 | printf("%s: No escape character " 167 | "specified.\n", progname); 168 | printf("Try '%s --help' for more " 169 | "information.\n", progname); 170 | return 1; 171 | } 172 | if (argv[0][0] == '^' && argv[0][1]) 173 | { 174 | if (argv[0][1] == '?') 175 | detach_char = '\177'; 176 | else 177 | detach_char = argv[0][1] & 037; 178 | } 179 | else 180 | detach_char = argv[0][0]; 181 | break; 182 | } 183 | else if (*p == 'r') 184 | { 185 | ++argv; --argc; 186 | if (argc < 1) 187 | { 188 | printf("%s: No redraw method " 189 | "specified.\n", progname); 190 | printf("Try '%s --help' for more " 191 | "information.\n", progname); 192 | return 1; 193 | } 194 | if (strcmp(argv[0], "none") == 0) 195 | redraw_method = REDRAW_NONE; 196 | else if (strcmp(argv[0], "ctrl_l") == 0) 197 | redraw_method = REDRAW_CTRL_L; 198 | else if (strcmp(argv[0], "winch") == 0) 199 | redraw_method = REDRAW_WINCH; 200 | else 201 | { 202 | printf("%s: Invalid redraw method " 203 | "specified.\n", progname); 204 | printf("Try '%s --help' for more " 205 | "information.\n", progname); 206 | return 1; 207 | } 208 | break; 209 | } 210 | else 211 | { 212 | printf("%s: Invalid option '-%c'\n", 213 | progname, *p); 214 | printf("Try '%s --help' for more information.\n", 215 | progname); 216 | return 1; 217 | } 218 | } 219 | ++argv; --argc; 220 | } 221 | 222 | if (mode != 'a' && argc < 1) 223 | { 224 | printf("%s: No command was specified.\n", progname); 225 | printf("Try '%s --help' for more information.\n", 226 | progname); 227 | return 1; 228 | } 229 | 230 | /* Save the original terminal settings. */ 231 | if (tcgetattr(0, &orig_term) < 0) 232 | { 233 | memset(&orig_term, 0, sizeof(struct termios)); 234 | dont_have_tty = 1; 235 | } 236 | 237 | if (dont_have_tty && mode != 'n' && mode != 'N') 238 | { 239 | printf("%s: Attaching to a session requires a terminal.\n", 240 | progname); 241 | return 1; 242 | } 243 | 244 | if (mode == 'a') 245 | { 246 | if (argc > 0) 247 | { 248 | printf("%s: Invalid number of arguments.\n", 249 | progname); 250 | printf("Try '%s --help' for more information.\n", 251 | progname); 252 | return 1; 253 | } 254 | return attach_main(0); 255 | } 256 | else if (mode == 'n') 257 | return master_main(argv, 0, 0); 258 | else if (mode == 'N') 259 | return master_main(argv, 0, 1); 260 | else if (mode == 'c') 261 | { 262 | if (master_main(argv, 1, 0) != 0) 263 | return 1; 264 | return attach_main(0); 265 | } 266 | else if (mode == 'A') 267 | { 268 | /* Try to attach first. If that doesn't work, create a new 269 | ** socket. */ 270 | if (attach_main(1) != 0) 271 | { 272 | if (errno == ECONNREFUSED || errno == ENOENT) 273 | { 274 | if (errno == ECONNREFUSED) 275 | unlink(sockname); 276 | if (master_main(argv, 1, 0) != 0) 277 | return 1; 278 | } 279 | return attach_main(0); 280 | } 281 | } 282 | return 0; 283 | } 284 | -------------------------------------------------------------------------------- /master.c: -------------------------------------------------------------------------------- 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, see . 17 | */ 18 | #include "dtach.h" 19 | 20 | /* The pty struct - The pty information is stored here. */ 21 | struct pty 22 | { 23 | /* File descriptor of the pty */ 24 | int fd; 25 | #ifdef BROKEN_MASTER 26 | /* File descriptor of the slave side of the pty. For broken systems. */ 27 | int slave; 28 | #endif 29 | /* Process id of the child. */ 30 | pid_t pid; 31 | /* The terminal parameters of the pty. Old and new for comparision 32 | ** purposes. */ 33 | struct termios term; 34 | /* The current window size of the pty. */ 35 | struct winsize ws; 36 | }; 37 | 38 | /* A connected client */ 39 | struct client 40 | { 41 | /* The next client in the linked list. */ 42 | struct client *next; 43 | /* The previous client in the linked list. */ 44 | struct client **pprev; 45 | /* File descriptor of the client. */ 46 | int fd; 47 | /* Whether or not the client is attached. */ 48 | int attached; 49 | }; 50 | 51 | /* The list of connected clients. */ 52 | static struct client *clients; 53 | /* The pseudo-terminal created for the child process. */ 54 | static struct pty the_pty; 55 | 56 | #ifndef HAVE_FORKPTY 57 | pid_t forkpty(int *amaster, char *name, struct termios *termp, 58 | struct winsize *winp); 59 | #endif 60 | 61 | /* Unlink the socket */ 62 | static void 63 | unlink_socket(void) 64 | { 65 | unlink(sockname); 66 | } 67 | 68 | /* Signal */ 69 | static RETSIGTYPE 70 | die(int sig) 71 | { 72 | /* Well, the child died. */ 73 | if (sig == SIGCHLD) 74 | { 75 | #ifdef BROKEN_MASTER 76 | /* Damn you Solaris! */ 77 | close(the_pty.fd); 78 | #endif 79 | return; 80 | } 81 | exit(1); 82 | } 83 | 84 | /* Sets a file descriptor to non-blocking mode. */ 85 | static int 86 | setnonblocking(int fd) 87 | { 88 | int flags; 89 | 90 | #if defined(O_NONBLOCK) 91 | flags = fcntl(fd, F_GETFL); 92 | if (flags < 0 || fcntl(fd, F_SETFL, flags | O_NONBLOCK) < 0) 93 | return -1; 94 | return 0; 95 | #elif defined(FIONBIO) 96 | flags = 1; 97 | if (ioctl(fd, FIONBIO, &flags) < 0) 98 | return -1; 99 | return 0; 100 | #else 101 | #warning Do not know how to set non-blocking mode. 102 | return 0; 103 | #endif 104 | } 105 | 106 | /* Initialize the pty structure. */ 107 | static int 108 | init_pty(char **argv, int statusfd) 109 | { 110 | /* Use the original terminal's settings. We don't have to set the 111 | ** window size here, because the attacher will send it in a packet. */ 112 | the_pty.term = orig_term; 113 | memset(&the_pty.ws, 0, sizeof(struct winsize)); 114 | 115 | /* Create the pty process */ 116 | if (!dont_have_tty) 117 | the_pty.pid = forkpty(&the_pty.fd, NULL, &the_pty.term, NULL); 118 | else 119 | the_pty.pid = forkpty(&the_pty.fd, NULL, NULL, NULL); 120 | if (the_pty.pid < 0) 121 | return -1; 122 | else if (the_pty.pid == 0) 123 | { 124 | /* Child.. Execute the program. */ 125 | execvp(*argv, argv); 126 | 127 | /* Report the error to statusfd if we can, or stdout if we 128 | ** can't. */ 129 | if (statusfd != -1) 130 | dup2(statusfd, 1); 131 | else 132 | printf(EOS "\r\n"); 133 | 134 | printf("%s: could not execute %s: %s\r\n", progname, 135 | *argv, strerror(errno)); 136 | fflush(stdout); 137 | _exit(127); 138 | } 139 | /* Parent.. Finish up and return */ 140 | #ifdef BROKEN_MASTER 141 | { 142 | char *buf; 143 | 144 | buf = ptsname(the_pty.fd); 145 | the_pty.slave = open(buf, O_RDWR|O_NOCTTY); 146 | } 147 | #endif 148 | return 0; 149 | } 150 | 151 | /* Send a signal to the slave side of a pseudo-terminal. */ 152 | static void 153 | killpty(struct pty *pty, int sig) 154 | { 155 | pid_t pgrp = -1; 156 | 157 | #ifdef TIOCSIGNAL 158 | if (ioctl(pty->fd, TIOCSIGNAL, sig) >= 0) 159 | return; 160 | #endif 161 | #ifdef TIOCSIG 162 | if (ioctl(pty->fd, TIOCSIG, sig) >= 0) 163 | return; 164 | #endif 165 | #ifdef TIOCGPGRP 166 | #ifdef BROKEN_MASTER 167 | if (ioctl(pty->slave, TIOCGPGRP, &pgrp) >= 0 && pgrp != -1 && 168 | kill(-pgrp, sig) >= 0) 169 | return; 170 | #endif 171 | if (ioctl(pty->fd, TIOCGPGRP, &pgrp) >= 0 && pgrp != -1 && 172 | kill(-pgrp, sig) >= 0) 173 | return; 174 | #endif 175 | 176 | /* Fallback using the child's pid. */ 177 | kill(-pty->pid, sig); 178 | } 179 | 180 | /* Creates a new unix domain socket. */ 181 | static int 182 | create_socket(char *name) 183 | { 184 | int s; 185 | struct sockaddr_un sockun; 186 | 187 | if (strlen(name) > sizeof(sockun.sun_path) - 1) 188 | { 189 | errno = ENAMETOOLONG; 190 | return -1; 191 | } 192 | 193 | s = socket(PF_UNIX, SOCK_STREAM, 0); 194 | if (s < 0) 195 | return -1; 196 | sockun.sun_family = AF_UNIX; 197 | strcpy(sockun.sun_path, name); 198 | if (bind(s, (struct sockaddr*)&sockun, sizeof(sockun)) < 0) 199 | { 200 | close(s); 201 | return -1; 202 | } 203 | if (listen(s, 128) < 0) 204 | { 205 | close(s); 206 | return -1; 207 | } 208 | if (setnonblocking(s) < 0) 209 | { 210 | close(s); 211 | return -1; 212 | } 213 | /* chmod it to prevent any suprises */ 214 | if (chmod(name, 0600) < 0) 215 | { 216 | close(s); 217 | return -1; 218 | } 219 | return s; 220 | } 221 | 222 | /* Update the modes on the socket. */ 223 | static void 224 | update_socket_modes(int exec) 225 | { 226 | struct stat st; 227 | mode_t newmode; 228 | 229 | if (stat(sockname, &st) < 0) 230 | return; 231 | 232 | if (exec) 233 | newmode = st.st_mode | S_IXUSR; 234 | else 235 | newmode = st.st_mode & ~S_IXUSR; 236 | 237 | if (st.st_mode != newmode) 238 | chmod(sockname, newmode); 239 | } 240 | 241 | /* Process activity on the pty - Input and terminal changes are sent out to 242 | ** the attached clients. If the pty goes away, we die. */ 243 | static void 244 | pty_activity(int s) 245 | { 246 | unsigned char buf[BUFSIZE]; 247 | ssize_t len; 248 | struct client *p; 249 | fd_set readfds, writefds; 250 | int highest_fd, nclients; 251 | 252 | /* Read the pty activity */ 253 | len = read(the_pty.fd, buf, sizeof(buf)); 254 | 255 | /* Error -> die */ 256 | if (len <= 0) 257 | exit(1); 258 | 259 | #ifdef BROKEN_MASTER 260 | /* Get the current terminal settings. */ 261 | if (tcgetattr(the_pty.slave, &the_pty.term) < 0) 262 | exit(1); 263 | #else 264 | /* Get the current terminal settings. */ 265 | if (tcgetattr(the_pty.fd, &the_pty.term) < 0) 266 | exit(1); 267 | #endif 268 | 269 | top: 270 | /* 271 | ** Wait until at least one client is writable. Also wait on the control 272 | ** socket in case a new client tries to connect. 273 | */ 274 | FD_ZERO(&readfds); 275 | FD_ZERO(&writefds); 276 | FD_SET(s, &readfds); 277 | highest_fd = s; 278 | for (p = clients, nclients = 0; p; p = p->next) 279 | { 280 | if (!p->attached) 281 | continue; 282 | FD_SET(p->fd, &writefds); 283 | if (p->fd > highest_fd) 284 | highest_fd = p->fd; 285 | nclients++; 286 | } 287 | if (nclients == 0) 288 | return; 289 | if (select(highest_fd + 1, &readfds, &writefds, NULL, NULL) < 0) 290 | return; 291 | 292 | /* Send the data out to the clients. */ 293 | for (p = clients, nclients = 0; p; p = p->next) 294 | { 295 | ssize_t written; 296 | 297 | if (!FD_ISSET(p->fd, &writefds)) 298 | continue; 299 | 300 | written = 0; 301 | while (written < len) 302 | { 303 | ssize_t n = write(p->fd, buf + written, len - written); 304 | 305 | if (n > 0) 306 | { 307 | written += n; 308 | continue; 309 | } 310 | else if (n < 0 && errno == EINTR) 311 | continue; 312 | else if (n < 0 && errno != EAGAIN) 313 | nclients = -1; 314 | break; 315 | } 316 | if (nclients != -1 && written == len) 317 | nclients++; 318 | } 319 | 320 | /* Try again if nothing happened. */ 321 | if (!FD_ISSET(s, &readfds) && nclients == 0) 322 | goto top; 323 | } 324 | 325 | /* Process activity on the control socket */ 326 | static void 327 | control_activity(int s) 328 | { 329 | int fd; 330 | struct client *p; 331 | 332 | /* Accept the new client and link it in. */ 333 | fd = accept(s, NULL, NULL); 334 | if (fd < 0) 335 | return; 336 | else if (setnonblocking(fd) < 0) 337 | { 338 | close(fd); 339 | return; 340 | } 341 | 342 | /* Link it in. */ 343 | p = malloc(sizeof(struct client)); 344 | p->fd = fd; 345 | p->attached = 0; 346 | p->pprev = &clients; 347 | p->next = *(p->pprev); 348 | if (p->next) 349 | p->next->pprev = &p->next; 350 | *(p->pprev) = p; 351 | } 352 | 353 | /* Process activity from a client. */ 354 | static void 355 | client_activity(struct client *p) 356 | { 357 | ssize_t len; 358 | struct packet pkt; 359 | 360 | /* Read the activity. */ 361 | len = read(p->fd, &pkt, sizeof(struct packet)); 362 | if (len < 0 && (errno == EAGAIN || errno == EINTR)) 363 | return; 364 | 365 | /* Close the client on an error. */ 366 | if (len <= 0) 367 | { 368 | close(p->fd); 369 | if (p->next) 370 | p->next->pprev = p->pprev; 371 | *(p->pprev) = p->next; 372 | free(p); 373 | return; 374 | } 375 | 376 | /* Push out data to the program. */ 377 | if (pkt.type == MSG_PUSH) 378 | { 379 | if (pkt.len <= sizeof(pkt.u.buf)) 380 | write(the_pty.fd, pkt.u.buf, pkt.len); 381 | } 382 | 383 | /* Attach or detach from the program. */ 384 | else if (pkt.type == MSG_ATTACH) 385 | p->attached = 1; 386 | else if (pkt.type == MSG_DETACH) 387 | p->attached = 0; 388 | 389 | /* Window size change request, without a forced redraw. */ 390 | else if (pkt.type == MSG_WINCH) 391 | { 392 | the_pty.ws = pkt.u.ws; 393 | ioctl(the_pty.fd, TIOCSWINSZ, &the_pty.ws); 394 | } 395 | 396 | /* Force a redraw using a particular method. */ 397 | else if (pkt.type == MSG_REDRAW) 398 | { 399 | int method = pkt.len; 400 | 401 | /* If the client didn't specify a particular method, use 402 | ** whatever we had on startup. */ 403 | if (method == REDRAW_UNSPEC) 404 | method = redraw_method; 405 | if (method == REDRAW_NONE) 406 | return; 407 | 408 | /* Set the window size. */ 409 | the_pty.ws = pkt.u.ws; 410 | ioctl(the_pty.fd, TIOCSWINSZ, &the_pty.ws); 411 | 412 | /* Send a ^L character if the terminal is in no-echo and 413 | ** character-at-a-time mode. */ 414 | if (method == REDRAW_CTRL_L) 415 | { 416 | char c = '\f'; 417 | 418 | if (((the_pty.term.c_lflag & (ECHO|ICANON)) == 0) && 419 | (the_pty.term.c_cc[VMIN] == 1)) 420 | { 421 | write(the_pty.fd, &c, 1); 422 | } 423 | } 424 | /* Send a WINCH signal to the program. */ 425 | else if (method == REDRAW_WINCH) 426 | { 427 | killpty(&the_pty, SIGWINCH); 428 | } 429 | } 430 | } 431 | 432 | /* The master process - It watches over the pty process and the attached */ 433 | /* clients. */ 434 | static void 435 | master_process(int s, char **argv, int waitattach, int statusfd) 436 | { 437 | struct client *p, *next; 438 | fd_set readfds; 439 | int highest_fd; 440 | int nullfd; 441 | 442 | int has_attached_client = 0; 443 | 444 | /* Okay, disassociate ourselves from the original terminal, as we 445 | ** don't care what happens to it. */ 446 | setsid(); 447 | 448 | /* Set a trap to unlink the socket when we die. */ 449 | atexit(unlink_socket); 450 | 451 | /* Create a pty in which the process is running. */ 452 | signal(SIGCHLD, die); 453 | if (init_pty(argv, statusfd) < 0) 454 | { 455 | if (statusfd != -1) 456 | dup2(statusfd, 1); 457 | if (errno == ENOENT) 458 | printf("%s: Could not find a pty.\n", progname); 459 | else 460 | printf("%s: init_pty: %s\n", progname, strerror(errno)); 461 | exit(1); 462 | } 463 | 464 | /* Set up some signals. */ 465 | signal(SIGPIPE, SIG_IGN); 466 | signal(SIGXFSZ, SIG_IGN); 467 | signal(SIGHUP, SIG_IGN); 468 | signal(SIGTTIN, SIG_IGN); 469 | signal(SIGTTOU, SIG_IGN); 470 | signal(SIGINT, die); 471 | signal(SIGTERM, die); 472 | 473 | /* Close statusfd, since we don't need it anymore. */ 474 | if (statusfd != -1) 475 | close(statusfd); 476 | 477 | /* Make sure stdin/stdout/stderr point to /dev/null. We are now a 478 | ** daemon. */ 479 | nullfd = open("/dev/null", O_RDWR); 480 | dup2(nullfd, 0); 481 | dup2(nullfd, 1); 482 | dup2(nullfd, 2); 483 | if (nullfd > 2) 484 | close(nullfd); 485 | 486 | /* Loop forever. */ 487 | while (1) 488 | { 489 | int new_has_attached_client = 0; 490 | 491 | /* Re-initialize the file descriptor set for select. */ 492 | FD_ZERO(&readfds); 493 | FD_SET(s, &readfds); 494 | highest_fd = s; 495 | 496 | /* 497 | ** When waitattach is set, wait until the client attaches 498 | ** before trying to read from the pty. 499 | */ 500 | if (waitattach) 501 | { 502 | if (clients && clients->attached) 503 | waitattach = 0; 504 | } 505 | else 506 | { 507 | FD_SET(the_pty.fd, &readfds); 508 | if (the_pty.fd > highest_fd) 509 | highest_fd = the_pty.fd; 510 | } 511 | 512 | for (p = clients; p; p = p->next) 513 | { 514 | FD_SET(p->fd, &readfds); 515 | if (p->fd > highest_fd) 516 | highest_fd = p->fd; 517 | 518 | if (p->attached) 519 | new_has_attached_client = 1; 520 | } 521 | 522 | /* chmod the socket if necessary. */ 523 | if (has_attached_client != new_has_attached_client) 524 | { 525 | update_socket_modes(new_has_attached_client); 526 | has_attached_client = new_has_attached_client; 527 | } 528 | 529 | /* Wait for something to happen. */ 530 | if (select(highest_fd + 1, &readfds, NULL, NULL, NULL) < 0) 531 | { 532 | if (errno == EINTR || errno == EAGAIN) 533 | continue; 534 | exit(1); 535 | } 536 | 537 | /* New client? */ 538 | if (FD_ISSET(s, &readfds)) 539 | control_activity(s); 540 | /* Activity on a client? */ 541 | for (p = clients; p; p = next) 542 | { 543 | next = p->next; 544 | if (FD_ISSET(p->fd, &readfds)) 545 | client_activity(p); 546 | } 547 | /* pty activity? */ 548 | if (FD_ISSET(the_pty.fd, &readfds)) 549 | pty_activity(s); 550 | } 551 | } 552 | 553 | int 554 | master_main(char **argv, int waitattach, int dontfork) 555 | { 556 | int fd[2] = {-1, -1}; 557 | int s; 558 | pid_t pid; 559 | 560 | /* Use a default redraw method if one hasn't been specified yet. */ 561 | if (redraw_method == REDRAW_UNSPEC) 562 | redraw_method = REDRAW_CTRL_L; 563 | 564 | /* Create the unix domain socket. */ 565 | s = create_socket(sockname); 566 | if (s < 0 && errno == ENAMETOOLONG) 567 | { 568 | char *slash = strrchr(sockname, '/'); 569 | 570 | /* Try to shorten the socket's path name by using chdir. */ 571 | if (slash) 572 | { 573 | int dirfd = open(".", O_RDONLY); 574 | 575 | if (dirfd >= 0) 576 | { 577 | *slash = '\0'; 578 | if (chdir(sockname) >= 0) 579 | { 580 | s = create_socket(slash + 1); 581 | fchdir(dirfd); 582 | } 583 | *slash = '/'; 584 | close(dirfd); 585 | } 586 | } 587 | } 588 | if (s < 0) 589 | { 590 | printf("%s: %s: %s\n", progname, sockname, strerror(errno)); 591 | return 1; 592 | } 593 | 594 | #if defined(F_SETFD) && defined(FD_CLOEXEC) 595 | fcntl(s, F_SETFD, FD_CLOEXEC); 596 | 597 | /* If FD_CLOEXEC works, create a pipe and use it to report any errors 598 | ** that occur while trying to execute the program. */ 599 | if (dontfork) 600 | { 601 | fd[1] = dup(2); 602 | if (fcntl(fd[1], F_SETFD, FD_CLOEXEC) < 0) 603 | { 604 | close(fd[1]); 605 | fd[1] = -1; 606 | } 607 | } 608 | else if (pipe(fd) >= 0) 609 | { 610 | if (fcntl(fd[0], F_SETFD, FD_CLOEXEC) < 0 || 611 | fcntl(fd[1], F_SETFD, FD_CLOEXEC) < 0) 612 | { 613 | close(fd[0]); 614 | close(fd[1]); 615 | fd[0] = fd[1] = -1; 616 | } 617 | } 618 | #endif 619 | 620 | if (dontfork) 621 | { 622 | master_process(s, argv, waitattach, fd[1]); 623 | return 0; 624 | } 625 | 626 | /* Fork off so we can daemonize and such */ 627 | pid = fork(); 628 | if (pid < 0) 629 | { 630 | printf("%s: fork: %s\n", progname, strerror(errno)); 631 | unlink_socket(); 632 | return 1; 633 | } 634 | else if (pid == 0) 635 | { 636 | /* Child - this becomes the master */ 637 | if (fd[0] != -1) 638 | close(fd[0]); 639 | master_process(s, argv, waitattach, fd[1]); 640 | return 0; 641 | } 642 | /* Parent - just return. */ 643 | 644 | #if defined(F_SETFD) && defined(FD_CLOEXEC) 645 | /* Check if an error occurred while trying to execute the program. */ 646 | if (fd[0] != -1) 647 | { 648 | char buf[1024]; 649 | ssize_t len; 650 | 651 | close(fd[1]); 652 | len = read(fd[0], buf, sizeof(buf)); 653 | if (len > 0) 654 | { 655 | write(2, buf, len); 656 | kill(pid, SIGTERM); 657 | return 1; 658 | } 659 | close(fd[0]); 660 | } 661 | #endif 662 | close(s); 663 | return 0; 664 | } 665 | 666 | /* BSDish functions for systems that don't have them. */ 667 | #ifndef HAVE_OPENPTY 668 | #define HAVE_OPENPTY 669 | /* openpty: Use /dev/ptmx and Unix98 if we have it. */ 670 | #if defined(HAVE_PTSNAME) && defined(HAVE_GRANTPT) && defined(HAVE_UNLOCKPT) 671 | int 672 | openpty(int *amaster, int *aslave, char *name, struct termios *termp, 673 | struct winsize *winp) 674 | { 675 | int master, slave; 676 | char *buf; 677 | 678 | #ifdef _AIX 679 | master = open("/dev/ptc", O_RDWR|O_NOCTTY); 680 | if (master < 0) 681 | return -1; 682 | buf = ttyname(master); 683 | if (!buf) 684 | return -1; 685 | 686 | slave = open(buf, O_RDWR|O_NOCTTY); 687 | if (slave < 0) 688 | return -1; 689 | #else 690 | master = open("/dev/ptmx", O_RDWR); 691 | if (master < 0) 692 | return -1; 693 | if (grantpt(master) < 0) 694 | return -1; 695 | if (unlockpt(master) < 0) 696 | return -1; 697 | buf = ptsname(master); 698 | if (!buf) 699 | return -1; 700 | 701 | slave = open(buf, O_RDWR|O_NOCTTY); 702 | if (slave < 0) 703 | return -1; 704 | 705 | #ifdef I_PUSH 706 | if (ioctl(slave, I_PUSH, "ptem") < 0) 707 | return -1; 708 | if (ioctl(slave, I_PUSH, "ldterm") < 0) 709 | return -1; 710 | #endif 711 | #endif 712 | 713 | *amaster = master; 714 | *aslave = slave; 715 | if (name) 716 | strcpy(name, buf); 717 | if (termp) 718 | tcsetattr(slave, TCSAFLUSH, termp); 719 | if (winp) 720 | ioctl(slave, TIOCSWINSZ, winp); 721 | return 0; 722 | } 723 | #else 724 | #error Do not know how to define openpty. 725 | #endif 726 | #endif 727 | 728 | #ifndef HAVE_FORKPTY 729 | #if defined(HAVE_OPENPTY) 730 | pid_t 731 | forkpty(int *amaster, char *name, struct termios *termp, 732 | struct winsize *winp) 733 | { 734 | pid_t pid; 735 | int master, slave; 736 | 737 | if (openpty(&master, &slave, name, termp, winp) < 0) 738 | return -1; 739 | *amaster = master; 740 | 741 | /* Fork off... */ 742 | pid = fork(); 743 | if (pid < 0) 744 | return -1; 745 | else if (pid == 0) 746 | { 747 | char *buf; 748 | int fd; 749 | 750 | setsid(); 751 | #ifdef TIOCSCTTY 752 | buf = NULL; 753 | if (ioctl(slave, TIOCSCTTY, NULL) < 0) 754 | _exit(1); 755 | #elif defined(_AIX) 756 | fd = open("/dev/tty", O_RDWR|O_NOCTTY); 757 | if (fd >= 0) 758 | { 759 | ioctl(fd, TIOCNOTTY, NULL); 760 | close(fd); 761 | } 762 | 763 | buf = ttyname(master); 764 | fd = open(buf, O_RDWR); 765 | close(fd); 766 | 767 | fd = open("/dev/tty", O_WRONLY); 768 | if (fd < 0) 769 | _exit(1); 770 | close(fd); 771 | 772 | if (termp && tcsetattr(slave, TCSAFLUSH, termp) == -1) 773 | _exit(1); 774 | if (ioctl(slave, TIOCSWINSZ, winp) == -1) 775 | _exit(1); 776 | #else 777 | buf = ptsname(master); 778 | fd = open(buf, O_RDWR); 779 | close(fd); 780 | #endif 781 | dup2(slave, 0); 782 | dup2(slave, 1); 783 | dup2(slave, 2); 784 | 785 | if (slave > 2) 786 | close(slave); 787 | close(master); 788 | return 0; 789 | } 790 | else 791 | { 792 | close(slave); 793 | return pid; 794 | } 795 | } 796 | #else 797 | #error Do not know how to define forkpty. 798 | #endif 799 | #endif 800 | --------------------------------------------------------------------------------