Beispiel #1
0
/* May return 1 to mean try again. This happens after a successful certificate
   signing request so that it connects again straight away with the new
   key/certificate.
   Returns 2 if there were restore/verify warnings.
   Returns 3 if timer conditions were not met.
*/
static int do_client(struct config *conf, enum action act, int vss_restore, int json)
{
	int ret=0;
	int rfd=-1;
	int resume=0;
	SSL *ssl=NULL;
	BIO *sbio=NULL;
	char buf[256]="";
	SSL_CTX *ctx=NULL;
	struct cntr cntr;
	struct cntr p1cntr;
	char *incexc=NULL;
	char *server_version=NULL;
	const char *phase1str="backupphase1";

	reset_filecounter(&p1cntr, time(NULL));
	reset_filecounter(&cntr, time(NULL));

	setup_signals_client();
//	settimers(0, 100);
	logp("begin client\n");

	if(act!=ACTION_ESTIMATE)
	{
		ssl_load_globals();
		if(!(ctx=ssl_initialise_ctx(conf)))
		{
			logp("error initialising ssl ctx\n");
			ret=-1;
			goto end;
		}

		SSL_CTX_set_session_id_context(ctx,
			(const unsigned char *)&s_server_session_id_context,
			sizeof(s_server_session_id_context));

		if((rfd=init_client_socket(conf->server, conf->port))<0)
		{
			ret=-1;
			goto end;
		}

		if(!(ssl=SSL_new(ctx))
		  || !(sbio=BIO_new_socket(rfd, BIO_NOCLOSE)))
		{
			ERR_error_string_n(ERR_get_error(), buf, sizeof(buf));
			logp("Problem joining SSL to the socket: %s\n", buf);
			ret=-1;
			goto end;
		}
		SSL_set_bio(ssl, sbio, sbio);
		if(SSL_connect(ssl)<=0)
		{
			ERR_error_string_n(ERR_get_error(), buf, sizeof(buf));
			logp("SSL connect error: %s\n", buf);
			ret=-1;
			goto end;
		}
	}

	if((ret=async_init(rfd, ssl, conf, act==ACTION_ESTIMATE)))
		goto end;

	// Set quality of service bits on backup packets.
	if(act==ACTION_BACKUP || act==ACTION_BACKUP_TIMED)
		set_bulk_packets();

	if(act!=ACTION_ESTIMATE)
	{
		char cmd=0;
		size_t len=0;
		char *feat=NULL;
		int ca_ret=0;
		if((ret=authorise_client(conf, &server_version, &p1cntr)))
			goto end;

		if(server_version)
		{
			logp("Server version: %s\n", server_version);
			// Servers before 1.3.2 did not tell us their versions.
			// 1.3.2 and above can do the automatic CA stuff that
			// follows.
			if((ca_ret=ca_client_setup(conf, &p1cntr))<0)
			{
				// Error
				logp("Error with certificate signing request\n");
				ret=-1;
				goto end;
			}
			else if(ca_ret>0)
			{
				// Certificate signed successfully.
				// Everything is OK, but we will reconnect now, in
				// order to use the new keys/certificates.
				ret=1;
				goto end;
			}
		}

		set_non_blocking(rfd);

		if((ret=ssl_check_cert(ssl, conf)))
		{
			logp("check cert failed\n");
			goto end;
		}

		if((ret=async_write_str(CMD_GEN, "extra_comms_begin")))
		{
			logp("Problem requesting extra_comms_begin\n");
			goto end;
		}
		// Servers greater than 1.3.0 will list the extra_comms
		// features they support.
		else if((ret=async_read(&cmd, &feat, &len)))
		{
			logp("Problem reading response to extra_comms_begin\n");
			goto end;
		}
		else if(cmd!=CMD_GEN)
		{
			logp("Unexpected command from server when reading response to extra_comms_begin: %c:%s\n", cmd, feat);
			ret=-1;
			goto end;
		}
		else if(strncmp(feat,
		  "extra_comms_begin ok", strlen("extra_comms_begin ok")))
		{
			logp("Unexpected response from server when reading response to extra_comms_begin: %c:%s\n", cmd, feat);
			ret=-1;
			goto end;
		}

		// Can add extra bits here. The first extra bit is the
		// autoupgrade stuff.

		if(server_supports_autoupgrade(feat))
		{
			if(conf->autoupgrade_dir && conf->autoupgrade_os
			  && (ret=autoupgrade_client(conf, &p1cntr)))
				goto end;
		}

		// :srestore: means that the server wants to do a restore.
		if(server_supports(feat, ":srestore:"))
		{
			if(conf->server_can_restore)
			{
				logp("Server is initiating a restore\n");
				if(incexc) { free(incexc); incexc=NULL; }
				if((ret=incexc_recv_client_restore(&incexc,
					conf, &p1cntr)))
						goto end;
				if(incexc)
				{
					if((ret=parse_incexcs_buf(conf,
						incexc))) goto end;
					act=ACTION_RESTORE;
					log_restore_settings(conf, 1);
				}
			}
			else
			{
				logp("Server wants to initiate a restore\n");
				logp("Client configuration says no\n");
				if(async_write_str(CMD_GEN, "srestore not ok"))
				{
					ret=-1;
					goto end;
				}
			}
		}

		if(conf->orig_client)
		{
			char str[512]="";
			snprintf(str, sizeof(str),
				"orig_client=%s", conf->orig_client);
			if(!server_supports(feat, ":orig_client:"))
			{
				logp("Server does not support switching client.\n");
				ret=-1;
				goto end;
			}
			if((ret=async_write_str(CMD_GEN, str))
			  || (ret=async_read_expect(CMD_GEN, "orig_client ok")))
			{
				logp("Problem requesting %s\n", str);
				ret=-1;
				goto end;
			}
			logp("Switched to client %s\n", conf->orig_client);
		}

		// :sincexc: is for the server giving the client the
		// incexc config.
		if(act==ACTION_BACKUP
		  || act==ACTION_BACKUP_TIMED)
		{
			if(!incexc && server_supports(feat, ":sincexc:"))
			{
				logp("Server is setting includes/excludes.\n");
				if(incexc) { free(incexc); incexc=NULL; }
				if((ret=incexc_recv_client(&incexc,
					conf, &p1cntr))) goto end;
				if(incexc && (ret=parse_incexcs_buf(conf,
					incexc))) goto end;
			}
		}

		if(server_supports(feat, ":counters:"))
		{
			if(async_write_str(CMD_GEN, "countersok"))
				goto end;
			conf->send_client_counters=1;
		}

		// :incexc: is for the client sending the server the
		// incexc config so that it better knows what to do on
		// resume.
		if(server_supports(feat, ":incexc:")
		  && (ret=incexc_send_client(conf, &p1cntr)))
			goto end;

		if((ret=async_write_str(CMD_GEN, "extra_comms_end"))
		  || (ret=async_read_expect(CMD_GEN, "extra_comms_end ok")))
		{
			logp("Problem requesting extra_comms_end\n");
			goto end;
		}

		if(feat) free(feat);
	}

	rfd=-1;
	switch(act)
	{
		case ACTION_BACKUP_TIMED:
			phase1str="backupphase1timed";
		case ACTION_BACKUP:
		{
			// Set bulk packets quality of service flags on backup.
			if(incexc)
			{
				logp("Server is overriding the configuration\n");
				logp("with the following settings:\n");
				if(log_incexcs_buf(incexc))
				{
					ret=-1;
					goto end;
				}
			}
			if(!conf->sdcount)
			{
				logp("Found no include paths!\n");
				ret=-1;
				goto end;
			}

			if(!(ret=maybe_check_timer(phase1str,
				conf, &resume)))
			{
				if(conf->backup_script_pre)
				{
					int a=0;
					const char *args[12];
					args[a++]=conf->backup_script_pre;
					args[a++]="pre";
					args[a++]="reserved2";
					args[a++]="reserved3";
					args[a++]="reserved4";
					args[a++]="reserved5";
					args[a++]=NULL;
					if(run_script(args,
						conf->backup_script_pre_arg,
						conf->bprecount,
						&p1cntr, 1, 1)) ret=-1;
				}

				if(!ret && do_backup_client(conf,
					resume, 0, &p1cntr, &cntr))
						ret=-1;

				if((conf->backup_script_post_run_on_fail
				  || !ret) && conf->backup_script_post)
				{
					int a=0;
					const char *args[12];
					args[a++]=conf->backup_script_post;
					args[a++]="post";
					// Tell post script whether the restore
					// failed.
					args[a++]=ret?"1":"0";
					args[a++]="reserved3";
					args[a++]="reserved4";
					args[a++]="reserved5";
					args[a++]=NULL;
					if(run_script(args,
						conf->backup_script_post_arg,
						conf->bpostcount,
						&cntr, 1, 1)) ret=-1;
				}
			}

			if(ret<0)
				logp("error in backup\n");
			else if(ret>0)
			{
				// Timer script said no.
				// Have a distinct return value to
				// differentiate between other cases
				// (ssl reconnection and restore/verify
				// warnings).
				ret=3;
			}
			else
				logp("backup finished ok\n");
			
			break;
		}
		case ACTION_RESTORE:
		case ACTION_VERIFY:
		{
			if(conf->restore_script_pre)
			{
				int a=0;
				const char *args[12];
				args[a++]=conf->restore_script_pre;
				args[a++]="pre";
				args[a++]="reserved2";
				args[a++]="reserved3";
				args[a++]="reserved4";
				args[a++]="reserved5";
				args[a++]=NULL;
				if(run_script(args,
					conf->restore_script_pre_arg,
					conf->rprecount, &cntr, 1, 1)) ret=-1;
			}
			if(!ret && do_restore_client(conf,
				act, vss_restore, &p1cntr, &cntr)) ret=-1;
			if((conf->restore_script_post_run_on_fail
			  || !ret) && conf->restore_script_post)
			{
				int a=0;
				const char *args[12];
				args[a++]=conf->restore_script_pre;
				args[a++]="post";
				// Tell post script whether the restore
				// failed.
				args[a++]=ret?"1":"0";
				args[a++]="reserved3";
				args[a++]="reserved4";
				args[a++]="reserved5";
				args[a++]=NULL;
				if(run_script(args,
					conf->restore_script_post_arg,
					conf->rpostcount, &cntr, 1, 1)) ret=-1;
			}

			// Return non-zero if there were warnings,
			// so that the test script can easily check.
			if(p1cntr.warning+cntr.warning)
				ret=2;

			break;
		}
		case ACTION_ESTIMATE:
			if(!ret) ret=do_backup_client(conf, 0, 1,
					&p1cntr, &cntr);
			break;
		case ACTION_DELETE:
			if(!ret) ret=do_delete_client(conf);
			break;
		case ACTION_LIST:
		case ACTION_LONG_LIST:
		default:
			ret=do_list_client(conf, act, json);
			break;
	}

end:
	close_fd(&rfd);
	async_free();
	if(act!=ACTION_ESTIMATE) ssl_destroy_ctx(ctx);

	if(incexc) free(incexc);
	if(server_version) free(server_version);

        //logp("end client\n");
	return ret;
}
Beispiel #2
0
static int run_server(struct conf **confs, const char *conffile,
	int *rfds, int *sfds)
{
	int i=0;
	int ret=-1;
	SSL_CTX *ctx=NULL;
	int found_normal_child=0;
	struct asfd *asfd=NULL;
	struct asfd *scfd=NULL;
	struct async *mainas=NULL;
	const char *port=get_string(confs[OPT_PORT]);
	const char *address=get_string(confs[OPT_ADDRESS]);
	const char *status_port=get_string(confs[OPT_STATUS_PORT]);
	const char *status_address=get_string(confs[OPT_STATUS_ADDRESS]);

	if(!(ctx=ssl_initialise_ctx(confs)))
	{
		logp("error initialising ssl ctx\n");
		goto end;
	}
	if((ssl_load_dh_params(ctx, confs)))
	{
		logp("error loading dh params\n");
		goto end;
	}

	if(init_listen_socket(address, port, rfds)
	  || init_listen_socket(status_address, status_port, sfds))
		goto end;

	if(!(mainas=async_alloc())
	  || mainas->init(mainas, 0))
		goto end;

	for(i=0; i<LISTEN_SOCKETS && rfds[i]!=-1; i++)
	{
		struct asfd *newfd;
		if(!(newfd=setup_asfd(mainas,
			"main server socket", &rfds[i])))
				goto end;
		newfd->fdtype=ASFD_FD_SERVER_LISTEN_MAIN;
	}
	for(i=0; i<LISTEN_SOCKETS && sfds[i]!=-1; i++)
	{
		struct asfd *newfd;
		if(!(newfd=setup_asfd(mainas,
			"main server status socket", &sfds[i])))
				goto end;
		newfd->fdtype=ASFD_FD_SERVER_LISTEN_STATUS;
	}

	while(!hupreload)
	{
		switch(mainas->read_write(mainas))
		{
			case 0:
				for(asfd=mainas->asfd; asfd; asfd=asfd->next)
				{
					if(asfd->new_client)
					{
						// Incoming client.
						asfd->new_client=0;
						if(process_incoming_client(asfd,
							ctx, conffile, confs))
								goto end;
						if(!get_int(confs[OPT_FORK]))
						{
							gentleshutdown++;
							ret=1;
							goto end;
						}
						continue;
					}
				}
				break;
			default:
				int removed=0;
				// Maybe one of the fds had a problem.
				// Find and remove it and carry on if possible.
				for(asfd=mainas->asfd; asfd; )
				{
					struct asfd *a;
					if(!asfd->want_to_remove)
					{
						asfd=asfd->next;
						continue;
					}
					mainas->asfd_remove(mainas, asfd);
					logp("%s: disconnected fd %d\n",
						asfd->desc, asfd->fd);
					a=asfd->next;
					asfd_free(&asfd);
					asfd=a;
					removed++;
				}
				if(removed) break;
				// If we got here, there was no fd to remove.
				// It is a fatal error.
				goto end;
		}

		for(asfd=mainas->asfd; asfd; asfd=asfd->next)
		{
			if(asfd->fdtype!=ASFD_FD_SERVER_PIPE_READ
			  || !asfd->rbuf->buf) continue;
			// One of the child processes is giving us information.
			// Try to append it to any of the status child pipes.
			for(scfd=mainas->asfd; scfd; scfd=scfd->next)
			{
				if(scfd->fdtype!=ASFD_FD_SERVER_PIPE_WRITE)
					continue;
				switch(scfd->append_all_to_write_buffer(scfd,
					asfd->rbuf))
				{
					case APPEND_OK:
					case APPEND_BLOCKED:
						break;
					default:
						goto end;
				}
			}
			// Free the information, even if we did not manage
			// to append it. That should be OK, more will be along
			// soon.
			iobuf_free_content(asfd->rbuf);
		}

		chld_check_for_exiting(mainas);

		// Leave if we had a SIGUSR1 and there are no children running.
		if(gentleshutdown)
		{
			if(!gentleshutdown_logged)
			{
				logp("got SIGUSR2 gentle reload signal\n");
				logp("will shut down once children have exited\n");
				gentleshutdown_logged++;
			}
// FIX THIS:
// found_normal_child=chld_add_fd_to_normal_sets(confs, &fsr, &fse, &mfd);
			else if(!found_normal_child)
			{
				logp("all children have exited - shutting down\n");
				break;
			}
		}
	}

	if(hupreload) logp("got SIGHUP reload signal\n");

	ret=0;
end:
	async_asfd_free_all(&mainas);
	if(ctx) ssl_destroy_ctx(ctx);
	return ret;
}
Beispiel #3
0
Datei: main.c Projekt: scosu/burp
static enum cliret do_client(struct conf **confs,
	enum action action, int vss_restore)
{
	enum cliret ret=CLIENT_OK;
	int rfd=-1;
	SSL *ssl=NULL;
	SSL_CTX *ctx=NULL;
	struct cntr *cntr=NULL;
	char *incexc=NULL;
	enum action act=action;
	struct async *as=NULL;
	struct asfd *asfd=NULL;

//	as->settimers(0, 100);

//	logp("begin client\n");
//	logp("action %d\n", action);

