void (*completed)(); /**< @brief user completion callback or 0 */
void *v; /**< @brief data for COMPLETED */
disorder_eclient *client; /**< @brief owning client */
- int sent; /**< @brief true if sent to server */
+
+ /** @brief true if sent to server
+ *
+ * This is cleared by disorder_eclient_close(), forcing all queued
+ * commands to be transparently resent.
+ */
+ int sent;
};
/** @brief Client structure */
struct vector vec; /**< @brief body */
const disorder_eclient_log_callbacks *log_callbacks; /**< @brief log callbacks */
void *log_v; /**< @brief user data */
- unsigned long statebits; /**< @brief current state */
+ unsigned long statebits; /**< @brief latest state */
};
/* Forward declarations ******************************************************/
return c;
}
-/** @brief Disconnect a client */
+/** @brief Disconnect a client
+ * @param c Client to disconnect
+ *
+ * NB that this routine just disconnnects the TCP connection. It does not
+ * destroy the client! If you continue to use it then it will attempt to
+ * reconnect.
+ */
void disorder_eclient_close(disorder_eclient *c) {
struct operation *op;
xclose(c->fd);
c->fd = -1;
c->state = state_disconnected;
+ c->statebits = 0;
}
c->output.nvec = 0;
c->input.nvec = 0;
op->sent = 0;
}
+/** @brief Return current state */
+unsigned long disorder_eclient_state(const disorder_eclient *c) {
+ return c->statebits | (c->state > state_connected ? DISORDER_CONNECTED : 0);
+}
+
/* Error reporting ***********************************************************/
-/** @brief called when a connection error occurs */
+/** @brief called when a connection error occurs
+ *
+ * After this called we will be disconnected (by disorder_eclient_close()),
+ * so there will be a reconnection before any commands can be sent.
+ */
static int comms_error(disorder_eclient *c, const char *fmt, ...) {
va_list ap;
char *s;
*/
void disorder_eclient_polled(disorder_eclient *c, unsigned mode) {
struct operation *op;
-
+
D(("disorder_eclient_polled fd=%d state=%s mode=[%s %s]",
c->fd, states[c->state],
mode & DISORDER_POLL_READ ? "READ" : "",
return 0;
}
-/** @brief Called when maybe connected */
+/** @brief Called when poll triggers while waiting for a connection */
static void maybe_connected(disorder_eclient *c) {
/* We either connected, or got an error. */
int err;
comms_error(c, "connecting to %s: %s", c->ident, strerror(err));
/* sets state_disconnected */
} else {
+ char *r;
+
/* The connection succeeded */
c->state = state_connected;
+ byte_xasprintf(&r, "connected to %s", c->ident);
+ c->callbacks->report(c->u, r);
}
}
static void authuser_opcallback(disorder_eclient *c,
struct operation *op) {
+ char *r;
+
D(("authuser_opcallback"));
if(c->rc / 100 != 2) {
/* Wrong password or something. We cannot proceed. */
}
/* OK, we're authenticated now. */
c->authenticated = 1;
+ byte_xasprintf(&r, "authenticated with %s", c->ident);
+ c->callbacks->report(c->u, r);
if(c->log_callbacks && !(c->ops && c->ops->opcallback == log_opcallback))
/* We are a log client, switch to logging mode */
stash_command(c, 0/*queuejump*/, log_opcallback, 0/*completed*/, c->log_v,
"search", terms, (char *)0);
}
+int disorder_eclient_nop(disorder_eclient *c,
+ disorder_eclient_no_response *completed,
+ void *v) {
+ return simple(c, no_response_opcallback, (void (*)())completed, v,
+ "nop", (char *)0);
+}
+
/* Log clients ***************************************************************/
+/** @brief Monitor the server log
+ * @param c Client
+ * @param callbacks Functions to call when anything happens
+ * @param v Passed to @p callbacks functions
+ *
+ * Once a client is being used for logging it cannot be used for anything else.
+ * There is magic in authuser_opcallback() to re-submit the @c log command
+ * after reconnection.
+ */
int disorder_eclient_log(disorder_eclient *c,
const disorder_eclient_log_callbacks *callbacks,
void *v) {
static void logentry_completed(disorder_eclient *c,
int attribute((unused)) nvec, char **vec) {
if(!c->log_callbacks->completed) return;
+ c->statebits &= ~DISORDER_PLAYING;
c->log_callbacks->completed(c->log_v, vec[0]);
+ if(c->log_callbacks->state)
+ c->log_callbacks->state(c->log_v, c->statebits | DISORDER_CONNECTED);
}
static void logentry_failed(disorder_eclient *c,
int attribute((unused)) nvec, char **vec) {
if(!c->log_callbacks->failed)return;
+ c->statebits &= ~DISORDER_PLAYING;
c->log_callbacks->failed(c->log_v, vec[0], vec[1]);
+ if(c->log_callbacks->state)
+ c->log_callbacks->state(c->log_v, c->statebits | DISORDER_CONNECTED);
}
static void logentry_moved(disorder_eclient *c,
static void logentry_playing(disorder_eclient *c,
int attribute((unused)) nvec, char **vec) {
if(!c->log_callbacks->playing) return;
+ c->statebits |= DISORDER_PLAYING;
c->log_callbacks->playing(c->log_v, vec[0], vec[1]);
+ if(c->log_callbacks->state)
+ c->log_callbacks->state(c->log_v, c->statebits | DISORDER_CONNECTED);
}
static void logentry_queue(disorder_eclient *c,
static void logentry_scratched(disorder_eclient *c,
int attribute((unused)) nvec, char **vec) {
if(!c->log_callbacks->scratched) return;
+ c->statebits &= ~DISORDER_PLAYING;
c->log_callbacks->scratched(c->log_v, vec[0], vec[1]);
+ if(c->log_callbacks->state)
+ c->log_callbacks->state(c->log_v, c->statebits | DISORDER_CONNECTED);
}
static const struct {
break;
}
if(!c->log_callbacks->state) return;
- c->log_callbacks->state(c->log_v, c->statebits);
+ c->log_callbacks->state(c->log_v, c->statebits | DISORDER_CONNECTED);
}
static void logentry_volume(disorder_eclient *c,
c->log_callbacks->volume(c->log_v, (int)l, (int)r);
}
+/** @brief Convert @p statebits to a string */
+char *disorder_eclient_interpret_state(unsigned long statebits) {
+ struct dynstr d[1];
+ size_t n;
+
+ static const struct {
+ unsigned long bit;
+ const char *name;
+ } bits[] = {
+ { DISORDER_PLAYING_ENABLED, "playing_enabled" },
+ { DISORDER_RANDOM_ENABLED, "random_enabled" },
+ { DISORDER_TRACK_PAUSED, "track_paused" },
+ { DISORDER_PLAYING, "playing" },
+ { DISORDER_CONNECTED, "connected" },
+ };
+#define NBITS (sizeof bits / sizeof *bits)
+
+ dynstr_init(d);
+ for(n = 0; n < NBITS; ++n)
+ if(statebits & bits[n].bit) {
+ if(d->nvec)
+ dynstr_append(d, '|');
+ dynstr_append_string(d, bits[n].name);
+ statebits ^= bits[n].bit;
+ }
+ if(statebits) {
+ char s[20];
+
+ if(d->nvec)
+ dynstr_append(d, '|');
+ sprintf(s, "%#lx", statebits);
+ dynstr_append_string(d, s);
+ }
+ dynstr_terminate(d);
+ return d->vec;
+}
+
/*
Local Variables:
c-basic-offset:2