chiark
/
gitweb
/
~mdw
/
disorder
/ blobdiff
commit
grep
author
committer
pickaxe
?
search:
re
summary
|
shortlog
|
log
|
commit
|
commitdiff
|
tree
raw
|
inline
| side by side
Convert track names and input lines to NFC. This is a database format
[disorder]
/
server
/
server.c
diff --git
a/server/server.c
b/server/server.c
index 4b71e4edd7036a16f06fde14b1cb7bcc2c98e712..64502c0330d7588e4afcd27b242abefcfd9c23c7 100644
(file)
--- a/
server/server.c
+++ b/
server/server.c
@@
-63,6
+63,7
@@
#include "eventlog.h"
#include "defs.h"
#include "cache.h"
#include "eventlog.h"
#include "defs.h"
#include "cache.h"
+#include "unicode.h"
#ifndef NONCE_SIZE
# define NONCE_SIZE 16
#ifndef NONCE_SIZE
# define NONCE_SIZE 16
@@
-81,16
+82,30
@@
struct listener {
int pf;
};
int pf;
};
+/** @brief One client connection */
struct conn {
struct conn {
+ /** @brief Read commands from here */
ev_reader *r;
ev_reader *r;
+ /** @brief Send responses to here */
ev_writer *w;
ev_writer *w;
+ /** @brief Underlying file descriptor */
int fd;
int fd;
+ /** @brief Unique identifier for connection used in log messages */
unsigned tag;
unsigned tag;
+ /** @brief Login name or NULL */
char *who;
char *who;
+ /** @brief Event loop */
ev_source *ev;
ev_source *ev;
+ /** @brief Nonce chosen for this connection */
unsigned char nonce[NONCE_SIZE];
unsigned char nonce[NONCE_SIZE];
+ /** @brief Current reader callback
+ *
+ * We change this depending on whether we're servicing the @b log command
+ */
ev_reader_callback *reader;
ev_reader_callback *reader;
+ /** @brief Event log output sending to this connection */
struct eventlog_output *lo;
struct eventlog_output *lo;
+ /** @brief Parent listener */
const struct listener *l;
};
const struct listener *l;
};
@@
-103,37
+118,54
@@
static int reader_callback(ev_source *ev,
static const char *noyes[] = { "no", "yes" };
static const char *noyes[] = { "no", "yes" };
+/** @brief Called when a connection's writer fails or is shut down
+ *
+ * If the connection still has a raeder that is cancelled.
+ */
static int writer_error(ev_source attribute((unused)) *ev,
int errno_value,
void *u) {
struct conn *c = u;
static int writer_error(ev_source attribute((unused)) *ev,
int errno_value,
void *u) {
struct conn *c = u;
- D(("server writer_error
%d"
, errno_value));
+ D(("server writer_error
S%x %d", c->tag
, errno_value));
if(errno_value == 0) {
/* writer is done */
if(errno_value == 0) {
/* writer is done */
- error(errno_value, "S%x writer completed", c->tag); /* TODO */
+ D(("S%x writer completed", c->tag));
} else {
if(errno_value != EPIPE)
error(errno_value, "S%x write error on socket", c->tag);
} else {
if(errno_value != EPIPE)
error(errno_value, "S%x write error on socket", c->tag);
- ev_reader_cancel(c->r);
+ if(c->r) {
+ D(("cancel reader"));
+ ev_reader_cancel(c->r);
+ c->r = 0;
+ }
+ D(("done cancel reader"));
}
}
+ c->w = 0;
ev_report(ev);
return 0;
}
ev_report(ev);
return 0;
}
+/** @brief Called when a conncetion's reader fails or is shut down
+ *
+ * If connection still has a writer then it is closed.
+ */
static int reader_error(ev_source attribute((unused)) *ev,
int errno_value,
void *u) {
struct conn *c = u;
static int reader_error(ev_source attribute((unused)) *ev,
int errno_value,
void *u) {
struct conn *c = u;
- D(("server reader_error %d", errno_value));
- error(errno, "S%x read error on socket", c->tag);
- ev_writer_close(c->w);
+ D(("server reader_error S%x %d", c->tag, errno_value));
+ error(errno_value, "S%x read error on socket", c->tag);
+ if(c->w)
+ ev_writer_close(c->w);
+ c->w = 0;
+ c->r = 0;
ev_report(ev);
return 0;
}
ev_report(ev);
return 0;
}
-/*
r
eturn true if we are talking to a trusted user */
+/*
* @brief R
eturn true if we are talking to a trusted user */
static int trusted(struct conn *c) {
int n;
static int trusted(struct conn *c) {
int n;
@@
-704,26
+736,31
@@
static int c_volume(struct conn *c,
return 1;
}
return 1;
}
-/* we are logging, and some data is available to read */
-static int logging_reader_callback(ev_source *ev,
+/** @brief Called when data arrives on a log connection
+ *
+ * We just discard all such data. The client may occasionally send data as a
+ * keepalive.
+ */
+static int logging_reader_callback(ev_source attribute((unused)) *ev,
ev_reader *reader,
ev_reader *reader,
- void *ptr,
+ void
attribute((unused))
*ptr,
size_t bytes,
size_t bytes,
- int eof,
- void *u) {
+ int
attribute((unused))
eof,
+ void
attribute((unused))
*u) {
struct conn *c = u;
struct conn *c = u;
- /* don't log to this conn any more */
- eventlog_remove(c->lo);
- if(c->w) {
- /* Terminate the log output, but only if the writer hasn't been killed off
- * from a failure on some earlier write */
- sink_writes(ev_writer_sink(c->w), ".\n");
+ ev_reader_consume(reader, bytes);
+ if(eof) {
+ /* Oops, that's all for now */
+ D(("logging reader eof"));
+ if(c->w) {
+ D(("close writer"));
+ ev_writer_close(c->w);
+ c->w = 0;
+ }
+ c->r = 0;
}
}
- /* restore the reader callback */
- c->reader = reader_callback;
- /* ...and exit via it */
- return c->reader(ev, reader, ptr, bytes, eof, u);
+ return 0;
}
static void logclient(const char *msg, void *user) {
}
static void logclient(const char *msg, void *user) {
@@
-885,6
+922,10
@@
static int c_tags(struct conn *c,
static int c_set_global(struct conn *c,
char **vec,
int attribute((unused)) nvec) {
static int c_set_global(struct conn *c,
char **vec,
int attribute((unused)) nvec) {
+ if(vec[0][0] == '_') {
+ sink_writes(ev_writer_sink(c->w), "550 cannot set internal global preferences\n");
+ return 1;
+ }
trackdb_set_global(vec[0], vec[1], c->who);
sink_printf(ev_writer_sink(c->w), "250 OK\n");
return 1;
trackdb_set_global(vec[0], vec[1], c->who);
sink_printf(ev_writer_sink(c->w), "250 OK\n");
return 1;
@@
-1004,6
+1045,11
@@
static int command(struct conn *c, char *line) {
int nvec, n;
D(("server command %s", line));
int nvec, n;
D(("server command %s", line));
+ /* We force everything into NFC as early as possible */
+ if(!(line = utf8_compose_canon(line, strlen(line), 0))) {
+ sink_writes(ev_writer_sink(c->w), "500 cannot normalize command\n");
+ return 1;
+ }
if(!(vec = split(line, &nvec, SPLIT_QUOTES, command_error, c))) {
sink_writes(ev_writer_sink(c->w), "500 cannot parse command\n");
return 1;
if(!(vec = split(line, &nvec, SPLIT_QUOTES, command_error, c))) {
sink_writes(ev_writer_sink(c->w), "500 cannot parse command\n");
return 1;
@@
-1082,8
+1128,13
@@
static int reader_callback(ev_source attribute((unused)) *ev,
if(eof) {
if(bytes)
error(0, "S%x unterminated line", c->tag);
if(eof) {
if(bytes)
error(0, "S%x unterminated line", c->tag);
+ D(("normal reader close"));
c->r = 0;
c->r = 0;
- return ev_writer_close(c->w);
+ if(c->w) {
+ D(("close associated writer"));
+ ev_writer_close(c->w);
+ c->w = 0;
+ }
}
return 0;
}
}
return 0;
}