File: [local] / src / usr.sbin / relayctl / relayctl.c (download)
Revision 1.58, Wed Nov 29 15:24:50 2017 UTC (6 years, 6 months ago) by benno
Branch: MAIN
CVS Tags: OPENBSD_7_5_BASE, OPENBSD_7_5, OPENBSD_7_4_BASE, OPENBSD_7_4, OPENBSD_7_3_BASE, OPENBSD_7_3, OPENBSD_7_2_BASE, OPENBSD_7_2, OPENBSD_7_1_BASE, OPENBSD_7_1, OPENBSD_7_0_BASE, OPENBSD_7_0, OPENBSD_6_9_BASE, OPENBSD_6_9, OPENBSD_6_8_BASE, OPENBSD_6_8, OPENBSD_6_7_BASE, OPENBSD_6_7, OPENBSD_6_6_BASE, OPENBSD_6_6, OPENBSD_6_5_BASE, OPENBSD_6_5, OPENBSD_6_4_BASE, OPENBSD_6_4, OPENBSD_6_3_BASE, OPENBSD_6_3, HEAD Changes since 1.57: +24 -5 lines
add options to specify the control socket in relayd and relayctl.
From Kapetanakis Giannis, thanks.
ok claudio@
|
/* $OpenBSD: relayctl.c,v 1.58 2017/11/29 15:24:50 benno Exp $ */
/*
* Copyright (c) 2007 - 2013 Reyk Floeter <reyk@openbsd.org>
* Copyright (c) 2006 Pierre-Yves Ritschard <pyr@openbsd.org>
* Copyright (c) 2005 Claudio Jeker <claudio@openbsd.org>
* Copyright (c) 2004, 2005 Esben Norby <norby@openbsd.org>
* Copyright (c) 2003 Henning Brauer <henning@openbsd.org>
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/time.h>
#include <sys/queue.h>
#include <sys/un.h>
#include <arpa/inet.h>
#include <stdio.h>
#include <stdlib.h>
#include <err.h>
#include <errno.h>
#include <limits.h>
#include <string.h>
#include <unistd.h>
#include <time.h>
#include <imsg.h>
#include "relayd.h"
#include "parser.h"
__dead void usage(void);
int show_summary_msg(struct imsg *, int);
int show_session_msg(struct imsg *);
int show_command_output(struct imsg *);
char *print_rdr_status(int);
char *print_host_status(int, int);
char *print_table_status(int, int);
char *print_relay_status(int);
void print_statistics(struct ctl_stats[PROC_MAX_INSTANCES + 1]);
struct imsgname {
int type;
char *name;
void (*func)(struct imsg *);
};
struct imsgname *monitor_lookup(u_int8_t);
void monitor_host_status(struct imsg *);
void monitor_id(struct imsg *);
int monitor(struct imsg *);
struct imsgname imsgs[] = {
{ IMSG_HOST_STATUS, "host_status", monitor_host_status },
{ IMSG_CTL_RDR_DISABLE, "ctl_rdr_disable", monitor_id },
{ IMSG_CTL_RDR_ENABLE, "ctl_rdr_enable", monitor_id },
{ IMSG_CTL_TABLE_DISABLE, "ctl_table_disable", monitor_id },
{ IMSG_CTL_TABLE_ENABLE, "ctl_table_enable", monitor_id },
{ IMSG_CTL_HOST_DISABLE, "ctl_host_disable", monitor_id },
{ IMSG_CTL_HOST_ENABLE, "ctl_host_enable", monitor_id },
{ IMSG_CTL_TABLE_CHANGED, "ctl_table_changed", monitor_id },
{ IMSG_CTL_PULL_RULESET, "ctl_pull_ruleset", monitor_id },
{ IMSG_CTL_PUSH_RULESET, "ctl_push_ruleset", monitor_id },
{ IMSG_SYNC, "sync", NULL },
{ 0, NULL, NULL }
};
struct imsgname imsgunknown = {
-1, "<unknown>", NULL
};
struct imsgbuf *ibuf;
int error = 0;
__dead void
usage(void)
{
extern char *__progname;
fprintf(stderr, "usage: %s [-s socket] command [argument ...]\n",
__progname);
exit(1);
}
int
main(int argc, char *argv[])
{
struct sockaddr_un sun;
struct parse_result *res;
struct imsg imsg;
int ctl_sock;
int done = 0;
int n, verbose = 0;
int ch;
const char *sockname;
sockname = RELAYD_SOCKET;
while ((ch = getopt(argc, argv, "s:")) != -1) {
switch (ch) {
case 's':
sockname = optarg;
break;
default:
usage();
/* NOTREACHED */
}
}
argc -= optind;
argv += optind;
/* parse options */
if ((res = parse(argc, argv)) == NULL)
exit(1);
/* connect to relayd control socket */
if ((ctl_sock = socket(AF_UNIX, SOCK_STREAM, 0)) == -1)
err(1, "socket");
bzero(&sun, sizeof(sun));
sun.sun_family = AF_UNIX;
if (strlcpy(sun.sun_path, sockname, sizeof(sun.sun_path)) >=
sizeof(sun.sun_path))
errx(1, "socket `%s' too long", sockname);
reconnect:
if (connect(ctl_sock, (struct sockaddr *)&sun, sizeof(sun)) == -1) {
/* Keep retrying if running in monitor mode */
if (res->action == MONITOR &&
(errno == ENOENT || errno == ECONNREFUSED)) {
usleep(100);
goto reconnect;
}
err(1, "connect: %s", sockname);
}
if (pledge("stdio", NULL) == -1)
err(1, "pledge");
if ((ibuf = malloc(sizeof(struct imsgbuf))) == NULL)
err(1, NULL);
imsg_init(ibuf, ctl_sock);
done = 0;
/* process user request */
switch (res->action) {
case NONE:
usage();
/* not reached */
case SHOW_SUM:
case SHOW_HOSTS:
case SHOW_RDRS:
case SHOW_RELAYS:
case SHOW_ROUTERS:
imsg_compose(ibuf, IMSG_CTL_SHOW_SUM, 0, 0, -1, NULL, 0);
printf("%-4s\t%-8s\t%-24s\t%-7s\tStatus\n",
"Id", "Type", "Name", "Avlblty");
break;
case SHOW_SESSIONS:
imsg_compose(ibuf, IMSG_CTL_SESSION, 0, 0, -1, NULL, 0);
break;
case RDR_ENABLE:
imsg_compose(ibuf, IMSG_CTL_RDR_ENABLE, 0, 0, -1,
&res->id, sizeof(res->id));
break;
case RDR_DISABLE:
imsg_compose(ibuf, IMSG_CTL_RDR_DISABLE, 0, 0, -1,
&res->id, sizeof(res->id));
break;
case TABLE_ENABLE:
imsg_compose(ibuf, IMSG_CTL_TABLE_ENABLE, 0, 0, -1,
&res->id, sizeof(res->id));
break;
case TABLE_DISABLE:
imsg_compose(ibuf, IMSG_CTL_TABLE_DISABLE, 0, 0, -1,
&res->id, sizeof(res->id));
break;
case HOST_ENABLE:
imsg_compose(ibuf, IMSG_CTL_HOST_ENABLE, 0, 0, -1,
&res->id, sizeof(res->id));
break;
case HOST_DISABLE:
imsg_compose(ibuf, IMSG_CTL_HOST_DISABLE, 0, 0, -1,
&res->id, sizeof(res->id));
break;
case SHUTDOWN:
imsg_compose(ibuf, IMSG_CTL_SHUTDOWN, 0, 0, -1, NULL, 0);
break;
case POLL:
imsg_compose(ibuf, IMSG_CTL_POLL, 0, 0, -1, NULL, 0);
break;
case LOAD:
imsg_compose(ibuf, IMSG_CTL_RELOAD, 0, 0, -1,
res->path, strlen(res->path));
done = 1;
break;
case RELOAD:
imsg_compose(ibuf, IMSG_CTL_RELOAD, 0, 0, -1, NULL, 0);
done = 1;
break;
case MONITOR:
imsg_compose(ibuf, IMSG_CTL_NOTIFY, 0, 0, -1, NULL, 0);
break;
case LOG_VERBOSE:
verbose = 2;
/* FALLTHROUGH */
case LOG_BRIEF:
imsg_compose(ibuf, IMSG_CTL_VERBOSE, 0, 0, -1,
&verbose, sizeof(verbose));
printf("logging request sent.\n");
done = 1;
break;
}
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;
switch (res->action) {
case SHOW_SUM:
case SHOW_HOSTS:
case SHOW_RDRS:
case SHOW_RELAYS:
case SHOW_ROUTERS:
done = show_summary_msg(&imsg, res->action);
break;
case SHOW_SESSIONS:
done = show_session_msg(&imsg);
break;
case RDR_DISABLE:
case RDR_ENABLE:
case TABLE_DISABLE:
case TABLE_ENABLE:
case HOST_DISABLE:
case HOST_ENABLE:
case POLL:
case SHUTDOWN:
done = show_command_output(&imsg);
break;
case NONE:
case LOG_VERBOSE:
case LOG_BRIEF:
case RELOAD:
case LOAD:
break;
case MONITOR:
done = monitor(&imsg);
break;
}
imsg_free(&imsg);
}
}
close(ctl_sock);
free(ibuf);
return (error ? 1 : 0);
}
struct imsgname *
monitor_lookup(u_int8_t type)
{
int i;
for (i = 0; imsgs[i].name != NULL; i++)
if (imsgs[i].type == type)
return (&imsgs[i]);
return (&imsgunknown);
}
void
monitor_host_status(struct imsg *imsg)
{
struct ctl_status cs;
memcpy(&cs, imsg->data, sizeof(cs));
printf("\tid: %u\n", cs.id);
printf("\tstate: ");
switch (cs.up) {
case HOST_UP:
printf("up\n");
break;
case HOST_DOWN:
printf("down\n");
break;
default:
printf("unknown\n");
break;
}
}
void
monitor_id(struct imsg *imsg)
{
struct ctl_id id;
memcpy(&id, imsg->data, sizeof(id));
printf("\tid: %u\n", id.id);
if (strlen(id.name))
printf("\tname: %s\n", id.name);
}
int
monitor(struct imsg *imsg)
{
time_t now;
int done = 0;
struct imsgname *imn;
now = time(NULL);
imn = monitor_lookup(imsg->hdr.type);
printf("%s: imsg type %u len %u peerid %u pid %d\n", imn->name,
imsg->hdr.type, imsg->hdr.len, imsg->hdr.peerid, imsg->hdr.pid);
printf("\ttimestamp: %lld, %s", (long long)now, ctime(&now));
if (imn->type == -1)
done = 1;
if (imn->func != NULL)
(*imn->func)(imsg);
return (done);
}
int
show_summary_msg(struct imsg *imsg, int type)
{
struct rdr *rdr;
struct table *table;
struct host *host;
struct relay *rlay;
struct router *rt;
struct netroute *nr;
struct ctl_stats stats[PROC_MAX_INSTANCES];
char name[HOST_NAME_MAX+1];
switch (imsg->hdr.type) {
case IMSG_CTL_RDR:
if (!(type == SHOW_SUM || type == SHOW_RDRS))
break;
rdr = imsg->data;
printf("%-4u\t%-8s\t%-24s\t%-7s\t%s\n",
rdr->conf.id, "redirect", rdr->conf.name, "",
print_rdr_status(rdr->conf.flags));
break;
case IMSG_CTL_TABLE:
if (!(type == SHOW_SUM || type == SHOW_HOSTS))
break;
table = imsg->data;
printf("%-4u\t%-8s\t%-24s\t%-7s\t%s\n",
table->conf.id, "table", table->conf.name, "",
print_table_status(table->up, table->conf.flags));
break;
case IMSG_CTL_HOST:
if (!(type == SHOW_SUM || type == SHOW_HOSTS))
break;
host = imsg->data;
if (host->conf.parentid)
snprintf(name, sizeof(name), "%s parent %u",
host->conf.name, host->conf.parentid);
else
strlcpy(name, host->conf.name, sizeof(name));
printf("%-4u\t%-8s\t%-24s\t%-7s\t%s\n",
host->conf.id, "host", name,
print_availability(host->check_cnt, host->up_cnt),
print_host_status(host->up, host->flags));
if (type == SHOW_HOSTS && host->check_cnt) {
printf("\t%8s\ttotal: %lu/%lu checks",
"", host->up_cnt, host->check_cnt);
if (host->retry_cnt)
printf(", %d retries", host->retry_cnt);
if (host->he && host->up == HOST_DOWN)
printf(", error: %s", host_error(host->he));
printf("\n");
}
break;
case IMSG_CTL_RELAY:
if (!(type == SHOW_SUM || type == SHOW_RELAYS))
break;
rlay = imsg->data;
printf("%-4u\t%-8s\t%-24s\t%-7s\t%s\n",
rlay->rl_conf.id, "relay", rlay->rl_conf.name, "",
print_relay_status(rlay->rl_conf.flags));
break;
case IMSG_CTL_RDR_STATS:
if (type != SHOW_RDRS)
break;
bcopy(imsg->data, &stats[0], sizeof(stats[0]));
stats[1].id = EMPTY_ID;
print_statistics(stats);
break;
case IMSG_CTL_RELAY_STATS:
if (type != SHOW_RELAYS)
break;
bcopy(imsg->data, &stats, sizeof(stats));
print_statistics(stats);
break;
case IMSG_CTL_ROUTER:
if (!(type == SHOW_SUM || type == SHOW_ROUTERS))
break;
rt = imsg->data;
printf("%-4u\t%-8s\t%-24s\t%-7s\t%s\n",
rt->rt_conf.id, "router", rt->rt_conf.name, "",
print_relay_status(rt->rt_conf.flags));
if (type != SHOW_ROUTERS)
break;
if (rt->rt_conf.rtable)
printf("\t%8s\trtable: %d\n", "", rt->rt_conf.rtable);
if (strlen(rt->rt_conf.label))
printf("\t%8s\trtlabel: %s\n", "", rt->rt_conf.label);
break;
case IMSG_CTL_NETROUTE:
if (type != SHOW_ROUTERS)
break;
nr = imsg->data;
(void)print_host(&nr->nr_conf.ss, name, sizeof(name));
printf("\t%8s\troute: %s/%d\n",
"", name, nr->nr_conf.prefixlen);
break;
case IMSG_CTL_END:
return (1);
default:
errx(1, "wrong message in summary: %u", imsg->hdr.type);
break;
}
return (0);
}
int
show_session_msg(struct imsg *imsg)
{
struct rsession *con;
char a[128], b[128];
struct timeval tv_now;
switch (imsg->hdr.type) {
case IMSG_CTL_SESSION:
con = imsg->data;
(void)print_host(&con->se_in.ss, a, sizeof(a));
(void)print_host(&con->se_out.ss, b, sizeof(b));
printf("session %u:%u %s:%u -> %s:%u\t%s\n",
imsg->hdr.peerid, con->se_id,
a, ntohs(con->se_in.port), b, ntohs(con->se_out.port),
con->se_done ? "DONE" : "RUNNING");
getmonotime(&tv_now);
print_time(&tv_now, &con->se_tv_start, a, sizeof(a));
print_time(&tv_now, &con->se_tv_last, b, sizeof(b));
printf("\tage %s, idle %s, relay %u, pid %u",
a, b, con->se_relayid, con->se_pid);
/* XXX grab tagname instead of tag id */
if (con->se_tag)
printf(", tag (id) %u", con->se_tag);
printf("\n");
break;
case IMSG_CTL_END:
return (1);
default:
errx(1, "wrong message in session: %u", imsg->hdr.type);
break;
}
return (0);
}
int
show_command_output(struct imsg *imsg)
{
switch (imsg->hdr.type) {
case IMSG_CTL_OK:
printf("command succeeded\n");
break;
case IMSG_CTL_FAIL:
printf("command failed\n");
error++;
break;
default:
errx(1, "wrong message in summary: %u", imsg->hdr.type);
}
return (1);
}
char *
print_rdr_status(int flags)
{
if (flags & F_DISABLE) {
return ("disabled");
} else if (flags & F_DOWN) {
return ("down");
} else if (flags & F_BACKUP) {
return ("active (using backup table)");
} else
return ("active");
}
char *
print_table_status(int up, int fl)
{
static char buf[1024];
bzero(buf, sizeof(buf));
if (fl & F_DISABLE) {
snprintf(buf, sizeof(buf) - 1, "disabled");
} else if (!up) {
snprintf(buf, sizeof(buf) - 1, "empty");
} else
snprintf(buf, sizeof(buf) - 1, "active (%d hosts)", up);
return (buf);
}
char *
print_host_status(int status, int fl)
{
if (fl & F_DISABLE)
return ("disabled");
switch (status) {
case HOST_DOWN:
return ("down");
case HOST_UNKNOWN:
return ("unknown");
case HOST_UP:
return ("up");
default:
errx(1, "invalid status: %d", status);
}
}
char *
print_relay_status(int flags)
{
if (flags & F_DISABLE) {
return ("disabled");
} else
return ("active");
}
void
print_statistics(struct ctl_stats stats[PROC_MAX_INSTANCES + 1])
{
struct ctl_stats crs;
int i;
bzero(&crs, sizeof(crs));
crs.interval = stats[0].interval;
for (i = 0; stats[i].id != EMPTY_ID; i++) {
crs.cnt += stats[i].cnt;
crs.last += stats[i].last;
crs.avg += stats[i].avg;
crs.last_hour += stats[i].last_hour;
crs.avg_hour += stats[i].avg_hour;
crs.last_day += stats[i].last_day;
crs.avg_day += stats[i].avg_day;
}
if (crs.cnt == 0)
return;
printf("\t%8s\ttotal: %llu sessions\n"
"\t%8s\tlast: %u/%llus %u/h %u/d sessions\n"
"\t%8s\taverage: %u/%llus %u/h %u/d sessions\n",
"", crs.cnt,
"", crs.last, crs.interval,
crs.last_hour, crs.last_day,
"", crs.avg, crs.interval,
crs.avg_hour, crs.avg_day);
}