'C' reloads configuration.
[shell-fm.git] / source / sckif.c
blobb3335633eccb200e9b6a52ac50243bf2eb71daf0
1 /*
2 Copyright (C) 2006 by Jonas Kramer
3 Published under the terms of the GNU General Public License (GPL).
4 */
6 #define _GNU_SOURCE
9 #include <stdio.h>
10 #include <unistd.h>
11 #include <string.h>
12 #include <errno.h>
13 #include <sys/types.h>
14 #include <sys/socket.h>
15 #include <sys/stat.h>
16 #include <stdlib.h>
17 #include <signal.h>
18 #include <time.h>
20 #include <netinet/in.h>
21 #include <arpa/inet.h>
22 #include <netdb.h>
24 #include <sys/select.h>
25 #include <sys/un.h>
27 #include "sckif.h"
28 #include "http.h"
29 #include "service.h"
30 #include "interface.h"
31 #include "hash.h"
32 #include "submit.h"
33 #include "getln.h"
34 #include "tag.h"
36 #include "globals.h"
38 struct hash track;
40 static int stcpsck = -1, sunixsck = -1;
41 static int waitread(int, unsigned, unsigned);
43 #define REPLYBUFSIZE 1024
45 int tcpsock(const char * ip, unsigned short port) {
46 static const int one = 1;
47 struct sockaddr_in host;
48 struct hostent * hostent;
50 if(!ip || !port)
51 return 0;
53 if(-1 == (stcpsck = socket(AF_INET, SOCK_STREAM, PF_UNSPEC))) {
54 fputs("Failed to create socket.\n", stderr);
55 return 0;
58 if(!(hostent = gethostbyname(ip))) {
59 fprintf(stderr, "Failed to lookup host. %s.\n", hstrerror(h_errno));
60 return 0;
63 host.sin_family = PF_INET;
64 host.sin_port = htons(port);
65 host.sin_addr.s_addr = * (unsigned *) hostent->h_addr;
67 if(-1 == setsockopt(stcpsck, SOL_SOCKET, SO_REUSEADDR, & one, sizeof one))
68 fprintf(stderr, "Couldn't make socket re-usable. %s\n", strerror(errno));
70 if(bind(stcpsck, (struct sockaddr *) & host, sizeof(struct sockaddr_in))) {
71 fprintf(stderr, "Failed to bind socket. %s.\n", strerror(errno));
72 return 0;
75 listen(stcpsck, 2);
77 return !0;
81 int unixsock(const char * path) {
82 struct sockaddr_un host;
84 if(path == NULL)
85 return 0;
88 if(!access(path, F_OK)) {
89 fprintf(stderr, "%s already existing. UNIX socket not created.\n", path);
90 return 0;
94 if(-1 == (sunixsck = socket(AF_UNIX, SOCK_STREAM, PF_UNSPEC))) {
95 fputs("Failed to create socket.\n", stderr);
96 return 0;
100 memset(& host, 0, sizeof(struct sockaddr_un));
101 strncpy(host.sun_path, path, sizeof(host.sun_path) - 1);
102 host.sun_family = AF_UNIX;
105 if(bind(sunixsck, (struct sockaddr *) & host, sizeof(struct sockaddr_un))) {
106 fprintf(stderr, "Failed to bind socket. %s.\n", strerror(errno));
107 return 0;
110 listen(sunixsck, 2);
112 return !0;
116 void rmsckif(void) {
117 if(stcpsck > 0) {
118 close(stcpsck);
119 stcpsck = -1;
122 if(sunixsck > 0) {
123 close(sunixsck);
124 sunixsck = -1;
129 void sckif(int timeout, int sck) {
131 /* If the given socket is -1, try both sockets, unix and tcp. */
132 if(sck == -1) {
133 if(stcpsck != -1)
134 sckif(timeout, stcpsck);
136 if(sunixsck != -1)
137 sckif(timeout, sunixsck);
139 return;
142 signal(SIGPIPE, SIG_IGN);
144 if(waitread(sck, timeout, 0)) {
145 struct sockaddr client;
146 socklen_t scksize = sizeof(struct sockaddr);
148 int csck = accept(sck, & client, & scksize);
150 if(-1 != csck) {
151 if(waitread(csck, 0, 100000)) {
152 FILE * fd = fdopen(csck, "r");
154 if(fd) {
155 char * line = NULL, * ptr = NULL, reply[REPLYBUFSIZE];
156 unsigned size = 0;
158 getln(& line, & size, fd);
160 if(line && size > 0 && !ferror(fd)) {
161 if((ptr = strchr(line, 13)) || (ptr = strchr(line, 10)))
162 * ptr = 0;
164 execcmd(line, reply);
167 if(line)
168 free(line);
170 if(strlen(reply)) {
171 write(csck, reply, strlen(reply));
174 fclose(fd);
178 shutdown(csck, SHUT_RDWR);
179 close(csck);
184 void execcmd(const char * cmd, char * reply) {
185 char arg[1024], * ptr;
186 unsigned ncmd;
187 const char * known [] = {
188 "play",
189 "love",
190 "ban",
191 "skip",
192 "quit",
193 "info",
194 "pause",
195 "discovery",
196 "tag-artist",
197 "tag-album",
198 "tag-track",
199 "artist-tags",
200 "album-tags",
201 "track-tags",
202 "stop",
205 memset(arg, 0, sizeof(arg));
206 memset(reply, 0, REPLYBUFSIZE);
208 for(ncmd = 0; ncmd < (sizeof(known) / sizeof(char *)); ++ncmd) {
209 if(!strncmp(known[ncmd], cmd, strlen(known[ncmd])))
210 break;
213 switch(ncmd) {
214 case (sizeof(known) / sizeof(char *)):
215 strncpy(reply, "ERROR", REPLYBUFSIZE);
216 break;
218 case 0:
219 if(sscanf(cmd, "play %128[a-zA-Z0-9:/_ %,*.-]", arg) == 1) {
220 char * url;
221 decode(arg, & url);
222 station(url);
223 free(url);
225 break;
227 case 1:
228 rate("L");
229 break;
231 case 2:
232 rate("B");
233 break;
235 case 3:
236 rate("S");
237 break;
239 case 4:
240 quit();
242 case 5:
243 if(* (cmd + 5))
244 strncpy(reply, meta(cmd + 5, 0, & track), REPLYBUFSIZE);
245 else if(haskey(& rc, "np-file-format"))
246 strncpy(
247 reply,
248 meta(value(& rc, "np-file-format"), 0, & track),
249 REPLYBUFSIZE
252 break;
254 case 6:
255 if(playfork) {
256 if(pausetime) {
257 kill(playfork, SIGCONT);
259 else {
260 time(& pausetime);
261 kill(playfork, SIGSTOP);
264 break;
266 case 7:
267 toggle(DISCOVERY);
268 break;
270 case 8:
271 if(sscanf(cmd, "tag-artist %128s", arg) == 1)
272 sendtag('a', arg, track);
273 break;
275 case 9:
276 if(sscanf(cmd, "tag-album %128s", arg) == 1)
277 sendtag('l', arg, track);
278 break;
280 case 10:
281 if(sscanf(cmd, "tag-track %128s", arg) == 1)
282 sendtag('t', arg, track);
283 break;
285 case 11:
286 if((ptr = oldtags('a', track)) != NULL) {
287 strncpy(reply, ptr, REPLYBUFSIZE);
288 free(ptr);
289 ptr = NULL;
291 break;
293 case 12:
294 if((ptr = oldtags('l', track)) != NULL) {
295 strncpy(reply, ptr, REPLYBUFSIZE);
296 free(ptr);
297 ptr = NULL;
299 break;
301 case 13:
302 if((ptr = oldtags('t', track)) != NULL) {
303 strncpy(reply, ptr, REPLYBUFSIZE);
304 free(ptr);
305 ptr = NULL;
307 break;
309 case 14:
310 if(playfork) {
311 enable(STOPPED);
312 kill(playfork, SIGUSR1);
314 break;
318 static int waitread(int fd, unsigned sec, unsigned usec) {
319 fd_set readfd;
320 struct timeval tv;
322 FD_ZERO(& readfd);
323 FD_SET(fd, & readfd);
325 tv.tv_sec = sec;
326 tv.tv_usec = usec;
328 return (select(fd + 1, & readfd, NULL, NULL, & tv) > 0);