Change volume socket commands to be more consistent with other commands.
[shell-fm.git] / source / sckif.c
blob186ef7a7302fc9a828c497e5064155caf23b5980
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",
203 "volume-up",
204 "volume-down"
207 memset(arg, 0, sizeof(arg));
208 memset(reply, 0, REPLYBUFSIZE);
210 for(ncmd = 0; ncmd < (sizeof(known) / sizeof(char *)); ++ncmd) {
211 if(!strncmp(known[ncmd], cmd, strlen(known[ncmd])))
212 break;
215 switch(ncmd) {
216 case (sizeof(known) / sizeof(char *)):
217 strncpy(reply, "ERROR", REPLYBUFSIZE);
218 break;
220 case 0:
221 if(sscanf(cmd, "play %128[a-zA-Z0-9:/_ %,*.-]", arg) == 1) {
222 char * url;
223 decode(arg, & url);
224 station(url);
225 free(url);
227 break;
229 case 1:
230 rate("L");
231 break;
233 case 2:
234 rate("B");
235 break;
237 case 3:
238 rate("S");
239 break;
241 case 4:
242 quit();
244 case 5:
245 if(* (cmd + 5))
246 strncpy(reply, meta(cmd + 5, 0, & track), REPLYBUFSIZE);
247 else if(haskey(& rc, "np-file-format"))
248 strncpy(
249 reply,
250 meta(value(& rc, "np-file-format"), 0, & track),
251 REPLYBUFSIZE
254 break;
256 case 6:
257 if(playfork) {
258 if(pausetime) {
259 kill(playfork, SIGCONT);
261 else {
262 time(& pausetime);
263 kill(playfork, SIGSTOP);
266 break;
268 case 7:
269 toggle(DISCOVERY);
270 break;
272 case 8:
273 if(sscanf(cmd, "tag-artist %128s", arg) == 1)
274 sendtag('a', arg, track);
275 break;
277 case 9:
278 if(sscanf(cmd, "tag-album %128s", arg) == 1)
279 sendtag('l', arg, track);
280 break;
282 case 10:
283 if(sscanf(cmd, "tag-track %128s", arg) == 1)
284 sendtag('t', arg, track);
285 break;
287 case 11:
288 if((ptr = oldtags('a', track)) != NULL) {
289 strncpy(reply, ptr, REPLYBUFSIZE);
290 free(ptr);
291 ptr = NULL;
293 break;
295 case 12:
296 if((ptr = oldtags('l', track)) != NULL) {
297 strncpy(reply, ptr, REPLYBUFSIZE);
298 free(ptr);
299 ptr = NULL;
301 break;
303 case 13:
304 if((ptr = oldtags('t', track)) != NULL) {
305 strncpy(reply, ptr, REPLYBUFSIZE);
306 free(ptr);
307 ptr = NULL;
309 break;
311 case 14:
312 if(playfork) {
313 enable(STOPPED);
314 kill(playfork, SIGUSR1);
316 break;
317 case 15:
318 // Volume +
319 if(volume < MAX_VOLUME)
320 volume += 1;
321 break;
322 case 16:
323 // Volume -
324 if(volume > 0)
325 volume -= 1;
326 break;
330 static int waitread(int fd, unsigned sec, unsigned usec) {
331 fd_set readfd;
332 struct timeval tv;
334 FD_ZERO(& readfd);
335 FD_SET(fd, & readfd);
337 tv.tv_sec = sec;
338 tv.tv_usec = usec;
340 return (select(fd + 1, & readfd, NULL, NULL, & tv) > 0);