Esempio n. 1
0
static int vss_opts_changed(struct sdirs *sdirs, struct conf **cconfs,
	const char *incexc)
{
	int ret=-1;
	struct conf **oldconfs;
	struct conf **newconfs;
	if(!(oldconfs=confs_alloc())
	  || !(newconfs=confs_alloc()))
		goto end;
	confs_init(oldconfs);
	confs_init(newconfs);

	// Figure out the old config, which is in the incexc file left
	// in the current backup directory on the server.
	if(conf_parse_incexcs_path(oldconfs, sdirs->cincexc))
	{
		// Assume that the file did not exist, and therefore
		// the old split_vss setting is 0.
		set_int(oldconfs[OPT_SPLIT_VSS], 0);
		set_int(oldconfs[OPT_STRIP_VSS], 0);
	}

	// Figure out the new config, which is either in the incexc file from
	// the client, or in the cconf on the server.
	if(incexc)
	{
		if(conf_parse_incexcs_buf(newconfs, incexc))
		{
			// Should probably not got here.
			set_int(newconfs[OPT_SPLIT_VSS], 0);
			set_int(newconfs[OPT_STRIP_VSS], 0);
		}
	}
	else
	{
		set_int(newconfs[OPT_SPLIT_VSS],
			get_int(cconfs[OPT_SPLIT_VSS]));
		set_int(newconfs[OPT_STRIP_VSS],
			get_int(cconfs[OPT_STRIP_VSS]));
	}

	if(get_int(newconfs[OPT_SPLIT_VSS])!=get_int(oldconfs[OPT_SPLIT_VSS]))
	{
		logp("split_vss=%d (changed since last backup)\n",
			get_int(newconfs[OPT_SPLIT_VSS]));
		ret=1; goto end;
	}
	if(get_int(newconfs[OPT_STRIP_VSS])!=get_int(oldconfs[OPT_STRIP_VSS]))
	{
		logp("strip_vss=%d (changed since last backup)\n",
			get_int(newconfs[OPT_STRIP_VSS]));
		ret=1; goto end;
	}
	ret=0;
end:
	if(ret==1) logp("All files will be treated as new\n");
	confs_free(&oldconfs);
	confs_free(&newconfs);
	return ret;
}
Esempio n. 2
0
// FIX THIS: need to unit test this.
static int do_conf_switch_to_orig_client(struct conf **globalcs,
	struct conf **cconfs, const char *orig_client, const char *buf)
{
	int ret=-1;
	int loadrc;
	struct conf **sconfs=NULL;
	if(!(sconfs=confs_alloc())
	  || confs_init(sconfs)) goto end;
	if(set_string(sconfs[OPT_CNAME], orig_client))
		goto end;
	logp("Client wants to switch to client: %s\n",
		get_string(sconfs[OPT_CNAME]));

	// Allow unit testing using a buffer.
#ifdef UTEST
	if(buf) loadrc=conf_load_overrides_buf(globalcs, sconfs, buf);
	else
#endif
		loadrc=conf_load_clientconfdir(globalcs, sconfs);
	if(loadrc)
	{
		logp("Could not load alternate config: %s",
			get_string(sconfs[OPT_CNAME]));
		goto end;
	}
	set_int(sconfs[OPT_SEND_CLIENT_CNTR],
		get_int(cconfs[OPT_SEND_CLIENT_CNTR]));

	if(!restore_client_allowed(cconfs, sconfs))
		goto end;

	if(set_string(sconfs[OPT_RESTORE_PATH],
		get_string(cconfs[OPT_RESTORE_PATH])))
			goto end;
	if(set_string(cconfs[OPT_RESTORE_PATH], NULL))
		goto end;
	set_cntr(sconfs[OPT_CNTR], get_cntr(cconfs[OPT_CNTR]));
	set_cntr(cconfs[OPT_CNTR], NULL);
	confs_free_content(cconfs);
	confs_init(cconfs);
	confs_memcpy(cconfs, sconfs);
	confs_null(sconfs);
	if(set_string(cconfs[OPT_RESTORE_CLIENT],
		get_string(cconfs[OPT_CNAME]))) goto end;
	if(set_string(cconfs[OPT_ORIG_CLIENT],
		get_string(cconfs[OPT_CNAME]))) goto end;

