X-Git-Url: http://www.chiark.greenend.org.uk/ucgi/~mdw/git/disorder/blobdiff_plain/5abe307a2878e3e523cfd76fa3343cf904c9297f..1563f6f65b853924d53c90f736f6c2486f613cb0:/server/server.c?ds=sidebyside diff --git a/server/server.c b/server/server.c index 1d3e682..89447c0 100644 --- a/server/server.c +++ b/server/server.c @@ -1,6 +1,6 @@ /* * This file is part of DisOrder. - * Copyright (C) 2004, 2005, 2006 Richard Kettlewell + * Copyright (C) 2004, 2005, 2006, 2007 Richard Kettlewell * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -42,6 +42,7 @@ #include "server.h" #include "syscalls.h" #include "queue.h" +#include "server-queue.h" #include "play.h" #include "log.h" #include "mem.h" @@ -95,7 +96,6 @@ struct conn { static int reader_callback(ev_source *ev, ev_reader *reader, - int fd, void *ptr, size_t bytes, int eof, @@ -104,42 +104,36 @@ static int reader_callback(ev_source *ev, static const char *noyes[] = { "no", "yes" }; static int writer_error(ev_source attribute((unused)) *ev, - int fd, int errno_value, void *u) { struct conn *c = u; - D(("server writer_error %d %d", fd, errno_value)); + D(("server writer_error %d", errno_value)); + info("writer_error S%x %d", c->tag, errno_value); if(errno_value == 0) { /* writer is done */ - c->w = 0; - if(c->r == 0) { - D(("server writer_error closes %d", fd)); - xclose(fd); /* reader is done too, close */ - } else { - D(("server writer_error shutdown %d SHUT_WR", fd)); - xshutdown(fd, SHUT_WR); /* reader is not done yet */ - } + error(errno_value, "S%x writer completed", c->tag); /* TODO */ } else { if(errno_value != EPIPE) error(errno_value, "S%x write error on socket", c->tag); - if(c->r) - ev_reader_cancel(c->r); - xclose(fd); + info("cancel reader"); + ev_reader_cancel(c->r); + info("done cancel reader"); } + ev_report(ev); return 0; } static int reader_error(ev_source attribute((unused)) *ev, - int fd, int errno_value, void *u) { struct conn *c = u; - D(("server reader_error %d %d", fd, errno_value)); - error(errno, "S%x read error on socket", c->tag); - ev_writer_cancel(c->w); - xclose(fd); + D(("server reader_error %d", errno_value)); + info("reader_error S%x %d", c->tag, errno_value); + error(errno_value, "S%x read error on socket", c->tag); + ev_writer_close(c->w); + ev_report(ev); return 0; } @@ -391,7 +385,8 @@ static int c_user(struct conn *c, sink_writes(ev_writer_sink(c->w), "530 authentication failed\n"); return 1; } - res = authhash(c->nonce, sizeof c->nonce, config->allow.s[n].s[1]); + res = authhash(c->nonce, sizeof c->nonce, config->allow.s[n].s[1], + config->authorization_algorithm); if(wideopen || (res && !strcmp(res, vec[1]))) { c->who = vec[0]; /* currently we only bother logging remote connections */ @@ -661,21 +656,19 @@ static int c_random_enabled(struct conn *c, return 1; /* completed */ } +static void got_stats(char *stats, void *u) { + struct conn *const c = u; + + sink_printf(ev_writer_sink(c->w), "253 stats\n%s\n.\n", stats); + /* Now we can start processing commands again */ + ev_reader_enable(c->r); +} + static int c_stats(struct conn *c, char attribute((unused)) **vec, int attribute((unused)) nvec) { - char **v; - int nv, n; - - v = trackdb_stats(&nv); - sink_printf(ev_writer_sink(c->w), "253 stats\n"); - for(n = 0; n < nv; ++n) { - if(v[n][0] == '.') - sink_writes(ev_writer_sink(c->w), "."); - sink_printf(ev_writer_sink(c->w), "%s\n", v[n]); - } - sink_writes(ev_writer_sink(c->w), ".\n"); - return 1; + trackdb_stats_subprocess(c->ev, got_stats, c); + return 0; /* not yet complete */ } static int c_volume(struct conn *c, @@ -718,7 +711,6 @@ static int c_volume(struct conn *c, /* we are logging, and some data is available to read */ static int logging_reader_callback(ev_source *ev, ev_reader *reader, - int fd, void *ptr, size_t bytes, int eof, @@ -727,17 +719,25 @@ static int logging_reader_callback(ev_source *ev, /* don't log to this conn any more */ eventlog_remove(c->lo); - /* terminate the log output */ - sink_writes(ev_writer_sink(c->w), ".\n"); + 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"); + } /* restore the reader callback */ c->reader = reader_callback; /* ...and exit via it */ - return c->reader(ev, reader, fd, ptr, bytes, eof, u); + return c->reader(ev, reader, ptr, bytes, eof, u); } static void logclient(const char *msg, void *user) { struct conn *c = user; + if(!c->w || !c->r) { + /* This connection has gone up in smoke for some reason */ + eventlog_remove(c->lo); + return; + } sink_printf(ev_writer_sink(c->w), "%"PRIxMAX" %s\n", (uintmax_t)time(0), msg); } @@ -762,6 +762,9 @@ static int c_log(struct conn *c, if(playing) sink_printf(ev_writer_sink(c->w), "%"PRIxMAX" state playing\n", (uintmax_t)now); + /* Initial volume */ + sink_printf(ev_writer_sink(c->w), "%"PRIxMAX" volume %d %d\n", + (uintmax_t)now, volume_left, volume_right); c->lo = xmalloc(sizeof *c->lo); c->lo->fn = logclient; c->lo->user = c; @@ -773,8 +776,8 @@ static int c_log(struct conn *c, static void post_move_cleanup(void) { struct queue_entry *q; - /* If we have caused the random track to not be at the end then we make it no - * longer be random. */ + /* If we have caused any random tracks to not be at the end then we make them + * no longer be random. */ for(q = qhead.next; q != &qhead; q = q->next) if(q->state == playing_random && q->next != &qhead) q->state = playing_unplayed; @@ -910,6 +913,34 @@ static int c_nop(struct conn *c, return 1; } +static int c_new(struct conn *c, + char **vec, + int nvec) { + char **tracks = trackdb_new(0, nvec > 0 ? atoi(vec[0]) : INT_MAX); + + sink_printf(ev_writer_sink(c->w), "253 New track list follows\n"); + while(*tracks) { + sink_printf(ev_writer_sink(c->w), "%s%s\n", + **tracks == '.' ? "." : "", *tracks); + ++tracks; + } + sink_writes(ev_writer_sink(c->w), ".\n"); + return 1; /* completed */ + +} + +static int c_rtp_address(struct conn *c, + char attribute((unused)) **vec, + int attribute((unused)) nvec) { + if(config->speaker_backend == BACKEND_NETWORK) { + sink_printf(ev_writer_sink(c->w), "252 %s %s\n", + quoteutf8(config->broadcast.s[0]), + quoteutf8(config->broadcast.s[1])); + } else + sink_writes(ev_writer_sink(c->w), "550 No RTP\n"); + return 1; +} + #define C_AUTH 0001 /* must be authenticated */ #define C_TRUSTED 0002 /* must be trusted user */ @@ -933,6 +964,7 @@ static const struct command { { "log", 0, 0, c_log, C_AUTH }, { "move", 2, 2, c_move, C_AUTH }, { "moveafter", 1, INT_MAX, c_moveafter, C_AUTH }, + { "new", 0, 1, c_new, C_AUTH }, { "nop", 0, 0, c_nop, C_AUTH }, { "part", 3, 3, c_part, C_AUTH }, { "pause", 0, 0, c_pause, C_AUTH }, @@ -949,6 +981,7 @@ static const struct command { { "rescan", 0, 0, c_rescan, C_AUTH|C_TRUSTED }, { "resolve", 1, 1, c_resolve, C_AUTH }, { "resume", 0, 0, c_resume, C_AUTH }, + { "rtp-address", 0, 0, c_rtp_address, C_AUTH }, { "scratch", 0, 1, c_scratch, C_AUTH }, { "search", 1, 1, c_search, C_AUTH }, { "set", 3, 3, c_set, C_AUTH, }, @@ -1012,20 +1045,18 @@ static int command(struct conn *c, char *line) { /* redirect to the right reader callback for our current state */ static int redirect_reader_callback(ev_source *ev, ev_reader *reader, - int fd, void *ptr, size_t bytes, int eof, void *u) { struct conn *c = u; - return c->reader(ev, reader, fd, ptr, bytes, eof, u); + return c->reader(ev, reader, ptr, bytes, eof, u); } /* the main command reader */ static int reader_callback(ev_source attribute((unused)) *ev, ev_reader *reader, - int attribute((unused)) fd, void *ptr, size_t bytes, int eof, @@ -1055,7 +1086,6 @@ static int reader_callback(ev_source attribute((unused)) *ev, if(eof) { if(bytes) error(0, "S%x unterminated line", c->tag); - c->r = 0; return ev_writer_close(c->w); } return 0; @@ -1075,13 +1105,24 @@ static int listen_callback(ev_source *ev, cloexec(fd); c->tag = tags++; c->ev = ev; - c->w = ev_writer_new(ev, fd, writer_error, c); - c->r = ev_reader_new(ev, fd, redirect_reader_callback, reader_error, c); + c->w = ev_writer_new(ev, fd, writer_error, c, + "client writer"); + c->r = ev_reader_new(ev, fd, redirect_reader_callback, reader_error, c, + "client reader"); + ev_tie(c->r, c->w); c->fd = fd; c->reader = reader_callback; c->l = l; gcry_randomize(c->nonce, sizeof c->nonce, GCRY_STRONG_RANDOM); - sink_printf(ev_writer_sink(c->w), "231 %s\n", hex(c->nonce, sizeof c->nonce)); + if(!strcmp(config->authorization_algorithm, "sha1") + || !strcmp(config->authorization_algorithm, "SHA1")) { + sink_printf(ev_writer_sink(c->w), "231 %s\n", + hex(c->nonce, sizeof c->nonce)); + } else { + sink_printf(ev_writer_sink(c->w), "231 %s %s\n", + config->authorization_algorithm, + hex(c->nonce, sizeof c->nonce)); + } return 0; } @@ -1104,7 +1145,8 @@ int server_start(ev_source *ev, int pf, cloexec(fd); l->name = name; l->pf = pf; - if(ev_listen(ev, fd, listen_callback, l)) exit(EXIT_FAILURE); + if(ev_listen(ev, fd, listen_callback, l, "server listener")) + exit(EXIT_FAILURE); return fd; }