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"
35 #include <sys/types.h>
36 #include <sys/socket.h>
38 #include <netinet/in.h>
39 #include <arpa/inet.h>
50 LIST_HEAD(client_head
);
54 struct sockaddr_un un
;
55 struct sockaddr_in in
;
58 #define MAX_CLIENTS 10
60 static const char *escape(const char *str
)
62 static char *buf
= NULL
;
63 static size_t alloc
= 0;
64 size_t len
= strlen(str
);
65 size_t need
= len
* 2 + 1;
69 alloc
= (need
+ 16) & ~(16 - 1);
70 buf
= xrealloc(buf
, alloc
);
74 for (s
= 0; str
[s
]; s
++) {
91 static int cmd_status(struct client
*client
)
93 const char *status
[] = { "stopped", "playing", "paused" };
94 const char *export_options
[] = {
108 const struct track_info
*ti
;
109 struct cmus_opt
*opt
;
110 char optbuf
[OPTION_MAX_SIZE
];
112 int vol_left
, vol_right
;
116 gbuf_addf(&buf
, "status %s\n", status
[player_info
.status
]);
119 gbuf_addf(&buf
, "file %s\n", escape(ti
->filename
));
120 gbuf_addf(&buf
, "duration %d\n", ti
->duration
);
121 gbuf_addf(&buf
, "position %d\n", player_info
.pos
);
122 for (i
= 0; ti
->comments
[i
].key
; i
++)
123 gbuf_addf(&buf
, "tag %s %s\n",
125 escape(ti
->comments
[i
].val
));
129 for (i
= 0; export_options
[i
]; i
++) {
130 opt
= option_find(export_options
[i
]);
132 opt
->get(opt
->id
, optbuf
);
133 gbuf_addf(&buf
, "set %s %s\n", opt
->name
, optbuf
);
137 /* get volume (copied from ui_curses.c) */
139 vol_left
= soft_vol_l
;
140 vol_right
= soft_vol_r
;
141 } else if (!volume_max
) {
142 vol_left
= vol_right
= -1;
144 vol_left
= scale_to_percentage(volume_l
, volume_max
);
145 vol_right
= scale_to_percentage(volume_r
, volume_max
);
149 gbuf_addf(&buf
, "set vol_left %d\n", vol_left
);
150 gbuf_addf(&buf
, "set vol_right %d\n", vol_right
);
152 gbuf_add_str(&buf
, "\n");
153 player_info_unlock();
155 ret
= write_all(client
->fd
, buf
.buffer
, buf
.len
);
160 static void read_commands(struct client
*client
)
164 int authenticated
= addr
.sa
.sa_family
== AF_UNIX
;
169 rc
= read(client
->fd
, buf
+ pos
, sizeof(buf
) - pos
);
182 for (i
= 0; i
< pos
; i
++) {
194 if (!authenticated
) {
195 if (!server_password
) {
196 d_print("password is unset, tcp/ip disabled\n");
199 authenticated
= !strcmp(line
, server_password
);
200 if (!authenticated
) {
201 d_print("authentication failed\n");
207 while (isspace(*line
))
213 search_direction
= SEARCH_FORWARD
;
218 search_text(line
, restricted
, 1);
219 ret
= write_all(client
->fd
, "\n", 1);
220 } else if (*line
== '?') {
223 search_direction
= SEARCH_BACKWARD
;
228 search_text(line
, restricted
, 1);
229 ret
= write_all(client
->fd
, "\n", 1);
230 } else if (parse_command(line
, &cmd
, &arg
)) {
231 if (!strcmp(cmd
, "status")) {
232 ret
= cmd_status(client
);
234 run_parsed_command(cmd
, arg
);
235 ret
= write_all(client
->fd
, "\n", 1);
240 // don't hang cmus-remote
241 ret
= write_all(client
->fd
, "\n", 1);
244 d_print("write: %s\n", strerror(errno
));
248 memmove(buf
, buf
+ s
, pos
- s
);
254 list_del(&client
->node
);
258 void server_accept(void)
260 struct client
*client
;
261 struct sockaddr saddr
;
262 socklen_t saddr_size
= sizeof(saddr
);
265 fd
= accept(server_socket
, &saddr
, &saddr_size
);
269 fcntl(fd
, F_SETFL
, O_NONBLOCK
);
271 client
= xnew(struct client
, 1);
273 list_add_tail(&client
->node
, &client_head
);
276 void server_serve(struct client
*client
)
278 /* unix connection is secure, other insecure */
279 run_only_safe_commands
= addr
.sa
.sa_family
!= AF_UNIX
;
280 read_commands(client
);
281 run_only_safe_commands
= 0;
284 static void gethostbyname_failed(void)
286 const char *error
= "Unknown error.";
291 error
= "Host not found.";
294 error
= "A non-recoverable name server error.";
297 error
= "A temporary error occurred on an authoritative name server.";
300 die("gethostbyname: %s\n", error
);
303 void server_init(char *address
)
305 int port
= DEFAULT_PORT
;
308 if (strchr(address
, '/')) {
309 addr
.sa
.sa_family
= AF_UNIX
;
310 strncpy(addr
.un
.sun_path
, address
, sizeof(addr
.un
.sun_path
) - 1);
312 addrlen
= sizeof(struct sockaddr_un
);
314 char *s
= strchr(address
, ':');
315 struct hostent
*hent
;
321 hent
= gethostbyname(address
);
323 gethostbyname_failed();
325 addr
.sa
.sa_family
= hent
->h_addrtype
;
326 switch (addr
.sa
.sa_family
) {
328 memcpy(&addr
.in
.sin_addr
, hent
->h_addr_list
[0], hent
->h_length
);
329 addr
.in
.sin_port
= htons(port
);
331 addrlen
= sizeof(addr
.in
);
334 die("unsupported address type\n");
338 server_socket
= socket(addr
.sa
.sa_family
, SOCK_STREAM
, 0);
339 if (server_socket
== -1)
342 if (bind(server_socket
, &addr
.sa
, addrlen
) == -1) {
345 if (errno
!= EADDRINUSE
)
348 /* address already in use */
349 if (addr
.sa
.sa_family
!= AF_UNIX
)
350 die("cmus is already listening on %s:%d\n", address
, port
);
352 /* try to connect to server */
353 sock
= socket(AF_UNIX
, SOCK_STREAM
, 0);
357 if (connect(sock
, &addr
.sa
, addrlen
) == -1) {
358 if (errno
!= ENOENT
&& errno
!= ECONNREFUSED
)
359 die_errno("connect");
361 /* server not running => dead socket */
363 /* try to remove dead socket */
364 if (unlink(addr
.un
.sun_path
) == -1 && errno
!= ENOENT
)
366 if (bind(server_socket
, &addr
.sa
, addrlen
) == -1)
369 /* server already running */
370 die("cmus is already listening on socket %s\n", address
);
375 if (listen(server_socket
, MAX_CLIENTS
) == -1)
379 void server_exit(void)
381 close(server_socket
);
382 if (addr
.sa
.sa_family
== AF_UNIX
)
383 unlink(addr
.un
.sun_path
);