	// Status monitor forks a child process instead of connecting to
	// the server directly.
	if(action==ACTION_STATUS
	  || action==ACTION_STATUS_SNAPSHOT)
	{
#ifdef HAVE_WIN32
		logp("Status mode not implemented on Windows.\n");
		goto error;
#endif
		if(status_client_ncurses_init(act)
		  || status_client_ncurses(confs)) ret=CLIENT_ERROR;
		goto end;
	}

	if(!(cntr=cntr_alloc())
	  || cntr_init(cntr, get_string(confs[OPT_CNAME]))) goto error;
	set_cntr(confs[OPT_CNTR], cntr);

	if(act!=ACTION_ESTIMATE
	  && ssl_setup(&rfd, &ssl, &ctx, action, confs))
		goto could_not_connect;

	if(!(as=async_alloc())
	  || !(asfd=asfd_alloc())
	  || as->init(as, act==ACTION_ESTIMATE)
	  || asfd->init(asfd, "main socket", as, rfd, ssl,
		ASFD_STREAM_STANDARD, confs))
			goto end;
	as->asfd_add(as, asfd);

	// Set quality of service bits on backup packets.
	if(act==ACTION_BACKUP
	  || act==ACTION_BACKUP_TIMED
	  || act==ACTION_TIMER_CHECK)
		as->asfd->set_bulk_packets(as->asfd);

