+/* --- @acmd_add@ --- *
+ *
+ * Arguments: @admin *a@ = connection which requested the addition
+ * @unsigned ac@ = argument count
+ * @char *av[]@ = pointer to the argument list
+ *
+ * Returns: ---
+ *
+ * Use: Adds a new peer.
+ */
+
+static void acmd_add(admin *a, unsigned ac, char *av[])
+{
+ unsigned i, j;
+ const char *tag = 0;
+ admin_addop *add = 0;
+
+ /* --- Set stuff up --- */
+
+ add = xmalloc(sizeof(*add));
+ add->peer.name = xstrdup(av[0]);
+ add->peer.t_ka = 0;
+ add->peer.tops = tun_default;
+
+ /* --- Make sure someone's not got there already --- */
+
+ if (p_find(av[0])) {
+ a_fail(a, "peer-exists", "%s", av[0], A_END);
+ goto fail;
+ }
+
+ /* --- Parse options --- */
+
+ i = 1;
+ for (;;) {
+ if (!av[i])
+ goto bad_syntax;
+ if (mystrieq(av[i], "-background")) {
+ if (!av[++i]) goto bad_syntax;
+ tag = av[i];
+ } else if (mystrieq(av[i], "-tunnel")) {
+ if (!av[++i]) goto bad_syntax;
+ for (j = 0;; j++) {
+ if (!tunnels[j]) {
+ a_fail(a, "unknown-tunnel", "%s", av[i], A_END);
+ goto fail;
+ }
+ if (mystrieq(av[i], tunnels[j]->name)) {
+ add->peer.tops = tunnels[j];
+ break;
+ }
+ }
+ } else if (mystrieq(av[i], "-keepalive")) {
+ long t;
+ if (!av[++i]) goto bad_syntax;
+ if ((t = a_parsetime(av[i])) < 0) {
+ a_fail(a, "bad-time-spec", "%s", av[i], A_END);
+ goto fail;
+ }
+ add->peer.t_ka = t;
+ } else if (mystrieq(av[i], "--")) {
+ i++;
+ break;
+ } else
+ break;
+ i++;
+ }
+
+ /* --- Crank up the resolver --- */
+
+ a_resolve(a, &add->r, tag, a_doadd, ac - i, av + i);
+ return;
+
+ /* --- Clearing up --- */
+
+bad_syntax:
+ a_fail(a, "bad-syntax", "add", "PEER [OPTIONS] ADDR ...", A_END);
+fail:
+ xfree(add->peer.name);
+ xfree(add);
+ return;
+}
+
+/*----- Ping --------------------------------------------------------------*/
+
+/* --- @a_pingcancel@ --- *
+ *
+ * Arguments: @admin_bgop *bg@ = background operation block
+ *
+ * Returns: ---
+ *
+ * Use: Cancels a running ping.
+ */
+
+static void a_pingcancel(admin_bgop *bg)
+{
+ admin_pingop *pg = (admin_pingop *)bg;
+ T( trace(T_ADMIN, "admin: cancel ping op %s", BGTAG(pg)); )
+ p_pingdone(&pg->ping, PING_NONOTIFY);
+}
+
+/* --- @a_pong@ --- *
+ *
+ * Arguments: @int rc@ = return code
+ * @void *v@ = ping operation block
+ *
+ * Returns: ---
+ *
+ * Use: Collects what happened to a ping message.
+ */
+
+static void a_pong(int rc, void *v)
+{
+ admin_pingop *pg = v;
+ struct timeval tv;
+ double millis;
+
+ switch (rc) {
+ case PING_OK:
+ gettimeofday(&tv, 0);
+ tv_sub(&tv, &tv, &pg->pingtime);
+ millis = (double)tv.tv_sec * 1000 + (double)tv.tv_usec/1000;
+ a_bginfo(&pg->bg, "ping-ok", "%.1f", millis, A_END);
+ a_bgok(&pg->bg);
+ break;
+ case PING_TIMEOUT:
+ a_bginfo(&pg->bg, "ping-timeout", A_END);
+ a_bgok(&pg->bg);
+ break;
+ case PING_PEERDIED:
+ a_bginfo(&pg->bg, "ping-peer-died", A_END);
+ a_bgok(&pg->bg);
+ break;
+ default:
+ abort();
+ }
+ T( trace(T_ADMIN, "admin: ponged ping op %s", BGTAG(pg)); )
+ a_bgrelease(&pg->bg);
+}
+
+/* --- @acmd_ping@, @acmd_eping@ --- *
+ *
+ * Arguments: @admin *a@ = connection which requested the ping
+ * @unsigned ac@ = argument count
+ * @char *av[]@ = pointer to the argument list
+ *
+ * Returns: ---
+ *
+ * Use: Pings a peer.
+ */
+
+static void a_ping(admin *a, unsigned ac, char *av[],
+ const char *cmd, unsigned msg)
+{
+ long t = T_PING;
+ int i;
+ peer *p;
+ admin_pingop *pg = 0;
+ const char *tag = 0;
+
+ i = 0;
+ for (;;) {
+ if (!av[i])
+ goto bad_syntax;
+ if (mystrieq(av[i], "-background")) {
+ if (!av[++i]) goto bad_syntax;
+ tag = av[i];
+ } else if (mystrieq(av[i], "-timeout")) {
+ if (!av[++i]) goto bad_syntax;
+ if ((t = a_parsetime(av[i])) < 0) {
+ a_fail(a, "bad-time-spec", "%s", av[i], A_END);
+ return;
+ }
+ } else if (mystrieq(av[i], "--")) {
+ i++;
+ break;
+ } else
+ break;
+ i++;
+ }
+
+ if (!av[i]) goto bad_syntax;
+ if ((p = a_findpeer(a, av[i])) == 0)
+ return;
+ pg = xmalloc(sizeof(*pg));
+ gettimeofday(&pg->pingtime, 0);
+ a_bgadd(a, &pg->bg, tag, a_pingcancel);
+ T( trace(T_ADMIN, "admin: ping op %s: %s to %s",
+ BGTAG(pg), cmd, p_name(p)); )
+ if (p_pingsend(p, &pg->ping, msg, t, a_pong, pg)) {
+ a_bgfail(&pg->bg, "ping-send-failed", A_END);
+ a_bgrelease(&pg->bg);
+ }
+ return;
+
+bad_syntax:
+ a_fail(a, "bad-syntax", "%s", cmd, "[OPTIONS] PEER", cmd, A_END);
+ return;
+}
+
+static void acmd_ping(admin *a, unsigned ac, char *av[])
+ { a_ping(a, ac, av, "ping", MISC_PING); }
+static void acmd_eping(admin *a, unsigned ac, char *av[])
+ { a_ping(a, ac, av, "eping", MISC_EPING); }
+