mirror of
https://git.tartarus.org/simon/putty.git
synced 2025-01-09 17:38:00 +00:00
Uppity: allow running multiple independent servers.
I've moved all the results of the command-line config options into a small struct instead of having them be local variables of main(). We maintain an array of those structs; most command-line options modify the last element in the array; and we respond to the new special option '--and' by appending a fresh struct to the end of the array and initialising it to default values. So now, if I want two or three SSH servers running on different ports with separately configured host keys, banners, etc, I can do that with a single command line along the lines of: ./uppity --listen 2222 --hostkey this.ppk --bannertext "this" \ --and --listen 2223 --hostkey that.ppk --bannertext "that" There's a single number space of connections used in log messages, and each new connection reports which of the servers it connects to. This is only a marginally useful feature: there's not much it does that couldn't have been done just as well by running multiple Uppitys each in their own process. But when I do want several servers at once (which I've been using recently to test the jump-host system), it's quite nice to have them all producing a single combined stream of log data and all conveniently killable with a single ^C.
This commit is contained in:
parent
fb663d4761
commit
d489c64f48
265
unix/uppity.c
265
unix/uppity.c
@ -130,6 +130,8 @@ struct server_instance {
|
||||
};
|
||||
|
||||
struct server_config {
|
||||
unsigned config_id;
|
||||
|
||||
Conf *conf;
|
||||
const SshServerConfig *ssc;
|
||||
|
||||
@ -140,16 +142,16 @@ struct server_config {
|
||||
|
||||
struct AuthPolicyShared *ap_shared;
|
||||
|
||||
unsigned next_id;
|
||||
|
||||
Socket *listening_socket;
|
||||
Plug listening_plug;
|
||||
};
|
||||
|
||||
static unsigned next_id = 0;
|
||||
|
||||
static void log_to_stderr(unsigned id, const char *msg)
|
||||
{
|
||||
if (id != (unsigned)-1)
|
||||
fprintf(stderr, "#%u: ", id);
|
||||
fprintf(stderr, "conn#%u: ", id);
|
||||
fputs(msg, stderr);
|
||||
fputc('\n', stderr);
|
||||
fflush(stderr);
|
||||
@ -348,7 +350,7 @@ static void show_help(FILE *fp)
|
||||
{
|
||||
safety_warning(fp);
|
||||
fputs("\n"
|
||||
"usage: uppity [options]\n"
|
||||
"usage: uppity [options] [--and <options>...]\n"
|
||||
"options: --listen [PORT|PATH] listen to a port on localhost, or Unix socket\n"
|
||||
" --listen-once (with --listen) stop after one "
|
||||
"connection\n"
|
||||
@ -385,6 +387,7 @@ static void show_help(FILE *fp)
|
||||
" --sshlog FILE write SSH packet log to FILE\n"
|
||||
" --sshrawlog FILE write SSH packets + raw data log"
|
||||
" to FILE\n"
|
||||
" --and run a separate server on another port\n"
|
||||
"also: uppity --help show this text\n"
|
||||
" uppity --version show version information\n"
|
||||
"\n", fp);
|
||||
@ -461,7 +464,7 @@ static Plug *server_conn_plug(
|
||||
|
||||
memset(inst, 0, sizeof(*inst));
|
||||
|
||||
inst->id = cfg->next_id++;
|
||||
inst->id = next_id++;
|
||||
inst->ap.shared = cfg->ap_shared;
|
||||
if (cfg->ssc->stunt_allow_trivial_ki_auth)
|
||||
inst->ap.kbdint_state = 1;
|
||||
@ -504,7 +507,7 @@ static int server_accepting(Plug *p, accept_fn_t constructor, accept_ctx_t ctx)
|
||||
cfg->listening_socket = NULL;
|
||||
}
|
||||
|
||||
unsigned old_next_id = cfg->next_id;
|
||||
unsigned old_next_id = next_id;
|
||||
|
||||
Plug *plug = server_conn_plug(cfg, &inst);
|
||||
s = constructor(ctx, plug);
|
||||
@ -514,15 +517,17 @@ static int server_accepting(Plug *p, accept_fn_t constructor, accept_ctx_t ctx)
|
||||
SocketPeerInfo *pi = sk_peer_info(s);
|
||||
|
||||
if (pi->addressfamily != ADDRTYPE_LOCAL && !sk_peer_trusted(s)) {
|
||||
fprintf(stderr, "rejected connection from %s (untrustworthy peer)\n",
|
||||
pi->log_text);
|
||||
fprintf(stderr, "rejected connection to serv#%u "
|
||||
"from %s (untrustworthy peer)\n",
|
||||
cfg->config_id, pi->log_text);
|
||||
sk_free_peer_info(pi);
|
||||
sk_close(s);
|
||||
cfg->next_id = old_next_id;
|
||||
next_id = old_next_id;
|
||||
return 1;
|
||||
}
|
||||
|
||||
char *msg = dupprintf("new connection from %s", pi->log_text);
|
||||
char *msg = dupprintf("new connection to serv#%u from %s",
|
||||
cfg->config_id, pi->log_text);
|
||||
log_to_stderr(inst->id, msg);
|
||||
sfree(msg);
|
||||
sk_free_peer_info(pi);
|
||||
@ -538,29 +543,87 @@ static const PlugVtable server_plugvt = {
|
||||
.accepting = server_accepting,
|
||||
};
|
||||
|
||||
int main(int argc, char **argv)
|
||||
{
|
||||
int listen_port = -1;
|
||||
const char *listen_socket = NULL;
|
||||
|
||||
ssh_key **hostkeys = NULL;
|
||||
size_t nhostkeys = 0, hostkeysize = 0;
|
||||
RSAKey *hostkey1 = NULL;
|
||||
|
||||
struct cmdline_instance {
|
||||
int listen_port;
|
||||
const char *listen_socket;
|
||||
ssh_key **hostkeys;
|
||||
size_t nhostkeys, hostkeysize;
|
||||
RSAKey *hostkey1;
|
||||
struct AuthPolicyShared aps;
|
||||
SshServerConfig ssc;
|
||||
Conf *conf;
|
||||
};
|
||||
|
||||
Conf *conf = make_ssh_server_conf();
|
||||
static void init_cmdline_instance(struct cmdline_instance *ci)
|
||||
{
|
||||
ci->listen_port = -1;
|
||||
ci->listen_socket = NULL;
|
||||
|
||||
aps.ssh1keys = NULL;
|
||||
aps.ssh2keys = NULL;
|
||||
ci->hostkeys = NULL;
|
||||
ci->nhostkeys = ci->hostkeysize = 0;
|
||||
ci->hostkey1 = NULL;
|
||||
|
||||
memset(&ssc, 0, sizeof(ssc));
|
||||
ci->conf = make_ssh_server_conf();
|
||||
|
||||
ssc.application_name = "Uppity";
|
||||
ssc.session_starting_dir = getenv("HOME");
|
||||
ssc.ssh1_cipher_mask = SSH1_SUPPORTED_CIPHER_MASK;
|
||||
ssc.ssh1_allow_compression = true;
|
||||
ci->aps.ssh1keys = NULL;
|
||||
ci->aps.ssh2keys = NULL;
|
||||
|
||||
memset(&ci->ssc, 0, sizeof(ci->ssc));
|
||||
|
||||
ci->ssc.application_name = "Uppity";
|
||||
ci->ssc.session_starting_dir = getenv("HOME");
|
||||
ci->ssc.ssh1_cipher_mask = SSH1_SUPPORTED_CIPHER_MASK;
|
||||
ci->ssc.ssh1_allow_compression = true;
|
||||
}
|
||||
|
||||
static void cmdline_instance_start(struct cmdline_instance *ci)
|
||||
{
|
||||
static unsigned next_server_config_id = 0;
|
||||
|
||||
struct server_config *scfg = snew(struct server_config);
|
||||
scfg->config_id = next_server_config_id++;
|
||||
scfg->conf = ci->conf;
|
||||
scfg->ssc = &ci->ssc;
|
||||
scfg->hostkeys = ci->hostkeys;
|
||||
scfg->nhostkeys = ci->nhostkeys;
|
||||
scfg->hostkey1 = ci->hostkey1;
|
||||
scfg->ap_shared = &ci->aps;
|
||||
|
||||
if (ci->listen_port >= 0 || ci->listen_socket) {
|
||||
listening = true;
|
||||
scfg->listening_plug.vt = &server_plugvt;
|
||||
char *msg;
|
||||
if (ci->listen_port >= 0) {
|
||||
scfg->listening_socket = sk_newlistener(
|
||||
NULL, ci->listen_port, &scfg->listening_plug, true,
|
||||
ADDRTYPE_UNSPEC);
|
||||
msg = dupprintf("serv#%u: listening on port %d",
|
||||
scfg->config_id, ci->listen_port);
|
||||
} else {
|
||||
SockAddr *addr = unix_sock_addr(ci->listen_socket);
|
||||
scfg->listening_socket = new_unix_listener(
|
||||
addr, &scfg->listening_plug);
|
||||
msg = dupprintf("serv#%u: listening on Unix socket %s",
|
||||
scfg->config_id, ci->listen_socket);
|
||||
}
|
||||
|
||||
log_to_stderr(-1, msg);
|
||||
sfree(msg);
|
||||
} else {
|
||||
struct server_instance *inst;
|
||||
Plug *plug = server_conn_plug(scfg, &inst);
|
||||
ssh_server_start(plug, make_fd_socket(0, 1, -1, NULL, 0, plug));
|
||||
log_to_stderr(inst->id, "speaking SSH on stdio");
|
||||
}
|
||||
}
|
||||
|
||||
int main(int argc, char **argv)
|
||||
{
|
||||
size_t ninstances = 0, instancessize = 8;
|
||||
struct cmdline_instance *instances = snewn(
|
||||
instancessize, struct cmdline_instance);
|
||||
struct cmdline_instance *ci = &instances[ninstances++];
|
||||
init_cmdline_instance(ci);
|
||||
|
||||
if (argc <= 1) {
|
||||
/*
|
||||
@ -584,13 +647,17 @@ int main(int argc, char **argv)
|
||||
show_version_and_exit();
|
||||
} else if (longoptnoarg(arg, "--verbose") || !strcmp(arg, "-v")) {
|
||||
verbose = true;
|
||||
} else if (longoptnoarg(arg, "--and")) {
|
||||
sgrowarray(instances, instancessize, ninstances);
|
||||
ci = &instances[ninstances++];
|
||||
init_cmdline_instance(ci);
|
||||
} else if (longoptarg(arg, "--listen", &val, &argc, &argv)) {
|
||||
if (val[0] == '/') {
|
||||
listen_port = -1;
|
||||
listen_socket = val;
|
||||
ci->listen_port = -1;
|
||||
ci->listen_socket = val;
|
||||
} else {
|
||||
listen_port = atoi(val);
|
||||
listen_socket = NULL;
|
||||
ci->listen_port = atoi(val);
|
||||
ci->listen_socket = NULL;
|
||||
}
|
||||
} else if (!strcmp(arg, "--listen-once")) {
|
||||
listen_once = true;
|
||||
@ -622,24 +689,24 @@ int main(int argc, char **argv)
|
||||
sfree(uk->comment);
|
||||
sfree(uk);
|
||||
|
||||
for (int i = 0; i < nhostkeys; i++)
|
||||
if (ssh_key_alg(hostkeys[i]) == ssh_key_alg(key)) {
|
||||
for (int i = 0; i < ci->nhostkeys; i++)
|
||||
if (ssh_key_alg(ci->hostkeys[i]) == ssh_key_alg(key)) {
|
||||
fprintf(stderr, "%s: host key '%s' duplicates key "
|
||||
"type %s\n", appname, val,
|
||||
ssh_key_alg(key)->ssh_id);
|
||||
exit(1);
|
||||
}
|
||||
|
||||
sgrowarray(hostkeys, hostkeysize, nhostkeys);
|
||||
hostkeys[nhostkeys++] = key;
|
||||
sgrowarray(ci->hostkeys, ci->hostkeysize, ci->nhostkeys);
|
||||
ci->hostkeys[ci->nhostkeys++] = key;
|
||||
} else if (keytype == SSH_KEYTYPE_SSH1) {
|
||||
if (hostkey1) {
|
||||
if (ci->hostkey1) {
|
||||
fprintf(stderr, "%s: host key '%s' is a redundant "
|
||||
"SSH-1 host key\n", appname, val);
|
||||
exit(1);
|
||||
}
|
||||
hostkey1 = snew(RSAKey);
|
||||
if (!rsa1_load_f(keyfile, hostkey1, NULL, &error)) {
|
||||
ci->hostkey1 = snew(RSAKey);
|
||||
if (!rsa1_load_f(keyfile, ci->hostkey1, NULL, &error)) {
|
||||
fprintf(stderr, "%s: unable to load host key '%s': "
|
||||
"%s\n", appname, val, error);
|
||||
exit(1);
|
||||
@ -665,19 +732,19 @@ int main(int argc, char **argv)
|
||||
exit(1);
|
||||
}
|
||||
|
||||
if (ssc.rsa_kex_key) {
|
||||
freersakey(ssc.rsa_kex_key);
|
||||
if (ci->ssc.rsa_kex_key) {
|
||||
freersakey(ci->ssc.rsa_kex_key);
|
||||
} else {
|
||||
ssc.rsa_kex_key = snew(RSAKey);
|
||||
ci->ssc.rsa_kex_key = snew(RSAKey);
|
||||
}
|
||||
|
||||
if (!rsa1_load_f(keyfile, ssc.rsa_kex_key, NULL, &error)) {
|
||||
if (!rsa1_load_f(keyfile, ci->ssc.rsa_kex_key, NULL, &error)) {
|
||||
fprintf(stderr, "%s: unable to load RSA kex key '%s': "
|
||||
"%s\n", appname, val, error);
|
||||
exit(1);
|
||||
}
|
||||
|
||||
ssc.rsa_kex_key->sshk.vt = &ssh_rsa;
|
||||
ci->ssc.rsa_kex_key->sshk.vt = &ssh_rsa;
|
||||
} else if (longoptarg(arg, "--userkey", &val, &argc, &argv)) {
|
||||
Filename *keyfile;
|
||||
int keytype;
|
||||
@ -704,8 +771,8 @@ int main(int argc, char **argv)
|
||||
memcpy(blob, sb->u, sb->len);
|
||||
node->public_blob = make_ptrlen(blob, sb->len);
|
||||
|
||||
node->next = aps.ssh2keys;
|
||||
aps.ssh2keys = node;
|
||||
node->next = ci->aps.ssh2keys;
|
||||
ci->aps.ssh2keys = node;
|
||||
|
||||
strbuf_free(sb);
|
||||
} else if (keytype == SSH_KEYTYPE_SSH1_PUBLIC) {
|
||||
@ -724,8 +791,8 @@ int main(int argc, char **argv)
|
||||
BinarySource_BARE_INIT(src, sb->u, sb->len);
|
||||
get_rsa_ssh1_pub(src, &node->key, RSA_SSH1_EXPONENT_FIRST);
|
||||
|
||||
node->next = aps.ssh1keys;
|
||||
aps.ssh1keys = node;
|
||||
node->next = ci->aps.ssh1keys;
|
||||
ci->aps.ssh1keys = node;
|
||||
|
||||
strbuf_free(sb);
|
||||
} else {
|
||||
@ -747,27 +814,27 @@ int main(int argc, char **argv)
|
||||
exit(1);
|
||||
}
|
||||
fclose(fp);
|
||||
ssc.banner = ptrlen_from_strbuf(sb);
|
||||
ci->ssc.banner = ptrlen_from_strbuf(sb);
|
||||
} else if (longoptarg(arg, "--bannertext", &val, &argc, &argv)) {
|
||||
ssc.banner = ptrlen_from_asciz(val);
|
||||
ci->ssc.banner = ptrlen_from_asciz(val);
|
||||
} else if (longoptarg(arg, "--sessiondir", &val, &argc, &argv)) {
|
||||
ssc.session_starting_dir = val;
|
||||
ci->ssc.session_starting_dir = val;
|
||||
} else if (longoptarg(arg, "--kexinit-kex", &val, &argc, &argv)) {
|
||||
ssc.kex_override[KEXLIST_KEX] = ptrlen_from_asciz(val);
|
||||
ci->ssc.kex_override[KEXLIST_KEX] = ptrlen_from_asciz(val);
|
||||
} else if (longoptarg(arg, "--kexinit-hostkey", &val, &argc, &argv)) {
|
||||
ssc.kex_override[KEXLIST_HOSTKEY] = ptrlen_from_asciz(val);
|
||||
ci->ssc.kex_override[KEXLIST_HOSTKEY] = ptrlen_from_asciz(val);
|
||||
} else if (longoptarg(arg, "--kexinit-cscipher", &val, &argc, &argv)) {
|
||||
ssc.kex_override[KEXLIST_CSCIPHER] = ptrlen_from_asciz(val);
|
||||
ci->ssc.kex_override[KEXLIST_CSCIPHER] = ptrlen_from_asciz(val);
|
||||
} else if (longoptarg(arg, "--kexinit-csmac", &val, &argc, &argv)) {
|
||||
ssc.kex_override[KEXLIST_CSMAC] = ptrlen_from_asciz(val);
|
||||
ci->ssc.kex_override[KEXLIST_CSMAC] = ptrlen_from_asciz(val);
|
||||
} else if (longoptarg(arg, "--kexinit-cscomp", &val, &argc, &argv)) {
|
||||
ssc.kex_override[KEXLIST_CSCOMP] = ptrlen_from_asciz(val);
|
||||
ci->ssc.kex_override[KEXLIST_CSCOMP] = ptrlen_from_asciz(val);
|
||||
} else if (longoptarg(arg, "--kexinit-sccipher", &val, &argc, &argv)) {
|
||||
ssc.kex_override[KEXLIST_SCCIPHER] = ptrlen_from_asciz(val);
|
||||
ci->ssc.kex_override[KEXLIST_SCCIPHER] = ptrlen_from_asciz(val);
|
||||
} else if (longoptarg(arg, "--kexinit-scmac", &val, &argc, &argv)) {
|
||||
ssc.kex_override[KEXLIST_SCMAC] = ptrlen_from_asciz(val);
|
||||
ci->ssc.kex_override[KEXLIST_SCMAC] = ptrlen_from_asciz(val);
|
||||
} else if (longoptarg(arg, "--kexinit-sccomp", &val, &argc, &argv)) {
|
||||
ssc.kex_override[KEXLIST_SCCOMP] = ptrlen_from_asciz(val);
|
||||
ci->ssc.kex_override[KEXLIST_SCCOMP] = ptrlen_from_asciz(val);
|
||||
} else if (longoptarg(arg, "--ssh1-ciphers", &val, &argc, &argv)) {
|
||||
ptrlen list = ptrlen_from_asciz(val);
|
||||
ptrlen word;
|
||||
@ -786,45 +853,58 @@ int main(int argc, char **argv)
|
||||
appname, PTRLEN_PRINTF(word));
|
||||
exit(1);
|
||||
}
|
||||
ssc.ssh1_cipher_mask = mask;
|
||||
ci->ssc.ssh1_cipher_mask = mask;
|
||||
} else if (longoptnoarg(arg, "--ssh1-no-compression")) {
|
||||
ssc.ssh1_allow_compression = false;
|
||||
ci->ssc.ssh1_allow_compression = false;
|
||||
} else if (longoptnoarg(arg, "--exitsignum")) {
|
||||
ssc.exit_signal_numeric = true;
|
||||
ci->ssc.exit_signal_numeric = true;
|
||||
} else if (longoptarg(arg, "--sshlog", &val, &argc, &argv) ||
|
||||
longoptarg(arg, "-sshlog", &val, &argc, &argv)) {
|
||||
Filename *logfile = filename_from_str(val);
|
||||
conf_set_filename(conf, CONF_logfilename, logfile);
|
||||
conf_set_filename(ci->conf, CONF_logfilename, logfile);
|
||||
filename_free(logfile);
|
||||
conf_set_int(conf, CONF_logtype, LGTYP_PACKETS);
|
||||
conf_set_int(conf, CONF_logxfovr, LGXF_OVR);
|
||||
conf_set_int(ci->conf, CONF_logtype, LGTYP_PACKETS);
|
||||
conf_set_int(ci->conf, CONF_logxfovr, LGXF_OVR);
|
||||
} else if (longoptarg(arg, "--sshrawlog", &val, &argc, &argv) ||
|
||||
longoptarg(arg, "-sshrawlog", &val, &argc, &argv)) {
|
||||
Filename *logfile = filename_from_str(val);
|
||||
conf_set_filename(conf, CONF_logfilename, logfile);
|
||||
conf_set_filename(ci->conf, CONF_logfilename, logfile);
|
||||
filename_free(logfile);
|
||||
conf_set_int(conf, CONF_logtype, LGTYP_SSHRAW);
|
||||
conf_set_int(conf, CONF_logxfovr, LGXF_OVR);
|
||||
conf_set_int(ci->conf, CONF_logtype, LGTYP_SSHRAW);
|
||||
conf_set_int(ci->conf, CONF_logxfovr, LGXF_OVR);
|
||||
} else if (!strcmp(arg, "--pretend-to-accept-any-pubkey")) {
|
||||
ssc.stunt_pretend_to_accept_any_pubkey = true;
|
||||
ci->ssc.stunt_pretend_to_accept_any_pubkey = true;
|
||||
} else if (!strcmp(arg, "--open-unconditional-agent-socket")) {
|
||||
ssc.stunt_open_unconditional_agent_socket = true;
|
||||
ci->ssc.stunt_open_unconditional_agent_socket = true;
|
||||
} else if (!strcmp(arg, "--allow-none-auth")) {
|
||||
ssc.stunt_allow_none_auth = true;
|
||||
ci->ssc.stunt_allow_none_auth = true;
|
||||
} else if (!strcmp(arg, "--allow-trivial-ki-auth")) {
|
||||
ssc.stunt_allow_trivial_ki_auth = true;
|
||||
ci->ssc.stunt_allow_trivial_ki_auth = true;
|
||||
} else if (!strcmp(arg, "--return-success-to-pubkey-offer")) {
|
||||
ssc.stunt_return_success_to_pubkey_offer = true;
|
||||
ci->ssc.stunt_return_success_to_pubkey_offer = true;
|
||||
} else {
|
||||
fprintf(stderr, "%s: unrecognised option '%s'\n", appname, arg);
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
if (nhostkeys == 0 && !hostkey1) {
|
||||
fprintf(stderr, "%s: specify at least one host key\n", appname);
|
||||
if (ninstances > 1 && listen_once) {
|
||||
fprintf(stderr, "%s: cannot listen once only with multiple server "
|
||||
"instances\n", appname);
|
||||
exit(1);
|
||||
}
|
||||
for (size_t i = 0; i < ninstances; i++) {
|
||||
ci = &instances[i];
|
||||
if (ci->nhostkeys == 0 && !ci->hostkey1) {
|
||||
fprintf(stderr, "%s: specify at least one host key\n", appname);
|
||||
exit(1);
|
||||
}
|
||||
if (ninstances > 1 && !(ci->listen_port >= 0 || ci->listen_socket)) {
|
||||
fprintf(stderr, "%s: cannot talk to stdio with multiple server "
|
||||
"instances\n", appname);
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
random_ref();
|
||||
|
||||
@ -837,41 +917,8 @@ int main(int argc, char **argv)
|
||||
sk_init();
|
||||
uxsel_init();
|
||||
|
||||
struct server_config scfg;
|
||||
scfg.conf = conf;
|
||||
scfg.ssc = &ssc;
|
||||
scfg.hostkeys = hostkeys;
|
||||
scfg.nhostkeys = nhostkeys;
|
||||
scfg.hostkey1 = hostkey1;
|
||||
scfg.ap_shared = &aps;
|
||||
scfg.next_id = 0;
|
||||
|
||||
if (listen_port >= 0 || listen_socket) {
|
||||
listening = true;
|
||||
scfg.listening_plug.vt = &server_plugvt;
|
||||
char *msg;
|
||||
if (listen_port >= 0) {
|
||||
scfg.listening_socket = sk_newlistener(
|
||||
NULL, listen_port, &scfg.listening_plug, true,
|
||||
ADDRTYPE_UNSPEC);
|
||||
msg = dupprintf("%s: listening on port %d",
|
||||
appname, listen_port);
|
||||
} else {
|
||||
SockAddr *addr = unix_sock_addr(listen_socket);
|
||||
scfg.listening_socket = new_unix_listener(
|
||||
addr, &scfg.listening_plug);
|
||||
msg = dupprintf("%s: listening on Unix socket %s",
|
||||
appname, listen_socket);
|
||||
}
|
||||
|
||||
log_to_stderr(-1, msg);
|
||||
sfree(msg);
|
||||
} else {
|
||||
struct server_instance *inst;
|
||||
Plug *plug = server_conn_plug(&scfg, &inst);
|
||||
ssh_server_start(plug, make_fd_socket(0, 1, -1, NULL, 0, plug));
|
||||
log_to_stderr(inst->id, "speaking SSH on stdio");
|
||||
}
|
||||
for (size_t i = 0; i < ninstances; i++)
|
||||
cmdline_instance_start(&instances[i]);
|
||||
|
||||
cli_main_loop(cliloop_no_pw_setup, cliloop_no_pw_check,
|
||||
cliloop_always_continue, NULL);
|
||||
|
Loading…
Reference in New Issue
Block a user