	if(act!=ACTION_ESTIMATE)
	{
		if((ret=initial_comms(as, &act, &incexc, confs)))
			goto end;
	}

	rfd=-1;
	switch(act)
	{
		case ACTION_BACKUP:
			ret=backup_wrapper(asfd, act, "backupphase1",
			  incexc, confs);
			break;
		case ACTION_BACKUP_TIMED:
			ret=backup_wrapper(asfd, act, "backupphase1timed",
			  incexc, confs);
			break;
		case ACTION_TIMER_CHECK:
			ret=backup_wrapper(asfd, act, "backupphase1timedcheck",
			  incexc, confs);
			break;
		case ACTION_RESTORE:
		case ACTION_VERIFY:
			ret=restore_wrapper(asfd, act, vss_restore, confs);
			break;
		case ACTION_ESTIMATE:
			if(do_backup_client(asfd, confs, act, 0))
				goto error;
			break;
		case ACTION_DELETE:
			if(do_delete_client(asfd, confs)) goto error;
			break;
		case ACTION_MONITOR:
			if(do_monitor_client(asfd, confs)) goto error;
			break;
		case ACTION_DIFF:
		case ACTION_DIFF_LONG:
/*
			if(!strcmp(get_string(confs[OPT_BACKUP2]), "n"))
				// Do a phase1 scan and diff that.
				ret=backup_wrapper(asfd, act,
					"backupphase1diff", incexc, confs);
			else
*/
			// Diff two backups that already exist.
			// Fall through, the list code is all we need
			// for simple diffs on the client side.
		case ACTION_LIST:
		case ACTION_LIST_LONG:
		default:
			if(do_list_client(asfd, act, confs)) goto error;
			break;
	}

