View | Details | Raw Unified | Return to bug 1330 | Differences between
and this patch

Collapse All | Expand All

(-)clientloop.c (-3 / +58 lines)
Lines 137-142 static volatile sig_atomic_t received_si Link Here
137
/* Flag indicating whether the user's terminal is in non-blocking mode. */
137
/* Flag indicating whether the user's terminal is in non-blocking mode. */
138
static int in_non_blocking_mode = 0;
138
static int in_non_blocking_mode = 0;
139
139
140
/* Time when backgrounded control master using ControlPersist should exit */
141
static time_t control_persist_exit_time = 0;
142
140
/* Common data for the client loop code. */
143
/* Common data for the client loop code. */
141
volatile sig_atomic_t quit_pending; /* Set non-zero to quit the loop. */
144
volatile sig_atomic_t quit_pending; /* Set non-zero to quit the loop. */
142
static int escape_char1;	/* Escape character. (proto1 only) */
145
static int escape_char1;	/* Escape character. (proto1 only) */
Lines 244-249 get_current_time(void) Link Here
244
	return (double) tv.tv_sec + (double) tv.tv_usec / 1000000.0;
247
	return (double) tv.tv_sec + (double) tv.tv_usec / 1000000.0;
245
}
248
}
246
249
250
/*
251
 * Sets control_persist_exit_time to the absolute time when the
252
 * backgrounded control master should exit due to expiry of the
253
 * ControlPersist timeout.  Sets it to 0 if we are not a backgrounded
254
 * control master process, or if there is no ControlPersist timeout.
255
 */
