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"
31 #include <sys/types.h>
32 #include <sys/socket.h>
34 #include <netinet/in.h>
35 #include <arpa/inet.h>
46 LIST_HEAD(client_head
);
50 struct sockaddr_un un
;
51 struct sockaddr_in in
;
54 #define MAX_CLIENTS 10
56 static void buf_grow(char **bufp
, size_t *allocp
, size_t *lenp
, size_t n
)
58 size_t alloc
= *lenp
+ n
+ 1;
60 if (alloc
> *allocp
) {
62 alloc
= (alloc
+ align
) & ~(align
- 1);
63 *bufp
= xrealloc(*bufp
, alloc
);
69 static void buf_addf(char **bufp
, size_t *allocp
, size_t *lenp
, const char *fmt
, ...)
72 size_t alloc
= *allocp
;
78 slen
= vsnprintf(buf
+ len
, alloc
- len
, fmt
, ap
);
81 if (slen
> alloc
- len
- 1) {
82 buf_grow(&buf
, &alloc
, &len
, slen
);
87 slen
= vsnprintf(buf
+ len
, alloc
- len
, fmt
, ap
);
94 static const char *escape(const char *str
)
96 static char *buf
= NULL
;
97 static size_t alloc
= 0;
98 size_t len
= strlen(str
);
99 size_t need
= len
* 2 + 1;
103 alloc
= (need
+ 16) & ~(16 - 1);
104 buf
= xrealloc(buf
, alloc
);
108 for (s
= 0; str
[s
]; s
++) {
109 if (str
[s
] == '\\') {
114 if (str
[s
] == '\n') {
125 static int cmd_status(struct client
*client
)
127 const char *status
[] = { "stopped", "playing", "paused" };
128 const struct track_info
*ti
;
134 buf_grow(&buf
, &alloc
, &len
, 0);
137 buf_addf(&buf
, &alloc
, &len
, "status %s\n", status
[player_info
.status
]);
140 buf_addf(&buf
, &alloc
, &len
, "file %s\n", escape(ti
->filename
));
141 buf_addf(&buf
, &alloc
, &len
, "duration %d\n", ti
->duration
);
142 buf_addf(&buf
, &alloc
, &len
, "position %d\n", player_info
.pos
);
143 for (i
= 0; ti
->comments
[i
].key
; i
++)
144 buf_addf(&buf
, &alloc
, &len
, "tag %s %s\n",
146 escape(ti
->comments
[i
].val
));
148 buf_addf(&buf
, &alloc
, &len
, "\n");
149 player_info_unlock();
151 ret
= write_all(client
->fd
, buf
, strlen(buf
));
156 static void read_commands(struct client
*client
)
160 int authenticated
= addr
.sa
.sa_family
== AF_UNIX
;
165 rc
= read(client
->fd
, buf
+ pos
, sizeof(buf
) - pos
);
178 for (i
= 0; i
< pos
; i
++) {
190 if (!authenticated
) {
191 if (!server_password
) {
192 d_print("password is unset, tcp/ip disabled\n");
195 authenticated
= !strcmp(line
, server_password
);
196 if (!authenticated
) {
197 d_print("authentication failed\n");
203 if (parse_command(line
, &cmd
, &arg
)) {
204 if (!strcmp(cmd
, "status")) {
205 ret
= cmd_status(client
);
207 run_parsed_command(cmd
, arg
);
208 ret
= write_all(client
->fd
, "\n", 1);
213 // don't hang cmus-remote
214 ret
= write_all(client
->fd
, "\n", 1);
217 d_print("write: %s\n", strerror(errno
));
221 memmove(buf
, buf
+ s
, pos
- s
);
227 list_del(&client
->node
);
231 void server_accept(void)
233 struct client
*client
;
234 struct sockaddr saddr
;
235 socklen_t saddr_size
= sizeof(saddr
);
238 fd
= accept(server_socket
, &saddr
, &saddr_size
);
242 fcntl(fd
, F_SETFL
, O_NONBLOCK
);
244 client
= xnew(struct client
, 1);
246 list_add_tail(&client
->node
, &client_head
);
249 void server_serve(struct client
*client
)
251 /* unix connection is secure, other insecure */
252 run_only_safe_commands
= addr
.sa
.sa_family
!= AF_UNIX
;
253 read_commands(client
);
254 run_only_safe_commands
= 0;
257 static void gethostbyname_failed(void)
259 const char *error
= "Unknown error.";
264 error
= "Host not found.";
267 error
= "A non-recoverable name server error.";
270 error
= "A temporary error occurred on an authoritative name server.";
273 die("gethostbyname: %s\n", error
);
276 void server_init(char *address
)
278 int port
= DEFAULT_PORT
;
281 if (strchr(address
, '/')) {
282 addr
.sa
.sa_family
= AF_UNIX
;
283 strncpy(addr
.un
.sun_path
, address
, sizeof(addr
.un
.sun_path
) - 1);
285 addrlen
= sizeof(struct sockaddr_un
);
287 char *s
= strchr(address
, ':');
288 struct hostent
*hent
;
294 hent
= gethostbyname(address
);
296 gethostbyname_failed();
298 addr
.sa
.sa_family
= hent
->h_addrtype
;
299 switch (addr
.sa
.sa_family
) {
301 memcpy(&addr
.in
.sin_addr
, hent
->h_addr_list
[0], hent
->h_length
);
302 addr
.in
.sin_port
= htons(port
);
304 addrlen
= sizeof(addr
.in
);
307 die("unsupported address type\n");
311 server_socket
= socket(addr
.sa
.sa_family
, SOCK_STREAM
, 0);
312 if (server_socket
== -1)
315 if (bind(server_socket
, &addr
.sa
, addrlen
) == -1) {
318 if (errno
!= EADDRINUSE
)
321 /* address already in use */
322 if (addr
.sa
.sa_family
!= AF_UNIX
)
323 die("cmus is already listening on %s:%d\n", address
, port
);
325 /* try to connect to server */
326 sock
= socket(AF_UNIX
, SOCK_STREAM
, 0);
330 if (connect(sock
, &addr
.sa
, addrlen
) == -1) {
331 if (errno
!= ENOENT
&& errno
!= ECONNREFUSED
)
332 die_errno("connect");
334 /* server not running => dead socket */
336 /* try to remove dead socket */
337 if (unlink(addr
.un
.sun_path
) == -1 && errno
!= ENOENT
)
339 if (bind(server_socket
, &addr
.sa
, addrlen
) == -1)
342 /* server already running */
343 die("cmus is already listening on socket %s\n", address
);
348 if (listen(server_socket
, MAX_CLIENTS
) == -1)
352 void server_exit(void)
354 close(server_socket
);
355 if (addr
.sa
.sa_family
== AF_UNIX
)
356 unlink(addr
.un
.sun_path
);