	if(asfd_flush_asio(asfd))
		ret=CLIENT_ERROR;

	goto end;
error:
	ret=CLIENT_ERROR; goto end;
could_not_connect:
	ret=CLIENT_COULD_NOT_CONNECT;
end:
	close_fd(&rfd);
	async_free(&as);
	asfd_free(&asfd);
	if(ctx) ssl_destroy_ctx(ctx);
	free_w(&incexc);
	set_cntr(confs[OPT_CNTR], NULL);
	cntr_free(&cntr);

	//logp("end client\n");
	return ret;
}
Beispiel #4
0
static enum cliret do_client(struct conf *conf,
	enum action action, int vss_restore, int json)
{
	enum cliret ret=CLIENT_OK;
	int rfd=-1;
	int resume=0;
	SSL *ssl=NULL;
	SSL_CTX *ctx=NULL;
	struct cntr *cntr=NULL;
	char *incexc=NULL;
	long name_max=0;
	enum action act=action;
	struct async *as=NULL;
	struct asfd *asfd=NULL;

//	as->settimers(0, 100);

	logp("begin client\n");

	if(!(cntr=cntr_alloc()) || cntr_init(cntr, conf->cname)) goto error;
	conf->cntr=cntr;

	if(act!=ACTION_ESTIMATE
	  && ssl_setup(&rfd, &ssl, &ctx, conf))
		goto error;

