+/*----- 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_bgok(&pg->bg);
+ break;
+ case PING_TIMEOUT:
+ a_bginfo(&pg->bg, "ping-timeout");
+ a_bgok(&pg->bg);
+ break;
+ case PING_PEERDIED:
+ a_bginfo(&pg->bg, "ping-peer-died");
+ 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]);
+ return;
+ }
+ } else if (mystrieq(av[i], "--")) {
+ i++;
+ break;
+ } else
+ break;
+ i++;
+ }
+
+ if (!av[i]) goto bad_syntax;
+ if ((p = p_find(av[i])) == 0) {
+ a_fail(a, "unknown-peer %s", av[i]);
+ 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_bgrelease(&pg->bg);
+ }
+ return;
+
+bad_syntax:
+ a_fail(a, "bad-syntax -- %s [OPTIONS] PEER", cmd);
+ 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); }
+