├── .gitignore ├── CHANGES ├── INSTALL ├── Makefile.in ├── README ├── autossh.1 ├── autossh.c ├── autossh.host ├── autossh.spec ├── config.h.in ├── configure ├── configure.ac ├── daemon.h ├── fakepoll.h ├── rscreen └── wrapper /.gitignore: -------------------------------------------------------------------------------- 1 | /*.swp 2 | /config.h 3 | /config.log 4 | /config.status 5 | /Makefile 6 | /autossh 7 | /autossh.o 8 | -------------------------------------------------------------------------------- /CHANGES: -------------------------------------------------------------------------------- 1 | 2 | Version 1.4c 3 | 4 | - updated option string up to OpenSSH 5.6 5 | - when using -f, set gate_time to 0; the assumption is that 6 | it is being used for infrastructure (i.e. in a system startup), 7 | has been tested, and bombing out just because the remote end is 8 | not up is not the desired behaviour. 9 | - add patch from Thorsten Glazer for configure, fixes misplaced 10 | parenthesis and check for __progname. 11 | 12 | Version 1.4b 13 | 14 | - add AUTOSSH_MAXLIFETIME (patch from Steven Clark) 15 | - include configure.ac in package 16 | - fix poll flags so not checking for writable when write done (patch 17 | from John Grahor) 18 | - compile time TOUCH_PIDFILE option to touch pid file on connection test. 19 | 20 | Version 1.4a 21 | 22 | - fix up pid file generation (Xander Hudson) 23 | - fix up Makefile.in (Andrew Schulman) 24 | 25 | Version 1.4 26 | 27 | - initialise ep in main() to avoid warning (Marcelo Goes) 28 | - fix where cast to int happens when calculating next time to poll 29 | connection (Omer Erdem Demir) 30 | - fix '--' use so can pass -M to autossh for session multiplexing 31 | - fix use of strcpy for overlapping copy in strip_arg (Filippo Giunchedi). 32 | - add basic GNU autoconf support and drop per-platform Makefiles (Andre Lucas) 33 | - pid file support (Ben Vitale) 34 | - arbitrary messages in echo string (Ron Yorston) 35 | 36 | Version 1.3 37 | 38 | - fix AUTOSSH_DEBUG for Solaris and AIX 39 | - fix attempt to free() static storage (affected platforms without 40 | getaddrinfo() -- mostly cygwin) (Andrew Schulman) 41 | - change test and placement of typedef for socklen_t under OS X; new 42 | OS X defines it 43 | - add ability to signal autossh to kill and restart ssh child, using 44 | SIGUSR1 45 | - add hostname to monitor message (Ron Yorston) 46 | - check on looping on poll() where connection has been lost (spinning 47 | and high CPU) 48 | - fix bug where length argument to accept() was not initialised 49 | - fix arg parsing bug where stripping -f from arguments would strip 50 | from a parameter to the argument: e.g -L8808:foo:80 would become 51 | -L8808:oo:80 (pointed out by Eric Larson) 52 | - pull out r/w loops in conn_test() into separate functions, makes 53 | logic more apparent 54 | - add echo port support: have the remote server use the inetd 55 | echo service to echo our test string back to us. Or use some other 56 | echo service. Idea and patch from Ron Yorston. This makes it 1.3. 57 | - remove bad strcpy() (left over from some testing?) thanks to Ron 58 | Yorston, change to memset read buffer to all zeros 59 | - fix ssh args when AUTOSSH_PORT=0 is used to turn monitor loop off 60 | (Karl Berry) 61 | - add more descriptive usage output, by popular request 62 | 63 | Version 1.2g 64 | 65 | - add AUTOSSH_NTSERVICE (Andrew Schulman) 66 | - fix bad calculation for seconds left in poll time (again from 67 | Andrew Schulman) 68 | - from Andrew Schulman: add support for older networking and cygwin 69 | - add AUTOSSH_MAXSTART (from Hugo Haas) 70 | - loop around waitpid() in ssh_kill() in case interrupted (thanks to 71 | Jens Krabbenhoeft) 72 | - update ssh argument string 73 | - move openlog above port error checking 74 | - handle environment variables and -M arg being set to the 75 | empty string (reported by Dan Christensen via Filippo Giunche) 76 | - add some rudimetary auto-adjust to the network timeouts for 77 | low poll times. So by default 15secs on each of accept() and 78 | poll()to deal with high-latency connections, but as poll time 79 | falls below 30secs, then adjust timeouts down as well. 80 | - adjust division in grace_time() to allow for low poll time 81 | - don't call shutdown() and close() on invalid socket () 82 | (found by Dmitry V. Levin) 83 | 84 | Version 1.2f 85 | 86 | - by popular request, support the -f flag by doing the fork/background 87 | ourselves, and not passing the flag to ssh (thanks to Dan Christensen 88 | for prodding me into it) 89 | - change timeout to from 5000 to 15000 ms to deal with reported issues 90 | with high-latency links; unused var removal; and man page typo (thanks 91 | to Michael Shields) 92 | 93 | Version 1.2e 94 | 95 | - check for attempt to use -f flag; exit as error 96 | - if AUTOSSH_GATETIME is 0, then disable any of the startup 97 | failure bailouts, and just retry 98 | - set SO_REUSEADDR on listening socket (Jedi One's suggestion) 99 | - show port number in some error messages 100 | - note in man page and README about -M 0 turning port monitoring off 101 | - remove duplicate include of sys/socket.h 102 | 103 | Version 1.2d 104 | 105 | - AIX support (thanks to Stefan Rodenstein) 106 | - fix argument rewrite bug when using AUTOSSH_PORT (thanks 107 | to Peter Williams) 108 | - log pid of ssh child just after fork 109 | 110 | Version 1.2c 111 | 112 | - use Marcus Friedl's suggestion to simply connect to "127.0.0.1" 113 | rather than "localhost", as not really using IPv6 anyway. And this 114 | gets rid of annoying ipv6 localhost message on Solaris at least. 115 | - support for MacOS X using Greg Parker's fakepoll. 116 | 117 | Version 1.2b 118 | 119 | (Thanks to Simon Easter for bug reports and trials) 120 | 121 | - fix file descriptor leak 122 | - setsockopt()'s don't work for Solaris either. Give up on them. 123 | - set close-on-exec for read socket so will be closed when 124 | ssh executed 125 | - do shutdown() before close() (paranoia) 126 | - close read socket before exit 127 | - pull read socket open and close up out of ssh_run() 128 | - cosmetic changes to some loops, get rid of newlines in some 129 | errlog strings. 130 | 131 | Version 1.2a 132 | 133 | - setsockopt() will not set timeouts on socket read/write ops 134 | under Linux 2.2 kernels (and earlier, I presume). So unless 135 | someone tells me we really need them, I've #ifdef'd the 136 | setsockopt()s out for Linux. 137 | - check value of fd returned by accept(). 138 | - Oh, there's a man page now. Unfortunately, it doesn't render 139 | for Solaris. Sorry, Solaris users are still stuck with the 140 | README. 141 | 142 | Version 1.2 143 | 144 | - Major restructuring of code, mostly to support the change to 145 | a loop of forwarded ports to test the network connection. 146 | - Incremental back off on rate of connection attempts when 147 | there are rapid failures to establish/maintain a connection. 148 | 149 | Version 1.1b 150 | 151 | - change handling of ssh exit status 152 | - introduce "starting gate" time: ssh process that dies too 153 | early is deemed not to have made it out of the starting 154 | gate, and autossh exits. 155 | 156 | Version 1.1a 157 | 158 | - fix race after failure to exec ssh where parent will 159 | loop attempting to restart 160 | - add -V version flag 161 | - use strtoul() for environment option parsing 162 | - drop useless intermediate function ssh_setwatch() 163 | 164 | Version 1.1 165 | 166 | Initial release 167 | -------------------------------------------------------------------------------- /INSTALL: -------------------------------------------------------------------------------- 1 | Copyright (C) 1994, 1995, 1996, 1999, 2000, 2001, 2002 Free Software 2 | Foundation, Inc. 3 | 4 | This file is free documentation; the Free Software Foundation gives 5 | unlimited permission to copy, distribute and modify it. 6 | 7 | Basic Installation 8 | ================== 9 | 10 | These are generic installation instructions. 11 | 12 | The `configure' shell script attempts to guess correct values for 13 | various system-dependent variables used during compilation. It uses 14 | those values to create a `Makefile' in each directory of the package. 15 | It may also create one or more `.h' files containing system-dependent 16 | definitions. Finally, it creates a shell script `config.status' that 17 | you can run in the future to recreate the current configuration, and a 18 | file `config.log' containing compiler output (useful mainly for 19 | debugging `configure'). 20 | 21 | It can also use an optional file (typically called `config.cache' 22 | and enabled with `--cache-file=config.cache' or simply `-C') that saves 23 | the results of its tests to speed up reconfiguring. (Caching is 24 | disabled by default to prevent problems with accidental use of stale 25 | cache files.) 26 | 27 | If you need to do unusual things to compile the package, please try 28 | to figure out how `configure' could check whether to do them, and mail 29 | diffs or instructions to the address given in the `README' so they can 30 | be considered for the next release. If you are using the cache, and at 31 | some point `config.cache' contains results you don't want to keep, you 32 | may remove or edit it. 33 | 34 | The file `configure.ac' (or `configure.in') is used to create 35 | `configure' by a program called `autoconf'. You only need 36 | `configure.ac' if you want to change it or regenerate `configure' using 37 | a newer version of `autoconf'. 38 | 39 | The simplest way to compile this package is: 40 | 41 | 1. `cd' to the directory containing the package's source code and type 42 | `./configure' to configure the package for your system. If you're 43 | using `csh' on an old version of System V, you might need to type 44 | `sh ./configure' instead to prevent `csh' from trying to execute 45 | `configure' itself. 46 | 47 | Running `configure' takes awhile. While running, it prints some 48 | messages telling which features it is checking for. 49 | 50 | 2. Type `make' to compile the package. 51 | 52 | 3. Optionally, type `make check' to run any self-tests that come with 53 | the package. 54 | 55 | 4. Type `make install' to install the programs and any data files and 56 | documentation. 57 | 58 | 5. You can remove the program binaries and object files from the 59 | source code directory by typing `make clean'. To also remove the 60 | files that `configure' created (so you can compile the package for 61 | a different kind of computer), type `make distclean'. There is 62 | also a `make maintainer-clean' target, but that is intended mainly 63 | for the package's developers. If you use it, you may have to get 64 | all sorts of other programs in order to regenerate files that came 65 | with the distribution. 66 | 67 | Compilers and Options 68 | ===================== 69 | 70 | Some systems require unusual options for compilation or linking that 71 | the `configure' script does not know about. Run `./configure --help' 72 | for details on some of the pertinent environment variables. 73 | 74 | You can give `configure' initial values for configuration parameters 75 | by setting variables in the command line or in the environment. Here 76 | is an example: 77 | 78 | ./configure CC=c89 CFLAGS=-O2 LIBS=-lposix 79 | 80 | *Note Defining Variables::, for more details. 81 | 82 | Compiling For Multiple Architectures 83 | ==================================== 84 | 85 | You can compile the package for more than one kind of computer at the 86 | same time, by placing the object files for each architecture in their 87 | own directory. To do this, you must use a version of `make' that 88 | supports the `VPATH' variable, such as GNU `make'. `cd' to the 89 | directory where you want the object files and executables to go and run 90 | the `configure' script. `configure' automatically checks for the 91 | source code in the directory that `configure' is in and in `..'. 92 | 93 | If you have to use a `make' that does not support the `VPATH' 94 | variable, you have to compile the package for one architecture at a 95 | time in the source code directory. After you have installed the 96 | package for one architecture, use `make distclean' before reconfiguring 97 | for another architecture. 98 | 99 | Installation Names 100 | ================== 101 | 102 | By default, `make install' will install the package's files in 103 | `/usr/local/bin', `/usr/local/man', etc. You can specify an 104 | installation prefix other than `/usr/local' by giving `configure' the 105 | option `--prefix=PATH'. 106 | 107 | You can specify separate installation prefixes for 108 | architecture-specific files and architecture-independent files. If you 109 | give `configure' the option `--exec-prefix=PATH', the package will use 110 | PATH as the prefix for installing programs and libraries. 111 | Documentation and other data files will still use the regular prefix. 112 | 113 | In addition, if you use an unusual directory layout you can give 114 | options like `--bindir=PATH' to specify different values for particular 115 | kinds of files. Run `configure --help' for a list of the directories 116 | you can set and what kinds of files go in them. 117 | 118 | If the package supports it, you can cause programs to be installed 119 | with an extra prefix or suffix on their names by giving `configure' the 120 | option `--program-prefix=PREFIX' or `--program-suffix=SUFFIX'. 121 | 122 | Optional Features 123 | ================= 124 | 125 | Some packages pay attention to `--enable-FEATURE' options to 126 | `configure', where FEATURE indicates an optional part of the package. 127 | They may also pay attention to `--with-PACKAGE' options, where PACKAGE 128 | is something like `gnu-as' or `x' (for the X Window System). The 129 | `README' should mention any `--enable-' and `--with-' options that the 130 | package recognizes. 131 | 132 | For packages that use the X Window System, `configure' can usually 133 | find the X include and library files automatically, but if it doesn't, 134 | you can use the `configure' options `--x-includes=DIR' and 135 | `--x-libraries=DIR' to specify their locations. 136 | 137 | Specifying the System Type 138 | ========================== 139 | 140 | There may be some features `configure' cannot figure out 141 | automatically, but needs to determine by the type of machine the package 142 | will run on. Usually, assuming the package is built to be run on the 143 | _same_ architectures, `configure' can figure that out, but if it prints 144 | a message saying it cannot guess the machine type, give it the 145 | `--build=TYPE' option. TYPE can either be a short name for the system 146 | type, such as `sun4', or a canonical name which has the form: 147 | 148 | CPU-COMPANY-SYSTEM 149 | 150 | where SYSTEM can have one of these forms: 151 | 152 | OS KERNEL-OS 153 | 154 | See the file `config.sub' for the possible values of each field. If 155 | `config.sub' isn't included in this package, then this package doesn't 156 | need to know the machine type. 157 | 158 | If you are _building_ compiler tools for cross-compiling, you should 159 | use the `--target=TYPE' option to select the type of system they will 160 | produce code for. 161 | 162 | If you want to _use_ a cross compiler, that generates code for a 163 | platform different from the build platform, you should specify the 164 | "host" platform (i.e., that on which the generated programs will 165 | eventually be run) with `--host=TYPE'. 166 | 167 | Sharing Defaults 168 | ================ 169 | 170 | If you want to set default values for `configure' scripts to share, 171 | you can create a site shell script called `config.site' that gives 172 | default values for variables like `CC', `cache_file', and `prefix'. 173 | `configure' looks for `PREFIX/share/config.site' if it exists, then 174 | `PREFIX/etc/config.site' if it exists. Or, you can set the 175 | `CONFIG_SITE' environment variable to the location of the site script. 176 | A warning: not all `configure' scripts look for a site script. 177 | 178 | Defining Variables 179 | ================== 180 | 181 | Variables not defined in a site shell script can be set in the 182 | environment passed to `configure'. However, some packages may run 183 | configure again during the build, and the customized values of these 184 | variables may be lost. In order to avoid this problem, you should set 185 | them in the `configure' command line, using `VAR=value'. For example: 186 | 187 | ./configure CC=/usr/local2/bin/gcc 188 | 189 | will cause the specified gcc to be used as the C compiler (unless it is 190 | overridden in the site shell script). 191 | 192 | `configure' Invocation 193 | ====================== 194 | 195 | `configure' recognizes the following options to control how it 196 | operates. 197 | 198 | `--help' 199 | `-h' 200 | Print a summary of the options to `configure', and exit. 201 | 202 | `--version' 203 | `-V' 204 | Print the version of Autoconf used to generate the `configure' 205 | script, and exit. 206 | 207 | `--cache-file=FILE' 208 | Enable the cache: use and save the results of the tests in FILE, 209 | traditionally `config.cache'. FILE defaults to `/dev/null' to 210 | disable caching. 211 | 212 | `--config-cache' 213 | `-C' 214 | Alias for `--cache-file=config.cache'. 215 | 216 | `--quiet' 217 | `--silent' 218 | `-q' 219 | Do not print messages saying which checks are being made. To 220 | suppress all normal output, redirect it to `/dev/null' (any error 221 | messages will still be shown). 222 | 223 | `--srcdir=DIR' 224 | Look for the package's source code in directory DIR. Usually 225 | `configure' can determine that directory automatically. 226 | 227 | `configure' also accepts some other, not widely useful, options. Run 228 | `configure --help' for more details. 229 | 230 | -------------------------------------------------------------------------------- /Makefile.in: -------------------------------------------------------------------------------- 1 | # $Id: Makefile.in,v 1.5 2011/10/12 20:29:22 harding Exp $ 2 | # 3 | # @configure_input@ 4 | 5 | VER= 1.4c 6 | 7 | SSH= @path_ssh@ 8 | 9 | prefix= @prefix@ 10 | exec_prefix= @exec_prefix@ 11 | bindir= @bindir@ 12 | datadir= @datadir@ 13 | mandir= @mandir@ 14 | 15 | SRCDIR= @srcdir@ 16 | VPATH= @srcdir@ 17 | 18 | CC= @CC@ 19 | CFLAGS= @CFLAGS@ -DVER=\"$(VER)\" -DSSH_PATH=\"$(SSH)\" 20 | CPPFLAGS= @CPPFLAGS@ 21 | 22 | OFILES= autossh.o 23 | 24 | LD= @LD@ 25 | LDFLAGS= @LDFLAGS@ 26 | LIBS= @LIBS@ 27 | 28 | TARGET= autossh 29 | 30 | all: $(TARGET) 31 | 32 | 33 | $(TARGET): $(OFILES) 34 | $(CC) $(CPPFLAGS) -o $(TARGET) $(OFILES) $(LIBS) 35 | 36 | clean: 37 | - /bin/rm -f *.o *.a *.core *~ 38 | 39 | allclean: clean 40 | - /bin/rm -f $(TARGET) 41 | 42 | distclean: allclean 43 | - /bin/rm -f config.log config.cache config.status config.h 44 | - /bin/rm -rf autom4te.cache 45 | - /bin/rm -f Makefile 46 | 47 | install: $(TARGET) 48 | mkdir -p -m 755 $(bindir) 49 | mkdir -p -m 755 $(prefix)/share/doc/autossh 50 | mkdir -p -m 755 $(datadir)/examples/autossh 51 | mkdir -p -m 755 $(mandir)/man1 52 | cp $(TARGET) $(bindir) 53 | cp CHANGES README $(datadir)/doc/autossh 54 | cp autossh.host $(datadir)/examples/autossh 55 | cp rscreen $(datadir)/examples/autossh 56 | cp autossh.1 $(mandir)/man1 57 | chmod 755 $(bindir)/$(TARGET) 58 | chmod 644 $(datadir)/doc/autossh/CHANGES 59 | chmod 644 $(datadir)/doc/autossh/README 60 | chmod 644 $(datadir)/examples/autossh/autossh.host 61 | chmod 644 $(datadir)/examples/autossh/rscreen 62 | chmod 644 $(mandir)/man1/autossh.1 63 | -------------------------------------------------------------------------------- /README: -------------------------------------------------------------------------------- 1 | 2 | autossh Version 1.4 3 | ------------------- 4 | 5 | Building and Installing Autossh 6 | -------------------------------- 7 | 8 | With version 1.4, autossh now uses autoconf. So the build procedure 9 | is now the well-known: 10 | 11 | ./configure 12 | make 13 | make install 14 | 15 | Look at autossh.host for an example wrapper script. 16 | 17 | 18 | Usage 19 | ----- 20 | autossh -M [:echo_port] [-f] [SSH OPTIONS] 21 | 22 | Description 23 | ----------- 24 | 25 | autossh is a program to start a copy of ssh and monitor it, restarting 26 | it as necessary should it die or stop passing traffic. 27 | 28 | The original idea and the mechanism were from rstunnel (Reliable SSH 29 | Tunnel). With version 1.2 the method changed: autossh now uses ssh to 30 | construct a loop of ssh forwardings (one from local to remote, one 31 | from remote to local), and then sends test data that it expects to get 32 | back. (The idea is thanks to Terrence Martin.) 33 | 34 | With version 1.3, a new method is added (thanks to Ron Yorston): a 35 | port may be specified for a remote echo service that will echo back 36 | the test data. This avoids the congestion and the aggravation of 37 | making sure all the port numbers on the remote machine do not 38 | collide. The loop-of -forwardings method remains available for 39 | situations where using an echo service may not be possible. 40 | 41 | autossh has only three arguments of its own: 42 | 43 | -M [:echo_port], to specify the base monitoring port to use, or 44 | alternatively, to specify the monitoring port and echo service 45 | port to use. 46 | 47 | When no echo service port is specified, this port and the port 48 | immediately above it (port# + 1) should be something nothing 49 | else is using. autossh will send test data on the base monitoring 50 | port, and receive it back on the port above. For example, if you 51 | specify "-M 20000", autossh will set up forwards so that it can 52 | send data on port 20000 and receive it back on 20001. 53 | 54 | Alternatively a port for a remote echo service may be 55 | specified. This should be port 7 if you wish to use the 56 | standard inetd echo service. When an echo port is specified, 57 | only the specified monitor port is used, and it carries the 58 | monitor message in both directions. 59 | 60 | Many people disable the echo service, or even disable inetd, 61 | so check that this service is available on the remote 62 | machine. Some operating systems allow one to specify that the 63 | service only listen on the localhost (loopback interface), 64 | which would suffice for this use. 65 | 66 | The echo service may also be something more complicated: 67 | perhaps a daemon that monitors a group of ssh tunnels. 68 | 69 | -M 0 will turn the monitoring off, and autossh will only 70 | restart ssh on ssh exit. 71 | 72 | For example, if you are using a recent version of OpenSSH, you 73 | may wish to explore using the ServerAliveInterval and 74 | ServerAliveCountMax options to have the SSH client exit if it 75 | finds itself no longer connected to the server. In many ways 76 | this may be a better solution than the monitoring port. 77 | 78 | -f Causes autossh to drop to the background before running ssh. The 79 | -f flag is stripped from arguments passed to ssh. Note that there 80 | is a crucial a difference between the -f with autossh, and -f 81 | with ssh: when used with autossh, ssh will be *unable* to ask for 82 | passwords or passphrases. When -f is used, the "starting gate" 83 | time (see AUTOSSH_GATETIME) will be set to 0. 84 | 85 | -V to have autossh display its version and exit. 86 | 87 | All other arguments are passed to ssh. There are a number of 88 | other settings, but these are all controlled through environment 89 | variables. ssh seems to be appropriating more and more letters for 90 | options, and this seems the easiest way to avoid collisions. 91 | 92 | autossh tries to distinguish the manner of death of the ssh process it 93 | is monitoring and act appropriately. The rules are: 94 | 95 | - If the ssh process exited normally (for example, someone typed 96 | "exit" in an interactive session), autossh exits rather than 97 | restarting; 98 | - If autossh itself receives a SIGTERM, SIGINT, or a SIGKILL 99 | signal, it assumes that it was deliberately signalled, and exits 100 | after killing the child ssh process; 101 | - If autossh itself receives a SIGUSR1 signal, it will kill the child 102 | ssh process and start a new one; 103 | - Periodically (by default every 10 minutes), autossh attempts to pass 104 | traffic on the monitor forwarded port. If this fails, autossh will 105 | kill the child ssh process (if it is still running) and start a new 106 | one; 107 | - If the child ssh process dies for any other reason, autossh will 108 | attempt to start a new one. 109 | 110 | Startup behaviour: 111 | 112 | - If the ssh session fails with an exit status of 1 on the very first 113 | try, autossh will assume that there is some problem with syntax or 114 | the connection setup, and will exit rather than retrying; 115 | - There is now a "starting gate" time. If the first ssh process fails 116 | within the first few seconds of being started, autossh assumes that 117 | it never made it "out of the starting gate", and exits. This is to handle 118 | initial failed authentication, connection, etc. This time is 30 seconds 119 | by default, and can be adjusted (see the AUTOSSH_GATETIME environment 120 | variable below). 121 | - NOTE: If AUTOSSH_GATETIME is set to 0, then BOTH of the above 122 | behaviours are disabled. This is useful for, for example, 123 | having autossh start on boot. The "starting gate" time is 124 | also set to 0 with the -f flag to autossh is used. 125 | 126 | Continued failures: 127 | 128 | - If the ssh connection fails and attempts to restart it fail in 129 | quick succession, autossh will start delaying its attempts to 130 | restart, gradually backing farther and farther off up to a 131 | maximum interval of the autossh poll time (usually 10 minutes). 132 | autossh can be "prodded" to retry by signalling it, perhaps with 133 | SIGHUP ("kill -HUP"). 134 | 135 | Connection Setup 136 | ---------------- 137 | 138 | As connections must be established unattended, the use of autossh 139 | requires that some form of automatic authentication be set up. The use 140 | of RSAAuthentication with ssh-agent is the recommended method. The 141 | example wrapper script attempts to check if there is an agent running 142 | for the current environment, and to start one if there isn't. 143 | 144 | It cannot be stressed enough that you must make sure ssh works on its 145 | own, that you can set up the session you want before you try to 146 | run it under autossh. 147 | 148 | If you are tunnelling and using an older version of ssh that does not 149 | support the -N flag, you should upgrade (your version has security 150 | flaws). If you can't upgrade, you may wish to do as rstunnel does, and 151 | give ssh a command to run, such as "sleep 99999999999". 152 | 153 | Disabling connection monitoring 154 | ------------------------------- 155 | 156 | A monitor port value of "0" ("autossh -M 0") will disable use of 157 | the monitor ports; autossh will then only react to signals and the 158 | death of the ssh process. 159 | 160 | Environment Variables 161 | --------------------- 162 | 163 | The following environment variables can be set: 164 | 165 | AUTOSSH_DEBUG - sets logging level to LOG_DEBUG, and if 166 | the operating system supports it, sets 167 | syslog to duplicate log entries to stderr. 168 | AUTOSSH_FIRST_POLL - time to initial poll (default is as 169 | AUTOSSH_POLL below). 170 | AUTOSSH_GATETIME - how long ssh must be up before we consider 171 | it a successful connection. Default is 30 172 | seconds. If set to 0, then this behaviour 173 | is disabled, and as well, autossh will retry 174 | even on failure of first attempt to run ssh. 175 | AUTOSSH_LOGFILE - sets autossh to use the named log file, 176 | rather than syslog. 177 | AUTOSSH_LOGLEVEL - log level, they correspond to the levels 178 | used by syslog; so 0-7 with 7 being the 179 | chattiest. 180 | AUTOSSH_MAXLIFETIME - Sets the maximum number of seconds the process 181 | should live for before killing off the ssh child 182 | and exiting. 183 | AUTOSSH_MAXSTART - specifies how many times ssh should be started. 184 | A negative number means no limit on the number 185 | of times ssh is started. The default value is -1. 186 | AUTOSSH_MESSAGE - append a custom message to the echo string (max 64 187 | bytes). 188 | AUTOSSH_NTSERVICE - when set to "yes" , setup autossh to run as an 189 | NT service under cygrunsrv. This adds the -N flag 190 | for ssh if not already set, sets the log output 191 | to stdout, and changes the behaviour on ssh exit 192 | so that it will restart even on a normal exit. 193 | AUTOSSH_PATH - path to the ssh executable, in case 194 | it is different than that compiled in. 195 | AUTOSSH_PIDFILE - write autossh pid to specified file. 196 | AUTOSSH_POLL - poll time in seconds; default is 600. 197 | If the poll time is less than twice the 198 | network timeouts (default 15 seconds) the 199 | network timeouts will be adjusted downward 200 | to 1/2 the poll time. 201 | AUTOSSH_PORT - set monitor port. Mostly in case ssh 202 | appropriates -M at some time. But because 203 | of this possible use, AUTOSSH_PORT overrides 204 | the -M flag. 205 | 206 | Logging and Syslog 207 | ------------------ 208 | 209 | autossh logs to syslog using the LOG_USER facility. Your syslog may 210 | have to be configured to accept messages for this facility. This is 211 | usually done in /etc/syslog.conf. 212 | 213 | -- 214 | Kudos and raspberries to harding [at] motd.ca 215 | -------------------------------------------------------------------------------- /autossh.1: -------------------------------------------------------------------------------- 1 | .\" -*- nroff -*- 2 | .\" 3 | .\" Author: Carson Harding 4 | .\" Copyright (c) 2002 Carson Harding. All rights reserved. 5 | .\" 6 | .\" Redistribution and use in source and binary forms, with or without 7 | .\" modification, are permitted. 8 | .\" 9 | .\" THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 10 | .\" IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 11 | .\" OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 12 | .\" IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 13 | .\" INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 14 | .\" NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 15 | .\" DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 16 | .\" THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 17 | .\" (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 18 | .\" THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 19 | .\" 20 | .\" $Id: autossh.1,v 1.22 2011/10/12 20:29:22 harding Exp $ 21 | .\" 22 | .Dd Jul 20, 2004 23 | .Dt AUTOSSH 1 24 | .Os 25 | .Sh NAME 26 | .Nm autossh 27 | .Nd monitor and restart ssh sessions 28 | .Sh SYNOPSIS 29 | .Nm autossh 30 | .Op Fl V 31 | .Op Fl M Ar port[:echo_port] 32 | .Op Fl f 33 | .Ar [SSH_OPTIONS] 34 | .Sh DESCRIPTION 35 | .Nm 36 | is a program to start a copy of ssh and monitor it, restarting it as 37 | necessary should it die or stop passing traffic. 38 | .Pp 39 | The original idea and the mechanism were from rstunnel (Reliable SSH 40 | Tunnel). With version 1.2 of 41 | .Nm 42 | the method changed: 43 | .Nm 44 | uses ssh to construct a loop of ssh forwardings (one from local to 45 | remote, one from remote to local), and then sends test data that it 46 | expects to get back. (The idea is thanks to Terrence Martin.) 47 | .Pp 48 | With version 1.3, a new method is added (thanks to Ron Yorston): a 49 | port may be specified for a remote echo service that will echo back 50 | the test data. This avoids the congestion and the aggravation of 51 | making sure all the port numbers on the remote machine do not 52 | collide. The loop-of-forwardings method remains available for 53 | situations where using an echo service may not be possible. 54 | .Pp 55 | .Sh CONTROLLING SSH 56 | .Pp 57 | .Ss SSH exits 58 | .Pp 59 | .Bl -tag -width Ds 60 | .Nm 61 | tries to distinguish the manner of death of the ssh process it 62 | is monitoring and act appropriately. The rules are: 63 | .Bl -tag -width Ds 64 | .It 1. 65 | If the ssh process exited normally (for example, someone typed 66 | "exit" in an interactive session), 67 | .Nm 68 | exits rather than restarting; 69 | .It 2. 70 | If 71 | .Nm 72 | itself receives a SIGTERM, SIGINT, or a SIGKILL signal, it assumes 73 | that it was deliberately signalled, and exits after killing the child 74 | ssh process; 75 | .It 3. 76 | If 77 | .Nm 78 | itself receives a SIGUSR1 signal, it kills the child ssh process and starts 79 | a new one; 80 | .It 4. 81 | Periodically (by default every 10 minutes), 82 | .Nm 83 | attempts to pass traffic on the monitor forwarded port. If this fails, 84 | .Nm 85 | will kill the child ssh process (if it is still running) and start a new one; 86 | .It 5. 87 | If the child ssh process dies for any other reason, 88 | .Nm 89 | will attempt to start a new one. 90 | .El 91 | .Pp 92 | .Ss Startup behaviour 93 | .Pp 94 | If the ssh session fails with an exit status of 1 on the very first 95 | try, 96 | .Nm 97 | .Bl -tag -width Ds 98 | .It 1. 99 | will assume that there is some problem with syntax or the connection 100 | setup, and will exit rather than retrying; 101 | .It 2. 102 | There is a "starting gate" time. If the first ssh process fails within 103 | the first few seconds of being started, 104 | .Nm 105 | assumes that 106 | it never made it "out of the starting gate", and exits. This is to handle 107 | initial failed authentication, connection, etc. This time is 30 seconds 108 | by default, and can be adjusted (see the AUTOSSH_GATETIME environment 109 | variable below). If AUTOSSH_GATETIME is set to 0, then both behaviours 110 | are disabled: there is no "starting gate", and autossh will restart even 111 | if ssh fails on the first run with an exit status of 1. The "starting gate" 112 | time is also set to 0 when the 113 | .Fl f 114 | flag to autossh is used. 115 | .El 116 | .Pp 117 | .Ss Continued failures 118 | .Pp 119 | If the ssh connection fails and attempts to restart it fail in 120 | quick succession, 121 | .Nm 122 | will start delaying its attempts to 123 | restart, gradually backing farther and farther off up to a 124 | maximum interval of the 125 | .Nm 126 | poll time (usually 10 minutes). 127 | .Nm 128 | can be "prodded" to retry by signalling it, perhaps with 129 | SIGHUP ("kill -HUP"). 130 | .Pp 131 | .Ss Connection setup 132 | .Pp 133 | As connections must be established unattended, the use of 134 | .Nm 135 | requires that some form of automatic authentication be set up. The use 136 | of RSAAuthentication with ssh-agent is the recommended method. The 137 | example wrapper script attempts to check if there is an agent running 138 | for the current environment, and to start one if there isn't. 139 | .Pp 140 | It cannot be stressed enough that you must make sure ssh works on its 141 | own, that you can set up the session you want before you try to 142 | run it under 143 | .Nm 144 | . 145 | .Pp 146 | If you are tunnelling and using an older version of ssh that does not 147 | support the 148 | .Fl N 149 | flag, you should upgrade (your version has security 150 | flaws). If you can't upgrade, you may wish to do as rstunnel does, and 151 | give ssh a command to run, such as "sleep 99999999999". 152 | .Sh OPTIONS 153 | .Bl -tag -width Ds 154 | .It Fl M Ar port[:echo_port] 155 | specifies the base monitoring port to use. Without the echo port, 156 | this port and the port 157 | immediately above it ( 158 | .Ar port 159 | + 1) should be something nothing else is 160 | using. 161 | .Nm 162 | will send test data on the base monitoring port, and 163 | receive it back on the port above. For example, if you specify "-M 164 | 20000", 165 | .Nm 166 | will set up forwards so that it can send data on port 167 | 20000 and receive it back on 20001. 168 | .Pp 169 | Alternatively, a port for a remote echo service may be specified. This 170 | should be port 7 if you wish to use the standard inetd echo service. 171 | When an echo port is specified, only the specified monitor port is 172 | used, and it carries the monitor message in both directions. 173 | .Pp 174 | Many people disable the echo service, or even disable inetd, so check 175 | that this service is available on the remote machine. Some operating 176 | systems allow one to specify that the service only listen on the 177 | localhost (loopback interface), which would suffice for this use. 178 | .Pp 179 | The echo service may also be something more complicated: perhaps 180 | a daemon that monitors a group of ssh tunnels. 181 | .Pp 182 | Setting the monitor port to 0 turns the monitoring function off, and 183 | autossh will only restart ssh upon ssh's exit. For example, if you are 184 | using a recent version of OpenSSH, you may wish to explore using the 185 | .Cm ServerAliveInterval 186 | and 187 | .Cm ServerAliveCountMax 188 | options to have the SSH client exit if it finds itself no longer 189 | connected to the server. In many ways this may be a better solution than 190 | the monitoring port. 191 | .It Fl f 192 | causes autossh to drop to the background before running ssh. The 193 | .Fl f 194 | flag is stripped from arguments passed to ssh. Note that there is a crucial 195 | a difference between 196 | .Fl f 197 | with autossh, and 198 | .Fl f 199 | with ssh: when used with 200 | .Nm 201 | ssh will be unable to ask for passwords or passphrases. When 202 | .Fl f 203 | is used, the "starting gate" time (see AUTOSSH_GATETIME) 204 | is set to 0. 205 | .It Fl V 206 | causes 207 | .Nm 208 | to display its version number and exit. 209 | .El 210 | .Sh ENVIRONMENT 211 | Other than the flag to set the connection monitoring port, 212 | .Nm 213 | uses environment variables to control features. ssh seems to be 214 | still collecting letters for options, and this seems the easiest 215 | way to avoid collisions. 216 | .Bl -tag -width Ds 217 | .It Ev AUTOSSH_DEBUG 218 | If this variable is set, the logging level is set to to LOG_DEBUG, and 219 | if the operating system supports it, syslog is set to duplicate log 220 | entries to stderr. 221 | .It Ev AUTOSSH_FIRST_POLL 222 | Specifies the time to wait before the first connection test. Thereafter 223 | the general poll time is used (see AUTOSSH_POLL below). 224 | .It Ev AUTOSSH_GATETIME 225 | Specifies how long ssh must be up before we consider it a successful 226 | connection. The default is 30 seconds. Note that if AUTOSSH_GATETIME 227 | is set to 0, then not only is the gatetime behaviour turned off, but 228 | autossh also ignores the first run failure of ssh. This may be useful 229 | when running autossh at boot. 230 | .It Ev AUTOSSH_LOGLEVEL 231 | Specifies the log level, corresponding to the levels used by syslog; 232 | so 0-7 with 7 being the chattiest. 233 | .It Ev AUTOSSH_LOGFILE 234 | Specifies that 235 | .Nm 236 | should use the named log file, rather than syslog. 237 | .It Ev AUTOSSH_MAXLIFETIME 238 | Sets the maximum number of seconds that the program should run. Once 239 | the number of seconds has been passed, the ssh child will be killed 240 | and the program will exit. 241 | .It Ev AUTOSSH_MAXSTART 242 | Specifies how many times ssh should be started. A negative number 243 | means no limit on the number of times ssh is started. The default 244 | value is -1. 245 | .It Ev AUTOSSH_MESSAGE 246 | Append message to echo message sent when testing connections. 247 | .It Ev AUTOSSH_NTSERVICE 248 | (Cygwin only.) When set to "yes" , autossh sets up to run as an NT 249 | service under cygrunsrv. This adds the -N flag for ssh if not already 250 | set, sets the log output to stdout, and changes the behaviour on ssh 251 | exit so that it will restart even on a normal exit. 252 | .It Ev AUTOSSH_PATH 253 | Specifies the path to the ssh executable, in case it is 254 | different than the path compiled in. 255 | .It Ev AUTOSSH_PIDFILE 256 | Write autossh pid to specified file. 257 | .It Ev AUTOSSH_POLL 258 | Specifies the connection poll time in seconds; default is 600 seconds. 259 | If the poll time is less than twice the network timeouts (default 15 260 | seconds) the network timeouts will be adjusted downward to 1/2 the 261 | poll time. 262 | .It Ev AUTOSSH_PORT 263 | Sets the connection monitoring port. Mostly in case ssh appropriates 264 | -M at some time. But because of this possible use, AUTOSSH_PORT 265 | overrides the -M flag. A value of 0 turns the monitoring function off. 266 | .El 267 | .Sh AUTHOR 268 | .Nm 269 | was written by Carson Harding. 270 | .Sh SEE ALSO 271 | .Xr ssh 1 , 272 | .Xr ssh-add 1 , 273 | .Xr ssh-agent 1 , 274 | .Xr ssh-keygen 1 , 275 | .Xr cygrunsrv 1 . 276 | -------------------------------------------------------------------------------- /autossh.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Start an ssh session (or tunnel) and monitor it. 3 | * If it fails or blocks, restart it. 4 | * 5 | * From the example of rstunnel. 6 | * 7 | * Copyright (c) Carson Harding, 2002-2008. 8 | * All rights reserved. 9 | * 10 | * Redistribution and use in source and binary forms, with or without 11 | * modification, are freely permitted. 12 | * 13 | * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, 14 | * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY 15 | * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL 16 | * THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 17 | * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, 18 | * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; 19 | * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, 20 | * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR 21 | * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF 22 | * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 23 | * 24 | */ 25 | 26 | #include "config.h" 27 | 28 | #include 29 | #include 30 | 31 | #ifndef HAVE_SOCKLEN_T 32 | typedef int32_t socklen_t; 33 | #endif 34 | 35 | #include 36 | #include 37 | #include 38 | #include 39 | #include 40 | #include 41 | #include 42 | #include 43 | #include 44 | #include 45 | #include 46 | #include 47 | #include 48 | #include 49 | #include 50 | #include 51 | #include 52 | #include 53 | 54 | #ifndef HAVE_POLL 55 | # ifdef HAVE_SELECT 56 | # include "fakepoll.h" 57 | # else 58 | # error "System lacks both select() and poll()!" 59 | # endif 60 | #else 61 | # include 62 | #endif 63 | 64 | #ifndef __attribute__ 65 | # if __GNUC__ < 2 || (__GNUC__ == 2 && __GNUC_MINOR__ < 8) || __STRICT_ANSI__ 66 | # define __attribute__(x) 67 | # endif 68 | #endif 69 | 70 | #ifndef _PATH_DEVNULL 71 | # define _PATH_DEVNULL "/dev/null" 72 | #endif 73 | 74 | #ifndef HAVE_DAEMON 75 | # include "daemon.h" 76 | #endif 77 | 78 | #ifdef HAVE___PROGNAME 79 | extern char *__progname; 80 | #else 81 | char *__progname; 82 | #endif 83 | 84 | const char *rcsid = "$Id: autossh.c,v 1.82 2011/10/12 20:29:22 harding Exp $"; 85 | 86 | #ifndef SSH_PATH 87 | # define SSH_PATH "/usr/bin/ssh" 88 | #endif 89 | 90 | #define POLL_TIME 600 /* 10 minutes default */ 91 | #define GATE_TIME 30 /* 30 seconds default */ 92 | #define MAX_LIFETIME 0 /* default max lifetime of forever */ 93 | #define TIMEO_NET 15000 /* poll on accept() and io (msecs) */ 94 | #define MAX_CONN_TRIES 3 /* how many attempts */ 95 | #define MAX_START (-1) /* max # of runs; <0 == forever */ 96 | #define MAX_MESSAGE 64 /* max length of message we can add */ 97 | 98 | #define P_CONTINUE 0 /* continue monitoring */ 99 | #define P_RESTART 1 /* restart ssh process */ 100 | #define P_EXIT 2 /* exit */ 101 | 102 | #define L_FILELOG 0x01 /* log to file */ 103 | #define L_SYSLOG 0x02 /* log to syslog */ 104 | 105 | #define NO_RD_SOCK -2 /* magic flag for echo: no read socket */ 106 | 107 | #define OPTION_STRING "M:V1246ab:c:e:fgi:kl:m:no:p:qstvw:xyACD:F:I:MKL:NO:PR:S:TVXY" 108 | 109 | int logtype = L_SYSLOG; /* default log to syslog */ 110 | int loglevel = LOG_INFO; /* default loglevel */ 111 | int syslog_perror; /* use PERROR option? */ 112 | FILE *flog; /* log file */ 113 | 114 | char *writep; /* write port as string */ 115 | char readp[16]; /* read port as string */ 116 | char *echop; /* echo port as string */ 117 | char *mhost = "127.0.0.1"; /* host in port forwards */ 118 | char *env_port; /* port spec'd in environment */ 119 | char *echo_message = ""; /* message to append to echo string */ 120 | char *pid_file_name; /* path to pid file */ 121 | int pid_file_created; /* we have created pid file */ 122 | time_t pid_start_time; /* time autossh process started */ 123 | int poll_time = POLL_TIME; /* default connection poll time */ 124 | int first_poll_time = POLL_TIME; /* initial connection poll time */ 125 | double gate_time = GATE_TIME; /* time to "make it out of the gate" */ 126 | int max_start = MAX_START; /* how many times to run (default no limit) */ 127 | double max_lifetime = MAX_LIFETIME; /* how long can the process/daemon live */ 128 | int net_timeout = TIMEO_NET; /* timeout on network data */ 129 | char *ssh_path = SSH_PATH; /* default path to ssh */ 130 | int start_count; /* # of times exec()d ssh */ 131 | time_t start_time; /* time we exec()d ssh */ 132 | 133 | int ssh_exit; /* the exit status of the ssh processs */ 134 | 135 | #if defined(__CYGWIN__) 136 | int ntservice; /* set some stuff for running as nt service */ 137 | #endif 138 | 139 | int newac; /* argc, argv for ssh */ 140 | char **newav; 141 | #define START_AV_SZ 16 142 | 143 | int cchild; /* current child */ 144 | 145 | volatile sig_atomic_t restart_ssh; /* signalled to restart ssh child */ 146 | volatile sig_atomic_t dolongjmp; 147 | sigjmp_buf jumpbuf; 148 | 149 | void usage(int code) __attribute__ ((__noreturn__)); 150 | void get_env_args(void); 151 | void add_arg(char *s); 152 | void strip_arg(char *arg, char ch, char *opts); 153 | void ssh_run(int sock, char **argv); 154 | int ssh_watch(int sock); 155 | int ssh_wait(int options); 156 | void ssh_kill(void); 157 | int conn_test(int sock, char *host, char *write_port); 158 | int conn_poll_for_accept(int sock, struct pollfd *pfd); 159 | int conn_send_and_receive(char *rp, char *wp, size_t len, 160 | struct pollfd *pfd, int ntopoll); 161 | #ifndef HAVE_ADDRINFO 162 | void conn_addr(char *host, char *port, struct sockaddr_in *resp); 163 | #else 164 | void conn_addr(char *host, char *port, struct addrinfo **resp); 165 | #endif 166 | int conn_listen(char *host, char *port); 167 | int conn_remote(char *host, char *port); 168 | void grace_time(time_t last_start); 169 | void unlink_pid_file(void); 170 | void errlog(int level, char *fmt, ...) 171 | __attribute__ ((__format__ (__printf__, 2, 3))); 172 | void xerrlog(int level, char *fmt, ...) 173 | __attribute__ ((__format__ (__printf__, 2, 3))); 174 | void doerrlog(int level, char *fmt, va_list ap); 175 | char *timestr(void); 176 | void sig_catch(int sig); 177 | int exceeded_lifetime(void); 178 | 179 | void 180 | usage(int code) 181 | { 182 | fprintf(code ? stderr : stdout, 183 | "usage: %s [-V] [-M monitor_port[:echo_port]] [-f] [SSH_OPTIONS]\n", 184 | __progname); 185 | if (code) { 186 | fprintf(stderr, "\n"); 187 | fprintf(stderr, 188 | " -M specifies monitor port. May be overridden by" 189 | " environment\n" 190 | " variable AUTOSSH_PORT. 0 turns monitoring" 191 | " loop off.\n" 192 | " Alternatively, a port for an echo service on" 193 | " the remote\n" 194 | " machine may be specified. (Normally port 7.)\n"); 195 | fprintf(stderr, 196 | " -f run in background (autossh handles this, and" 197 | " does not\n" 198 | " pass it to ssh.)\n"); 199 | fprintf(stderr, 200 | " -V print autossh version and exit.\n"); 201 | fprintf(stderr, 202 | " -t exit with the exit code of ran command.\n"); 203 | fprintf(stderr, "\n"); 204 | fprintf(stderr, "Environment variables are:\n"); 205 | fprintf(stderr, 206 | " AUTOSSH_GATETIME " 207 | "- how long must an ssh session be established\n" 208 | " " 209 | " before we decide it really was established\n" 210 | " " 211 | " (in seconds). Default is %d seconds; use of -f\n" 212 | " " 213 | " flag sets this to 0.\n", GATE_TIME); 214 | fprintf(stderr, 215 | " AUTOSSH_LOGFILE " 216 | "- file to log to (default is to use the syslog\n" 217 | " " 218 | " facility)\n"); 219 | fprintf(stderr, 220 | " AUTOSSH_LOGLEVEL " 221 | "- level of log verbosity\n"); 222 | fprintf(stderr, 223 | " AUTOSSH_MAXLIFETIME " 224 | "- set the maximum time to live (seconds)\n"); 225 | fprintf(stderr, 226 | " AUTOSSH_MAXSTART " 227 | "- max times to restart (default is no limit)\n"); 228 | fprintf(stderr, 229 | " AUTOSSH_MESSAGE " 230 | "- message to append to echo string (max 64 bytes)\n"); 231 | #if defined(__CYGWIN__) 232 | fprintf(stderr, 233 | " AUTOSSH_NTSERVICE " 234 | "- tweak some things for running under cygrunsrv\n"); 235 | #endif 236 | fprintf(stderr, 237 | " AUTOSSH_PATH " 238 | "- path to ssh if not default\n"); 239 | fprintf(stderr, 240 | " AUTOSSH_PIDFILE " 241 | "- write pid to this file\n"); 242 | fprintf(stderr, 243 | " AUTOSSH_POLL " 244 | "- how often to check the connection (seconds)\n"); 245 | fprintf(stderr, 246 | " AUTOSSH_FIRST_POLL " 247 | "- time before first connection check (seconds)\n"); 248 | fprintf(stderr, 249 | " AUTOSSH_PORT " 250 | "- port to use for monitor connection\n"); 251 | fprintf(stderr, 252 | " AUTOSSH_DEBUG " 253 | "- turn logging to maximum verbosity and log to\n" 254 | " " 255 | " stderr\n"); 256 | fprintf(stderr, "\n"); 257 | } 258 | exit(code); 259 | } 260 | 261 | int main(int argc, char **argv) { 262 | int i; 263 | int n; 264 | int ch; 265 | char *s; 266 | int wp, rp, ep = 0; 267 | char wmbuf[256], rmbuf[256]; 268 | FILE *pid_file; 269 | 270 | int sock = -1; 271 | int done_fwds = 0; 272 | int runasdaemon = 0; 273 | int sawargstop = 0; 274 | #if defined(__CYGWIN__) 275 | int sawoptionn = 0; 276 | #endif 277 | int returnsshexit = 0; 278 | 279 | #ifndef HAVE___PROGNAME 280 | __progname = "autossh"; 281 | #endif 282 | 283 | /* 284 | * set up options from environment 285 | */ 286 | get_env_args(); 287 | 288 | /* 289 | * We accept all ssh args, and quietly pass them on 290 | * to ssh when we call it. 291 | */ 292 | while ((ch = getopt(argc, argv, OPTION_STRING)) != -1) { 293 | switch(ch) { 294 | case 'M': 295 | if (!env_port) 296 | writep = optarg; 297 | break; 298 | case 'V': 299 | fprintf(stdout, "%s %s\n", __progname, VER); 300 | exit(0); 301 | break; 302 | case 'f': 303 | runasdaemon = 1; 304 | break; 305 | #if defined(__CYGWIN__) 306 | case 'N': 307 | sawoptionn = 1; 308 | break; 309 | #endif 310 | case 't': 311 | returnsshexit = 1; 312 | break; 313 | case '?': 314 | usage(1); 315 | break; 316 | default: 317 | /* other options get passed to ssh */ 318 | break; 319 | } 320 | } 321 | 322 | /* if we got it from the environment */ 323 | if (env_port) 324 | writep = env_port; 325 | 326 | /* 327 | * We must at least have a monitor port and a remote host. 328 | */ 329 | if (env_port) { 330 | if (argc < 2) 331 | usage(1); 332 | } else if (!writep || argc < 4) 333 | usage(1); 334 | 335 | if (logtype & L_SYSLOG) 336 | openlog(__progname, LOG_PID|syslog_perror, LOG_USER); 337 | 338 | /* 339 | * Check for echo port 340 | */ 341 | if ((s = strchr(writep, ':')) != NULL) { 342 | *s = '\0'; 343 | echop = s + 1; 344 | ep = strtoul(echop, &s, 0); 345 | if (*echop == '\0' || *s != '\0' || ep == 0) 346 | xerrlog(LOG_ERR, "invalid echo port \"%s\"", echop); 347 | } 348 | 349 | /* 350 | * Check, and get the read port (write port + 1); 351 | * then construct port-forwarding arguments for ssh. 352 | */ 353 | wp = strtoul(writep, &s, 0); 354 | if (*writep == '\0' || *s != '\0') 355 | xerrlog(LOG_ERR, "invalid port \"%s\"", writep); 356 | if (wp == 0) { 357 | errlog(LOG_INFO, "port set to 0, monitoring disabled"); 358 | writep = NULL; 359 | } 360 | else if (wp > 65534 || wp < 0) 361 | xerrlog(LOG_ERR, "monitor port (%d) out of range", wp); 362 | else { 363 | rp = wp+1; 364 | /* all this for solaris; we could use asprintf() */ 365 | (void)snprintf(readp, sizeof(readp), "%d", rp); 366 | 367 | /* port-forward arg strings */ 368 | n = snprintf(wmbuf, sizeof(wmbuf), "%d:%s:%d", wp, mhost, 369 | echop ? ep : wp); 370 | if (n > sizeof(wmbuf)) 371 | xerrlog(LOG_ERR, 372 | "overflow building forwarding string"); 373 | if (!echop) { 374 | n = snprintf(rmbuf, sizeof(rmbuf), "%d:%s:%d", 375 | wp, mhost, rp); 376 | if (n > sizeof(rmbuf)) 377 | xerrlog(LOG_ERR, 378 | "overflow building forwarding string"); 379 | } 380 | } 381 | 382 | /* 383 | * Adjust timeouts if necessary: net_timeout is first 384 | * the timeout for accept and then for io, so if the 385 | * poll_time is set less than 2 timeouts, the timeouts need 386 | * to be adjusted to be at least 1/2. Perhaps there should be 387 | * be some padding here as well.... 388 | */ 389 | if ((poll_time * 1000) / 2 < net_timeout) { 390 | net_timeout = (poll_time * 1000) / 2; 391 | errlog(LOG_INFO, 392 | "short poll time: adjusting net timeouts to %d", 393 | net_timeout); 394 | } 395 | 396 | /* 397 | * Build a new arg list, skipping -f, -M and inserting 398 | * port forwards. 399 | */ 400 | add_arg(ssh_path); 401 | 402 | #if defined(__CYGWIN__) 403 | if (ntservice && !sawoptionn) 404 | add_arg("-N"); 405 | #endif 406 | 407 | for (i = 1; i < argc; i++) { 408 | /* 409 | * We step past the first '--', taking it as ours 410 | * (autossh's). Any further ones we pass to ssh. 411 | */ 412 | if (argv[i][0] == '-' && argv[i][1] == '-') { 413 | if (!sawargstop) { 414 | sawargstop = 1; 415 | continue; 416 | } 417 | } 418 | if (wp && env_port && !done_fwds) { 419 | add_arg("-L"); 420 | add_arg(wmbuf); 421 | if (!echop) { 422 | add_arg("-R"); 423 | add_arg(rmbuf); 424 | } 425 | done_fwds = 1; 426 | } else if (!sawargstop && argv[i][0] == '-' && argv[i][1] == 'M') { 427 | if (argv[i][2] == '\0') 428 | i++; 429 | if (wp && !done_fwds) { 430 | add_arg("-L"); 431 | add_arg(wmbuf); 432 | if (!echop) { 433 | add_arg("-R"); 434 | add_arg(rmbuf); 435 | } 436 | done_fwds = 1; 437 | } 438 | continue; 439 | } 440 | /* look for -f in option args and strip out */ 441 | strip_arg(argv[i], 'f', OPTION_STRING); 442 | add_arg(argv[i]); 443 | } 444 | 445 | /* 446 | * Only if we're doing the network monitor thing. 447 | * Socket once opened stays open for listening for 448 | * the duration of the program. 449 | */ 450 | if (writep) { 451 | if (!echop) { 452 | sock = conn_listen(mhost, readp); 453 | /* set close-on-exec */ 454 | (void)fcntl(sock, F_SETFD, FD_CLOEXEC); 455 | } else 456 | sock = NO_RD_SOCK; 457 | } 458 | 459 | if (runasdaemon) { 460 | if (daemon(0, 0) == -1) { 461 | xerrlog(LOG_ERR, "run as daemon failed: %s", 462 | strerror(errno)); 463 | } 464 | /* 465 | * If running as daemon, the user likely wants it 466 | * to just run and not fail early (perhaps machines 467 | * are coming up, etc.) 468 | */ 469 | gate_time = 0; 470 | } 471 | 472 | if (pid_file_name) { 473 | pid_file = fopen(pid_file_name, "w"); 474 | if (!pid_file) { 475 | xerrlog(LOG_ERR, "cannot open pid file \"%s\": %s", 476 | pid_file_name, strerror(errno)); 477 | } 478 | pid_file_created = 1; 479 | atexit(unlink_pid_file); 480 | if (fprintf(pid_file, "%d\n", (int)getpid()) == 0) 481 | xerrlog(LOG_ERR, "write failed to pid file \"%s\": %s", 482 | pid_file_name, strerror(errno)); 483 | fflush(pid_file); 484 | fclose(pid_file); 485 | } 486 | 487 | ssh_run(sock, newav); 488 | 489 | if (sock >= 0) { 490 | shutdown(sock, SHUT_RDWR); 491 | close(sock); 492 | } 493 | 494 | if (logtype & L_SYSLOG) 495 | closelog(); 496 | 497 | if (returnsshexit == 1) { 498 | exit(ssh_exit); 499 | } 500 | else { 501 | exit(0); 502 | } 503 | } 504 | 505 | /* 506 | * Add an argument to the argument array. 507 | */ 508 | void add_arg(char *s) { 509 | char *p; 510 | size_t len; 511 | static size_t newamax = START_AV_SZ; 512 | 513 | len = strlen(s); 514 | if (len == 0) 515 | return; 516 | 517 | if (!newav) { 518 | newav = malloc(START_AV_SZ * sizeof(char *)); 519 | if (!newav) 520 | xerrlog(LOG_ERR, "malloc: %s", strerror(errno)); 521 | } 522 | else if (newac >= newamax-1) { 523 | newamax *= 2; 524 | newav = realloc(newav, newamax * sizeof(char *)); 525 | if (!newav) 526 | xerrlog(LOG_ERR, "realloc: %s", strerror(errno)); 527 | } 528 | p = malloc(len+1); 529 | if (!p) xerrlog(LOG_ERR, "malloc: %s", strerror(errno)); 530 | memmove(p, s, len); 531 | p[len] = '\0'; 532 | newav[newac++] = p; 533 | newav[newac] = NULL; 534 | 535 | return; 536 | } 537 | 538 | /* 539 | * strip an argument option from an option string; strings that 540 | * end up with just a '-' become zero length (add_arg() will 541 | * skip them). An option that enters as '-' is untouched. 542 | * 543 | */ 544 | void 545 | strip_arg(char *arg, char ch, char *opts) 546 | { 547 | char *f, *o; 548 | size_t len; 549 | 550 | if (arg[0] == '-' && arg[1] != '\0') { 551 | for (len = strlen(arg), f = arg; *f != '\0'; f++, len--) { 552 | /* 553 | * If f in option string and next char is ':' then 554 | * what follows is a parameter to the flag, and 555 | * what we're stripping may be valid in it. We do 556 | * not validate f in opts: that is really someone 557 | * else's job, and the options may change. In that 558 | * case, this provides a best effort. This is 559 | * terribly inefficient. 560 | */ 561 | if ((o = strchr(opts, *f)) != NULL) { 562 | if (*(o+1) == ':') 563 | return; 564 | } 565 | if (*f == ch) 566 | (void)memmove(f, f+1, len); 567 | } 568 | /* left with "-" alone? then truncate */ 569 | if (arg[1] == '\0') 570 | arg[0] = '\0'; 571 | } 572 | 573 | return; 574 | } 575 | 576 | /* 577 | * Ugly, but as we've used so many command args... 578 | */ 579 | void get_env_args(void) { 580 | char *s; 581 | char *t; 582 | 583 | if ((s = getenv("AUTOSSH_PATH")) != NULL) 584 | ssh_path = s; 585 | 586 | if ((s = getenv("AUTOSSH_DEBUG")) != NULL) { 587 | #ifdef HAVE_LOG_PERROR 588 | syslog_perror = LOG_PERROR; 589 | #else 590 | syslog_perror = 0; 591 | logtype |= L_FILELOG; 592 | flog = stderr; 593 | #endif 594 | loglevel = LOG_DEBUG; 595 | } else if ((s = getenv("AUTOSSH_LOGLEVEL")) != NULL) { 596 | loglevel = strtoul(s, &t, 0); 597 | if (*s == '\0' || *t != '\0' || 598 | loglevel < LOG_EMERG || loglevel > LOG_DEBUG) 599 | xerrlog(LOG_ERR, "invalid log level \"%s\"", s); 600 | } 601 | 602 | if ((s = getenv("AUTOSSH_FIRST_POLL")) != NULL) { 603 | first_poll_time = strtoul(s, &t, 0); 604 | if (*s == '\0' || first_poll_time == 0 || *t != '\0' ) 605 | xerrlog(LOG_ERR, 606 | "invalid first poll time \"%s\"", s); 607 | if (first_poll_time <= 0) 608 | first_poll_time = POLL_TIME; 609 | } 610 | 611 | if ((s = getenv("AUTOSSH_POLL")) != NULL) { 612 | poll_time = strtoul(s, &t, 0); 613 | if (*s == '\0' || poll_time == 0 || *t != '\0' ) 614 | xerrlog(LOG_ERR, 615 | "invalid poll time \"%s\"", s); 616 | if (poll_time <= 0) 617 | poll_time = POLL_TIME; 618 | } 619 | 620 | if ((s = getenv("AUTOSSH_GATETIME")) != NULL) { 621 | gate_time = (double)strtol(s, &t, 0); 622 | if (*s == '\0' || gate_time < 0 || *t != '\0' ) 623 | xerrlog(LOG_ERR, "invalid gate time \"%s\"", s); 624 | } 625 | 626 | if ((s = getenv("AUTOSSH_MAXSTART")) != NULL) { 627 | max_start = (int)strtol(s, &t, 0); 628 | if (*s == '\0' || max_start < 0 || *t != '\0') 629 | xerrlog(LOG_ERR, "invalid max start number \"%s\"", s); 630 | } 631 | 632 | if ((s = getenv("AUTOSSH_MESSAGE")) != NULL) { 633 | if (*s != '\0') 634 | echo_message = s; 635 | if (strlen(echo_message) > MAX_MESSAGE) 636 | xerrlog(LOG_ERR, "echo message may only be %d bytes long", 637 | MAX_MESSAGE); 638 | } 639 | 640 | 641 | if ((s = getenv("AUTOSSH_PORT")) != NULL) 642 | if (*s != '\0') 643 | env_port = s; 644 | 645 | if ((s = getenv("AUTOSSH_MAXLIFETIME")) != NULL) { 646 | max_lifetime = (double)strtoul(s, &t, 0); 647 | if (*s == '\0' || *t != '\0' ) 648 | xerrlog(LOG_ERR, 649 | "invalid max lifetime \"%s\"", s); 650 | /* can't really be < 0, as converted as unsigned long */ 651 | if (max_lifetime <= 0 ) 652 | max_lifetime = MAX_LIFETIME; 653 | else { 654 | if (poll_time > max_lifetime) { 655 | errlog( LOG_INFO, 656 | "poll time is greater then lifetime," 657 | " dropping poll time to %.0f", max_lifetime ); 658 | poll_time = max_lifetime; 659 | } 660 | 661 | if (first_poll_time > max_lifetime) { 662 | errlog( LOG_INFO, 663 | "first poll time is greater then lifetime," 664 | " dropping first poll time to %.0f", max_lifetime ); 665 | first_poll_time = max_lifetime; 666 | } 667 | 668 | time(&pid_start_time); 669 | } 670 | } 671 | 672 | if ((s = getenv("AUTOSSH_PIDFILE")) != NULL) 673 | if (*s != '\0') 674 | pid_file_name = s; 675 | 676 | #if defined(__CYGWIN__) 677 | if ((s = getenv("AUTOSSH_NTSERVICE")) != NULL) { 678 | if (*s != '\0' && strncasecmp("yes", s, strlen(s)) == 0) { 679 | ntservice = 1; 680 | logtype = L_FILELOG; 681 | flog = stdout; 682 | } 683 | } 684 | #endif 685 | 686 | /* 687 | * Look for this after nt service; in case we may wish to log 688 | * elsewhere than stdout when running under cygrunsrv. 689 | */ 690 | if ((s = getenv("AUTOSSH_LOGFILE")) != NULL) { 691 | flog = fopen(s, "a"); 692 | if (!flog) 693 | xerrlog(LOG_ERR, "%s: %s", s, strerror(errno)); 694 | logtype = L_FILELOG; 695 | } 696 | 697 | return; 698 | } 699 | 700 | /* 701 | * Run ssh 702 | */ 703 | void 704 | ssh_run(int sock, char **av) 705 | { 706 | struct sigaction act; 707 | struct timeval tv; 708 | 709 | act.sa_handler = sig_catch; 710 | sigemptyset(&act.sa_mask); 711 | act.sa_flags = 0; 712 | 713 | sigaction(SIGTERM, &act, NULL); 714 | sigaction(SIGINT, &act, NULL); 715 | sigaction(SIGHUP, &act, NULL); 716 | sigaction(SIGUSR1, &act, NULL); 717 | sigaction(SIGUSR2, &act, NULL); 718 | sigaction(SIGCHLD, &act, NULL); 719 | 720 | act.sa_flags |= SA_RESTART; 721 | sigaction(SIGALRM, &act, NULL); 722 | 723 | /* 724 | * There are much better things. and we all wait 725 | * for solaris to get /dev/random. 726 | */ 727 | gettimeofday(&tv, NULL); 728 | srandom(getpid() ^ tv.tv_usec ^ tv.tv_sec); 729 | 730 | while (max_start < 0 || start_count < max_start) { 731 | if (exceeded_lifetime()) 732 | return; 733 | restart_ssh = 0; 734 | start_count++; 735 | grace_time(start_time); 736 | time(&start_time); 737 | if (max_start < 0) 738 | errlog(LOG_INFO, "starting ssh (count %d)", 739 | start_count); 740 | else 741 | errlog(LOG_INFO, "starting ssh (count %d of %d)", 742 | start_count, max_start); 743 | cchild = fork(); 744 | switch (cchild) { 745 | case 0: 746 | errlog(LOG_DEBUG, "execing %s", av[0]); 747 | execvp(av[0], av); 748 | xerrlog(LOG_ERR, "%s: %s", av[0], strerror(errno)); 749 | /* else can loop restarting! */ 750 | kill(SIGTERM, getppid()); 751 | exit(1); 752 | break; 753 | case -1: 754 | cchild = 0; 755 | xerrlog(LOG_ERR, "fork: %s", strerror(errno)); 756 | break; 757 | default: 758 | errlog(LOG_INFO, "ssh child pid is %d", (int)cchild); 759 | if (ssh_watch(sock) == P_EXIT) 760 | return; 761 | break; 762 | } 763 | } 764 | 765 | errlog(LOG_INFO, "max start count reached; exiting"); 766 | } 767 | 768 | /* 769 | * Periodically test network connection. On signals, determine what 770 | * happened or what to do with child. Return as necessary for exit 771 | * or restart of child. 772 | */ 773 | int 774 | ssh_watch(int sock) 775 | { 776 | int r; 777 | int val; 778 | static int secs_left; 779 | int my_poll_time = first_poll_time; 780 | time_t now; 781 | double secs_to_shutdown; 782 | 783 | #if defined(HAVE_SETPROCTITLE) 784 | setproctitle("parent of %d (%d)", 785 | (int)cchild, start_count); 786 | #endif 787 | 788 | for (;;) { 789 | if (restart_ssh) { 790 | errlog(LOG_INFO, "signalled to kill and restart ssh"); 791 | ssh_kill(); 792 | return P_RESTART; 793 | } 794 | if ((val = sigsetjmp(jumpbuf, 1)) == 0) { 795 | 796 | errlog(LOG_DEBUG, "check on child %d", cchild); 797 | 798 | /* poll for expired child */ 799 | r = ssh_wait(WNOHANG); 800 | if (r != P_CONTINUE) { 801 | errlog(LOG_DEBUG, 802 | "expired child, returning %d", r); 803 | return r; 804 | } 805 | 806 | secs_left = alarm(0); 807 | if (secs_left == 0) 808 | secs_left = my_poll_time; 809 | 810 | my_poll_time = poll_time; 811 | 812 | if (max_lifetime != 0) { 813 | time(&now); 814 | secs_to_shutdown = max_lifetime - difftime(now,pid_start_time); 815 | if (secs_to_shutdown < poll_time) 816 | secs_left = secs_to_shutdown; 817 | } 818 | 819 | errlog(LOG_DEBUG, 820 | "set alarm for %d secs", secs_left); 821 | 822 | alarm(secs_left); 823 | dolongjmp = 1; 824 | pause(); 825 | 826 | } else { 827 | 828 | switch(val) { 829 | case SIGINT: 830 | case SIGTERM: 831 | case SIGQUIT: 832 | case SIGABRT: 833 | errlog(LOG_INFO, 834 | "received signal to exit (%d)", val); 835 | ssh_kill(); 836 | return P_EXIT; 837 | break; 838 | case SIGALRM: 839 | if (exceeded_lifetime()) { 840 | ssh_kill(); 841 | return P_EXIT; 842 | } 843 | 844 | if (writep && sock != -1 && 845 | !conn_test(sock, mhost, writep)) { 846 | errlog(LOG_INFO, 847 | "port down, restarting ssh"); 848 | ssh_kill(); 849 | return P_RESTART; 850 | } 851 | #ifdef TOUCH_PIDFILE 852 | /* 853 | * utimes() with a NULL time argument sets 854 | * file access and modification times to 855 | * the current time 856 | */ 857 | if (pid_file_name && 858 | utimes(pid_file_name, NULL) != 0) { 859 | errlog(LOG_ERR, 860 | "could not touch pid file: %s", 861 | strerror(errno)); 862 | } 863 | #endif 864 | break; 865 | default: 866 | break; 867 | } 868 | } 869 | } 870 | } 871 | 872 | /* 873 | * Checks to see if we have exceeded our time to live 874 | * Returns 1 if we have, 0 if we haven't 875 | */ 876 | int 877 | exceeded_lifetime(void) 878 | { 879 | time_t now; 880 | 881 | if (max_lifetime > 0 ) { 882 | time(&now); 883 | if (difftime(now, pid_start_time) >= max_lifetime ) { 884 | errlog(LOG_INFO, 885 | "exceeded maximum time to live, shutting down"); 886 | return 1; 887 | } 888 | } 889 | 890 | return 0; 891 | } 892 | 893 | /* 894 | * Wait on child: with options == WNOHANG, poll for 895 | * dead child, else if options == 0, then wait for 896 | * known dead child. 897 | * 898 | * If child was deliberately killed (TERM, INT, KILL), 899 | * or if child called exit(0) or _exit(0), then pass 900 | * message on return to give up (P_EXIT). Otherwise death 901 | * was unnatural (or unintended), and pass message back 902 | * to restart (P_RESTART). 903 | * 904 | * However, if child died with exit(1) on first try, then 905 | * there is some startup error (anything from network 906 | * connection to authentication failure), so we exit. 907 | * If on a restart, however, we keep trying as it must 908 | * have worked once. This doesn't necessarily work if 909 | * the user did an interactive authentication, and then 910 | * isn't there on the restart to enter his password.... 911 | * But we can only know very little about what's going 912 | * on inside ssh. 913 | * 914 | * This is further complicated by the behaviour of 915 | * OpenSSH when sent SIGTERM (15). It is possible to 916 | * kill it before it installs the handler for that 917 | * signal, in which case it autossh behaves as above 918 | * and exits. But, in at least interactive use, it 919 | * appears that once the session is established ssh 920 | * installs a handler, and then when signalled (killed) 921 | * it exits with status 255. autossh does not know 922 | * it (ssh) was signalled, so restarts it. 923 | * 924 | */ 925 | int ssh_wait(int options) { 926 | 927 | int status; 928 | int evalue; 929 | time_t now; 930 | 931 | if (waitpid(cchild, &status, options) > 0) { 932 | if (WIFSIGNALED(status)) { 933 | switch(WTERMSIG(status)) { 934 | case SIGINT: 935 | case SIGTERM: 936 | case SIGKILL: 937 | /* someone meant it */ 938 | errlog(LOG_INFO, 939 | "ssh exited on signal %d; parent exiting", 940 | WTERMSIG(status)); 941 | return P_EXIT; 942 | break; 943 | default: 944 | /* continue on and restart */ 945 | errlog(LOG_INFO, 946 | "ssh exited on signal %d, restarting ssh", 947 | WTERMSIG(status)); 948 | return P_RESTART; 949 | break; 950 | } 951 | } else if (WIFEXITED(status)) { 952 | evalue = WEXITSTATUS(status); 953 | if (start_count == 1 && gate_time != 0) { 954 | /* 955 | * If ssh exits too quickly, give up. 956 | */ 957 | time(&now); 958 | if (difftime(now, start_time) <= gate_time) { 959 | errlog(LOG_ERR, 960 | "ssh exited prematurely " 961 | "with status %d; %s exiting", 962 | evalue, __progname); 963 | ssh_exit = evalue; 964 | return P_EXIT; 965 | } 966 | } 967 | switch(evalue) { 968 | case 255: 969 | /* 970 | * we can get this on an initial 971 | * connection if the connection itself 972 | * is ok, but authentication fails. 973 | * But there's no way to do this nicely: 974 | * we don't have enough info from the 975 | * ssh session and we get the same exit 976 | * status from a dropped connection. 977 | * Hence the gate_time above. 978 | */ 979 | errlog(LOG_INFO, 980 | "ssh exited with error " 981 | "status %d; restarting ssh", 982 | evalue); 983 | return P_RESTART; 984 | break; 985 | case 1: 986 | /* 987 | * the first time, it could be any of 988 | * a number of errors; so we exit and let 989 | * the user fix. But if been running ok 990 | * already, then network may be down and 991 | * then ssh fails exit(1) on the attempt 992 | * to reconnect....so we try to restart. 993 | */ 994 | if (start_count > 1 || gate_time == 0) { 995 | errlog(LOG_INFO, 996 | "ssh exited with error " 997 | "status %d; restarting ssh", 998 | evalue); 999 | return P_RESTART; 1000 | } 1001 | /* FALLTHROUGH */ 1002 | case 0: /* exited on success */ 1003 | #if defined(__CYGWIN__) 1004 | if (ntservice) 1005 | return P_RESTART; 1006 | /* FALLTHROUGH */ 1007 | #endif 1008 | default: /* remote command error status */ 1009 | errlog(LOG_INFO, 1010 | "ssh exited with status %d; %s exiting", 1011 | evalue, __progname); 1012 | return P_EXIT; 1013 | break; 1014 | } 1015 | } 1016 | } 1017 | 1018 | /* do nothing */ 1019 | return P_CONTINUE; 1020 | } 1021 | 1022 | /* 1023 | * Kill ssh child. This can be overly aggressive, and 1024 | * result in kill KILL before TERM has time to take.... 1025 | * Perhaps just use TERM? 1026 | */ 1027 | void 1028 | ssh_kill(void) 1029 | { 1030 | int w; 1031 | int status; 1032 | 1033 | if (cchild) { 1034 | /* overkill */ 1035 | kill(cchild, SIGTERM); 1036 | /* if (kill(cchild, 0) != -1) 1037 | + kill(cchild, SIGKILL); 1038 | */ 1039 | do { 1040 | errno = 0; 1041 | w = waitpid(cchild, &status, 0); 1042 | } while (w < 0 && errno == EINTR ); 1043 | 1044 | if (w <= 0) { 1045 | errlog(LOG_ERR, 1046 | "waitpid() not successful: %s", 1047 | strerror(errno)); 1048 | } 1049 | } 1050 | return; 1051 | } 1052 | 1053 | /* 1054 | * Try to prevent rapid-fire restarts on such things 1055 | * as connection refused. Back off and try more slowly. 1056 | * Calculate a grace period to wait based time between 1057 | * now and the last restart and the number of tries 1058 | * in a row that have had less than the poll_time 1059 | * between them. 1060 | * 1061 | * Questions: 1062 | * - should it back off faster? slower? 1063 | */ 1064 | void 1065 | grace_time(time_t last_start) 1066 | { 1067 | int n; 1068 | double t; 1069 | int interval; 1070 | time_t now; 1071 | static int tries; 1072 | 1073 | double min_time; 1074 | 1075 | /* 1076 | * Minimum time we have to stay up to avoid backoff 1077 | * behaviour. With default poll_time this is 60 secs. 1078 | * This may be too complicated. 1079 | */ 1080 | min_time = (double)(poll_time / 10); 1081 | if (min_time < 10) min_time = 10; 1082 | 1083 | time(&now); 1084 | if (difftime(now, last_start) >= min_time) 1085 | tries = 0; 1086 | else 1087 | tries++; 1088 | 1089 | errlog(LOG_DEBUG, 1090 | "checking for grace period, tries = %d", tries); 1091 | 1092 | if (tries > 5) { 1093 | t = (double)(tries - 5); 1094 | n = (int)((poll_time / 100.0) * (t * (t/3))); 1095 | interval = (n > poll_time) ? poll_time : n; 1096 | if (interval) { 1097 | errlog(LOG_DEBUG, 1098 | "sleeping for grace time %d secs", interval); 1099 | sleep(interval); 1100 | } 1101 | } 1102 | return; 1103 | } 1104 | 1105 | /* 1106 | * If we're primed, longjump back. 1107 | */ 1108 | void 1109 | sig_catch(int sig) 1110 | { 1111 | if (sig == SIGUSR1) 1112 | restart_ssh = 1; 1113 | if (dolongjmp) { 1114 | dolongjmp = 0; 1115 | siglongjmp(jumpbuf, sig); 1116 | } 1117 | return; 1118 | } 1119 | 1120 | /* 1121 | * Test the connection monitor loop can pass traffic, and that 1122 | * we get back what we send. This needs the most testing. 1123 | */ 1124 | int 1125 | conn_test(int sock, char *host, char *write_port) 1126 | { 1127 | int rval; /* default return value (failure) */ 1128 | int tries; /* message attempts */ 1129 | int send_error; /* did it go/come ok? */ 1130 | struct pollfd pfd[2]; /* poll fds */ 1131 | int ntopoll; /* # fds to poll */ 1132 | int rd, wd; /* read and write descriptors */ 1133 | long id; /* for a random number */ 1134 | 1135 | struct utsname uts; 1136 | char wbuf[64+sizeof(uts.nodename)+MAX_MESSAGE]; 1137 | char rbuf[sizeof(wbuf)]; 1138 | 1139 | wd = -1; /* default desc. values */ 1140 | rd = -1; 1141 | rval = 0; /* default return value : no success */ 1142 | tries = 0; /* number of attempts */ 1143 | 1144 | uts.nodename[0] = '\0'; 1145 | (void)uname(&uts); 1146 | id = random(); 1147 | 1148 | if (dolongjmp != 0) 1149 | errlog(LOG_ERR, "conn_test(): error: dolongjmp != 0"); 1150 | 1151 | /* set up write connection */ 1152 | if ((wd = conn_remote(host, write_port)) == -1) 1153 | return 0; 1154 | 1155 | pfd[1].fd = wd; 1156 | pfd[1].events = POLLOUT; 1157 | 1158 | while (tries++ < MAX_CONN_TRIES) { 1159 | 1160 | if (tries >= MAX_CONN_TRIES) { 1161 | errlog(LOG_DEBUG, 1162 | "tried connection %d times and failed", 1163 | tries); 1164 | break; /* give up */ 1165 | } 1166 | 1167 | /* close read socket if we're coming around again */ 1168 | if (sock != NO_RD_SOCK && rd != -1) { 1169 | shutdown(rd, SHUT_RDWR); 1170 | close(rd); 1171 | rd = -1; 1172 | } 1173 | 1174 | /* 1175 | * Some data to send: something that is identifiable 1176 | * as coming from ourselves. Any user can still trash 1177 | * our listening port. We'd really like to be able to 1178 | * connect and accept connections from certain pids 1179 | * (ourself, our children). 1180 | */ 1181 | if (snprintf(wbuf, sizeof(wbuf), 1182 | "%s %s %d %ld %s\r\n", uts.nodename, __progname, 1183 | (int)getpid(), id, echo_message) >= sizeof(wbuf)) 1184 | xerrlog(LOG_ERR, "conn_test: buffer overflow"); 1185 | memset(rbuf, '\0', sizeof(rbuf)); 1186 | 1187 | if (sock != NO_RD_SOCK) { 1188 | /* 1189 | * If doing loop of connections, then accept() the read 1190 | * connection and use both read and write fds for 1191 | * poll(). Replace poll fd with accepted connection fd. 1192 | */ 1193 | rd = conn_poll_for_accept(sock, pfd); 1194 | if (rd < 0) 1195 | break; /* give up */ 1196 | pfd[0].fd = rd; 1197 | pfd[0].events = POLLIN; 1198 | ntopoll = 2; 1199 | } else { 1200 | /* 1201 | * For talking to echo service, shift over and 1202 | * just use the one descriptor for both read and 1203 | * write. 1204 | */ 1205 | pfd[0].fd = wd; 1206 | pfd[0].events = POLLIN|POLLOUT; 1207 | ntopoll = 1; 1208 | } 1209 | 1210 | send_error = conn_send_and_receive(rbuf, wbuf, 1211 | strlen(wbuf), pfd, ntopoll); 1212 | if (send_error == 0) { 1213 | /* we try again if received does not match sent */ 1214 | if (strcmp(rbuf, wbuf) == 0) { 1215 | errlog(LOG_DEBUG, "connection ok"); 1216 | rval = 1; /* success */ 1217 | break; /* out of here */ 1218 | } else { 1219 | errlog(LOG_DEBUG, 1220 | "not what I sent: \"%s\" : \"%s\"", 1221 | wbuf, rbuf); 1222 | /* loop again */ 1223 | } 1224 | } else if (send_error == 1) { 1225 | errlog(LOG_DEBUG, 1226 | "timeout on io poll, looping to accept again"); 1227 | } else { 1228 | errlog(LOG_DEBUG, "error on poll: %s", 1229 | strerror(errno)); 1230 | break; /* hard error, we're out of here */ 1231 | } 1232 | } 1233 | 1234 | shutdown(wd, SHUT_RDWR); 1235 | close(wd); 1236 | if (sock != NO_RD_SOCK) { 1237 | shutdown(rd, SHUT_RDWR); 1238 | close(rd); 1239 | } 1240 | 1241 | return rval; 1242 | } 1243 | 1244 | /* 1245 | * poll for accept(), return file descriptor for accepted connection, 1246 | * or -1 for error. 1247 | */ 1248 | int 1249 | conn_poll_for_accept(int sock, struct pollfd *pfd) 1250 | { 1251 | int rd; /* new descriptor on accept */ 1252 | int timeo_polla; /* for accept() */ 1253 | struct sockaddr cliaddr; 1254 | socklen_t len; /* listen socket info */ 1255 | 1256 | rd = 0; 1257 | timeo_polla = net_timeout; /* timeout value for accept() */ 1258 | len = sizeof(struct sockaddr); 1259 | 1260 | /* 1261 | * first we're going to poll for accept() 1262 | */ 1263 | pfd[0].fd = sock; 1264 | pfd[0].events = POLLIN; 1265 | 1266 | for (;;) { 1267 | switch(poll(pfd, 1, timeo_polla)) { 1268 | case 0: 1269 | errlog(LOG_INFO, 1270 | "timeout polling to accept read connection"); 1271 | return -1; 1272 | case -1: 1273 | errlog(LOG_ERR, 1274 | "error polling to accept read connection: %s", 1275 | strerror(errno)); 1276 | return -1; 1277 | default: 1278 | break; 1279 | } 1280 | 1281 | if (pfd[0].revents & POLLIN) { 1282 | rd = accept(sock, &cliaddr, &len); 1283 | if (rd == -1) { 1284 | errlog(LOG_ERR, 1285 | "error accepting read connection: %s", 1286 | strerror(errno)); 1287 | return -1; 1288 | } 1289 | break; 1290 | } 1291 | break; 1292 | } 1293 | 1294 | return rd; 1295 | } 1296 | 1297 | /* 1298 | * Send from wp and receive into rp. 1299 | * 1 = try again 1300 | * 0 = ok 1301 | * -1 = error 1302 | */ 1303 | int 1304 | conn_send_and_receive(char *rp, char *wp, size_t len, 1305 | struct pollfd *pfd, int ntopoll) 1306 | { 1307 | ssize_t nwrite, nread; 1308 | size_t rleft, wleft; 1309 | int timeo_pollio; 1310 | int ird, iwr; 1311 | int loops = 0; 1312 | 1313 | timeo_pollio = net_timeout; /* timeout value for net io */ 1314 | rleft = wleft = len; 1315 | 1316 | /* 1317 | * If two fds, one is to read, one is to write, 1318 | * else read and write on the same fd. 1319 | */ 1320 | if (ntopoll == 2) { 1321 | ird = 0; 1322 | iwr = 1; 1323 | } else { 1324 | iwr = ird = 0; 1325 | } 1326 | 1327 | 1328 | /* 1329 | * Now, send and receive. When we're doing the loop thing, we stop 1330 | * polling for write() once we've sent the whole message. 1331 | */ 1332 | while (rleft > 0) { 1333 | 1334 | switch(poll(pfd, ntopoll, timeo_pollio)) { 1335 | case 0: 1336 | return 1; 1337 | break; 1338 | case -1: 1339 | return -1; 1340 | break; 1341 | default: 1342 | break; 1343 | } 1344 | 1345 | if (wleft && pfd[iwr].revents & POLLOUT) { 1346 | while (wleft > 0) { 1347 | nwrite = write(pfd[iwr].fd, wp, wleft); 1348 | if (nwrite == 0) { 1349 | wleft = 0; /* EOF */ 1350 | break; 1351 | } else if (nwrite == -1) { 1352 | if (errno == EINTR || errno == EAGAIN) 1353 | break; 1354 | else 1355 | return -1; 1356 | } 1357 | wleft -= nwrite; 1358 | wp += nwrite; 1359 | } 1360 | /* if complete, turn off polling for write */ 1361 | if (wleft == 0) { 1362 | ntopoll = 1; 1363 | /* 1364 | * if we are reading and writing to the 1365 | * same fd then we must clear the write bit 1366 | * so that poll doesn't loop tight. 1367 | */ 1368 | if (iwr == ird) 1369 | pfd[ird].events = POLLIN; 1370 | } 1371 | } 1372 | 1373 | if (pfd[ird].revents & POLLIN || pfd[ird].revents & POLLHUP) { 1374 | while (rleft > 0) { 1375 | nread = read(pfd[ird].fd, rp, rleft); 1376 | if (nread == 0) { 1377 | rleft = 0; /* EOF */ 1378 | break; 1379 | } else if (nread == -1) { 1380 | if (errno == EINTR || errno == EAGAIN) 1381 | break; 1382 | else 1383 | return -1; 1384 | } 1385 | rleft -= nread; 1386 | rp += nread; 1387 | } 1388 | } 1389 | 1390 | /* 1391 | * we can run into situations where the data gets black-holed 1392 | * and poll() can't tell. And then we loop fast and 1393 | * things go nuts. So if we do that, give up after a while. 1394 | */ 1395 | if (loops++ > 5) { 1396 | sleep(1); 1397 | if (loops > 10) { 1398 | errlog(LOG_INFO, 1399 | "too many loops without data"); 1400 | return -1; 1401 | } 1402 | } 1403 | } 1404 | 1405 | return 0; 1406 | } 1407 | 1408 | #ifndef HAVE_ADDRINFO 1409 | 1410 | /* 1411 | * Convert names to addresses, setup for connection. 1412 | */ 1413 | void 1414 | conn_addr(char *host, char *port, struct sockaddr_in *resp) 1415 | { 1416 | struct hostent *h; 1417 | 1418 | if ((h = gethostbyname(host)) == NULL) 1419 | xerrlog(LOG_ERR, "%s: %s", host, hstrerror(h_errno)); 1420 | 1421 | resp->sin_family = h->h_addrtype; 1422 | resp->sin_port = htons(atoi(port)); 1423 | resp->sin_addr = *((struct in_addr *) h->h_addr_list[0]); 1424 | 1425 | return; 1426 | } 1427 | 1428 | /* 1429 | * Open connection we're writing to. 1430 | */ 1431 | int 1432 | conn_remote(char *host, char *port) 1433 | { 1434 | int sock; 1435 | static struct sockaddr_in res = {AF_UNSPEC}; 1436 | 1437 | /* Cache the address info */ 1438 | if (res.sin_family == AF_UNSPEC) 1439 | conn_addr(host, port, &res); 1440 | 1441 | if ((sock = socket(res.sin_family, SOCK_STREAM, 0)) == -1) 1442 | xerrlog(LOG_ERR, "socket: %s", strerror(errno)); 1443 | 1444 | if (connect(sock, (struct sockaddr *) &res, sizeof(res)) == -1) { 1445 | errlog(LOG_INFO, "%s:%s: %s", host, port, strerror(errno)); 1446 | close(sock); 1447 | return -1; 1448 | } 1449 | 1450 | return sock; 1451 | } 1452 | 1453 | /* 1454 | * Returns a socket listening on a local port, bound to specified source 1455 | * address. Errors in binding to the local listening port are fatal. 1456 | */ 1457 | int 1458 | conn_listen(char *host, char *port) 1459 | { 1460 | int sock; 1461 | struct sockaddr_in res; 1462 | int on = 1; 1463 | 1464 | /* 1465 | * Unlike conn_remote, we don't need to cache the 1466 | * info; we're only calling once at start. All errors 1467 | * here are fatal. 1468 | */ 1469 | conn_addr(host, port, &res); 1470 | 1471 | if ((sock = socket(res.sin_family, SOCK_STREAM, 0)) == -1) 1472 | xerrlog(LOG_ERR, "socket: %s", strerror(errno)); 1473 | 1474 | if (setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, 1475 | (char *) &on, sizeof on) != 0) { 1476 | xerrlog(LOG_ERR, "setsockopt: %s", strerror(errno)); 1477 | } 1478 | 1479 | if (bind(sock, (struct sockaddr *)&res, sizeof(res)) == -1) 1480 | xerrlog(LOG_ERR, "bind on %s:%s: %s", 1481 | host, port, strerror(errno)); 1482 | 1483 | if (listen(sock, 1) < 0) 1484 | xerrlog(LOG_ERR, "listen: %s", strerror(errno)); 1485 | 1486 | return sock; 1487 | } 1488 | 1489 | #else /* HAVE_ADDRINFO */ 1490 | 1491 | /* 1492 | * Convert names to addresses, setup for connection. 1493 | */ 1494 | void 1495 | conn_addr(char *host, char *port, struct addrinfo **resp) 1496 | { 1497 | int family = AF_INET; 1498 | struct addrinfo hints; 1499 | int error; 1500 | 1501 | memset(&hints, 0, sizeof(struct addrinfo)); 1502 | hints.ai_family = family; 1503 | hints.ai_socktype = SOCK_STREAM; 1504 | hints.ai_protocol = IPPROTO_TCP; 1505 | 1506 | /* Allow nodename to be null */ 1507 | hints.ai_flags |= AI_PASSIVE; 1508 | 1509 | /* 1510 | * In the case of binding to a wildcard address 1511 | * default to binding to an ipv4 address. 1512 | */ 1513 | if (host == NULL && hints.ai_family == AF_UNSPEC) 1514 | hints.ai_family = AF_INET; 1515 | 1516 | if ((error = getaddrinfo(host, port, &hints, resp))) 1517 | xerrlog(LOG_ERR, "%s", gai_strerror(error)); 1518 | 1519 | return; 1520 | } 1521 | 1522 | /* 1523 | * Open connection we're writing to. 1524 | */ 1525 | int 1526 | conn_remote(char *host, char *port) 1527 | { 1528 | int sock; 1529 | static struct addrinfo *res; 1530 | 1531 | /* Cache the address info */ 1532 | if (!res) 1533 | conn_addr(host, port, &res); 1534 | 1535 | if ((sock = socket(res->ai_family, res->ai_socktype, 1536 | res->ai_protocol)) == -1) 1537 | xerrlog(LOG_ERR, "socket: %s", strerror(errno)); 1538 | 1539 | if (connect(sock, res->ai_addr, res->ai_addrlen) == -1) { 1540 | errlog(LOG_INFO, "%s:%s: %s", host, port, strerror(errno)); 1541 | close(sock); 1542 | return -1; 1543 | } 1544 | 1545 | return sock; 1546 | } 1547 | 1548 | /* 1549 | * Returns a socket listening on a local port, bound to specified source 1550 | * address. Errors in binding to the local listening port are fatal. 1551 | */ 1552 | int 1553 | conn_listen(char *host, char *port) 1554 | { 1555 | int sock; 1556 | struct addrinfo *res; 1557 | int on = 1; 1558 | 1559 | /* 1560 | * Unlike conn_remote, we don't need to cache the 1561 | * info; we're only calling once at start. All errors 1562 | * here are fatal. 1563 | */ 1564 | conn_addr(host, port, &res); 1565 | 1566 | if ((sock = socket(res->ai_family, res->ai_socktype, 1567 | res->ai_protocol)) == -1) 1568 | xerrlog(LOG_ERR, "socket: %s", strerror(errno)); 1569 | 1570 | if (setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, 1571 | (char *) &on, sizeof on) != 0) { 1572 | xerrlog(LOG_ERR, "setsockopt: %s", strerror(errno)); 1573 | } 1574 | 1575 | if (bind(sock, (struct sockaddr *)res->ai_addr, 1576 | res->ai_addrlen) == -1) 1577 | xerrlog(LOG_ERR, "bind on %s:%s: %s", 1578 | host, port, strerror(errno)); 1579 | 1580 | if (listen(sock, 1) < 0) 1581 | xerrlog(LOG_ERR, "listen: %s", strerror(errno)); 1582 | 1583 | freeaddrinfo(res); 1584 | 1585 | return sock; 1586 | } 1587 | #endif /* ! HAVE_ADDRINFO */ 1588 | 1589 | /* 1590 | * On OpenBSD _exit() calls atexit() registered functions. 1591 | * Solaris has a function, _exithandle(), you can call 1592 | * before _exit(). 1593 | */ 1594 | void 1595 | unlink_pid_file(void) 1596 | { 1597 | if (pid_file_created) 1598 | (void)unlink(pid_file_name); 1599 | pid_file_created = 0; 1600 | } 1601 | 1602 | /* 1603 | * Nicely formatted time string for logging 1604 | */ 1605 | char * 1606 | timestr(void) 1607 | { 1608 | static char timestr[32]; 1609 | time_t now; 1610 | struct tm *tm; 1611 | 1612 | (void)time(&now); 1613 | tm = localtime(&now); 1614 | (void)strftime(timestr, sizeof(timestr), 1615 | "%Y/%m/%d %H:%M:%S", tm); 1616 | 1617 | return timestr; 1618 | } 1619 | 1620 | /* 1621 | * Log errors. 1622 | */ 1623 | void 1624 | errlog(int level, char *fmt, ...) 1625 | { 1626 | va_list ap; 1627 | 1628 | va_start(ap, fmt); 1629 | doerrlog(level, fmt, ap); 1630 | va_end(ap); 1631 | } 1632 | 1633 | /* 1634 | * Log and then exit with error status. 1635 | */ 1636 | void 1637 | xerrlog(int level, char *fmt, ...) 1638 | { 1639 | va_list ap; 1640 | 1641 | va_start(ap, fmt); 1642 | doerrlog(level, fmt, ap); 1643 | va_end(ap); 1644 | 1645 | ssh_kill(); 1646 | unlink_pid_file(); 1647 | _exit(1); 1648 | } 1649 | 1650 | /* 1651 | * Log to file and/or syslog as directed. We want different 1652 | * behaviour before syslog has been called and set up; and 1653 | * different behaviour before we fork for ssh: errors before 1654 | * that point result in exit. 1655 | */ 1656 | void 1657 | doerrlog(int level, char *fmt, va_list ap) 1658 | { 1659 | FILE *fl; 1660 | #ifndef HAVE_VSYSLOG 1661 | char logbuf[1024]; 1662 | #endif 1663 | 1664 | fl = flog; /* only set per-call */ 1665 | 1666 | if (loglevel >= level) { 1667 | if (logtype & L_SYSLOG) { 1668 | #ifndef HAVE_VSYSLOG 1669 | (void)vsnprintf(logbuf, sizeof(logbuf), fmt, ap); 1670 | syslog(level, logbuf); 1671 | #else 1672 | vsyslog(level, fmt, ap); 1673 | #endif 1674 | } else if (!fl) { 1675 | /* 1676 | * if we're not using syslog, and we 1677 | * don't have a log file, then use 1678 | * stderr. 1679 | */ 1680 | fl = stderr; 1681 | } 1682 | if ((logtype & L_FILELOG) && fl) { 1683 | fprintf(fl, 1684 | "%s %s[%d]: ", timestr(), 1685 | __progname, (int)getpid()); 1686 | vfprintf(fl, fmt, ap); 1687 | fprintf(fl, "\n"); 1688 | fflush(fl); 1689 | } 1690 | } 1691 | return; 1692 | } 1693 | 1694 | /* END */ 1695 | -------------------------------------------------------------------------------- /autossh.host: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | # 3 | # Example script to start up tunnel with autossh. 4 | # 5 | # This script will tunnel 2200 from the remote host 6 | # to 22 on the local host. On remote host do: 7 | # ssh -p 2200 localhost 8 | # 9 | # $Id: autossh.host,v 1.6 2004/01/24 05:53:09 harding Exp $ 10 | # 11 | 12 | ID=username 13 | HOST=hostname.your.net 14 | 15 | if [ "X$SSH_AUTH_SOCK" = "X" ]; then 16 | eval `ssh-agent -s` 17 | ssh-add $HOME/.ssh/id_rsa 18 | fi 19 | 20 | #AUTOSSH_POLL=600 21 | #AUTOSSH_PORT=20000 22 | #AUTOSSH_GATETIME=30 23 | #AUTOSSH_LOGFILE=$HOST.log 24 | #AUTOSSH_DEBUG=yes 25 | #AUTOSSH_PATH=/usr/local/bin/ssh 26 | export AUTOSSH_POLL AUTOSSH_LOGFILE AUTOSSH_DEBUG AUTOSSH_PATH AUTOSSH_GATETIME AUTOSSH_PORT 27 | 28 | autossh -2 -fN -M 20000 -R 2200:localhost:22 ${ID}@${HOST} 29 | -------------------------------------------------------------------------------- /autossh.spec: -------------------------------------------------------------------------------- 1 | Summary: Automatically restart SSH sessions and tunnels 2 | Name: autossh 3 | Version: 1.4b 4 | Release: 1 5 | License: Distributable 6 | Group: Applications/Networking 7 | Vendor: Carson Harding 8 | URL: http://www.harding.motd.ca/autossh/ 9 | Source0: %{name}-%{version}.tgz 10 | BuildRoot: %{_tmppath}/%{name}-%{version}-%{release}-buildroot 11 | 12 | %description 13 | autossh is a program to start a copy of ssh and monitor it, restarting 14 | it as necessary should it die or stop passing traffic. 15 | 16 | %prep 17 | %setup -q 18 | 19 | %build 20 | %configure 21 | 22 | %{__make} 23 | 24 | %install 25 | rm -rf $RPM_BUILD_ROOT 26 | install -d $RPM_BUILD_ROOT{%{_bindir},%{_mandir}/man1} 27 | 28 | install autossh $RPM_BUILD_ROOT%{_bindir} 29 | install autossh.1 $RPM_BUILD_ROOT%{_mandir}/man1 30 | 31 | %clean 32 | rm -rf $RPM_BUILD_ROOT 33 | 34 | %files 35 | %defattr(644,root,root,755) 36 | %doc README CHANGES autossh.host rscreen 37 | %attr(755,root,root) %{_bindir}/* 38 | %{_mandir}/man1/* 39 | 40 | %changelog 41 | * Fri Mar 28 2008 Carson Harding 42 | - update to 1.4b 43 | 44 | * Sat May 20 2006 Carson Harding 45 | - update to 1.4 and use autoconf 46 | 47 | * Wed Feb 02 2005 Carson Harding 48 | - very minor changes to spec file 49 | 50 | * Thu Oct 21 2004 Ron Yorston 1.3-1 51 | - Original version 52 | -------------------------------------------------------------------------------- /config.h.in: -------------------------------------------------------------------------------- 1 | /* config.h.in. Generated from configure.ac by autoheader. */ 2 | 3 | /* Define to 1 if you have struct addrinfo. */ 4 | #undef HAVE_ADDRINFO 5 | 6 | /* Define to 1 if you have the `alarm' function. */ 7 | #undef HAVE_ALARM 8 | 9 | /* Define to 1 if you have the header file. */ 10 | #undef HAVE_ARPA_INET_H 11 | 12 | /* Define to 1 if you have the `daemon' function. */ 13 | #undef HAVE_DAEMON 14 | 15 | /* Define to 1 if you don't have `vprintf' but do have `_doprnt.' */ 16 | #undef HAVE_DOPRNT 17 | 18 | /* Define to 1 if you have the `dup2' function. */ 19 | #undef HAVE_DUP2 20 | 21 | /* Define to 1 if you have the header file. */ 22 | #undef HAVE_FCNTL_H 23 | 24 | /* Define to 1 if you have the `fork' function. */ 25 | #undef HAVE_FORK 26 | 27 | /* Define to 1 if you have the `gethostbyname' function. */ 28 | #undef HAVE_GETHOSTBYNAME 29 | 30 | /* Define to 1 if you have the `gettimeofday' function. */ 31 | #undef HAVE_GETTIMEOFDAY 32 | 33 | /* Define to 1 if you have the header file. */ 34 | #undef HAVE_INTTYPES_H 35 | 36 | /* Define to 1 if you have the `nsl' library (-lnsl). */ 37 | #undef HAVE_LIBNSL 38 | 39 | /* Define to 1 if you have the `socket' library (-lsocket). */ 40 | #undef HAVE_LIBSOCKET 41 | 42 | /* Define to 1 if you have the header file. */ 43 | #undef HAVE_LIMITS_H 44 | 45 | /* Define to 1 if you have LOG_PERROR. */ 46 | #undef HAVE_LOG_PERROR 47 | 48 | /* Define to 1 if your system has a GNU libc compatible `malloc' function, and 49 | to 0 otherwise. */ 50 | #undef HAVE_MALLOC 51 | 52 | /* Define to 1 if you have the `memmove' function. */ 53 | #undef HAVE_MEMMOVE 54 | 55 | /* Define to 1 if you have the header file. */ 56 | #undef HAVE_MEMORY_H 57 | 58 | /* Define to 1 if you have the `memset' function. */ 59 | #undef HAVE_MEMSET 60 | 61 | /* Define to 1 if you have the header file. */ 62 | #undef HAVE_NETDB_H 63 | 64 | /* Define to 1 if you have the header file. */ 65 | #undef HAVE_NETINET_IN_H 66 | 67 | /* Define to 1 if you have the header file. */ 68 | #undef HAVE_PATHS_H 69 | 70 | /* Define to 1 if you have the `poll' function. */ 71 | #undef HAVE_POLL 72 | 73 | /* Define to 1 if your system has a GNU libc compatible `realloc' function, 74 | and to 0 otherwise. */ 75 | #undef HAVE_REALLOC 76 | 77 | /* Define to 1 if you have the `select' function. */ 78 | #undef HAVE_SELECT 79 | 80 | /* Define to 1 if you have the `setproctitle' function. */ 81 | #undef HAVE_SETPROCTITLE 82 | 83 | /* Define to 1 if you have the `socket' function. */ 84 | #undef HAVE_SOCKET 85 | 86 | /* Define to 1 if you have socklen_t. */ 87 | #undef HAVE_SOCKLEN_T 88 | 89 | /* Define to 1 if you have the header file. */ 90 | #undef HAVE_STDINT_H 91 | 92 | /* Define to 1 if you have the header file. */ 93 | #undef HAVE_STDLIB_H 94 | 95 | /* Define to 1 if you have the `strchr' function. */ 96 | #undef HAVE_STRCHR 97 | 98 | /* Define to 1 if you have the `strerror' function. */ 99 | #undef HAVE_STRERROR 100 | 101 | /* Define to 1 if you have the `strftime' function. */ 102 | #undef HAVE_STRFTIME 103 | 104 | /* Define to 1 if you have the header file. */ 105 | #undef HAVE_STRINGS_H 106 | 107 | /* Define to 1 if you have the header file. */ 108 | #undef HAVE_STRING_H 109 | 110 | /* Define to 1 if you have the `strncasecmp' function. */ 111 | #undef HAVE_STRNCASECMP 112 | 113 | /* Define to 1 if you have the `strtoul' function. */ 114 | #undef HAVE_STRTOUL 115 | 116 | /* Define to 1 if you have the header file. */ 117 | #undef HAVE_SYSLOG_H 118 | 119 | /* Define to 1 if you have the header file. */ 120 | #undef HAVE_SYS_SELECT_H 121 | 122 | /* Define to 1 if you have the header file. */ 123 | #undef HAVE_SYS_SOCKET_H 124 | 125 | /* Define to 1 if you have the header file. */ 126 | #undef HAVE_SYS_STAT_H 127 | 128 | /* Define to 1 if you have the header file. */ 129 | #undef HAVE_SYS_TIME_H 130 | 131 | /* Define to 1 if you have the header file. */ 132 | #undef HAVE_SYS_TYPES_H 133 | 134 | /* Define to 1 if you have that is POSIX.1 compatible. */ 135 | #undef HAVE_SYS_WAIT_H 136 | 137 | /* Define to 1 if you have uint16_t. */ 138 | #undef HAVE_UINT16_T 139 | 140 | /* Define to 1 if you have the `uname' function. */ 141 | #undef HAVE_UNAME 142 | 143 | /* Define to 1 if you have the header file. */ 144 | #undef HAVE_UNISTD_H 145 | 146 | /* Define to 1 if you have u_int16_t. */ 147 | #undef HAVE_U_INT16_T 148 | 149 | /* Define to 1 if you have the `vfork' function. */ 150 | #undef HAVE_VFORK 151 | 152 | /* Define to 1 if you have the header file. */ 153 | #undef HAVE_VFORK_H 154 | 155 | /* Define to 1 if you have the `vprintf' function. */ 156 | #undef HAVE_VPRINTF 157 | 158 | /* Define to 1 if you have the `vsyslog' function. */ 159 | #undef HAVE_VSYSLOG 160 | 161 | /* Define to 1 if `fork' works. */ 162 | #undef HAVE_WORKING_FORK 163 | 164 | /* Define to 1 if `vfork' works. */ 165 | #undef HAVE_WORKING_VFORK 166 | 167 | /* Define to 1 if you __progname is available. */ 168 | #undef HAVE___PROGNAME 169 | 170 | /* Define to the address where bug reports for this package should be sent. */ 171 | #undef PACKAGE_BUGREPORT 172 | 173 | /* Define to the full name of this package. */ 174 | #undef PACKAGE_NAME 175 | 176 | /* Define to the full name and version of this package. */ 177 | #undef PACKAGE_STRING 178 | 179 | /* Define to the one symbol short name of this package. */ 180 | #undef PACKAGE_TARNAME 181 | 182 | /* Define to the version of this package. */ 183 | #undef PACKAGE_VERSION 184 | 185 | /* Set this to the path to your ssh program. */ 186 | #undef PATH_SSH 187 | 188 | /* Define to the type of arg 1 for `select'. */ 189 | #undef SELECT_TYPE_ARG1 190 | 191 | /* Define to the type of args 2, 3 and 4 for `select'. */ 192 | #undef SELECT_TYPE_ARG234 193 | 194 | /* Define to the type of arg 5 for `select'. */ 195 | #undef SELECT_TYPE_ARG5 196 | 197 | /* Define to 1 if you have the ANSI C header files. */ 198 | #undef STDC_HEADERS 199 | 200 | /* Define to 1 if you can safely include both and . */ 201 | #undef TIME_WITH_SYS_TIME 202 | 203 | /* Define to 1 if your declares `struct tm'. */ 204 | #undef TM_IN_SYS_TIME 205 | 206 | /* Define to empty if `const' does not conform to ANSI C. */ 207 | #undef const 208 | 209 | /* Define to `__inline__' or `__inline' if that's what the C compiler 210 | calls it, or to nothing if 'inline' is not supported under any name. */ 211 | #ifndef __cplusplus 212 | #undef inline 213 | #endif 214 | 215 | /* Define to rpl_malloc if the replacement function should be used. */ 216 | #undef malloc 217 | 218 | /* Define to `int' if does not define. */ 219 | #undef pid_t 220 | 221 | /* Define to rpl_realloc if the replacement function should be used. */ 222 | #undef realloc 223 | 224 | /* Define to `unsigned' if does not define. */ 225 | #undef size_t 226 | 227 | /* Define as `fork' if `vfork' does not work. */ 228 | #undef vfork 229 | 230 | /* Define to empty if the keyword `volatile' does not work. Warning: valid 231 | code using `volatile' can become incorrect without. Disable with care. */ 232 | #undef volatile 233 | -------------------------------------------------------------------------------- /configure.ac: -------------------------------------------------------------------------------- 1 | # -*- Autoconf -*- 2 | # Process this file with autoconf to produce a configure script. 3 | 4 | AC_PREREQ(2.59) 5 | AC_INIT(autossh, 1.4, [Carson Harding ]) 6 | AC_REVISION($Revision: 1.2 $) 7 | AC_CONFIG_SRCDIR([daemon.h]) 8 | AC_CONFIG_HEADER([config.h]) 9 | AC_CONFIG_FILES([Makefile]) 10 | 11 | # Checks for programs. 12 | AC_PROG_CC 13 | # Add -Wall for gcc 14 | if test "$ac_compiler_gnu" = "yes"; then 15 | CFLAGS="$CFLAGS -Wall" 16 | fi 17 | 18 | AC_ARG_WITH(ssh, 19 | AC_HELP_STRING([--with-ssh=ARG], [specify path to ssh executable]), 20 | [ac_cv_path_ssh=$withval], [] 21 | ) 22 | if test -z "$ac_cv_path_ssh"; then 23 | AC_PATH_PROG([ssh], [ssh]) 24 | fi 25 | if test -n "$ac_cv_path_ssh"; then 26 | AC_DEFINE_UNQUOTED( 27 | PATH_SSH, "$ac_cv_path_ssh", 28 | [Set this to the path to your ssh program.] 29 | ) 30 | else 31 | AC_MSG_ERROR([ssh program not found! Modify PATH, or use --with-ssh.]) 32 | fi 33 | path_ssh="$ac_cv_path_ssh" 34 | AC_SUBST(path_ssh) 35 | 36 | # Checks for libraries. 37 | 38 | # Checks for header files. 39 | AC_HEADER_STDC 40 | AC_HEADER_SYS_WAIT 41 | AC_CHECK_HEADERS([arpa/inet.h fcntl.h limits.h netdb.h]) 42 | AC_CHECK_HEADERS([netinet/in.h paths.h stdlib.h string.h sys/socket.h]) 43 | AC_CHECK_HEADERS([sys/time.h syslog.h unistd.h]) 44 | 45 | 46 | # Checks for typedefs, structures, and compiler characteristics. 47 | AC_C_CONST 48 | AC_C_INLINE 49 | AC_TYPE_SIZE_T 50 | AC_HEADER_TIME 51 | AC_STRUCT_TM 52 | AC_C_VOLATILE 53 | AC_CHECK_TYPE(socklen_t, 54 | AC_DEFINE(HAVE_SOCKLEN_T, 1, [Define to 1 if you have socklen_t.]), 55 | [], 56 | [ 57 | #ifdef HAVE_SYS_TYPES_H 58 | # include 59 | #endif 60 | #ifdef HAVE_SYS_SOCKET_H 61 | # include 62 | #endif 63 | #ifdef HAVE_ARPA_INET_H 64 | # include 65 | #endif 66 | ] 67 | ) 68 | AC_CHECK_TYPE([struct addrinfo], 69 | AC_DEFINE(HAVE_ADDRINFO, 1, [Define to 1 if you have struct addrinfo.]), 70 | [], 71 | [ 72 | #ifdef HAVE_NETDB_H 73 | # include 74 | #endif 75 | ] 76 | ) 77 | AC_CHECK_TYPE(u_int16_t, 78 | AC_DEFINE(HAVE_U_INT16_T, 1, [Define to 1 if you have u_int16_t.]), 79 | [], [ 80 | #ifdef HAVE_SYS_TYPES_H 81 | # include 82 | #endif 83 | ] 84 | ) 85 | AC_CHECK_TYPE(uint16_t, 86 | AC_DEFINE(HAVE_UINT16_T, 1, [Define to 1 if you have uint16_t.]), 87 | [], 88 | [ 89 | #ifdef HAVE_SYS_TYPES_H 90 | # include 91 | #endif 92 | ] 93 | ) 94 | AC_CHECK_DECL(LOG_PERROR, 95 | AC_DEFINE(HAVE_LOG_PERROR, 1, [Define to 1 if you have LOG_PERROR.]), 96 | [], 97 | [ 98 | #ifdef HAVE_SYSLOG_H 99 | # include 100 | #endif 101 | ] 102 | ) 103 | 104 | 105 | # Checks for library functions. 106 | AC_FUNC_FORK 107 | AC_FUNC_MALLOC 108 | AC_FUNC_REALLOC 109 | AC_FUNC_SELECT_ARGTYPES 110 | AC_FUNC_STRFTIME 111 | AC_FUNC_VPRINTF 112 | AC_CHECK_FUNCS([alarm daemon dup2 gethostbyname gettimeofday memmove]) 113 | AC_CHECK_FUNCS([memset poll select setproctitle socket strchr strerror]) 114 | AC_CHECK_FUNCS([strncasecmp strtoul uname vsyslog]) 115 | 116 | AC_CHECK_LIB(nsl, gethostbyname) 117 | AC_CHECK_LIB(socket, connect) 118 | 119 | # Other checks 120 | 121 | # Copyright (c) 1999-2004 Damien Miller 122 | # 123 | # Permission to use, copy, modify, and distribute this software for any 124 | # purpose with or without fee is hereby granted, provided that the above 125 | # copyright notice and this permission notice appear in all copies. 126 | # 127 | # THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 128 | # WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 129 | # MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 130 | # ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 131 | # WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 132 | # ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 133 | # OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 134 | AC_CACHE_CHECK([if libc defines __progname], ac_cv_libc_defines___progname, [ 135 | AC_TRY_LINK([], 136 | [ extern char *__progname; printf("%s", __progname); ], 137 | [ ac_cv_libc_defines___progname="yes" ], 138 | [ ac_cv_libc_defines___progname="no" ] 139 | ) 140 | ]) 141 | if test "x$ac_cv_libc_defines___progname" = "xyes" ; then 142 | AC_DEFINE(HAVE___PROGNAME, 1, [Define if libc defines __progname]) 143 | fi 144 | 145 | AC_OUTPUT 146 | -------------------------------------------------------------------------------- /daemon.h: -------------------------------------------------------------------------------- 1 | /* 2 | * this is BSD's daemon() for things that don't have it; cut from OpenBSD 3 | * $Id: daemon.h,v 1.5 2006/05/21 03:33:01 harding Exp $ 4 | */ 5 | 6 | /*- 7 | * Copyright (c) 1990, 1993 8 | * The Regents of the University of California. All rights reserved. 9 | * 10 | * Redistribution and use in source and binary forms, with or without 11 | * modification, are permitted provided that the following conditions 12 | * are met: 13 | * 1. Redistributions of source code must retain the above copyright 14 | * notice, this list of conditions and the following disclaimer. 15 | * 2. Redistributions in binary form must reproduce the above copyright 16 | * notice, this list of conditions and the following disclaimer in the 17 | * documentation and/or other materials provided with the distribution. 18 | * 3. All advertising materials mentioning features or use of this software 19 | * must display the following acknowledgement: 20 | * This product includes software developed by the University of 21 | * California, Berkeley and its contributors. 22 | * 4. Neither the name of the University nor the names of its contributors 23 | * may be used to endorse or promote products derived from this software 24 | * without specific prior written permission. 25 | * 26 | * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 27 | * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 28 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 29 | * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 30 | * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 31 | * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 32 | * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 33 | * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 34 | * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 35 | * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 36 | * SUCH DAMAGE. 37 | */ 38 | 39 | #if !defined(HAVE_DAEMON) 40 | 41 | #if defined(LIBC_SCCS) && !defined(lint) 42 | static char rcsid[] = "$OpenBSD: daemon.c,v 1.2 1996/08/19 08:22:13 tholo Exp $"; 43 | #endif /* LIBC_SCCS and not lint */ 44 | 45 | int daemon(int nochdir, int noclose); 46 | 47 | int 48 | daemon(int nochdir, int noclose) 49 | { 50 | int fd; 51 | 52 | switch (fork()) { 53 | case -1: 54 | return -1; 55 | case 0: 56 | break; 57 | default: 58 | _exit(0); 59 | } 60 | 61 | if (setsid() == -1) 62 | return -1; 63 | 64 | if (!nochdir) 65 | (void)chdir("/"); 66 | 67 | if (!noclose && (fd = open(_PATH_DEVNULL, O_RDWR, 0)) != -1) { 68 | (void)dup2(fd, STDIN_FILENO); 69 | (void)dup2(fd, STDOUT_FILENO); 70 | (void)dup2(fd, STDERR_FILENO); 71 | if (fd > 2) 72 | (void)close (fd); 73 | } 74 | return 0; 75 | } 76 | #endif 77 | -------------------------------------------------------------------------------- /fakepoll.h: -------------------------------------------------------------------------------- 1 | /* fakepoll.h 2 | * poll using select 3 | * 4 | * Warning: a call to this poll() takes about 4K of stack space. 5 | * Greg Parker gparker-web@sealiesoftware.com December 2000 6 | * This code is in the public domain and may be copied or modified without 7 | * permission. 8 | * 9 | * Updated May 2002: 10 | * * fix crash when an fd is less than 0 11 | * * set errno=EINVAL if an fd is greater or equal to FD_SETSIZE 12 | * * don't set POLLIN or POLLOUT in revents if it wasn't requested 13 | * in events (only happens when an fd is in the poll set twice) 14 | * 15 | * Updated Dec 2002: 16 | * * change comment style (for autossh, Carson Harding) 17 | * * undef FD_SETSIZE first 18 | */ 19 | 20 | #ifndef _FAKE_POLL_H 21 | #define _FAKE_POLL_H 22 | 23 | #include 24 | #undef FD_SETSIZE 25 | #define FD_SETSIZE OPEN_MAX 26 | #include 27 | #include 28 | #include 29 | #include 30 | 31 | typedef struct pollfd { 32 | int fd; /* file desc to poll */ 33 | short events; /* events of interest on fd */ 34 | short revents; /* events that occurred on fd */ 35 | } pollfd_t; 36 | 37 | 38 | /* poll flags */ 39 | #define POLLIN 0x0001 40 | #define POLLOUT 0x0004 41 | #define POLLERR 0x0008 42 | 43 | /* synonyms */ 44 | #define POLLNORM POLLIN 45 | #define POLLPRI POLLIN 46 | #define POLLRDNORM POLLIN 47 | #define POLLRDBAND POLLIN 48 | #define POLLWRNORM POLLOUT 49 | #define POLLWRBAND POLLOUT 50 | 51 | /* ignored */ 52 | #define POLLHUP 0x0010 53 | #define POLLNVAL 0x0020 54 | 55 | inline int poll(struct pollfd *pollSet, int pollCount, int pollTimeout) 56 | { 57 | struct timeval tv; 58 | struct timeval *tvp; 59 | fd_set readFDs, writeFDs, exceptFDs; 60 | fd_set *readp, *writep, *exceptp; 61 | struct pollfd *pollEnd, *p; 62 | int selected; 63 | int result; 64 | int maxFD; 65 | 66 | if (!pollSet) { 67 | pollEnd = NULL; 68 | readp = NULL; 69 | writep = NULL; 70 | exceptp = NULL; 71 | maxFD = 0; 72 | } 73 | else { 74 | pollEnd = pollSet + pollCount; 75 | readp = &readFDs; 76 | writep = &writeFDs; 77 | exceptp = &exceptFDs; 78 | 79 | FD_ZERO(readp); 80 | FD_ZERO(writep); 81 | FD_ZERO(exceptp); 82 | 83 | /* Find the biggest fd in the poll set */ 84 | maxFD = 0; 85 | for (p = pollSet; p < pollEnd; p++) { 86 | if (p->fd > maxFD) maxFD = p->fd; 87 | } 88 | 89 | if (maxFD >= FD_SETSIZE) { 90 | /* At least one fd is too big */ 91 | errno = EINVAL; 92 | return -1; 93 | } 94 | 95 | /* Transcribe flags from the poll set to the fd sets */ 96 | for (p = pollSet; p < pollEnd; p++) { 97 | if (p->fd < 0) { 98 | /* Negative fd checks nothing and always reports zero */ 99 | } else { 100 | if (p->events & POLLIN) FD_SET(p->fd, readp); 101 | if (p->events & POLLOUT) FD_SET(p->fd, writep); 102 | if (p->events != 0) FD_SET(p->fd, exceptp); 103 | /* POLLERR is never set coming in; poll() always reports errors 104 | * But don't report if we're not listening to anything at all. 105 | */ 106 | } 107 | } 108 | } 109 | 110 | /* poll timeout is in milliseconds. Convert to struct timeval. 111 | * poll timeout == -1 : wait forever : select timeout of NULL 112 | * poll timeout == 0 : return immediately : select timeout of zero 113 | */ 114 | if (pollTimeout >= 0) { 115 | tv.tv_sec = pollTimeout / 1000; 116 | tv.tv_usec = (pollTimeout % 1000) * 1000; 117 | tvp = &tv; 118 | } else { 119 | tvp = NULL; 120 | } 121 | 122 | 123 | selected = select(maxFD+1, readp, writep, exceptp, tvp); 124 | 125 | 126 | if (selected < 0) { 127 | /* Error during select */ 128 | result = -1; 129 | } 130 | else if (selected > 0) { 131 | /* Select found something 132 | * Transcribe result from fd sets to poll set. 133 | * Also count the number of selected fds. poll returns the 134 | * number of ready fds; select returns the number of bits set. 135 | */ 136 | int polled = 0; 137 | for (p = pollSet; p < pollEnd; p++) { 138 | p->revents = 0; 139 | if (p->fd < 0) { 140 | /* Negative fd always reports zero */ 141 | } else { 142 | if ((p->events & POLLIN) && FD_ISSET(p->fd, readp)) { 143 | p->revents |= POLLIN; 144 | } 145 | if ((p->events & POLLOUT) && FD_ISSET(p->fd, writep)) { 146 | p->revents |= POLLOUT; 147 | } 148 | if ((p->events != 0) && FD_ISSET(p->fd, exceptp)) { 149 | p->revents |= POLLERR; 150 | } 151 | 152 | if (p->revents) polled++; 153 | } 154 | } 155 | result = polled; 156 | } 157 | else { 158 | /* selected == 0, select timed out before anything happened 159 | * Clear all result bits and return zero. 160 | */ 161 | for (p = pollSet; p < pollEnd; p++) { 162 | p->revents = 0; 163 | } 164 | result = 0; 165 | } 166 | 167 | return result; 168 | } 169 | 170 | 171 | #endif 172 | -------------------------------------------------------------------------------- /rscreen: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | # 3 | # sample script to use autossh to open up a remote screen 4 | # session, or reconnect to an existing one. 5 | # 6 | # $Id: rscreen,v 1.4 2002/05/07 17:54:13 harding Exp $ 7 | # 8 | if [ "X$1" = "X" ]; then 9 | echo "usage: `basename $0` " 10 | exit 1 11 | fi 12 | 13 | if [ "X$SSH_AUTH_SOCK" = "X" ]; then 14 | eval `ssh-agent -s` 15 | ssh-add $HOME/.ssh/id_rsa 16 | fi 17 | 18 | #AUTOSSH_POLL=600 19 | #AUTOSSH_PORT=20000 20 | #AUTOSSH_GATETIME=30 21 | #AUTOSSH_LOGFILE=$HOST.log 22 | #AUTOSSH_DEBUG=yes 23 | #AUTOSSH_PATH=/usr/local/bin/ssh 24 | export AUTOSSH_POLL AUTOSSH_LOGFILE AUTOSSH_DEBUG AUTOSSH_PATH AUTOSSH_GATETIME AUTOSSH_PORT 25 | 26 | autossh -M 20004 -t $1 "screen -e^Zz -D -R" 27 | -------------------------------------------------------------------------------- /wrapper: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | if [ $1 == '-t' ]; then ATTR="-t "; shift; fi 3 | ADDR=$1; shift 4 | 5 | while [ `echo $1 | egrep -c "^\-[a-zA-Z]$"` -eq 1 ]; do 6 | SSH_ATTR="${SSH_ATTR} $1"; shift 7 | done 8 | 9 | export AUTOSSH="/usr/local/src/autossh-1.4c/autossh" 10 | #export AUTOSSH_LOGFILE="/tmp/autossh.log" 11 | #export AUTOSSH_DEBUG=no 12 | export AUTOSSH_PORT=$[ ( $RANDOM % 1000 ) + 20000 ] 13 | 14 | ${AUTOSSH} ${ATTR} "${ADDR}" ${SSH_ATTR} -- "$@" 15 | --------------------------------------------------------------------------------