Handle streams separately in tree_add_track()
[cmus.git] / server.c
blob7fb9a74566dd8b4b4dbe2cf42f4645985f719958
1 /*
2 * Copyright 2004-2005 Timo Hirvonen
3 *
4 * This program is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU General Public License as
6 * published by the Free Software Foundation; either version 2 of the
7 * License, or (at your option) any later version.
9 * This program is distributed in the hope that it will be useful, but
10 * WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 * General Public License for more details.
14 * You should have received a copy of the GNU General Public License
15 * along with this program; if not, write to the Free Software
16 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
17 * 02111-1307, USA.
20 #include "server.h"
21 #include "prog.h"
22 #include "command_mode.h"
23 #include "search_mode.h"
24 #include "options.h"
25 #include "xmalloc.h"
26 #include "player.h"
27 #include "file.h"
28 #include "compiler.h"
29 #include "debug.h"
30 #include "gbuf.h"
32 #include <unistd.h>
33 #include <sys/types.h>
34 #include <sys/socket.h>
35 #include <sys/un.h>
36 #include <netinet/in.h>
37 #include <arpa/inet.h>
38 #include <netdb.h>
39 #include <errno.h>
40 #include <stdio.h>
41 #include <string.h>
42 #include <stdlib.h>
43 #include <unistd.h>
44 #include <fcntl.h>
45 #include <ctype.h>
47 int server_socket;
48 LIST_HEAD(client_head);
50 static union {
51 struct sockaddr sa;
52 struct sockaddr_un un;
53 struct sockaddr_in in;
54 } addr;
56 #define MAX_CLIENTS 10
58 static const char *escape(const char *str)
60 static char *buf = NULL;
61 static size_t alloc = 0;
62 size_t len = strlen(str);
63 size_t need = len * 2 + 1;
64 int s, d;
66 if (need > alloc) {
67 alloc = (need + 16) & ~(16 - 1);
68 buf = xrealloc(buf, alloc);
71 d = 0;
72 for (s = 0; str[s]; s++) {
73 if (str[s] == '\\') {
74 buf[d++] = '\\';
75 buf[d++] = '\\';
76 continue;
78 if (str[s] == '\n') {
79 buf[d++] = '\\';
80 buf[d++] = 'n';
81 continue;
83 buf[d++] = str[s];
85 buf[d] = 0;
86 return buf;
89 static int cmd_status(struct client *client)
91 const char *status[] = { "stopped", "playing", "paused" };
92 const struct track_info *ti;
93 GBUF(buf);
94 int i, ret;
96 player_info_lock();
97 gbuf_addf(&buf, "status %s\n", status[player_info.status]);
98 ti = player_info.ti;
99 if (ti) {
100 gbuf_addf(&buf, "file %s\n", escape(ti->filename));
101 gbuf_addf(&buf, "duration %d\n", ti->duration);
102 gbuf_addf(&buf, "position %d\n", player_info.pos);
103 for (i = 0; ti->comments[i].key; i++)
104 gbuf_addf(&buf, "tag %s %s\n",
105 ti->comments[i].key,
106 escape(ti->comments[i].val));
108 gbuf_add_str(&buf, "\n");
109 player_info_unlock();
111 ret = write_all(client->fd, buf.buffer, buf.len);
112 gbuf_free(&buf);
113 return ret;
116 static void read_commands(struct client *client)
118 char buf[1024];
119 int pos = 0;
120 int authenticated = addr.sa.sa_family == AF_UNIX;
122 while (1) {
123 int rc, s, i;
125 rc = read(client->fd, buf + pos, sizeof(buf) - pos);
126 if (rc == -1) {
127 if (errno == EINTR)
128 continue;
129 if (errno == EAGAIN)
130 return;
131 goto close;
133 if (rc == 0)
134 goto close;
135 pos += rc;
137 s = 0;
138 for (i = 0; i < pos; i++) {
139 const char *line;
140 char *cmd, *arg;
141 int ret;
143 if (buf[i] != '\n')
144 continue;
146 buf[i] = 0;
147 line = buf + s;
148 s = i + 1;
150 if (!authenticated) {
151 if (!server_password) {
152 d_print("password is unset, tcp/ip disabled\n");
153 goto close;
155 authenticated = !strcmp(line, server_password);
156 if (!authenticated) {
157 d_print("authentication failed\n");
158 goto close;
160 continue;
163 while (isspace(*line))
164 line++;
166 if (*line == '/') {
167 int restricted = 0;
168 line++;
169 search_direction = SEARCH_FORWARD;
170 if (*line == '/') {
171 line++;
172 restricted = 1;
174 search_text(line, restricted, 1);
175 ret = write_all(client->fd, "\n", 1);
176 } else if (*line == '?') {
177 int restricted = 0;
178 line++;
179 search_direction = SEARCH_BACKWARD;
180 if (*line == '?') {
181 line++;
182 restricted = 1;
184 search_text(line, restricted, 1);
185 ret = write_all(client->fd, "\n", 1);
186 } else if (parse_command(line, &cmd, &arg)) {
187 if (!strcmp(cmd, "status")) {
188 ret = cmd_status(client);
189 } else {
190 run_parsed_command(cmd, arg);
191 ret = write_all(client->fd, "\n", 1);
193 free(cmd);
194 free(arg);
195 } else {
196 // don't hang cmus-remote
197 ret = write_all(client->fd, "\n", 1);
199 if (ret < 0) {
200 d_print("write: %s\n", strerror(errno));
201 goto close;
204 memmove(buf, buf + s, pos - s);
205 pos -= s;
207 return;
208 close:
209 close(client->fd);
210 list_del(&client->node);
211 free(client);
214 void server_accept(void)
216 struct client *client;
217 struct sockaddr saddr;
218 socklen_t saddr_size = sizeof(saddr);
219 int fd;
221 fd = accept(server_socket, &saddr, &saddr_size);
222 if (fd == -1)
223 return;
225 fcntl(fd, F_SETFL, O_NONBLOCK);
227 client = xnew(struct client, 1);
228 client->fd = fd;
229 list_add_tail(&client->node, &client_head);
232 void server_serve(struct client *client)
234 /* unix connection is secure, other insecure */
235 run_only_safe_commands = addr.sa.sa_family != AF_UNIX;
236 read_commands(client);
237 run_only_safe_commands = 0;
240 static void gethostbyname_failed(void)
242 const char *error = "Unknown error.";
244 switch (h_errno) {
245 case HOST_NOT_FOUND:
246 case NO_ADDRESS:
247 error = "Host not found.";
248 break;
249 case NO_RECOVERY:
250 error = "A non-recoverable name server error.";
251 break;
252 case TRY_AGAIN:
253 error = "A temporary error occurred on an authoritative name server.";
254 break;
256 die("gethostbyname: %s\n", error);
259 void server_init(char *address)
261 int port = DEFAULT_PORT;
262 int addrlen;
264 if (strchr(address, '/')) {
265 addr.sa.sa_family = AF_UNIX;
266 strncpy(addr.un.sun_path, address, sizeof(addr.un.sun_path) - 1);
268 addrlen = sizeof(struct sockaddr_un);
269 } else {
270 char *s = strchr(address, ':');
271 struct hostent *hent;
273 if (s) {
274 *s++ = 0;
275 port = atoi(s);
277 hent = gethostbyname(address);
278 if (!hent)
279 gethostbyname_failed();
281 addr.sa.sa_family = hent->h_addrtype;
282 switch (addr.sa.sa_family) {
283 case AF_INET:
284 memcpy(&addr.in.sin_addr, hent->h_addr_list[0], hent->h_length);
285 addr.in.sin_port = htons(port);
287 addrlen = sizeof(addr.in);
288 break;
289 default:
290 die("unsupported address type\n");
294 server_socket = socket(addr.sa.sa_family, SOCK_STREAM, 0);
295 if (server_socket == -1)
296 die_errno("socket");
298 if (bind(server_socket, &addr.sa, addrlen) == -1) {
299 int sock;
301 if (errno != EADDRINUSE)
302 die_errno("bind");
304 /* address already in use */
305 if (addr.sa.sa_family != AF_UNIX)
306 die("cmus is already listening on %s:%d\n", address, port);
308 /* try to connect to server */
309 sock = socket(AF_UNIX, SOCK_STREAM, 0);
310 if (sock == -1)
311 die_errno("socket");
313 if (connect(sock, &addr.sa, addrlen) == -1) {
314 if (errno != ENOENT && errno != ECONNREFUSED)
315 die_errno("connect");
317 /* server not running => dead socket */
319 /* try to remove dead socket */
320 if (unlink(addr.un.sun_path) == -1 && errno != ENOENT)
321 die_errno("unlink");
322 if (bind(server_socket, &addr.sa, addrlen) == -1)
323 die_errno("bind");
324 } else {
325 /* server already running */
326 die("cmus is already listening on socket %s\n", address);
328 close(sock);
331 if (listen(server_socket, MAX_CLIENTS) == -1)
332 die_errno("listen");
335 void server_exit(void)
337 close(server_socket);
338 if (addr.sa.sa_family == AF_UNIX)
339 unlink(addr.un.sun_path);