X-Git-Url: http://www.chiark.greenend.org.uk/ucgi/~mdw/git/disorder/blobdiff_plain/3dc3d7dbde4b666c7e829a27d5838e5ed49bdbe6..802bb5963b7883e640a87a11dcd7a81bf811a076:/server/state.c diff --git a/server/state.c b/server/state.c index 91bc078..19e8be3 100644 --- a/server/state.c +++ b/server/state.c @@ -1,164 +1,220 @@ /* * This file is part of DisOrder. - * Copyright (C) 2004, 2005 Richard Kettlewell + * Copyright (C) 2004, 2005, 2007-2009, 2012 Richard Kettlewell * - * This program is free software; you can redistribute it and/or modify + * 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 - * the Free Software Foundation; either version 2 of the License, or + * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * - * This program is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * General Public License for more details. - * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 - * USA + * along with this program. If not, see . + */ +/** @file server/state.c + * @brief Global server state */ +#include "disorder-server.h" + +/** @brief A unix domain socket */ +struct unix_socket { + const char *path; + int fd; +}; + +struct unix_socket main_socket[1], priv_socket[1]; + +/** @brief TCP listener definition */ +struct listener { + /** @brief Next listener */ + struct listener *next; + + /** @brief Local socket address */ + struct sockaddr *sa; -#include - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "event.h" -#include "play.h" -#include "trackdb.h" -#include "state.h" -#include "configuration.h" -#include "log.h" -#include "queue.h" -#include "server-queue.h" -#include "server.h" -#include "printf.h" -#include "addr.h" - -static const char *current_unix; -static int current_unix_fd; - -static struct addrinfo *current_listen_addrinfo; -static int current_listen_fd; + /** @brief File descriptor */ + int fd; +}; +/** @brief Current listeners */ +static struct listener *listeners; + +/** @brief Current audio API */ +const struct uaudio *api; + +/** @brief Quit DisOrder */ void quit(ev_source *ev) { + disorder_info("shutting down..."); quitting(ev); trackdb_close(); - trackdb_deinit(); - info("terminating"); - _exit(0); + trackdb_deinit(ev); + /* Shutdown subprocesses. + * + * Subprocesses that use ev_child: + * - the speaker + * - the current rescan + * - any decoders + * - ...and players + * - the track picker + * - mail sender + * - the deadlock manager + * + * Subprocesses that don't: + * - any normalizers + * These are not shut down currently. + */ + ev_child_killall(ev); + disorder_info("exiting"); + exit(0); } -static void reset_socket(ev_source *ev) { - const char *new_unix; - struct addrinfo *res; - struct sockaddr_un sun; - char *name; - - static const struct addrinfo pref = { - AI_PASSIVE, - PF_INET, - SOCK_STREAM, - IPPROTO_TCP, - 0, - 0, - 0, - 0 - }; +/** @brief Create a copy of an @c addrinfo structure */ +static struct sockaddr *copy_sockaddr(const struct resolved *raddr) { + struct sockaddr *sa = xmalloc_noptr(raddr->len); + memcpy(sa, raddr->sa, raddr->len); + return sa; +} - /* unix first */ - new_unix = config_get_file("socket"); - if(!current_unix || strcmp(current_unix, new_unix)) { +static void reset_unix_socket(ev_source *ev, + struct unix_socket *s, + const char *new_path, + int privileged) { + struct sockaddr_un sun; + if(!s->path || strcmp(s->path, new_path)) { /* either there was no socket, or there was but a different path */ - if(current_unix) { + if(s->path) { /* stop the old one and remove it from the filesystem */ - server_stop(ev, current_unix_fd); - if(unlink(current_unix) < 0) - fatal(errno, "unlink %s", current_unix); + server_stop(ev, s->fd); + if(unlink(s->path) < 0) + disorder_fatal(errno, "unlink %s", s->path); } /* start the new one */ - if(strlen(new_unix) >= sizeof sun.sun_path) - fatal(0, "socket path %s is too long", new_unix); + if(strlen(new_path) >= sizeof sun.sun_path) + disorder_fatal(0, "socket path %s is too long", new_path); memset(&sun, 0, sizeof sun); sun.sun_family = AF_UNIX; - strcpy(sun.sun_path, new_unix); - if(unlink(new_unix) < 0 && errno != ENOENT) - fatal(errno, "unlink %s", new_unix); - if((current_unix_fd = server_start(ev, PF_UNIX, sizeof sun, - (const struct sockaddr *)&sun, - new_unix)) >= 0) { - current_unix = new_unix; - if(chmod(new_unix, 0777) < 0) - fatal(errno, "error calling chmod %s", new_unix); + strcpy(sun.sun_path, new_path); + if(unlink(new_path) < 0 && errno != ENOENT) + disorder_fatal(errno, "unlink %s", new_path); + if((s->fd = server_start(ev, PF_UNIX, sizeof sun, + (const struct sockaddr *)&sun, + new_path, + privileged)) >= 0) { + s->path = new_path; + if(chmod(new_path, 0777) < 0) /* TODO */ + disorder_fatal(errno, "error calling chmod %s", new_path); } else - current_unix = 0; + s->path = 0; } +} + +/** @brief Create and destroy sockets to set current configuration */ +void reset_sockets(ev_source *ev) { + struct resolved *res; + size_t i, nres; + struct listener *l, **ll; + const char *private_dir = config_get_file("private"); + + /* create the private socket directory */ + unlink(private_dir); + if(mkdir(private_dir, 0700) < 0 && errno != EEXIST) + disorder_fatal(errno, "error creating %s", private_dir); + + /* unix first */ + reset_unix_socket(ev, main_socket, config_get_file("socket"), 0); + reset_unix_socket(ev, priv_socket, config_get_file("private/socket"), 1); /* get the new listen config */ - if(config->listen.n) - res = get_address(&config->listen, &pref, &name); - else - res = 0; - - if((res && !current_listen_addrinfo) - || (current_listen_addrinfo - && (!res - || addrinfocmp(res, current_listen_addrinfo)))) { - /* something has to change */ - if(current_listen_addrinfo) { - /* delete the old listener */ - server_stop(ev, current_listen_fd); - freeaddrinfo(current_listen_addrinfo); - current_listen_addrinfo = 0; + res = 0; nres = 0; + if(config->listen.af != -1) + netaddress_resolve(&config->listen, 1, SOCK_STREAM, &res, &nres); + + /* Close any current listeners that aren't required any more */ + ll = &listeners; + while((l = *ll)) { + for(i = 0; i < nres; i++) + if(!sockaddrcmp(res[i].sa, l->sa)) + break; + if(i >= nres) { + /* Didn't find a match, remove this one */ + server_stop(ev, l->fd); + *ll = l->next; + } else { + /* This address is still wanted */ + ll = &l->next; } - if(res) { - /* start the new listener */ - if((current_listen_fd = server_start(ev, res->ai_family, res->ai_addrlen, - res->ai_addr, name)) >= 0) { - current_listen_addrinfo = res; - res = 0; + } + + /* Open any new listeners that are required */ + for(i = 0; i < nres; i++) { + for(l = listeners; l; l = l->next) + if(!sockaddrcmp(res[i].sa, l->sa)) + break; + if(!l) { + /* Didn't find a match, need a new listener */ + int fd = server_start(ev, res[i].sa->sa_family, res[i].len, res[i].sa, + format_sockaddr(res[i].sa), 0); + if(fd >= 0) { + l = xmalloc(sizeof *l); + l->next = listeners; + l->sa = copy_sockaddr(&res[i]); + l->fd = fd; + listeners = l; } + /* We ignore any failures (though server_start() will have + * logged them). */ } } /* if res is still set it needs freeing */ if(res) - freeaddrinfo(res); + netaddress_free_resolved(res, nres); } -int reconfigure(ev_source *ev, int reload) { +/** @brief Reconfigure the server + * @param ev Event loop + * @param flags Flags + * @return As config_read(); 0 on success, -1 if could not (re-)read config + */ +int reconfigure(ev_source *ev, unsigned flags) { int need_another_rescan = 0; int ret = 0; - D(("reconfigure(%d)", reload)); - if(reload) { + D(("reconfigure(%x)", flags)); + /* Deconfigure the old audio API if there is one */ + if(api) { + if(api->close_mixer) + api->close_mixer(); + api = NULL; + } + if(flags & RECONFIGURE_RELOADING) { + /* If there's a rescan in progress, cancel it but remember to start a fresh + * one after the reload. */ need_another_rescan = trackdb_rescan_cancel(); - trackdb_close(); - if(config_read(1)) + /* (Re-)read the configuration */ + if(config_read(1/*server*/, config)) ret = -1; else { /* Tell the speaker it needs to reload its config too. */ speaker_reload(); - info("%s: installed new configuration", configfile); + disorder_info("%s: installed new configuration", configfile); } } - trackdb_open(0); + /* New audio API */ + api = uaudio_find(config->api); + if(api->configure) + api->configure(); + if(api->open_mixer) + api->open_mixer(); + /* If we interrupted a rescan of all the tracks, start a new one */ if(need_another_rescan) - trackdb_rescan(ev); - if(!ret) { - queue_read(); - recent_read(); - reset_socket(ev); + trackdb_rescan(ev, 1/*check*/, 0, 0); + if(!ret && !(flags & RECONFIGURE_FIRST)) { + /* Open/close sockets */ + reset_sockets(ev); } return ret; } @@ -167,5 +223,7 @@ int reconfigure(ev_source *ev, int reload) { Local Variables: c-basic-offset:2 comment-column:40 +fill-column:79 +indent-tabs-mode:nil End: */