chiark / gitweb /
lib/addr.c: Introduce our own `freeaddrinfo' function.
[disorder] / server / state.c
1 /*
2  * This file is part of DisOrder.
3  * Copyright (C) 2004, 2005, 2007-2009, 2012 Richard Kettlewell
4  *
5  * This program is free software: you can redistribute it and/or modify
6  * it under the terms of the GNU General Public License as published by
7  * the Free Software Foundation, either version 3 of the License, or
8  * (at your option) any later version.
9  *
10  * This program is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13  * GNU General Public License for more details.
14  * 
15  * You should have received a copy of the GNU General Public License
16  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
17  */
18 /** @file server/state.c
19  * @brief Global server state
20  */
21 #include "disorder-server.h"
22
23 /** @brief A unix domain socket */
24 struct unix_socket {
25   const char *path;
26   int fd;
27 };
28
29 struct unix_socket main_socket[1], priv_socket[1];
30
31 /** @brief TCP listener definition */
32 struct listener {
33   /** @brief Next listener */
34   struct listener *next;
35
36   /** @brief Local socket address */
37   struct sockaddr *sa;
38
39   /** @brief File descriptor */
40   int fd;
41 };
42
43 /** @brief Current listeners */
44 static struct listener *listeners;
45
46 /** @brief Current audio API */
47 const struct uaudio *api;
48
49 /** @brief Quit DisOrder */
50 void quit(ev_source *ev) {
51   disorder_info("shutting down...");
52   quitting(ev);
53   trackdb_close();
54   trackdb_deinit(ev);
55   /* Shutdown subprocesses.
56    *
57    * Subprocesses that use ev_child:
58    * - the speaker
59    * - the current rescan
60    * - any decoders
61    * - ...and players
62    * - the track picker
63    * - mail sender
64    * - the deadlock manager
65    *
66    * Subprocesses that don't:
67    * - any normalizers
68    * These are not shut down currently.
69    */
70   ev_child_killall(ev);
71   disorder_info("exiting");
72   exit(0);
73 }
74
75 /** @brief Create a copy of an @c addrinfo structure */
76 static struct sockaddr *copy_sockaddr(const struct addrinfo *addr) {
77   struct sockaddr *sa = xmalloc_noptr(addr->ai_addrlen);
78   memcpy(sa, addr->ai_addr, addr->ai_addrlen);
79   return sa;
80 }
81
82 static void reset_unix_socket(ev_source *ev,
83                               struct unix_socket *s,
84                               const char *new_path,
85                               int privileged) {
86   struct sockaddr_un sun;
87   if(!s->path || strcmp(s->path, new_path)) {
88     /* either there was no socket, or there was but a different path */
89     if(s->path) {
90       /* stop the old one and remove it from the filesystem */
91       server_stop(ev, s->fd);
92       if(unlink(s->path) < 0)
93         disorder_fatal(errno, "unlink %s", s->path);
94     }
95     /* start the new one */
96     if(strlen(new_path) >= sizeof sun.sun_path)
97       disorder_fatal(0, "socket path %s is too long", new_path);
98     memset(&sun, 0, sizeof sun);
99     sun.sun_family = AF_UNIX;
100     strcpy(sun.sun_path, new_path);
101     if(unlink(new_path) < 0 && errno != ENOENT)
102       disorder_fatal(errno, "unlink %s", new_path);
103     if((s->fd = server_start(ev, PF_UNIX, sizeof sun,
104                              (const struct sockaddr *)&sun,
105                              new_path,
106                              privileged)) >= 0) {
107       s->path = new_path;
108       if(chmod(new_path, 0777) < 0)     /* TODO */
109         disorder_fatal(errno, "error calling chmod %s", new_path);
110     } else
111       s->path = 0;
112   }
113 }
114
115 /** @brief Create and destroy sockets to set current configuration */
116 void reset_sockets(ev_source *ev) {
117   struct addrinfo *res, *r;
118   struct listener *l, **ll;
119   const char *private_dir = config_get_file("private");
120   
121   /* create the private socket directory */
122   unlink(private_dir);
123   if(mkdir(private_dir, 0700) < 0 && errno != EEXIST)
124     disorder_fatal(errno, "error creating %s", private_dir);
125
126   /* unix first */
127   reset_unix_socket(ev, main_socket, config_get_file("socket"), 0);
128   reset_unix_socket(ev, priv_socket, config_get_file("private/socket"), 1);
129
130   /* get the new listen config */
131   if(config->listen.af != -1)
132     res = netaddress_resolve(&config->listen, 1, IPPROTO_TCP);
133   else
134     res = 0;
135
136   /* Close any current listeners that aren't required any more */
137   ll = &listeners;
138   while((l = *ll)) {
139     for(r = res; r; r = r->ai_next)
140       if(!sockaddrcmp(r->ai_addr, l->sa))
141         break;
142     if(!r) {
143       /* Didn't find a match, remove this one */
144       server_stop(ev, l->fd);
145       *ll = l->next;
146     } else {
147       /* This address is still wanted */
148       ll = &l->next;
149     }
150   }
151
152   /* Open any new listeners that are required */
153   for(r = res; r; r = r->ai_next) {
154     for(l = listeners; l; l = l->next)
155       if(!sockaddrcmp(r->ai_addr, l->sa))
156         break;
157     if(!l) {
158       /* Didn't find a match, need a new listener */
159       int fd = server_start(ev, r->ai_family, r->ai_addrlen, r->ai_addr,
160                             format_sockaddr(r->ai_addr), 0);
161       if(fd >= 0) {
162         l = xmalloc(sizeof *l);
163         l->next = listeners;
164         l->sa = copy_sockaddr(r);
165         l->fd = fd;
166         listeners = l;
167       }
168       /* We ignore any failures (though server_start() will have
169        * logged them). */
170     }
171   }
172   /* if res is still set it needs freeing */
173   if(res)
174     netaddress_freeaddrinfo(res);
175 }
176
177 /** @brief Reconfigure the server
178  * @param ev Event loop
179  * @param flags Flags
180  * @return As config_read(); 0 on success, -1 if could not (re-)read config
181  */
182 int reconfigure(ev_source *ev, unsigned flags) {
183   int need_another_rescan = 0;
184   int ret = 0;
185
186   D(("reconfigure(%x)", flags));
187   /* Deconfigure the old audio API if there is one */
188   if(api) {
189     if(api->close_mixer)
190       api->close_mixer();
191     api = NULL;
192   }
193   if(flags & RECONFIGURE_RELOADING) {
194     /* If there's a rescan in progress, cancel it but remember to start a fresh
195      * one after the reload. */
196     need_another_rescan = trackdb_rescan_cancel();
197     /* (Re-)read the configuration */
198     if(config_read(1/*server*/, config))
199       ret = -1;
200     else {
201       /* Tell the speaker it needs to reload its config too. */
202       speaker_reload();
203       disorder_info("%s: installed new configuration", configfile);
204     }
205   }
206   /* New audio API */
207   api = uaudio_find(config->api);
208   if(api->configure)
209     api->configure();
210   if(api->open_mixer)
211     api->open_mixer();
212   /* If we interrupted a rescan of all the tracks, start a new one */
213   if(need_another_rescan)
214     trackdb_rescan(ev, 1/*check*/, 0, 0);
215   if(!ret && !(flags & RECONFIGURE_FIRST)) {
216     /* Open/close sockets */
217     reset_sockets(ev);
218   }
219   return ret;
220 }
221
222 /*
223 Local Variables:
224 c-basic-offset:2
225 comment-column:40
226 fill-column:79
227 indent-tabs-mode:nil
228 End:
229 */