Bugzilla – Attachment 1897 Details for
Bug 1330
ControlPersist: fork and leave ControlMaster behind as a daemon
Home
|
New
|
Browse
|
Search
|
[?]
|
Reports
|
Requests
|
Help
|
New Account
|
Log In
[x]
|
Forgot Password
Login:
[x]
[patch]
/home/djm/ssh-controlpersist1.diff
ssh-controlpersist1.diff (text/plain), 13.62 KB, created by
Damien Miller
on 2010-07-13 14:18:16 AEST
(
hide
)
Description:
/home/djm/ssh-controlpersist1.diff
Filename:
MIME Type:
Creator:
Damien Miller
Created:
2010-07-13 14:18:16 AEST
Size:
13.62 KB
patch
obsolete
>Index: clientloop.c >=================================================================== >RCS file: /cvs/src/usr.bin/ssh/clientloop.c,v >retrieving revision 1.221 >diff -u -p -r1.221 clientloop.c >--- clientloop.c 25 Jun 2010 23:15:36 -0000 1.221 >+++ clientloop.c 13 Jul 2010 04:16:02 -0000 >@@ -137,6 +137,9 @@ static volatile sig_atomic_t received_si > /* Flag indicating whether the user's terminal is in non-blocking mode. */ > static int in_non_blocking_mode = 0; > >+/* Time when backgrounded control master using ControlPersist should exit */ >+static time_t control_persist_exit_time = 0; >+ > /* Common data for the client loop code. */ > volatile sig_atomic_t quit_pending; /* Set non-zero to quit the loop. */ > static int escape_char1; /* Escape character. (proto1 only) */ >@@ -244,6 +247,34 @@ get_current_time(void) > return (double) tv.tv_sec + (double) tv.tv_usec / 1000000.0; > } > >+/* >+ * Sets control_persist_exit_time to the absolute time when the >+ * backgrounded control master should exit due to expiry of the >+ * ControlPersist timeout. Sets it to 0 if we are not a backgrounded >+ * control master process, or if there is no ControlPersist timeout. >+ */ >+static void >+set_control_persist_exit_time(void) >+{ >+ if (muxserver_sock == -1 || !options.control_persist >+ || options.control_persist_timeout == 0) >+ /* not using a ControlPersist timeout */ >+ control_persist_exit_time = 0; >+ else if (channel_still_open()) { >+ /* some client connections are still open */ >+ if (control_persist_exit_time > 0) >+ debug2("%s: cancel scheduled exit", __func__); >+ control_persist_exit_time = 0; >+ } else if (control_persist_exit_time <= 0) { >+ /* a client connection has recently closed */ >+ control_persist_exit_time = time(NULL) + >+ (time_t)options.control_persist_timeout; >+ debug2("%s: schedule exit in %d seconds", __func__, >+ options.control_persist_timeout); >+ } >+ /* else we are already counting down to the timeout */ >+} >+ > #define SSH_X11_PROTO "MIT-MAGIC-COOKIE-1" > void > client_x11_get_proto(const char *display, const char *xauth_path, >@@ -525,6 +556,7 @@ client_wait_until_can_do_something(fd_se > int *maxfdp, u_int *nallocp, int rekeying) > { > struct timeval tv, *tvp; >+ int timeout_secs; > int ret; > > /* Add any selections by the channel mechanism. */ >@@ -568,16 +600,27 @@ client_wait_until_can_do_something(fd_se > /* > * Wait for something to happen. This will suspend the process until > * some selected descriptor can be read, written, or has some other >- * event pending. >+ * event pending, or a timeout expires. > */ > >- if (options.server_alive_interval == 0 || !compat20) >+ timeout_secs = INT_MAX; /* we use INT_MAX to mean no timeout */ >+ if (options.server_alive_interval > 0 && compat20) >+ timeout_secs = options.server_alive_interval; >+ set_control_persist_exit_time(); >+ if (control_persist_exit_time > 0) { >+ timeout_secs = MIN(timeout_secs, >+ control_persist_exit_time - time(NULL)); >+ if (timeout_secs < 0) >+ timeout_secs = 0; >+ } >+ if (timeout_secs == INT_MAX) > tvp = NULL; > else { >- tv.tv_sec = options.server_alive_interval; >+ tv.tv_sec = timeout_secs; > tv.tv_usec = 0; > tvp = &tv; > } >+ > ret = select((*maxfdp)+1, *readsetp, *writesetp, NULL, tvp); > if (ret < 0) { > char buf[100]; >@@ -1466,6 +1509,18 @@ client_loop(int have_pty, int escape_cha > */ > if (FD_ISSET(connection_out, writeset)) > packet_write_poll(); >+ >+ /* >+ * If we are a backgrounded control master, and the >+ * timeout has expired without any active client >+ * connections, then quit. >+ */ >+ if (control_persist_exit_time > 0) { >+ if (time(NULL) >= control_persist_exit_time) { >+ debug("ControlPersist timeout expired"); >+ break; >+ } >+ } > } > if (readset) > xfree(readset); >Index: readconf.c >=================================================================== >RCS file: /cvs/src/usr.bin/ssh/readconf.c,v >retrieving revision 1.186 >diff -u -p -r1.186 readconf.c >--- readconf.c 25 Jun 2010 23:15:36 -0000 1.186 >+++ readconf.c 13 Jul 2010 04:16:02 -0000 >@@ -125,7 +125,8 @@ typedef enum { > oEnableSSHKeysign, oRekeyLimit, oVerifyHostKeyDNS, oConnectTimeout, > oAddressFamily, oGssAuthentication, oGssDelegateCreds, > oServerAliveInterval, oServerAliveCountMax, oIdentitiesOnly, >- oSendEnv, oControlPath, oControlMaster, oHashKnownHosts, >+ oSendEnv, oControlPath, oControlMaster, oControlPersist, >+ oHashKnownHosts, > oTunnel, oTunnelDevice, oLocalCommand, oPermitLocalCommand, > oVisualHostKey, oUseRoaming, oZeroKnowledgePasswordAuthentication, > oDeprecated, oUnsupported >@@ -222,6 +223,7 @@ static struct { > { "sendenv", oSendEnv }, > { "controlpath", oControlPath }, > { "controlmaster", oControlMaster }, >+ { "controlpersist", oControlPersist }, > { "hashknownhosts", oHashKnownHosts }, > { "tunnel", oTunnel }, > { "tunneldevice", oTunnelDevice }, >@@ -878,6 +880,30 @@ parse_int: > *intptr = value; > break; > >+ case oControlPersist: >+ /* no/false/yes/true, or a time spec */ >+ intptr = &options->control_persist; >+ arg = strdelim(&s); >+ if (!arg || *arg == '\0') >+ fatal("%.200s line %d: Missing ControlPersist" >+ " argument.", filename, linenum); >+ value = 0; >+ value2 = 0; /* timeout */ >+ if (strcmp(arg, "no") == 0 || strcmp(arg, "false") == 0) >+ value = 0; >+ else if (strcmp(arg, "yes") == 0 || strcmp(arg, "true") == 0) >+ value = 1; >+ else if ((value2 = convtime(arg)) >= 0) >+ value = 1; >+ else >+ fatal("%.200s line %d: Bad ControlPersist argument.", >+ filename, linenum); >+ if (*activep && *intptr == -1) { >+ *intptr = value; >+ options->control_persist_timeout = value2; >+ } >+ break; >+ > case oHashKnownHosts: > intptr = &options->hash_known_hosts; > goto parse_flag; >@@ -1079,6 +1105,8 @@ initialize_options(Options * options) > options->num_send_env = 0; > options->control_path = NULL; > options->control_master = -1; >+ options->control_persist = -1; >+ options->control_persist_timeout = 0; > options->hash_known_hosts = -1; > options->tun_open = -1; > options->tun_local = -1; >@@ -1214,6 +1242,10 @@ fill_default_options(Options * options) > options->server_alive_count_max = 3; > if (options->control_master == -1) > options->control_master = 0; >+ if (options->control_persist == -1) { >+ options->control_persist = 0; >+ options->control_persist_timeout = 0; >+ } > if (options->hash_known_hosts == -1) > options->hash_known_hosts = 0; > if (options->tun_open == -1) >Index: readconf.h >=================================================================== >RCS file: /cvs/src/usr.bin/ssh/readconf.h,v >retrieving revision 1.85 >diff -u -p -r1.85 readconf.h >--- readconf.h 25 Jun 2010 23:15:36 -0000 1.85 >+++ readconf.h 13 Jul 2010 04:16:02 -0000 >@@ -114,6 +114,8 @@ typedef struct { > > char *control_path; > int control_master; >+ int control_persist; /* ControlPersist flag */ >+ int control_persist_timeout; /* ControlPersist timeout (seconds) */ > > int hash_known_hosts; > >Index: ssh.c >=================================================================== >RCS file: /cvs/src/usr.bin/ssh/ssh.c,v >retrieving revision 1.343 >diff -u -p -r1.343 ssh.c >--- ssh.c 12 Jul 2010 22:41:13 -0000 1.343 >+++ ssh.c 13 Jul 2010 04:16:03 -0000 >@@ -119,6 +119,15 @@ int no_shell_flag = 0; > int stdin_null_flag = 0; > > /* >+ * Flag indicating that the current process should be backgrounded and >+ * a new slave launched in the foreground for ControlPersist. >+ */ >+int need_controlpersist_detach = 0; >+ >+/* Copies of flags for ControlPersist foreground slave */ >+int ostdin_null_flag, ono_shell_flag, ono_tty_flag, otty_flag; >+ >+/* > * Flag indicating that ssh should fork after authentication. This is useful > * so that the passphrase can be entered manually, and then ssh goes to the > * background. >@@ -719,6 +728,8 @@ main(int ac, char **av) > fatal("No ControlPath specified for \"-O\" command"); > if (options.control_path != NULL) > muxclient(options.control_path); >+ /* If muxclient() can connect to a master socket, >+ * then it doesn't return. */ > > timeout_ms = options.connection_timeout * 1000; > >@@ -858,6 +869,50 @@ main(int ac, char **av) > return exit_status; > } > >+static void >+control_persist_detach(void) >+{ >+ pid_t pid; >+ >+ debug("%s: backgrounding master process", __func__); >+ >+ /* >+ * master (current process) into the background, and make the >+ * foreground process a client of the backgrounded master. >+ */ >+ switch ((pid = fork())) { >+ case -1: >+ fatal("%s: fork: %s", __func__, strerror(errno)); >+ case 0: >+ /* Child: master process continues mainloop */ >+ break; >+ default: >+ /* Parent: set up mux slave to connect to backgrounded master */ >+ debug2("%s: background process is %ld", __func__, (long)pid); >+ stdin_null_flag = ostdin_null_flag; >+ no_shell_flag = ono_shell_flag; >+ no_tty_flag = ono_tty_flag; >+ tty_flag = otty_flag; >+ close(muxserver_sock); >+ muxserver_sock = -1; >+ muxclient(options.control_path); >+ /* muxclient() doesn't return on success. */ >+ fatal("Failed to connect to new control master"); >+ } >+} >+ >+/* Do fork() after authentication. Used by "ssh -f" */ >+static void >+fork_postauth(void) >+{ >+ if (need_controlpersist_detach) >+ control_persist_detach(); >+ debug("forking to background"); >+ fork_after_authentication_flag = 0; >+ if (daemon(1, 1) < 0) >+ fatal("daemon() failed: %.200s", strerror(errno)); >+} >+ > /* Callback for remote forward global requests */ > static void > ssh_confirm_remote_forward(int type, u_int32_t seq, void *ctxt) >@@ -885,12 +940,8 @@ ssh_confirm_remote_forward(int type, u_i > } > if (++remote_forward_confirms_received == options.num_remote_forwards) { > debug("All remote forwarding requests processed"); >- if (fork_after_authentication_flag) { >- fork_after_authentication_flag = 0; >- if (daemon(1, 1) < 0) >- fatal("daemon() failed: %.200s", >- strerror(errno)); >- } >+ if (fork_after_authentication_flag) >+ fork_postauth(); > } > } > >@@ -1134,12 +1185,13 @@ ssh_session(void) > * If requested and we are not interested in replies to remote > * forwarding requests, then let ssh continue in the background. > */ >- if (fork_after_authentication_flag && >- (!options.exit_on_forward_failure || >- options.num_remote_forwards == 0)) { >- fork_after_authentication_flag = 0; >- if (daemon(1, 1) < 0) >- fatal("daemon() failed: %.200s", strerror(errno)); >+ if (fork_after_authentication_flag) { >+ if (options.exit_on_forward_failure && >+ options.num_remote_forwards > 0) { >+ debug("deferring postauth fork until remote forward " >+ "confirmation received"); >+ } else >+ fork_postauth(); > } > > /* >@@ -1262,6 +1314,31 @@ ssh_session2(void) > /* XXX should be pre-session */ > ssh_init_forwarding(); > >+ /* Start listening for multiplex clients */ >+ muxserver_listen(); >+ >+ /* >+ * If we are in control persist mode, then prepare to background >+ * ourselves and have a foreground client attach as a control >+ * slave. NB. we must save copies of the flags that we override for >+ * the backgrounding, since we defer attachment of the slave until >+ * after the connection is fully established (in particular, >+ * async rfwd replies have been received for ExitOnForwardFailure). >+ */ >+ if (options.control_persist && muxserver_sock != -1) { >+ ostdin_null_flag = stdin_null_flag; >+ ono_shell_flag = no_shell_flag; >+ ono_tty_flag = no_tty_flag; >+ otty_flag = tty_flag; >+ stdin_null_flag = 1; >+ no_shell_flag = 1; >+ no_tty_flag = 1; >+ tty_flag = 0; >+ if (!fork_after_authentication_flag) >+ need_controlpersist_detach = 1; >+ fork_after_authentication_flag = 1; >+ } >+ > if (!no_shell_flag || (datafellows & SSH_BUG_DUMMYCHAN)) > id = ssh_session2_open(); > >@@ -1280,19 +1357,17 @@ ssh_session2(void) > options.permit_local_command) > ssh_local_cmd(options.local_command); > >- /* Start listening for multiplex clients */ >- muxserver_listen(); >- > /* > * If requested and we are not interested in replies to remote > * forwarding requests, then let ssh continue in the background. > */ >- if (fork_after_authentication_flag && >- (!options.exit_on_forward_failure || >- options.num_remote_forwards == 0)) { >- fork_after_authentication_flag = 0; >- if (daemon(1, 1) < 0) >- fatal("daemon() failed: %.200s", strerror(errno)); >+ if (fork_after_authentication_flag) { >+ if (options.exit_on_forward_failure && >+ options.num_remote_forwards > 0) { >+ debug("deferring postauth fork until remote forward " >+ "confirmation received"); >+ } else >+ fork_postauth(); > } > > if (options.use_roaming) >Index: ssh_config.5 >=================================================================== >RCS file: /cvs/src/usr.bin/ssh/ssh_config.5,v >retrieving revision 1.136 >diff -u -p -r1.136 ssh_config.5 >--- ssh_config.5 12 Jul 2010 22:41:13 -0000 1.136 >+++ ssh_config.5 13 Jul 2010 04:16:03 -0000 >@@ -319,6 +319,28 @@ It is recommended that any > used for opportunistic connection sharing include > at least %h, %p, and %r. > This ensures that shared connections are uniquely identified. >+.It Cm ControlPersist >+When used in conjunction with >+.Cm ControlMaster , >+specifies that the master connection should remain open >+in the background (waiting for future client connections) >+after the initial client connection has been closed. >+If set to >+.Dq no , >+then the master connection will not be placed into the background, >+and will close as soon as the initial client connection is closed. >+If set to >+.Dq yes , >+then the master connection will remain in the background indefinitely >+(until killed or closed via a mechanism such as the >+.Xr ssh 1 >+.Dq Fl O No exit >+option). >+If set to a time in seconds, or a time in any of the formats documented in >+.Xr sshd_config 5 , >+then the backgrounded master connection will automatically terminate >+after it has remained idle (with no client connections) for the >+specified time. > .It Cm DynamicForward > Specifies that a TCP port on the local machine be forwarded > over the secure channel, and the application
You cannot view the attachment while viewing its details because your browser does not support IFRAMEs.
View the attachment on a separate page
.
View Attachment As Diff
View Attachment As Raw
Actions:
View
|
Diff
Attachments on
bug 1330
:
1319
|
1322
|
1331
|
1338
|
1605
|
1858
| 1897