	logp("Switched to client %s\n", get_string(cconfs[OPT_CNAME]));
	ret=0;
end:
	confs_free(&sconfs);
	return ret;
}
Esempio n. 3
0
static ya_result
config_zone_section_init(config_data *config)
{
    ya_result return_code;

    tmp_zone_idx++;

    /* store the previously configured zone, if any */

    config_zone_section_register(config);

    /* make a new zone section ready */

    tmp_zones = zone_alloc();

    if(FAIL(return_code = confs_init(zone_tab, tmp_zones)))
    {
        zone_free(tmp_zones);
        tmp_zones = NULL;

        osformatln(termerr, "config: zone: configuration initialize (zone): %r", return_code);
    }

    return return_code;
}
Esempio n. 4
0
int reload(struct conf **confs, const char *conffile, bool firsttime)
{
	if(!firsttime) logp("Reloading config\n");

	if(confs_init(confs)) return -1;

	if(conf_load_global_only(conffile, confs)) return -1;

	umask(get_mode_t(confs[OPT_UMASK]));

	// This will turn on syslogging which could not be turned on before
	// conf_load.
	log_fzp_set(NULL, confs);

#ifndef HAVE_WIN32
	if(get_e_burp_mode(confs[OPT_BURP_MODE])==BURP_MODE_SERVER)
		setup_signals();
#endif

	// Do not try to change user or group after the first time.
	if(firsttime && chuser_and_or_chgrp(
		get_string(confs[OPT_USER]), get_string(confs[OPT_GROUP])))
			return -1;

	return 0;
}
Esempio n. 5
0
// The return code of this is the return code of the standalone process.
int champ_chooser_server_standalone(struct conf **globalcs)
{
	int ret=1;
	struct sdirs *sdirs=NULL;
	struct conf **cconfs=NULL;
	const char *orig_client=get_string(globalcs[OPT_ORIG_CLIENT]);

	if(!(cconfs=confs_alloc()))
		goto end;
	confs_init(cconfs);
	// We need to be given a client name and load the relevant server side
	// clientconfdir file, because various settings may be overridden
	// there.
	if(set_string(cconfs[OPT_CNAME], orig_client)
	  || conf_load_clientconfdir(globalcs, cconfs)
	  || !(sdirs=sdirs_alloc())
	  || sdirs_init_from_confs(sdirs, cconfs)
	  || champ_chooser_server(sdirs, cconfs, 0 /* resume */))
		goto end;
	ret=0;
end:
	confs_free(&cconfs);
	sdirs_free(&sdirs);
	return ret;
}
Esempio n. 6
0
// FIX THIS: need to unit test this.
int conf_switch_to_orig_client(struct conf **globalcs,
	struct conf **cconfs, const char *orig_client)
{
	int ret=-1;
	struct conf **sconfs=NULL;
	if(!(sconfs=confs_alloc())
	  || confs_init(sconfs)) goto end;
	if(set_string(sconfs[OPT_CNAME], orig_client))
		goto end;
	logp("Client wants to switch to client: %s\n",
		get_string(sconfs[OPT_CNAME]));

	if(conf_load_clientconfdir(globalcs, sconfs))
	{
		logp("Could not load alternate config: %s",
			get_string(sconfs[OPT_CNAME]));
		goto end;
	}
	set_int(sconfs[OPT_SEND_CLIENT_CNTR],
		get_int(cconfs[OPT_SEND_CLIENT_CNTR]));

	if(!restore_client_allowed(cconfs, sconfs))
		goto end;

	if(set_string(sconfs[OPT_RESTORE_PATH],
		get_string(cconfs[OPT_RESTORE_PATH])))
			goto end;
	if(set_string(cconfs[OPT_RESTORE_PATH], NULL))
		goto end;
	set_cntr(sconfs[OPT_CNTR], get_cntr(cconfs));
	set_cntr(cconfs[OPT_CNTR], NULL);
	confs_free_content(cconfs);
	confs_init(cconfs);
	confs_memcpy(cconfs, sconfs);
	confs_null(sconfs);
	if(set_string(cconfs[OPT_RESTORE_CLIENT],
		get_string(cconfs[OPT_CNAME]))) goto end;
	if(set_string(cconfs[OPT_ORIG_CLIENT],
		get_string(cconfs[OPT_CNAME]))) goto end;

