2 * Copyright 2004-2005 Timo Hirvonen
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
22 #include "command_mode.h"
23 #include "search_mode.h"
33 #include <sys/types.h>
34 #include <sys/socket.h>
36 #include <netinet/in.h>
37 #include <arpa/inet.h>
48 LIST_HEAD(client_head
);
52 struct sockaddr_un un
;
53 struct sockaddr_in in
;
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;
67 alloc
= (need
+ 16) & ~(16 - 1);
68 buf
= xrealloc(buf
, alloc
);
72 for (s
= 0; str
[s
]; s
++) {
89 static int cmd_status(struct client
*client
)
91 const char *status
[] = { "stopped", "playing", "paused" };
92 const struct track_info
*ti
;
97 gbuf_addf(&buf
, "status %s\n", status
[player_info
.status
]);
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",
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
);
116 static void read_commands(struct client
*client
)
120 int authenticated
= addr
.sa
.sa_family
== AF_UNIX
;
125 rc
= read(client
->fd
, buf
+ pos
, sizeof(buf
) - pos
);
138 for (i
= 0; i
< pos
; i
++) {
150 if (!authenticated
) {
151 if (!server_password
) {
152 d_print("password is unset, tcp/ip disabled\n");
155 authenticated
= !strcmp(line
, server_password
);
156 if (!authenticated
) {
157 d_print("authentication failed\n");
163 while (isspace(*line
))
169 search_direction
= SEARCH_FORWARD
;
174 search_text(line
, restricted
, 1);
175 ret
= write_all(client
->fd
, "\n", 1);
176 } else if (*line
== '?') {
179 search_direction
= SEARCH_BACKWARD
;
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
);
190 run_parsed_command(cmd
, arg
);
191 ret
= write_all(client
->fd
, "\n", 1);
196 // don't hang cmus-remote
197 ret
= write_all(client
->fd
, "\n", 1);
200 d_print("write: %s\n", strerror(errno
));
204 memmove(buf
, buf
+ s
, pos
- s
);
210 list_del(&client
->node
);
214 void server_accept(void)
216 struct client
*client
;
217 struct sockaddr saddr
;
218 socklen_t saddr_size
= sizeof(saddr
);
221 fd
= accept(server_socket
, &saddr
, &saddr_size
);
225 fcntl(fd
, F_SETFL
, O_NONBLOCK
);
227 client
= xnew(struct client
, 1);
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.";
247 error
= "Host not found.";
250 error
= "A non-recoverable name server error.";
253 error
= "A temporary error occurred on an authoritative name server.";
256 die("gethostbyname: %s\n", error
);
259 void server_init(char *address
)
261 int port
= DEFAULT_PORT
;
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
);
270 char *s
= strchr(address
, ':');
271 struct hostent
*hent
;
277 hent
= gethostbyname(address
);
279 gethostbyname_failed();
281 addr
.sa
.sa_family
= hent
->h_addrtype
;
282 switch (addr
.sa
.sa_family
) {
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
);
290 die("unsupported address type\n");
294 server_socket
= socket(addr
.sa
.sa_family
, SOCK_STREAM
, 0);
295 if (server_socket
== -1)
298 if (bind(server_socket
, &addr
.sa
, addrlen
) == -1) {
301 if (errno
!= EADDRINUSE
)
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);
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
)
322 if (bind(server_socket
, &addr
.sa
, addrlen
) == -1)
325 /* server already running */
326 die("cmus is already listening on socket %s\n", address
);
331 if (listen(server_socket
, MAX_CLIENTS
) == -1)
335 void server_exit(void)
337 close(server_socket
);
338 if (addr
.sa
.sa_family
== AF_UNIX
)
339 unlink(addr
.un
.sun_path
);