Exemplo n.º 1
0
/* we got a connection to the ocsp responder */
int
ocsp_receive_fd(struct iked *env, struct imsg *imsg)
{
	struct iked_ocsp_entry	*ioe = NULL;
	struct iked_ocsp	*ocsp = NULL;
	struct iked_socket	*sock;
	char			*path = NULL;
	int			 ret = -1;

	log_debug("%s: received socket fd %d", __func__, imsg->fd);
	if ((ioe = TAILQ_FIRST(&env->sc_ocsp)) == NULL) {
		log_debug("%s: oops, no request for", __func__);
		close(imsg->fd);
		return (-1);
	}
	TAILQ_REMOVE(&env->sc_ocsp, ioe, ioe_entry);
	ocsp = ioe->ioe_ocsp;
	free(ioe);

	if ((sock = calloc(1, sizeof(*sock))) == NULL)
		fatal("ocsp_receive_fd: calloc sock");

	/* note that sock_addr is not set */
	sock->sock_fd = imsg->fd;
	sock->sock_env = env;
	ocsp->ocsp_sock = sock;

	/* fetch 'path' and 'fd' from imsg */
	if ((path = get_string(imsg->data, IMSG_DATA_SIZE(imsg))) == NULL)
		goto done;

	BIO_set_fd(ocsp->ocsp_cbio, imsg->fd, BIO_NOCLOSE);

	if ((ocsp->ocsp_req_ctx = OCSP_sendreq_new(ocsp->ocsp_cbio,
	    path, NULL, -1)) == NULL)
		goto done;
	if (!OCSP_REQ_CTX_set1_req(ocsp->ocsp_req_ctx, ocsp->ocsp_req))
		goto done;

	event_set(&sock->sock_ev, sock->sock_fd, EV_WRITE, ocsp_callback, ocsp);
	event_add(&sock->sock_ev, NULL);
	ret = 0;
 done:
	if (ret == -1)
		ocsp_validate_finish(ocsp, 0);	/* failed */
	free(path);
	return (ret);
}
Exemplo n.º 2
0
/* ARGSUSED */
void
control_dispatch_ext(int fd, short event, void *arg)
{
	struct ctl_conn		*c;
	struct imsg		 imsg;
	int			 n;
	uid_t			 euid;
	gid_t			 egid;

	if (getpeereid(fd, &euid, &egid) == -1)
		fatal("getpeereid");

	if ((c = control_connbyfd(fd)) == NULL) {
		log_warn("control_dispatch_ext: fd %d: not found", fd);
		return;
	}

	if (event & EV_READ) {
		if ((n = imsg_read(&c->iev.ibuf)) == -1 || n == 0) {
			control_close(fd);
			return;
		}
	}

	if (event & EV_WRITE) {
		if (msgbuf_write(&c->iev.ibuf.w) < 0) {
			control_close(fd);
			return;
		}
	}

	for (;;) {
		if ((n = imsg_get(&c->iev.ibuf, &imsg)) == -1) {
			control_close(fd);
			return;
		}

		if (n == 0)
			break;

		switch (imsg.hdr.type) {
		case IMSG_SMTP_ENQUEUE:
			if (env->sc_flags & (SMTPD_SMTP_PAUSED |
			    SMTPD_CONFIGURING | SMTPD_EXITING)) {
				imsg_compose_event(&c->iev, IMSG_CTL_FAIL, 0, 0, -1,
					NULL, 0);
				break;
			}
			imsg_compose_event(env->sc_ievs[PROC_SMTP],
			    IMSG_SMTP_ENQUEUE, fd, 0, -1, &euid, sizeof(euid));
			break;
		case IMSG_STATS:
			if (euid)
				goto badcred;
			imsg_compose_event(&c->iev, IMSG_STATS, 0, 0, -1,
			    env->stats, sizeof(struct stats));
			break;
		case IMSG_CTL_SHUTDOWN:
			/* NEEDS_FIX */
			log_debug("received shutdown request");

			if (euid)
				goto badcred;

			if (env->sc_flags & SMTPD_EXITING) {
				imsg_compose_event(&c->iev, IMSG_CTL_FAIL, 0, 0, -1,
					NULL, 0);
				break;
			}
			env->sc_flags |= SMTPD_EXITING;
			imsg_compose_event(&c->iev, IMSG_CTL_OK, 0, 0, -1, NULL, 0);
			break;
		case IMSG_CTL_VERBOSE: {
			int verbose;

			if (euid)
				goto badcred;

			if (IMSG_DATA_SIZE(&imsg) != sizeof(verbose))
				goto badcred;

			memcpy(&verbose, imsg.data, sizeof(verbose));
			log_verbose(verbose);
			imsg_compose_event(env->sc_ievs[PROC_PARENT], IMSG_CTL_VERBOSE,
			    0, 0, -1, &verbose, sizeof(verbose));
			imsg_compose_event(&c->iev, IMSG_CTL_OK, 0, 0, -1, NULL, 0);
			break;
		}
		case IMSG_QUEUE_PAUSE_MDA:
			if (euid)
				goto badcred;

			if (env->sc_flags & SMTPD_MDA_PAUSED) {
				imsg_compose_event(&c->iev, IMSG_CTL_FAIL, 0, 0, -1,
					NULL, 0);
				break;
			}
			env->sc_flags |= SMTPD_MDA_PAUSED;
			imsg_compose_event(env->sc_ievs[PROC_QUEUE],
			    IMSG_QUEUE_PAUSE_MDA, 0, 0, -1, NULL, 0);
			imsg_compose_event(&c->iev, IMSG_CTL_OK, 0, 0, -1, NULL, 0);
			break;
		case IMSG_QUEUE_PAUSE_MTA:
			if (euid)
				goto badcred;

			if (env->sc_flags & SMTPD_MTA_PAUSED) {
				imsg_compose_event(&c->iev, IMSG_CTL_FAIL, 0, 0, -1,
					NULL, 0);
				break;
			}
			env->sc_flags |= SMTPD_MTA_PAUSED;
			imsg_compose_event(env->sc_ievs[PROC_QUEUE],
			    IMSG_QUEUE_PAUSE_MTA, 0, 0, -1, NULL, 0);
			imsg_compose_event(&c->iev, IMSG_CTL_OK, 0, 0, -1, NULL, 0);
			break;
		case IMSG_SMTP_PAUSE:
			if (euid)
				goto badcred;

			if (env->sc_flags & SMTPD_SMTP_PAUSED) {
				imsg_compose_event(&c->iev, IMSG_CTL_FAIL, 0, 0, -1,
					NULL, 0);
				break;
			}
			env->sc_flags |= SMTPD_SMTP_PAUSED;
			imsg_compose_event(env->sc_ievs[PROC_SMTP], IMSG_SMTP_PAUSE,			
			    0, 0, -1, NULL, 0);
			imsg_compose_event(&c->iev, IMSG_CTL_OK, 0, 0, -1, NULL, 0);
			break;
		case IMSG_QUEUE_RESUME_MDA:
			if (euid)
				goto badcred;

			if (! (env->sc_flags & SMTPD_MDA_PAUSED)) {
				imsg_compose_event(&c->iev, IMSG_CTL_FAIL, 0, 0, -1,
					NULL, 0);
				break;
			}
			env->sc_flags &= ~SMTPD_MDA_PAUSED;
			imsg_compose_event(env->sc_ievs[PROC_QUEUE],
			    IMSG_QUEUE_RESUME_MDA, 0, 0, -1, NULL, 0);
			imsg_compose_event(&c->iev, IMSG_CTL_OK, 0, 0, -1, NULL, 0);
			break;
		case IMSG_QUEUE_RESUME_MTA:
			if (euid)
				goto badcred;

			if (!(env->sc_flags & SMTPD_MTA_PAUSED)) {
				imsg_compose_event(&c->iev, IMSG_CTL_FAIL, 0, 0, -1,
					NULL, 0);
				break;
			}
			env->sc_flags &= ~SMTPD_MTA_PAUSED;
			imsg_compose_event(env->sc_ievs[PROC_QUEUE],
			    IMSG_QUEUE_RESUME_MTA, 0, 0, -1, NULL, 0);
			imsg_compose_event(&c->iev, IMSG_CTL_OK, 0, 0, -1, NULL, 0);
			break;

		case IMSG_SMTP_RESUME:
			if (euid)
				goto badcred;

			if (!(env->sc_flags & SMTPD_SMTP_PAUSED)) {
				imsg_compose_event(&c->iev, IMSG_CTL_FAIL, 0, 0, -1,
					NULL, 0);
				break;
			}
			env->sc_flags &= ~SMTPD_SMTP_PAUSED;
			imsg_compose_event(env->sc_ievs[PROC_SMTP], IMSG_SMTP_RESUME,
			    0, 0, -1, NULL, 0);
			imsg_compose_event(&c->iev, IMSG_CTL_OK, 0, 0, -1, NULL, 0);
			break;

		case IMSG_RUNNER_SCHEDULE: {
			u_int64_t ullval;

			if (euid)
				goto badcred;

			ullval = *(u_int64_t *)imsg.data;

			imsg_compose_event(env->sc_ievs[PROC_RUNNER], IMSG_RUNNER_SCHEDULE,
			    0, 0, -1, &ullval, sizeof(ullval));

			imsg_compose_event(&c->iev, IMSG_CTL_OK, 0, 0, -1, NULL, 0);
			break;
		}

		case IMSG_RUNNER_REMOVE: {
			u_int64_t ullval;

			if (euid)
				goto badcred;

			ullval = *(u_int64_t *)imsg.data;

			imsg_compose_event(env->sc_ievs[PROC_RUNNER], IMSG_RUNNER_REMOVE,
			    0, 0, -1, &ullval, sizeof(ullval));

			imsg_compose_event(&c->iev, IMSG_CTL_OK, 0, 0, -1, NULL, 0);
			break;
		}
		default:
			log_debug("control_dispatch_ext: "
			    "error handling %s imsg",
			    imsg_to_str(imsg.hdr.type));
			break;
		}
		imsg_free(&imsg);
		continue;

badcred:
		imsg_compose_event(&c->iev, IMSG_CTL_FAIL, 0, 0, -1,
		    NULL, 0);
	}

	imsg_event_add(&c->iev);
}
Exemplo n.º 3
0
int
ca_getreq(struct iked *env, struct imsg *imsg)
{
	struct ca_store		*store = env->sc_priv;
	struct iked_sahdr	 sh;
	uint8_t			 type;
	uint8_t			*ptr;
	size_t			 len;
	unsigned int		 i, n;
	X509			*ca = NULL, *cert = NULL;
	struct ibuf		*buf;
	struct iked_static_id	 id;

	ptr = (uint8_t *)imsg->data;
	len = IMSG_DATA_SIZE(imsg);
	i = sizeof(id) + sizeof(uint8_t) + sizeof(sh);
	if (len < i || ((len - i) % SHA_DIGEST_LENGTH) != 0)
		return (-1);

	memcpy(&id, ptr, sizeof(id));
	if (id.id_type == IKEV2_ID_NONE)
		return (-1);
	memcpy(&sh, ptr + sizeof(id), sizeof(sh));
	memcpy(&type, ptr + sizeof(id) + sizeof(sh), sizeof(uint8_t));

	switch (type) {
	case IKEV2_CERT_RSA_KEY:
		if (store->ca_pubkey.id_type != type ||
		    (buf = store->ca_pubkey.id_buf) == NULL)
			return (-1);

		log_debug("%s: using local public key of type %s", __func__,
		    print_map(type, ikev2_cert_map));
		break;
	case IKEV2_CERT_X509_CERT:
		for (n = 1; i < len; n++, i += SHA_DIGEST_LENGTH) {
			if ((ca = ca_by_subjectpubkey(store->ca_cas, ptr + i,
			    SHA_DIGEST_LENGTH)) == NULL)
				continue;

			log_debug("%s: found CA %s", __func__, ca->name);

			if ((cert = ca_by_issuer(store->ca_certs,
			    X509_get_subject_name(ca), &id)) != NULL) {
				/* XXX
				 * should we re-validate our own cert here?
				 */
				break;
			}
		}
		if (ca == NULL || cert == NULL) {
			log_warnx("%s: no valid local certificate found",
			    __func__);
			type = IKEV2_CERT_NONE;
			ca_setcert(env, &sh, NULL, type, NULL, 0, PROC_IKEV2);
			return (0);
		}
		log_debug("%s: found local certificate %s", __func__,
		    cert->name);

		if ((buf = ca_x509_serialize(cert)) == NULL)
			return (-1);
		break;
	default:
		log_warnx("%s: unknown cert type requested", __func__);
		return (-1);
	}

	ca_setcert(env, &sh, NULL, type,
	    ibuf_data(buf), ibuf_size(buf), PROC_IKEV2);

	return (0);
}
Exemplo n.º 4
0
int
ca_getreq(struct iked *env, struct imsg *imsg)
{
	struct ca_store	*store = env->sc_priv;
	struct iked_sahdr	 sh;
	u_int8_t		 type;
	u_int8_t		*ptr;
	size_t			 len;
	u_int			 i, n;
	X509			*ca = NULL, *cert = NULL;
	struct ibuf		*buf;
	struct iked_static_id	 id;

	ptr = (u_int8_t *)imsg->data;
	len = IMSG_DATA_SIZE(imsg);
	i = sizeof(id) + sizeof(u_int8_t) + sizeof(sh);
	if (len < i || ((len - i) % SHA_DIGEST_LENGTH) != 0)
		return (-1);

	memcpy(&id, ptr, sizeof(id));
	if (id.id_type == IKEV2_ID_NONE)
		return (-1);
	memcpy(&sh, ptr + sizeof(id), sizeof(sh));
	memcpy(&type, ptr + sizeof(id) + sizeof(sh), sizeof(u_int8_t));
	if (type != IKEV2_CERT_X509_CERT)
		return (-1);

	for (n = 1; i < len; n++, i += SHA_DIGEST_LENGTH) {
		if ((ca = ca_by_subjectpubkey(store->ca_cas,
		    ptr + i, SHA_DIGEST_LENGTH)) == NULL) {
			log_debug("%s: CA %d not found", __func__, n);
			print_hex(ptr, i, SHA_DIGEST_LENGTH);
			continue;
		}

		log_debug("%s: found CA %s", __func__, ca->name);

		if ((cert = ca_by_issuer(store->ca_certs,
		    X509_get_subject_name(ca), &id)) != NULL) {
			/* XXX should we re-validate our own cert here? */
			break;
		}

		log_debug("%s: no valid certificate for this CA", __func__);
	}
	if (ca == NULL || cert == NULL) {
		log_warnx("%s: no valid local certificate found", __func__);
		type = IKEV2_CERT_NONE;
		ca_setcert(env, &sh, NULL, type, NULL, 0, PROC_IKEV2);
		return (0);
	}

	log_debug("%s: found local certificate %s", __func__, cert->name);

	if ((buf = ca_x509_serialize(cert)) == NULL)
		return (-1);

	type = IKEV2_CERT_X509_CERT;
	ca_setcert(env, &sh, NULL, type,
	    ibuf_data(buf), ibuf_size(buf), PROC_IKEV2);

	return (0);
}
Exemplo n.º 5
0
static int
show_stats_output(struct imsg *imsg)
{
	struct stats	*stats;

	if (imsg->hdr.type != IMSG_STATS)
		errx(1, "show_stats_output: bad hdr type (%d)", imsg->hdr.type);
	
	if (IMSG_DATA_SIZE(imsg) != sizeof(*stats))
		errx(1, "show_stats_output: bad data size");

	stats = imsg->data;
	stat_init(stats->counters, STATS_MAX);

	stat_print(STATS_CONTROL_SESSION, STAT_COUNT);
	stat_print(STATS_CONTROL_SESSION, STAT_ACTIVE);
	stat_print(STATS_CONTROL_SESSION, STAT_MAXACTIVE);

	stat_print(STATS_MDA_SESSION, STAT_COUNT);
	stat_print(STATS_MDA_SESSION, STAT_ACTIVE);
	stat_print(STATS_MDA_SESSION, STAT_MAXACTIVE);

	stat_print(STATS_MTA_SESSION, STAT_COUNT);
	stat_print(STATS_MTA_SESSION, STAT_ACTIVE);
	stat_print(STATS_MTA_SESSION, STAT_MAXACTIVE);

	stat_print(STATS_LKA_SESSION, STAT_COUNT);
	stat_print(STATS_LKA_SESSION, STAT_ACTIVE);
	stat_print(STATS_LKA_SESSION, STAT_MAXACTIVE);
	stat_print(STATS_LKA_SESSION_MX, STAT_COUNT);
	stat_print(STATS_LKA_SESSION_HOST, STAT_COUNT);
	stat_print(STATS_LKA_SESSION_CNAME, STAT_COUNT);
	stat_print(STATS_LKA_FAILURE, STAT_COUNT);

	printf("parent.uptime=%lld\n",
	    (long long int) (time(NULL) - stats->parent.start));

	stat_print(STATS_QUEUE_LOCAL, STAT_COUNT);
	stat_print(STATS_QUEUE_REMOTE, STAT_COUNT);

	stat_print(STATS_SCHEDULER, STAT_COUNT);
	stat_print(STATS_SCHEDULER, STAT_ACTIVE);
	stat_print(STATS_SCHEDULER, STAT_MAXACTIVE);

	stat_print(STATS_SCHEDULER_BOUNCES, STAT_COUNT);
	stat_print(STATS_SCHEDULER_BOUNCES, STAT_ACTIVE);
	stat_print(STATS_SCHEDULER_BOUNCES, STAT_MAXACTIVE);

	stat_print(STATS_RAMQUEUE_HOST, STAT_ACTIVE);
	stat_print(STATS_RAMQUEUE_BATCH, STAT_ACTIVE);
	stat_print(STATS_RAMQUEUE_MESSAGE, STAT_ACTIVE);
	stat_print(STATS_RAMQUEUE_ENVELOPE, STAT_ACTIVE);

	stat_print(STATS_RAMQUEUE_HOST, STAT_MAXACTIVE);
	stat_print(STATS_RAMQUEUE_BATCH, STAT_MAXACTIVE);
	stat_print(STATS_RAMQUEUE_MESSAGE, STAT_MAXACTIVE);
	stat_print(STATS_RAMQUEUE_ENVELOPE, STAT_MAXACTIVE);

	printf("smtp.errors.delays=%zd\n", stats->smtp.delays);
	printf("smtp.errors.linetoolong=%zd\n", stats->smtp.linetoolong);
	printf("smtp.errors.read_eof=%zd\n", stats->smtp.read_eof);
	printf("smtp.errors.read_system=%zd\n", stats->smtp.read_error);
	printf("smtp.errors.read_timeout=%zd\n", stats->smtp.read_timeout);
	printf("smtp.errors.tempfail=%zd\n", stats->smtp.tempfail);
	printf("smtp.errors.toofast=%zd\n", stats->smtp.toofast);
	printf("smtp.errors.write_eof=%zd\n", stats->smtp.write_eof);
	printf("smtp.errors.write_system=%zd\n", stats->smtp.write_error);
	printf("smtp.errors.write_timeout=%zd\n", stats->smtp.write_timeout);

	stat_print(STATS_SMTP_SESSION, STAT_COUNT);
	stat_print(STATS_SMTP_SESSION_INET4, STAT_COUNT);
	stat_print(STATS_SMTP_SESSION_INET6, STAT_COUNT);
	printf("smtp.sessions.aborted=%zd\n", stats->smtp.read_eof +
	    stats->smtp.read_error + stats->smtp.write_eof +
	    stats->smtp.write_error);

	stat_print(STATS_SMTP_SESSION, STAT_ACTIVE);
	stat_print(STATS_SMTP_SESSION, STAT_MAXACTIVE);

	printf("smtp.sessions.timeout=%zd\n", stats->smtp.read_timeout +
	    stats->smtp.write_timeout);

	stat_print(STATS_SMTP_SMTPS, STAT_COUNT);
	stat_print(STATS_SMTP_SMTPS, STAT_ACTIVE);
	stat_print(STATS_SMTP_SMTPS, STAT_MAXACTIVE);

	stat_print(STATS_SMTP_STARTTLS, STAT_COUNT);
	stat_print(STATS_SMTP_STARTTLS, STAT_ACTIVE);
	stat_print(STATS_SMTP_STARTTLS, STAT_MAXACTIVE);

	return (1);
}
Exemplo n.º 6
0
/* ARGSUSED */
void
control_dispatch_imsg(int fd, short event, void *arg)
{
	struct ctl_conn		*c = arg;
	struct control_sock	*cs = c->cs;
	struct snmpd		*env = cs->cs_env;
	struct imsg		 imsg;
	int			 n, v, i;

	if (event & EV_READ) {
		if (((n = imsg_read_nofd(&c->iev.ibuf)) == -1 &&
		    errno != EAGAIN) || n == 0) {
			control_close(c, "could not read imsg", NULL);
			return;
		}
	}
	if (event & EV_WRITE) {
		if (msgbuf_write(&c->iev.ibuf.w) <= 0 && errno != EAGAIN) {
			control_close(c, "could not write imsg", NULL);
			return;
		}
	}

	for (;;) {
		if ((n = imsg_get(&c->iev.ibuf, &imsg)) == -1) {
			control_close(c, "could not get imsg", NULL);
			return;
		}

		if (n == 0)
			break;

		if (cs->cs_restricted || (c->flags & CTL_CONN_LOCKED)) {
			switch (imsg.hdr.type) {
			case IMSG_SNMP_AGENTX:
			case IMSG_SNMP_ELEMENT:
			case IMSG_SNMP_END:
			case IMSG_SNMP_LOCK:
				break;
			default:
				control_close(c,
				    "client requested restricted command",
				    &imsg);
				return;
			}
		}

		control_imsg_forward(&imsg);

		switch (imsg.hdr.type) {
		case IMSG_CTL_NOTIFY:
			if (IMSG_DATA_SIZE(&imsg))
				return control_close(c, "invalid size", &imsg);

			if (c->flags & CTL_CONN_NOTIFY) {
				log_debug("%s: "
				    "client requested notify more than once",
				    __func__);
				imsg_compose_event(&c->iev, IMSG_CTL_FAIL,
				    0, 0, -1, NULL, 0);
				break;
			}
			c->flags |= CTL_CONN_NOTIFY;
			break;

		case IMSG_SNMP_LOCK:
			if (IMSG_DATA_SIZE(&imsg))
				return control_close(c, "invalid size", &imsg);

			/* enable restricted control mode */
			c->flags |= CTL_CONN_LOCKED;
			break;

		case IMSG_SNMP_AGENTX:
			if (IMSG_DATA_SIZE(&imsg))
				return control_close(c, "invalid size", &imsg);

			/* rendezvous with the client */
			imsg_compose_event(&c->iev, IMSG_CTL_OK, 0, 0, -1, NULL, 0);
			if (imsg_flush(&c->iev.ibuf) == -1) {
				control_close(c,
				    "could not rendezvous with agentx client",
				    &imsg);
				return;
			}

			/* enable AgentX socket */
			c->handle = snmp_agentx_alloc(c->iev.ibuf.fd);
			if (c->handle == NULL) {
				control_close(c,
				    "could not allocate agentx socket",
				    &imsg);
				return;
			}
			/* disable IMSG notifications */
			c->flags &= ~CTL_CONN_NOTIFY;
			c->flags |= CTL_CONN_LOCKED;
			c->iev.handler = control_dispatch_agentx;
			break;

		case IMSG_CTL_VERBOSE:
			if (IMSG_DATA_SIZE(&imsg) != sizeof(v))
				return control_close(c, "invalid size", &imsg);

			memcpy(&v, imsg.data, sizeof(v));
			log_verbose(v);

			for (i = 0; i < PROC_MAX; i++) {
				if (privsep_process == PROC_CONTROL)
					continue;
				proc_forward_imsg(&env->sc_ps, &imsg, i, -1);
			}
			break;
		case IMSG_CTL_RELOAD:
			if (IMSG_DATA_SIZE(&imsg))
				return control_close(c, "invalid size", &imsg);
			proc_forward_imsg(&env->sc_ps, &imsg, PROC_PARENT, -1);
			break;
		default:
			control_close(c, "invalid type", &imsg);
			return;
		}

		imsg_free(&imsg);
	}

	imsg_event_add(&c->iev);
}
Exemplo n.º 7
0
int
vmmaction(struct parse_result *res)
{
	struct sockaddr_un	 sun;
	struct imsg		 imsg;
	int			 done = 0;
	int			 n;
	int			 ret, action;

	if (ctl_sock == -1) {
		if ((ctl_sock = socket(AF_UNIX,
		    SOCK_STREAM|SOCK_CLOEXEC, 0)) == -1)
			err(1, "socket");

		bzero(&sun, sizeof(sun));
		sun.sun_family = AF_UNIX;
		strlcpy(sun.sun_path, socket_name, sizeof(sun.sun_path));

		if (connect(ctl_sock,
		    (struct sockaddr *)&sun, sizeof(sun)) == -1)
			err(1, "connect: %s", socket_name);

		if ((ibuf = malloc(sizeof(struct imsgbuf))) == NULL)
			err(1, "malloc");
		imsg_init(ibuf, ctl_sock);
	}

	switch (res->action) {
	case CMD_START:
		/* XXX validation should be done in start_vm() */
		if (res->size < 1)
			errx(1, "specified memory size too small");
		if (res->path == NULL)
			errx(1, "no kernel specified");
		if (res->ndisks > VMM_MAX_DISKS_PER_VM)
			errx(1, "too many disks");
		else if (res->ndisks == 0)
			warnx("starting without disks");
		if (res->nifs == -1)
			res->nifs = 0;
		if (res->nifs == 0)
			warnx("starting without network interfaces");

		ret = start_vm(res->name, res->size, res->nifs,
		    res->ndisks, res->disks, res->path);
		if (ret) {
			errno = ret;
			err(1, "start VM operation failed");
		}
		break;
	case CMD_STOP:
		terminate_vm(res->id, res->name);
		break;
	case CMD_STATUS:
		get_info_vm(res->id, res->name, 0);
		break;
	case CMD_CONSOLE:
		get_info_vm(res->id, res->name, 1);
		break;
	case CMD_RELOAD:
		imsg_compose(ibuf, IMSG_VMDOP_RELOAD, 0, 0, -1,
		    res->path, res->path == NULL ? 0 : strlen(res->path) + 1);
		done = 1;
		break;
	case CMD_LOAD:
		imsg_compose(ibuf, IMSG_VMDOP_LOAD, 0, 0, -1,
		    res->path, res->path == NULL ? 0 : strlen(res->path) + 1);
		done = 1;
		break;
	case CMD_CREATE:
	case NONE:
		break;
	}

	action = res->action;
	parse_free(res);

	while (ibuf->w.queued)
		if (msgbuf_write(&ibuf->w) <= 0 && errno != EAGAIN)
			err(1, "write error");

	while (!done) {
		if ((n = imsg_read(ibuf)) == -1 && errno != EAGAIN)
			errx(1, "imsg_read error");
		if (n == 0)
			errx(1, "pipe closed");

		while (!done) {
			if ((n = imsg_get(ibuf, &imsg)) == -1)
				errx(1, "imsg_get error");
			if (n == 0)
				break;

			if (imsg.hdr.type == IMSG_CTL_FAIL) {
				if (IMSG_DATA_SIZE(&imsg) == sizeof(ret)) {
					memcpy(&ret, imsg.data, sizeof(ret));
					errno = ret;
					warn("command failed");
				} else {
					warnx("command failed");
				}
				done = 1;
				break;
			}

			ret = 0;
			switch (action) {
			case CMD_START:
				done = start_vm_complete(&imsg, &ret,
				    tty_autoconnect);
				break;
			case CMD_STOP:
				done = terminate_vm_complete(&imsg, &ret);
				break;
			case CMD_CONSOLE:
			case CMD_STATUS:
				done = add_info(&imsg, &ret);
				break;
			default:
				done = 1;
				break;
			}

			imsg_free(&imsg);
		}
	}

	return (0);
}