	logp("Switched to client %s\n", get_string(cconfs[OPT_CNAME]));
	ret=0;
end:
	confs_free(&sconfs);
	return ret;
}
Esempio n. 7
0
END_TEST

static void check_restore_clients(struct cstat *cstat,
	struct conf **parentconf, const char *restore_clients, int permitted)
{
	struct conf **cconfs=NULL;
	fail_unless((cconfs=confs_alloc())!=NULL);
	fail_unless(!confs_init(cconfs));
	fail_unless(!set_string(cconfs[OPT_CNAME], "cli1"));
	build_clientconfdir_file("cli1", restore_clients);
	fail_unless(!conf_load_clientconfdir(parentconf, cconfs));
	fail_unless(!set_string(parentconf[OPT_CNAME], "cli2"));
	fail_unless(cstat_permitted(cstat, parentconf, cconfs)==permitted);
	confs_free(&cconfs);
}
Esempio n. 8
0
END_TEST

static struct cstat *test_cstat_remove_setup(struct conf ***globalcs,
	const char *cnames[])
{
	struct cstat *clist=NULL;
	clean();
	fail_unless((*globalcs=confs_alloc())!=NULL);
	fail_unless(!confs_init(*globalcs));
	build_file(GLOBAL_CONF, MIN_SERVER_CONF);
	fail_unless(!conf_load_global_only(GLOBAL_CONF, *globalcs));
	build_clientconfdir_files(cnames);
	fail_unless(!cstat_get_client_names(&clist, CLIENTCONFDIR));
	assert_cstat_list(clist, cnames);
	return clist;
}
Esempio n. 9
0
END_TEST

