chiark / gitweb /
Override lintian warning about start-stop-daemon
[disorder] / server / state.c
1 /*
2  * This file is part of DisOrder.
3  * Copyright (C) 2004, 2005, 2007-2009 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 static const char *current_unix;
24 static int current_unix_fd;
25
26 /** @brief TCP listener definition */
27 struct listener {
28   struct listener *next;
29   struct sockaddr *sa;
30   int fd;
31 };
32
33 /** @brief Current listeners */
34 static struct listener *listeners;
35
36 /** @brief Current audio API */
37 const struct uaudio *api;
38
39 void quit(ev_source *ev) {
40   info("shutting down...");
41   quitting(ev);
42   trackdb_close();
43   trackdb_deinit();
44   info("exiting");
45   exit(0);
46 }
47
48 static struct sockaddr *copy_sockaddr(const struct addrinfo *addr) {
49   struct sockaddr *sa = xmalloc_noptr(addr->ai_addrlen);
50   memcpy(sa, addr->ai_addr, addr->ai_addrlen);
51   return sa;
52 }
53
54 static void reset_socket(ev_source *ev) {
55   const char *new_unix;
56   struct addrinfo *res, *r;
57   struct listener *l, **ll;
58   struct sockaddr_un sun;
59
60   /* unix first */
61   new_unix = config_get_file("socket");
62   if(!current_unix || strcmp(current_unix, new_unix)) {
63     /* either there was no socket, or there was but a different path */
64     if(current_unix) {
65       /* stop the old one and remove it from the filesystem */
66       server_stop(ev, current_unix_fd);
67       if(unlink(current_unix) < 0)
68         fatal(errno, "unlink %s", current_unix);
69     }
70     /* start the new one */
71     if(strlen(new_unix) >= sizeof sun.sun_path)
72       fatal(0, "socket path %s is too long", new_unix);
73     memset(&sun, 0, sizeof sun);
74     sun.sun_family = AF_UNIX;
75     strcpy(sun.sun_path, new_unix);
76     if(unlink(new_unix) < 0 && errno != ENOENT)
77       fatal(errno, "unlink %s", new_unix);
78     if((current_unix_fd = server_start(ev, PF_UNIX, sizeof sun,
79                                        (const struct sockaddr *)&sun,
80                                        new_unix)) >= 0) {
81       current_unix = new_unix;
82       if(chmod(new_unix, 0777) < 0)
83         fatal(errno, "error calling chmod %s", new_unix);
84     } else
85       current_unix = 0;
86   }
87
88   /* get the new listen config */
89   if(config->listen.af != -1)
90     res = netaddress_resolve(&config->listen, 1, IPPROTO_TCP);
91   else
92     res = 0;
93
94   /* Close any current listeners that aren't required any more */
95   ll = &listeners;
96   while((l = *ll)) {
97     for(r = res; r; r = r->ai_next)
98       if(!sockaddrcmp(r->ai_addr, l->sa))
99         break;
100     if(!r) {
101       /* Didn't find a match, remove this one */
102       server_stop(ev, l->fd);
103       *ll = l->next;
104     } else {
105       /* This address is still wanted */
106       ll = &l->next;
107     }
108   }
109
110   /* Open any new listeners that are required */
111   for(r = res; r; r = r->ai_next) {
112     for(l = listeners; l; l = l->next)
113       if(!sockaddrcmp(r->ai_addr, l->sa))
114         break;
115     if(!l) {
116       /* Didn't find a match, need a new listener */
117       int fd = server_start(ev, r->ai_family, r->ai_addrlen, r->ai_addr,
118                             format_sockaddr(r->ai_addr));
119       if(fd >= 0) {
120         l = xmalloc(sizeof *l);
121         l->next = listeners;
122         l->sa = copy_sockaddr(r);
123         l->fd = fd;
124         listeners = l;
125       }
126     }
127   }
128   /* if res is still set it needs freeing */
129   if(res)
130     freeaddrinfo(res);
131 }
132
133 int reconfigure(ev_source *ev, int reload) {
134   int need_another_rescan = 0;
135   int ret = 0;
136
137   D(("reconfigure(%d)", reload));
138   if(api) {
139     if(api->close_mixer)
140       api->close_mixer();
141     api = NULL;
142   }
143   if(reload) {
144     need_another_rescan = trackdb_rescan_cancel();
145     trackdb_close();
146     if(config_read(1))
147       ret = -1;
148     else {
149       /* Tell the speaker it needs to reload its config too. */
150       speaker_reload();
151       info("%s: installed new configuration", configfile);
152     }
153     trackdb_open(TRACKDB_NO_UPGRADE);
154   } else
155     /* We only allow for upgrade at startup */
156     trackdb_open(TRACKDB_CAN_UPGRADE);
157   api = uaudio_find(config->api);
158   if(api->configure)
159     api->configure();
160   if(api->open_mixer)
161     api->open_mixer();
162   if(need_another_rescan)
163     trackdb_rescan(ev, 1/*check*/, 0, 0);
164   /* Arrange timeouts for schedule actions */
165   schedule_init(ev);
166   if(!ret) {
167     queue_read();
168     recent_read();
169     reset_socket(ev);
170   }
171   return ret;
172 }
173
174 /*
175 Local Variables:
176 c-basic-offset:2
177 comment-column:40
178 End:
179 */