	if(!(as=async_alloc())
	  || !(asfd=asfd_alloc())
	  || as->init(as, act==ACTION_ESTIMATE)
	  || asfd->init(asfd, as, rfd, ssl, conf))
		goto end;
	as->add_asfd(as, asfd);

	// Set quality of service bits on backup packets.
	if(act==ACTION_BACKUP
	  || act==ACTION_BACKUP_TIMED
	  || act==ACTION_TIMER_CHECK)
		as->asfd->set_bulk_packets(as->asfd);

	if(act!=ACTION_ESTIMATE)
	{
		if((ret=initial_comms(as, &act, &incexc, &name_max, conf)))
			goto end;
	}

	rfd=-1;
	switch(act)
	{
		case ACTION_BACKUP:
			ret=backup_wrapper(asfd, act, "backupphase1",
			  incexc, resume, name_max, conf);
			break;
		case ACTION_BACKUP_TIMED:
			ret=backup_wrapper(asfd, act, "backupphase1timed",
			  incexc, resume, name_max, conf);
			break;
		case ACTION_TIMER_CHECK:
			ret=backup_wrapper(asfd, act, "backupphase1timedcheck",
			  incexc, resume, name_max, conf);
			break;
		case ACTION_RESTORE:
		case ACTION_VERIFY:
			ret=restore_wrapper(asfd, act, vss_restore, conf);
			break;
		case ACTION_ESTIMATE:
			if(do_backup_client(asfd, conf, act, name_max, 0))
				goto error;
			break;
		case ACTION_DELETE:
			if(do_delete_client(asfd, conf)) goto error;
			break;
		case ACTION_LIST:
		case ACTION_LONG_LIST:
		default:
			if(do_list_client(asfd, conf, act, json)) goto error;
			break;
	}