START_TEST(test_cstat_add_out_of_order)
{
	struct cstat *clist=NULL;
	struct conf **globalcs;
	const char *cnames31204[]
		= {"cli3", "cli1", "cli2", "cli0", "cli4", NULL};
	const char *cnames01234[]
		= {"cli0", "cli1", "cli2", "cli3", "cli4", NULL};
	clean();
	fail_unless((globalcs=confs_alloc())!=NULL);
	fail_unless(!confs_init(globalcs));
	build_file(GLOBAL_CONF, MIN_SERVER_CONF);
	fail_unless(!conf_load_global_only(GLOBAL_CONF, globalcs));
	build_clientconfdir_files(cnames31204);
	fail_unless(!cstat_get_client_names(&clist, CLIENTCONFDIR));
	assert_cstat_list(clist, cnames01234);
	test_cstat_remove_teardown(&globalcs, &clist);
}
Esempio n. 10
0
static int run_test_confs(struct conf **confs, const char *client)
{
	int ret=-1;
	struct conf **cconfs=NULL;
	if(!client)
	{
		confs_dump(confs, 0);
		ret=0;
		goto end;
	}
	if(!(cconfs=confs_alloc()))
		goto end;
	confs_init(cconfs);
	if(set_string(cconfs[OPT_CNAME], client)
	  || set_string(cconfs[OPT_PEER_VERSION], VERSION)
	  || conf_load_clientconfdir(confs, cconfs))
		goto end;
	confs_dump(cconfs, CONF_FLAG_CC_OVERRIDE|CONF_FLAG_INCEXC);

end:
	confs_free(&cconfs);
	return ret;
}
Esempio n. 11
0
static int conf_init_save_cname_and_version(struct conf **cconfs)
{
	int ret=-1;
	char *cname=NULL;
	char *cversion=NULL;
	char *orig_cname=get_string(cconfs[OPT_CNAME]);
	char *orig_cversion=get_string(cconfs[OPT_PEER_VERSION]);

	if((orig_cname && !(cname=strdup_w(orig_cname, __func__)))
	  || (orig_cversion
	    && !(cversion=strdup_w(orig_cversion, __func__))))
		goto end;

	set_string(cconfs[OPT_CNAME], NULL);
	set_string(cconfs[OPT_PEER_VERSION], NULL);
	if(confs_init(cconfs)) goto end;
	set_string(cconfs[OPT_CNAME], cname);
	set_string(cconfs[OPT_PEER_VERSION], cversion);
	ret=0;
end:
	free_w(&cname);
	free_w(&cversion);
	return ret;
}
Esempio n. 12
0
File: bedup.c Progetto: kaptk2/burp
int run_bedup(int argc, char *argv[])
{
	int i=1;
	int ret=0;
	int option=0;
	int nonburp=0;
	unsigned int maxlinks=DEF_MAX_LINKS;
	char *groups=NULL;
	char ext[16]="";
	int givenconfigfile=0;
	const char *configfile=NULL;

	configfile=get_config_path();
	snprintf(ext, sizeof(ext), ".bedup.%d", getpid());

	while((option=getopt(argc, argv, "c:dg:hlm:nvV?"))!=-1)
	{
		switch(option)
		{
			case 'c':
				configfile=optarg;
				givenconfigfile=1;
				break;
			case 'd':
				deletedups=1;
				break;
			case 'g':
				groups=optarg;
				break;
			case 'l':
				makelinks=1;
				break;
			case 'm':
				maxlinks=atoi(optarg);
				break;
			case 'n':
				nonburp=1;
				break;
			case 'V':
				printf("%s-%s\n", prog, VERSION);
				return 0;
			case 'v':
				verbose=1;
				break;
			case 'h':
			case '?':
				return usage();
		}
	}

	if(nonburp && givenconfigfile)
	{
		logp("-n and -c options are mutually exclusive\n");
		return 1;
	}
	if(nonburp && groups)
	{
		logp("-n and -g options are mutually exclusive\n");
		return 1;
	}
	if(!nonburp && maxlinks!=DEF_MAX_LINKS)
	{
		logp("-m option is specified via the configuration file in burp mode (max_hardlinks=)\n");
		return 1;
	}
	if(deletedups && makelinks)
	{
		logp("-d and -l options are mutually exclusive\n");
		return 1;
	}
	if(deletedups && !nonburp)
	{
		logp("-d option requires -n option\n");
		return 1;
	}

	if(optind>=argc)
	{
		if(nonburp)
		{
			logp("No directories found after options\n");
			return 1;
		}
	}
	else
	{
		if(!nonburp)
		{
			logp("Do not specify extra arguments.\n");
			return 1;
		}
	}

	if(maxlinks<2)
	{
		logp("The argument to -m needs to be greater than 1.\n");
		return 1;
	}

	if(nonburp)
	{
		// Read directories from command line.
		for(i=optind; i<argc; i++)
		{
			// Strip trailing slashes, for tidiness.
			if(argv[i][strlen(argv[i])-1]=='/')
				argv[i][strlen(argv[i])-1]='\0';
			if(process_dir("", argv[i], ext, maxlinks,
				0 /* not burp mode */, 0 /* level */))
			{
				ret=1;
				break;
			}
		}
	}
	else
	{
		struct conf **globalcs=NULL;
		struct strlist *grouplist=NULL;
		struct lock *globallock=NULL;

		if(groups)
		{
			char *tok=NULL;
			if((tok=strtok(groups, ",\n")))
			{
				do
				{
					if(strlist_add(&grouplist, tok, 1))
					{
						log_out_of_memory(__func__);
						return -1;
					}
				} while((tok=strtok(NULL, ",\n")));
			}
			if(!grouplist)
			{
				logp("unable to read list of groups\n");
				return -1;
			}
		}

		// Read directories from config files, and get locks.
		if(!(globalcs=confs_alloc())) return -1;
		if(confs_init(globalcs)) return -1;
		if(conf_load_global_only(configfile, globalcs)) return 1;
		if(get_e_burp_mode(globalcs[OPT_BURP_MODE])!=BURP_MODE_SERVER)
		{
			logp("%s is not a server config file\n", configfile);
			confs_free(&globalcs);
			return 1;
		}
		logp("Dedup clients from %s\n",
			get_string(globalcs[OPT_CLIENTCONFDIR]));
		maxlinks=get_int(globalcs[OPT_MAX_HARDLINKS]);
		if(grouplist)
		{
			struct strlist *g=NULL;
			logp("in dedup groups:\n");
			for(g=grouplist; g; g=g->next)
				logp("%s\n", g->path);
		}
		else
		{
			char *lockpath=NULL;
			// Only get the global lock when doing a global run.
			// If you are doing individual groups, you are likely
			// to want to do many different dedup jobs and a
			// global lock would get in the way.
			if(!(lockpath=prepend(
				get_string(globalcs[OPT_LOCKFILE]),
				".bedup", ""))
			  || !(globallock=lock_alloc_and_init(lockpath)))
				return 1;
			lock_get(globallock);
			if(globallock->status!=GET_LOCK_GOT)
			{
				logp("Could not get lock %s (%d)\n", lockpath,
					globallock->status);
				free_w(&lockpath);
				return 1;
			}
			logp("Got %s\n", lockpath);
		}
		ret=iterate_over_clients(globalcs, grouplist, ext, maxlinks);
		confs_free(&globalcs);

		lock_release(globallock);
		lock_free(&globallock);
		strlists_free(&grouplist);
	}

	if(!nonburp)
	{
		logp("%d client storages scanned\n", ccount);
	}
	logp("%llu duplicate %s found\n",
		count, count==1?"file":"files");
	logp("%llu bytes %s%s\n",
		savedbytes, (makelinks || deletedups)?"saved":"saveable",
			bytes_to_human(savedbytes));
	return ret;
}
Esempio n. 13
0
File: bedup.c Progetto: kaptk2/burp
static int iterate_over_clients(struct conf **globalcs,
	struct strlist *grouplist, const char *ext, unsigned int maxlinks)
{
	int ret=0;
	DIR *dirp=NULL;
	struct conf **cconfs=NULL;
	struct dirent *dirinfo=NULL;
	const char *globalclientconfdir=get_string(globalcs[OPT_CLIENTCONFDIR]);

