cmus-remote: TCP/IP support
[cmus.git] / server.c
blobe7c9dbab777351c11e5f7e973f2d81ad90e6847b
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"
24 #include <unistd.h>
25 #include <sys/types.h>
26 #include <sys/socket.h>
27 #include <sys/un.h>
28 #include <netinet/in.h>
29 #include <arpa/inet.h>
30 #include <netdb.h>
31 #include <errno.h>
32 #include <stdio.h>
33 #include <string.h>
34 #include <stdlib.h>
36 int server_socket;
38 static union {
39 struct sockaddr sa;
40 struct sockaddr_un un;
41 struct sockaddr_in in;
42 } addr;
44 #define MAX_CLIENTS 10
46 static void read_commands(int fd)
48 char buf[1024];
49 int pos = 0;
51 while (1) {
52 int rc, s;
54 rc = read(fd, buf + pos, sizeof(buf) - pos);
55 if (rc == -1) {
56 break;
58 if (rc == 0) {
59 break;
61 pos += rc;
63 s = 0;
64 while (1) {
65 int i;
67 for (i = s; i < pos; i++) {
68 if (buf[i] == '\n') {
69 buf[i] = 0;
70 run_command(buf + s);
71 s = i + 1;
72 break;
75 if (i == pos)
76 break;
78 memmove(buf, buf + s, pos - s);
79 pos -= s;
83 int server_serve(void)
85 struct sockaddr saddr;
86 socklen_t saddr_size = sizeof(saddr);
87 int fd;
89 fd = accept(server_socket, &saddr, &saddr_size);
90 if (fd == -1) {
91 return -1;
93 read_commands(fd);
94 close(fd);
95 return 0;
98 static void gethostbyname_failed(void)
100 const char *error = "Unknown error.";
102 switch (h_errno) {
103 case HOST_NOT_FOUND:
104 case NO_ADDRESS:
105 error = "Host not found.";
106 break;
107 case NO_RECOVERY:
108 error = "A non-recoverable name server error.";
109 break;
110 case TRY_AGAIN:
111 error = "A temporary error occurred on an authoritative name server.";
112 break;
114 die("gethostbyname: %s\n", error);
117 void server_init(char *address)
119 int port = -1;
120 int addrlen;
122 if (strchr(address, '/')) {
123 addr.sa.sa_family = AF_UNIX;
124 strncpy(addr.un.sun_path, address, sizeof(addr.un.sun_path) - 1);
126 addrlen = sizeof(struct sockaddr_un);
127 } else {
128 char *s = strchr(address, ':');
129 struct hostent *hent;
131 if (s) {
132 *s++ = 0;
133 port = atoi(s);
135 hent = gethostbyname(address);
136 if (!hent)
137 gethostbyname_failed();
139 addr.sa.sa_family = hent->h_addrtype;
140 switch (addr.sa.sa_family) {
141 case AF_INET:
142 memcpy(&addr.in.sin_addr, hent->h_addr_list[0], hent->h_length);
143 addr.in.sin_port = htons(port);
145 addrlen = sizeof(addr.in);
146 break;
147 default:
148 die("unsupported address type\n");
152 server_socket = socket(addr.sa.sa_family, SOCK_STREAM, 0);
153 if (server_socket == -1)
154 die_errno("socket");
156 if (bind(server_socket, &addr.sa, addrlen) == -1) {
157 int sock;
159 if (errno != EADDRINUSE)
160 die_errno("bind");
162 /* address already in use */
163 if (addr.sa.sa_family != AF_UNIX)
164 die("cmus is already listening on %s:%d\n", address, port);
166 /* try to connect to server */
167 sock = socket(AF_UNIX, SOCK_STREAM, 0);
168 if (sock == -1)
169 die_errno("socket");
171 if (connect(sock, &addr.sa, addrlen) == -1) {
172 if (errno != ENOENT && errno != ECONNREFUSED)
173 die_errno("connect");
175 /* server not running => dead socket */
177 /* try to remove dead socket */
178 if (unlink(addr.un.sun_path) == -1 && errno != ENOENT)
179 die_errno("unlink");
180 if (bind(server_socket, &addr.sa, addrlen) == -1)
181 die_errno("bind");
182 } else {
183 /* server already running */
184 die("cmus is already listening on socket %s\n", address);
186 close(sock);
189 if (listen(server_socket, MAX_CLIENTS) == -1)
190 die_errno("listen");
193 void server_exit(void)
195 close(server_socket);
196 if (addr.sa.sa_family == AF_UNIX)
197 unlink(addr.un.sun_path);