256
static void
257
set_control_persist_exit_time(void)
258
{
259
	if (muxserver_sock == -1 || !options.control_persist
260
	    || options.control_persist_timeout == 0)
261
		/* not using a ControlPersist timeout */
262
		control_persist_exit_time = 0;
263
	else if (channel_still_open()) {
264
		/* some client connections are still open */
265
		if (control_persist_exit_time > 0)
266
			debug2("%s: cancel scheduled exit", __func__);
267
		control_persist_exit_time = 0;
268
	} else if (control_persist_exit_time <= 0) {
269
		/* a client connection has recently closed */
270
		control_persist_exit_time = time(NULL) +
271
			(time_t)options.control_persist_timeout;
272
		debug2("%s: schedule exit in %d seconds", __func__,
273
		    options.control_persist_timeout);
274
	}
275
	/* else we are already counting down to the timeout */
276
}
277
247
#define SSH_X11_PROTO "MIT-MAGIC-COOKIE-1"
278
#define SSH_X11_PROTO "MIT-MAGIC-COOKIE-1"
248
void
279
void
249
client_x11_get_proto(const char *display, const char *xauth_path,
280
client_x11_get_proto(const char *display, const char *xauth_path,
Lines 525-530 client_wait_until_can_do_something(fd_se Link Here
525
    int *maxfdp, u_int *nallocp, int rekeying)
556
    int *maxfdp, u_int *nallocp, int rekeying)
526
{
557
{
527
	struct timeval tv, *tvp;
558
	struct timeval tv, *tvp;
559
	int timeout_secs;
528
	int ret;
560
	int ret;
529
561
530
	/* Add any selections by the channel mechanism. */
562
	/* Add any selections by the channel mechanism. */
Lines 568-583 client_wait_until_can_do_something(fd_se Link Here
568
	/*
600
	/*
569
	 * Wait for something to happen.  This will suspend the process until
601
	 * Wait for something to happen.  This will suspend the process until
570
	 * some selected descriptor can be read, written, or has some other
602
	 * some selected descriptor can be read, written, or has some other
571
	 * event pending.
603
	 * event pending, or a timeout expires.
572
	 */
604
	 */
573
605
574
	if (options.server_alive_interval == 0 || !compat20)
606
	timeout_secs = INT_MAX; /* we use INT_MAX to mean no timeout */
607
	if (options.server_alive_interval > 0 && compat20)
608
		timeout_secs = options.server_alive_interval;
609
	set_control_persist_exit_time();
610
	if (control_persist_exit_time > 0) {
611
		timeout_secs = MIN(timeout_secs,
612
			control_persist_exit_time - time(NULL));
613
		if (timeout_secs < 0)
614
			timeout_secs = 0;
615
	}
616
	if (timeout_secs == INT_MAX)
575
		tvp = NULL;
617
		tvp = NULL;
576
	else {
618
	else {
577
		tv.tv_sec = options.server_alive_interval;
619
		tv.tv_sec = timeout_secs;
578
		tv.tv_usec = 0;
620
		tv.tv_usec = 0;
579
		tvp = &tv;
621
		tvp = &tv;
580
	}
622
	}
623
581
	ret = select((*maxfdp)+1, *readsetp, *writesetp, NULL, tvp);
624
	ret = select((*maxfdp)+1, *readsetp, *writesetp, NULL, tvp);
582
	if (ret < 0) {
625
	if (ret < 0) {
583
		char buf[100];
626
		char buf[100];
Lines 1466-1471 client_loop(int have_pty, int escape_cha Link Here
1466
		 */
1509
		 */
1467
		if (FD_ISSET(connection_out, writeset))
1510
		if (FD_ISSET(connection_out, writeset))
1468
			packet_write_poll();
1511
			packet_write_poll();
1512
1513
		/*
1514
		 * If we are a backgrounded control master, and the
1515
		 * timeout has expired without any active client
1516
		 * connections, then quit.
1517
		 */
1518
		if (control_persist_exit_time > 0) {
1519
			if (time(NULL) >= control_persist_exit_time) {
1520
				debug("ControlPersist timeout expired");
1521
				break;
1522
			}
1523
		}
1469
	}
1524
	}
1470
	if (readset)
1525
	if (readset)
1471
		xfree(readset);
1526
		xfree(readset);
(-)readconf.c (-1 / +33 lines)
Lines 125-131 typedef enum { Link Here
125
	oEnableSSHKeysign, oRekeyLimit, oVerifyHostKeyDNS, oConnectTimeout,
125
	oEnableSSHKeysign, oRekeyLimit, oVerifyHostKeyDNS, oConnectTimeout,
126
	oAddressFamily, oGssAuthentication, oGssDelegateCreds,
126
	oAddressFamily, oGssAuthentication, oGssDelegateCreds,
127
	oServerAliveInterval, oServerAliveCountMax, oIdentitiesOnly,
127
	oServerAliveInterval, oServerAliveCountMax, oIdentitiesOnly,
128
	oSendEnv, oControlPath, oControlMaster, oHashKnownHosts,
128
	oSendEnv, oControlPath, oControlMaster, oControlPersist,
129
	oHashKnownHosts,
129
	oTunnel, oTunnelDevice, oLocalCommand, oPermitLocalCommand,
130
	oTunnel, oTunnelDevice, oLocalCommand, oPermitLocalCommand,
130
	oVisualHostKey, oUseRoaming, oZeroKnowledgePasswordAuthentication,
131
	oVisualHostKey, oUseRoaming, oZeroKnowledgePasswordAuthentication,
131
	oDeprecated, oUnsupported
132
	oDeprecated, oUnsupported
Lines 222-227 static struct { Link Here
222
	{ "sendenv", oSendEnv },
223
	{ "sendenv", oSendEnv },
223
	{ "controlpath", oControlPath },
224
	{ "controlpath", oControlPath },
224
	{ "controlmaster", oControlMaster },
225
	{ "controlmaster", oControlMaster },
226
	{ "controlpersist", oControlPersist },
225
	{ "hashknownhosts", oHashKnownHosts },
227
	{ "hashknownhosts", oHashKnownHosts },
226
	{ "tunnel", oTunnel },
228
	{ "tunnel", oTunnel },
227
	{ "tunneldevice", oTunnelDevice },
229
	{ "tunneldevice", oTunnelDevice },
Lines 878-883 parse_int: Link Here
878
			*intptr = value;
880
			*intptr = value;
879
		break;
881
		break;
880
882
883
	case oControlPersist:
884
		/* no/false/yes/true, or a time spec */
885
		intptr = &options->control_persist;
886
		arg = strdelim(&s);
887
		if (!arg || *arg == '\0')
888
			fatal("%.200s line %d: Missing ControlPersist"
889
			    " argument.", filename, linenum);
890
		value = 0;
891
		value2 = 0;	/* timeout */
892
		if (strcmp(arg, "no") == 0 || strcmp(arg, "false") == 0)
893
			value = 0;
894
		else if (strcmp(arg, "yes") == 0 || strcmp(arg, "true") == 0)
895
			value = 1;
896
		else if ((value2 = convtime(arg)) >= 0)
897
			value = 1;
898
		else
899
			fatal("%.200s line %d: Bad ControlPersist argument.",
900
			    filename, linenum);
901
		if (*activep && *intptr == -1) {
902
			*intptr = value;
903
			options->control_persist_timeout = value2;
904
		}
905
		break;
906
881
	case oHashKnownHosts:
907
	case oHashKnownHosts:
882
		intptr = &options->hash_known_hosts;
908
		intptr = &options->hash_known_hosts;
883
		goto parse_flag;
909
		goto parse_flag;
Lines 1079-1084 initialize_options(Options * options) Link Here
1079
	options->num_send_env = 0;
1105
	options->num_send_env = 0;
1080
	options->control_path = NULL;
1106
	options->control_path = NULL;
1081
	options->control_master = -1;
1107
	options->control_master = -1;
1108
	options->control_persist = -1;
1109
	options->control_persist_timeout = 0;
1082
	options->hash_known_hosts = -1;
1110
	options->hash_known_hosts = -1;
1083
	options->tun_open = -1;
1111
	options->tun_open = -1;
1084
	options->tun_local = -1;
1112
	options->tun_local = -1;
Lines 1214-1219 fill_default_options(Options * options) Link Here
1214
		options->server_alive_count_max = 3;
1242
		options->server_alive_count_max = 3;
1215
	if (options->control_master == -1)
1243
	if (options->control_master == -1)
1216
		options->control_master = 0;
1244
		options->control_master = 0;
1245
	if (options->control_persist == -1) {
1246
		options->control_persist = 0;
1247
		options->control_persist_timeout = 0;
1248
	}
1217
	if (options->hash_known_hosts == -1)
1249
	if (options->hash_known_hosts == -1)
1218
		options->hash_known_hosts = 0;
1250
		options->hash_known_hosts = 0;
1219
	if (options->tun_open == -1)
1251
	if (options->tun_open == -1)
(-)readconf.h (+2 lines)
Lines 114-119 typedef struct { Link Here
114
114
115
	char	*control_path;
115
	char	*control_path;
116
	int	control_master;
116
	int	control_master;
117
	int     control_persist; /* ControlPersist flag */
118
	int     control_persist_timeout; /* ControlPersist timeout (seconds) */
117
119
118
	int	hash_known_hosts;
120
	int	hash_known_hosts;
119
121
(-)ssh.c (-21 / +96 lines)
Lines 119-124 int no_shell_flag = 0; Link Here
119
int stdin_null_flag = 0;
119
int stdin_null_flag = 0;
120
120
121
/*
121
/*
122
 * Flag indicating that the current process should be backgrounded and
123
 * a new slave launched in the foreground for ControlPersist.
124
 */
125
int need_controlpersist_detach = 0;
126
127
/* Copies of flags for ControlPersist foreground slave */
128
int ostdin_null_flag, ono_shell_flag, ono_tty_flag, otty_flag;
129
130
/*
122
 * Flag indicating that ssh should fork after authentication.  This is useful
131
 * Flag indicating that ssh should fork after authentication.  This is useful
123
 * so that the passphrase can be entered manually, and then ssh goes to the
132
 * so that the passphrase can be entered manually, and then ssh goes to the
124
 * background.
133
 * background.
Lines 719-724 main(int ac, char **av) Link Here
719
		fatal("No ControlPath specified for \"-O\" command");
728
		fatal("No ControlPath specified for \"-O\" command");
720
	if (options.control_path != NULL)
729
	if (options.control_path != NULL)
721
		muxclient(options.control_path);
730
		muxclient(options.control_path);
731
		/* If muxclient() can connect to a master socket,
732
		 * then it doesn't return. */
722
733
723
	timeout_ms = options.connection_timeout * 1000;
734
	timeout_ms = options.connection_timeout * 1000;
724
735
Lines 858-863 main(int ac, char **av) Link Here
858
	return exit_status;
869
	return exit_status;
859
}
870
}
860
871
872
static void
873
control_persist_detach(void)
874
{
875
	pid_t pid;
876
877
	debug("%s: backgrounding master process", __func__);
878
879
 	/*
880
 	 * master (current process) into the background, and make the
881
 	 * foreground process a client of the backgrounded master.
882
 	 */
883
	switch ((pid = fork())) {
884
	case -1:
885
		fatal("%s: fork: %s", __func__, strerror(errno));
886
	case 0:
887
		/* Child: master process continues mainloop */
888
 		break;
889
 	default:
890
		/* Parent: set up mux slave to connect to backgrounded master */
891
		debug2("%s: background process is %ld", __func__, (long)pid);
892
		stdin_null_flag = ostdin_null_flag;
893
		no_shell_flag = ono_shell_flag;
894
		no_tty_flag = ono_tty_flag;
895
		tty_flag = otty_flag;
896
 		close(muxserver_sock);
897
 		muxserver_sock = -1;
898
 		muxclient(options.control_path);
899
		/* muxclient() doesn't return on success. */
900
 		fatal("Failed to connect to new control master");
901
 	}
902
}
903
904
/* Do fork() after authentication. Used by "ssh -f" */
905
static void
906
fork_postauth(void)
907
{
908
	if (need_controlpersist_detach)
909
		control_persist_detach();
910
	debug("forking to background");
911
	fork_after_authentication_flag = 0;
912
	if (daemon(1, 1) < 0)
913
		fatal("daemon() failed: %.200s", strerror(errno));
914
}
915
861
/* Callback for remote forward global requests */
916
/* Callback for remote forward global requests */
862
static void
917
static void
863
ssh_confirm_remote_forward(int type, u_int32_t seq, void *ctxt)
918
ssh_confirm_remote_forward(int type, u_int32_t seq, void *ctxt)
Lines 885-896 ssh_confirm_remote_forward(int type, u_i Link Here
885
	}
940
	}
886
	if (++remote_forward_confirms_received == options.num_remote_forwards) {
941
	if (++remote_forward_confirms_received == options.num_remote_forwards) {
887
		debug("All remote forwarding requests processed");
942
		debug("All remote forwarding requests processed");
888
		if (fork_after_authentication_flag) {
943
		if (fork_after_authentication_flag)
889
			fork_after_authentication_flag = 0;
944
			fork_postauth();
890
			if (daemon(1, 1) < 0)
891
				fatal("daemon() failed: %.200s",
892
				    strerror(errno));
893
		}
894
	}
945
	}
895
}
946
}
896
947
Lines 1134-1145 ssh_session(void) Link Here
1134
	 * If requested and we are not interested in replies to remote
1185
	 * If requested and we are not interested in replies to remote
1135
	 * forwarding requests, then let ssh continue in the background.
1186
	 * forwarding requests, then let ssh continue in the background.
1136
	 */
1187
	 */
1137
	if (fork_after_authentication_flag &&
1188
	if (fork_after_authentication_flag) {
1138
	    (!options.exit_on_forward_failure ||
1189
		if (options.exit_on_forward_failure &&
1139
	    options.num_remote_forwards == 0)) {
1190
		    options.num_remote_forwards > 0) {
1140
		fork_after_authentication_flag = 0;
1191
			debug("deferring postauth fork until remote forward "
1141
		if (daemon(1, 1) < 0)
1192
			    "confirmation received");
1142
			fatal("daemon() failed: %.200s", strerror(errno));
1193
		} else
1194
			fork_postauth();
1143
	}
1195
	}
1144
1196
1145
	/*
1197
	/*
Lines 1262-1267 ssh_session2(void) Link Here
1262
	/* XXX should be pre-session */
1314
	/* XXX should be pre-session */
1263
	ssh_init_forwarding();
1315
	ssh_init_forwarding();
1264
1316
1317
	/* Start listening for multiplex clients */
1318
	muxserver_listen();
1319
1320
 	/*
1321
	 * If we are in control persist mode, then prepare to background
1322
	 * ourselves and have a foreground client attach as a control
1323
	 * slave. NB. we must save copies of the flags that we override for
1324
	 * the backgrounding, since we defer attachment of the slave until
1325
	 * after the connection is fully established (in particular,
1326
	 * async rfwd replies have been received for ExitOnForwardFailure).
1327
	 */
1328
 	if (options.control_persist && muxserver_sock != -1) {
1329
		ostdin_null_flag = stdin_null_flag;
1330
		ono_shell_flag = no_shell_flag;
1331
		ono_tty_flag = no_tty_flag;
1332
		otty_flag = tty_flag;
1333
 		stdin_null_flag = 1;
1334
 		no_shell_flag = 1;
1335
 		no_tty_flag = 1;
1336
 		tty_flag = 0;
1337
		if (!fork_after_authentication_flag)
1338
			need_controlpersist_detach = 1;
1339
		fork_after_authentication_flag = 1;
1340
 	}
1341
1265
	if (!no_shell_flag || (datafellows & SSH_BUG_DUMMYCHAN))
1342
	if (!no_shell_flag || (datafellows & SSH_BUG_DUMMYCHAN))
1266
		id = ssh_session2_open();
1343
		id = ssh_session2_open();
1267
1344
Lines 1280-1298 ssh_session2(void) Link Here
1280
	    options.permit_local_command)
1357
	    options.permit_local_command)
1281
		ssh_local_cmd(options.local_command);
1358
		ssh_local_cmd(options.local_command);
1282
1359
1283
	/* Start listening for multiplex clients */
1284
	muxserver_listen();
1285
1286
	/*
1360
	/*
1287
	 * If requested and we are not interested in replies to remote
1361
	 * If requested and we are not interested in replies to remote
1288
	 * forwarding requests, then let ssh continue in the background.
1362
	 * forwarding requests, then let ssh continue in the background.
1289
	 */
1363
	 */
1290
	if (fork_after_authentication_flag &&
1364
	if (fork_after_authentication_flag) {
1291
	    (!options.exit_on_forward_failure ||
1365
		if (options.exit_on_forward_failure &&
1292
	    options.num_remote_forwards == 0)) {
1366
		    options.num_remote_forwards > 0) {
1293
		fork_after_authentication_flag = 0;
1367
			debug("deferring postauth fork until remote forward "
1294
		if (daemon(1, 1) < 0)
1368
			    "confirmation received");
1295
			fatal("daemon() failed: %.200s", strerror(errno));
1369
		} else
1370
			fork_postauth();
1296
	}
1371
	}
1297
1372
1298
	if (options.use_roaming)
1373
	if (options.use_roaming)
(-)ssh_config.5 (+22 lines)
Lines 319-324 It is recommended that any Link Here
319
used for opportunistic connection sharing include
319
used for opportunistic connection sharing include
320
at least %h, %p, and %r.
320
at least %h, %p, and %r.
321
This ensures that shared connections are uniquely identified.
321
This ensures that shared connections are uniquely identified.
322
.It Cm ControlPersist
323
When used in conjunction with
324
.Cm ControlMaster ,
325
specifies that the master connection should remain open
326
in the background (waiting for future client connections)
327
after the initial client connection has been closed.
328
If set to
329
.Dq no ,
330
then the master connection will not be placed into the background,
331
and will close as soon as the initial client connection is closed.
332
If set to
333
.Dq yes ,
334
then the master connection will remain in the background indefinitely
335
(until killed or closed via a mechanism such as the
336
.Xr ssh 1
337
.Dq Fl O No exit
338
option).
339
If set to a time in seconds, or a time in any of the formats documented in
340
.Xr sshd_config 5 ,
341
then the backgrounded master connection will automatically terminate
342
after it has remained idle (with no client connections) for the
343
specified time.
322
.It Cm DynamicForward
344
.It Cm DynamicForward
323
Specifies that a TCP port on the local machine be forwarded
345
Specifies that a TCP port on the local machine be forwarded
324
over the secure channel, and the application
346
over the secure channel, and the application

Return to bug 1330