	signal(SIGABRT, &sighandler);
	signal(SIGTERM, &sighandler);
	signal(SIGINT, &sighandler);

	if(!(cconfs=confs_alloc())) return -1;
	if(confs_init(cconfs)) return -1;

	if(!(dirp=opendir(globalclientconfdir)))
	{
		logp("Could not opendir '%s': %s\n",
			globalclientconfdir, strerror(errno));
		return 0;
	}
	while((dirinfo=readdir(dirp)))
	{
		char *lockfile=NULL;
		char *lockfilebase=NULL;
		char *client_lockdir=NULL;
		struct lock *lock=NULL;

		if(dirinfo->d_ino==0
		// looks_like...() also avoids '.' and '..'.
		  || looks_like_tmp_or_hidden_file(dirinfo->d_name)
		  || !is_regular_file(globalclientconfdir, dirinfo->d_name))
			continue;

		confs_free_content(cconfs);
		if(confs_init(cconfs)) return -1;

		if(set_string(cconfs[OPT_CNAME], dirinfo->d_name))
			return -1;

		if(conf_load_clientconfdir(globalcs, cconfs))
		{
			logp("could not load config for client %s\n",
				dirinfo->d_name);
			return 0;
		}

		if(grouplist)
		{
			const char *dedup_group=
				get_string(cconfs[OPT_DEDUP_GROUP]);
			if(!dedup_group
			  || !in_group(grouplist, dedup_group))
				continue;
		}

		if(!(client_lockdir=get_string(cconfs[OPT_CLIENT_LOCKDIR])))
			client_lockdir=get_string(cconfs[OPT_DIRECTORY]);

		if(!(lockfilebase=prepend(client_lockdir,
			dirinfo->d_name, "/"))
		 || !(lockfile=prepend(lockfilebase,
			BEDUP_LOCKFILE_NAME, "/")))
		{
			free_w(&lockfilebase);
			free_w(&lockfile);
			ret=-1;
			break;
		}
		free_w(&lockfilebase);

		if(!(lock=lock_alloc_and_init(lockfile)))
		{
			ret=-1;
			break;
		}
		lock_get(lock);
		free_w(&lockfile);

		if(lock->status!=GET_LOCK_GOT)
		{
			logp("Could not get %s\n", lock->path);
			continue;
		}
		logp("Got %s\n", lock->path);

		// Remember that we got that lock.
		lock_add_to_list(&locklist, lock);

		if(process_dir(get_string(cconfs[OPT_DIRECTORY]),
			dirinfo->d_name,
			ext, maxlinks, 1 /* burp mode */, 0 /* level */))
		{
			ret=-1;
			break;
		}

		ccount++;
	}
	closedir(dirp);