	goto end;
error:
	ret=CLIENT_ERROR;
end:
	close_fd(&rfd);
	async_free(&as);
	asfd_free(&asfd);
	if(ctx) ssl_destroy_ctx(ctx);
	if(incexc) free(incexc);
	conf->cntr=NULL;
	if(cntr) cntr_free(&cntr);

        //logp("end client\n");
	return ret;
}
Beispiel #5
0
static int run_server(struct conf *conf, const char *conffile, int *rfd,
	const char *oldport, const char *oldstatusport)
{
	int ret=0;
	SSL_CTX *ctx=NULL;
	int found_normal_child=0;

	if(!(ctx=ssl_initialise_ctx(conf)))
	{
		logp("error initialising ssl ctx\n");
		return 1;
	}
	if((ssl_load_dh_params(ctx, conf)))
	{
		logp("error loading dh params\n");
		return 1;
	}

	if(!oldport
	  || strcmp(oldport, conf->port))
	{
		close_fd(rfd);
		if((*rfd=init_listen_socket(conf->port, 1))<0)
			return 1;
	}
	if(conf->status_port
	  && (!oldstatusport
		|| strcmp(oldstatusport, conf->status_port)))
	{
		close_fd(&sfd);
		if((sfd=init_listen_socket(conf->status_port, 0))<0)
			return 1;
	}

	while(!hupreload)
	{
		int mfd=-1;
		fd_set fsr;
		fd_set fsw;
		fd_set fse;
		struct timeval tval;

		if(sigchld)
		{
			chld_check_for_exiting();
			sigchld=0;
		}

		FD_ZERO(&fsr);
		FD_ZERO(&fse);

		tval.tv_sec=1;
		tval.tv_usec=0;

		add_fd_to_sets(*rfd, &fsr, NULL, &fse, &mfd);
		if(sfd>=0) add_fd_to_sets(sfd, &fsr, NULL, &fse, &mfd);

		// Add read fds of normal children.
		found_normal_child=chld_add_fd_to_normal_sets(conf,
			&fsr, &fse, &mfd);

		// Leave if we had a SIGUSR1 and there are no children
		// running.
		if(gentleshutdown)
		{
			if(!gentleshutdown_logged)
			{
				logp("got SIGUSR2 gentle reload signal\n");
				logp("will shut down once children have exited\n");
				gentleshutdown_logged++;
			}
			else if(!found_normal_child)
			{
				logp("all children have exited - shutting down\n");
				break;
			}
		}

		if(select(mfd+1, &fsr, NULL, &fse, &tval)<0)
		{
			if(errno!=EAGAIN && errno!=EINTR)
			{
				logp("select error in normal part of %s: %s\n",
					__func__, strerror(errno));
				ret=1;
				break;
			}
		}

		if(FD_ISSET(*rfd, &fse))
		{
			// Happens when a client exits.
			//logp("error on listening socket.\n");
			if(!conf->forking) { gentleshutdown++; break; }
			continue;
		}

		if((sfd>=0 && FD_ISSET(sfd, &fse)))
		{
			// Happens when a client exits.
			//logp("error on status socket.\n");
			if(!conf->forking) { gentleshutdown++; break; }
			continue;
		}

		if(FD_ISSET(*rfd, &fsr))
		{
			// A normal client is incoming.
			if(process_incoming_client(*rfd, conf, ctx,
				conffile, 0 /* not a status client */))
			{
				ret=1;
				break;
			}
			if(!conf->forking) { gentleshutdown++; break; }
		}

		if(sfd>=0 && FD_ISSET(sfd, &fsr))
		{
			// A status client is incoming.
			//printf("status client?\n");
			if(process_incoming_client(sfd, conf, ctx,
				conffile, 1 /* a status client */))
			{
				ret=1;
				break;
			}
			if(!conf->forking) { gentleshutdown++; break; }
		}

		if(chld_fd_isset_normal(conf, &fsr, &fse))
		{
			ret=1;
			break;
		}

		// Have a separate select for writing to status server children

		mfd=-1;
		FD_ZERO(&fsw);
		FD_ZERO(&fse);
		if(!chld_add_fd_to_status_sets(conf, &fsw, &fse, &mfd))
		{
			// Did not find any status server children.
			// No need to do the select.
			continue;
		}

		// Do not hang around - doing the status stuff is a lower
		// priority thing than dealing with normal clients.
		tval.tv_sec=0;
		tval.tv_usec=500;

		//printf("try status server\n");

		if(select(mfd+1, NULL, &fsw, &fse, &tval)<0)
		{
			if(errno!=EAGAIN && errno!=EINTR)
			{
				logp("select error in status part of %s: %s\n",
					__func__, strerror(errno));
				ret=1;
				break;
			}
		}

		if(chld_fd_isset_status(conf, &fsw, &fse))
		{
			ret=1;
			break;
		}
	}

	if(hupreload) logp("got SIGHUP reload signal\n");

	ssl_destroy_ctx(ctx);

	return ret;
}