	locks_release_and_free(&locklist);

	confs_free(&cconfs);

	return ret;
}
Esempio n. 14
0
File: main.c Progetto: EmisFR/burp
static int process_incoming_client(struct asfd *asfd, SSL_CTX *ctx,
	const char *conffile, struct conf **confs)
{
	int cfd=-1;
	pid_t childpid;
	int pipe_rfd[2];
	int pipe_wfd[2];
	socklen_t client_length=0;
	struct sockaddr_storage client_name;
	enum asfd_fdtype fdtype=asfd->fdtype;
	int forking=get_int(confs[OPT_FORK]);

	client_length=sizeof(client_name);
	if((cfd=accept(asfd->fd,
		(struct sockaddr *)&client_name, &client_length))==-1)
	{
		// Look out, accept will get interrupted by SIGCHLDs.
		if(errno==EINTR) return 0;
		logp("accept failed on %s (%d) in %s: %s\n", asfd->desc,
			asfd->fd, __func__, strerror(errno));
		return -1;
	}
	reuseaddr(cfd);
	if(log_peer_address(&client_name))
		return -1;

	if(!forking)
		return run_child(&cfd, ctx,
			&client_name, -1, -1, conffile, forking);

	if(chld_check_counts(confs, asfd))
	{
		logp("Closing new connection.\n");
		close_fd(&cfd);
		return 0;
	}

	if(pipe(pipe_rfd)<0 || pipe(pipe_wfd)<0)
	{
		logp("pipe failed: %s", strerror(errno));
		close_fd(&cfd);
		return -1;
	}

	switch((childpid=fork()))
	{
		case -1:
			logp("fork failed: %s\n", strerror(errno));
			return -1;
		case 0:
		{
			// Child.
			int p;
			int ret;
			struct sigaction sa;
			struct async *as=asfd->as;
			async_asfd_free_all(&as);

			// Close unnecessary file descriptors.
			// Go up to FD_SETSIZE and hope for the best.
			// FIX THIS: Now that async_asfd_free_all() is doing
			// everything, double check whether this is needed.
			for(p=3; p<(int)FD_SETSIZE; p++)
			{
				if(p!=pipe_rfd[1]
				  && p!=pipe_wfd[0]
				  && p!=cfd)
					close(p);
			}

			// Set SIGCHLD back to default, so that I
			// can get sensible returns from waitpid.
			memset(&sa, 0, sizeof(sa));
			sa.sa_handler=SIG_DFL;
			sigaction(SIGCHLD, &sa, NULL);

			close(pipe_rfd[0]); // close read end
			close(pipe_wfd[1]); // close write end

			confs_free_content(confs);
			confs_init(confs);

			ret=run_child(&cfd, ctx, &client_name, pipe_rfd[1],
			  fdtype==ASFD_FD_SERVER_LISTEN_STATUS?pipe_wfd[0]:-1,
			  conffile, forking);

			close(pipe_rfd[1]);
			close(pipe_wfd[0]);
			close_fd(&cfd);
			exit(ret);
		}
		default:
			// Parent.
			close(pipe_rfd[1]); // close write end
			close(pipe_wfd[0]); // close read end
			close_fd(&cfd);

			return setup_parent_child_pipes(asfd, childpid,
				&pipe_rfd[0], &pipe_wfd[1]);
	}
}
Esempio n. 15
0
File: main.c Progetto: EmisFR/burp
static int run_child(int *cfd, SSL_CTX *ctx, struct sockaddr_storage *addr,
	int status_wfd, int status_rfd, const char *conffile, int forking)
{
	int ret=-1;
	int ca_ret=0;
	SSL *ssl=NULL;
	BIO *sbio=NULL;
	struct conf **confs=NULL;
	struct conf **cconfs=NULL;
	struct cntr *cntr=NULL;
	struct async *as=NULL;
	const char *cname=NULL;
	struct asfd *asfd=NULL;
	int is_status_server=0;

	if(!(confs=confs_alloc())
	  || !(cconfs=confs_alloc()))
		goto end;

	set_peer_env_vars(addr);

	// Reload global config, in case things have changed. This means that
	// the server does not need to be restarted for most conf changes.
	confs_init(confs);
	confs_init(cconfs);
	if(conf_load_global_only(conffile, confs)) goto end;

	// Hack to keep forking turned off if it was specified as off on the
	// command line.
	if(!forking) set_int(confs[OPT_FORK], 0);

	if(!(sbio=BIO_new_socket(*cfd, BIO_NOCLOSE))
	  || !(ssl=SSL_new(ctx)))
	{
		logp("There was a problem joining ssl to the socket\n");
		goto end;
	}
	SSL_set_bio(ssl, sbio, sbio);

	/* Do not try to check peer certificate straight away.
	   Clients can send a certificate signing request when they have
	   no certificate. */
	SSL_set_verify(ssl, SSL_VERIFY_PEER
		/* | SSL_VERIFY_FAIL_IF_NO_PEER_CERT */, 0);

	if(ssl_do_accept(ssl))
		goto end;
	if(!(as=async_alloc())
	  || as->init(as, 0)
	  || !(asfd=setup_asfd_ssl(as, "main socket", cfd, ssl)))
		goto end;
	asfd->set_timeout(asfd, get_int(confs[OPT_NETWORK_TIMEOUT]));
	asfd->ratelimit=get_float(confs[OPT_RATELIMIT]);

	if(authorise_server(as->asfd, confs, cconfs)
	  || !(cname=get_string(cconfs[OPT_CNAME])) || !*cname)
	{
		// Add an annoying delay in case they are tempted to
		// try repeatedly.
		log_and_send(as->asfd, "unable to authorise on server");
		sleep(1);
		goto end;
	}

	if(!get_int(cconfs[OPT_ENABLED]))
	{
		log_and_send(as->asfd, "client not enabled on server");
		sleep(1);
		goto end;
	}

	// Set up counters. Have to wait until here to get cname.
	if(!(cntr=cntr_alloc())
	  || cntr_init(cntr, cname))
		goto end;
	set_cntr(confs[OPT_CNTR], cntr);
	set_cntr(cconfs[OPT_CNTR], cntr);

	/* At this point, the client might want to get a new certificate
	   signed. Clients on 1.3.2 or newer can do this. */
	if((ca_ret=ca_server_maybe_sign_client_cert(as->asfd, confs, cconfs))<0)
	{
		logp("Error signing client certificate request for %s\n",
			cname);
		goto end;
	}
	else if(ca_ret>0)
	{
		// Certificate signed and sent back.
		// Everything is OK, but we will close this instance
		// so that the client can start again with a new
		// connection and its new certificates.
		logp("Signed and returned client certificate request for %s\n",
			cname);
		ret=0;
		goto end;
	}

	/* Now it is time to check the certificate. */
	if(ssl_check_cert(ssl, confs, cconfs))
	{
		log_and_send(as->asfd, "check cert failed on server");
		goto end;
	}

	if(status_rfd>=0)
	{
		is_status_server=1;
		if(!setup_asfd(as, "status server parent socket", &status_rfd))
			goto end;
	}

	ret=child(as, is_status_server, status_wfd, confs, cconfs);
end:
	*cfd=-1;
	if(as && asfd_flush_asio(as->asfd))
		ret=-1;
	async_asfd_free_all(&as); // This closes cfd for us.
	logp("exit child\n");
	if(cntr) cntr_free(&cntr);
	if(confs)
	{
		set_cntr(confs[OPT_CNTR], NULL);
		confs_free(&confs);
	}
	if(cconfs)
	{
		set_cntr(cconfs[OPT_CNTR], NULL);
		confs_free(&cconfs);
	}
	return ret;
}