cmd.c (8890B)
1 #include <sys/select.h> 2 3 #include <ctype.h> 4 #include <err.h> 5 #include <fcntl.h> 6 #include <limits.h> 7 #include <signal.h> 8 #include <stdio.h> 9 #include <stdlib.h> 10 #include <string.h> 11 #include <unistd.h> 12 13 #include "sad.h" 14 15 static void cmdstatus(int fd, char *arg) { 16 Song *s; 17 int r = 0; 18 19 if (!arg[0]) { 20 dprintf(fd, "ERR expected argument\n"); 21 return; 22 } 23 24 if (!strncmp(arg, "random", 7)) { 25 r = 1; 26 dprintf(fd, "random: %d\n", (getplaylistmode() & RANDOM) == 1); 27 } else if (!strncmp(arg, "repeat", 7)) { 28 r = 1; 29 dprintf(fd, "repeat: %d\n", (getplaylistmode() & REPEAT) == 1); 30 } else if (!strncmp(arg, "single", 7)) { 31 r = 1; 32 dprintf(fd, "single: %d\n", (getplaylistmode() & SINGLE) == 1); 33 } else if (!strncmp(arg, "volume", 7)) { 34 r = 1; 35 dprintf(fd, "volume: %d\n", getvol()); 36 } else if (!strncmp(arg, "songid", 7)) { 37 r = 1; 38 s = getcursong(); 39 40 if (!s) { 41 dprintf(fd, "ERR no song is active\n"); 42 return; 43 } 44 45 dprintf(fd, "songid: %d\n", s->id); 46 } else if (!strncmp(arg, "playback", 9)) { 47 r = 1; 48 s = getcursong(); 49 50 dprintf(fd, "playback: "); 51 52 if (s) 53 switch (s->state) { 54 case PLAYING: 55 dprintf(fd, "play\n"); 56 break; 57 case PAUSED: 58 dprintf(fd, "pause\n"); 59 break; 60 case NONE: 61 dprintf(fd, "stop\n"); 62 break; 63 } 64 else 65 dprintf(fd, "stop\n"); 66 } 67 68 if (r) { 69 dprintf(fd, "OK\n"); 70 return; 71 } 72 73 dprintf(fd, "ERR unknown command\n"); 74 } 75 76 static void cmdrepeat(int fd, char *arg) { 77 if (arg[0]) { 78 dprintf(fd, "ERR unexpected argument\n"); 79 return; 80 } 81 82 playlistmode(REPEAT); 83 dprintf(fd, "OK\n"); 84 } 85 86 static void cmdrandom(int fd, char *arg) { 87 if (arg[0]) { 88 dprintf(fd, "ERR unexpected argument\n"); 89 return; 90 } 91 92 playlistmode(RANDOM); 93 dprintf(fd, "OK\n"); 94 } 95 96 static void cmdvolume(int fd, char *arg) { 97 int vol; 98 const char *errstr; 99 100 if (!arg[0]) { 101 dprintf(fd, "ERR expected volume\n"); 102 return; 103 } 104 105 vol = strtonum(arg, 0, 100, &errstr); 106 if (errstr) { 107 dprintf(fd, "ERR volume should be between [0, 100]\n"); 108 return; 109 } 110 if (setvol(vol) < 0) { 111 dprintf(fd, "ERR failed to change volume\n"); 112 return; 113 } 114 dprintf(fd, "OK\n"); 115 } 116 117 static void cmdnext(int fd, char *arg) { 118 Song *s; 119 120 if (arg[0]) { 121 dprintf(fd, "ERR unexpected argument\n"); 122 return; 123 } 124 125 s = getcursong(); 126 if (!s) { 127 dprintf(fd, "ERR playlist is empty\n"); 128 return; 129 } 130 131 playsong(getnextsong()); 132 dprintf(fd, "OK\n"); 133 } 134 135 static void cmdpause(int fd, char *arg) { 136 Song *s; 137 int pause; 138 const char *errstr; 139 140 if (!arg[0]) { 141 s = getcursong(); 142 if (s->state == PAUSED) 143 s->state = PLAYING; 144 else 145 s->state = PAUSED; 146 } else { 147 148 pause = strtonum(arg, 0, 1, &errstr); 149 if (errstr) { 150 dprintf(fd, "ERR argument should be 0 or 1, or no argument\n"); 151 return; 152 } 153 154 s = getcursong(); 155 if (!s) { 156 dprintf(fd, "ERR playlist is empty\n"); 157 return; 158 } 159 160 switch (s->state) { 161 case PLAYING: 162 if (pause == 1) 163 s->state = PAUSED; 164 break; 165 case PAUSED: 166 if (pause == 0) 167 s->state = PLAYING; 168 break; 169 case NONE: 170 dprintf(fd, "ERR no song is active\n"); 171 return; 172 } 173 } 174 dprintf(fd, "OK\n"); 175 } 176 177 static void cmdplay(int fd, char *arg) { 178 Song *s, *cur; 179 int id; 180 const char *errstr; 181 182 cur = getcursong(); 183 if (!cur) { 184 dprintf(fd, "ERR playlist is empty\n"); 185 return; 186 } 187 188 if (arg[0]) { 189 id = strtonum(arg, 0, INT_MAX, &errstr); 190 if (errstr) { 191 dprintf(fd, "ERR invalid song id\n"); 192 return; 193 } 194 195 s = findsongid(id); 196 if (!s) { 197 dprintf(fd, "ERR cannot find song with given id\n"); 198 return; 199 } 200 } else { 201 s = cur; 202 } 203 204 playsong(s); 205 dprintf(fd, "OK\n"); 206 } 207 208 static void cmdprev(int fd, char *arg) { 209 Song *s; 210 211 if (arg[0]) { 212 dprintf(fd, "ERR unexpected argument\n"); 213 return; 214 } 215 216 s = getcursong(); 217 if (!s) { 218 dprintf(fd, "ERR playlist is empty\n"); 219 return; 220 } 221 222 playsong(getprevsong()); 223 dprintf(fd, "OK\n"); 224 } 225 226 static void cmdstop(int fd, char *arg) { 227 Song *s; 228 229 if (arg[0]) { 230 dprintf(fd, "ERR unexpected argument\n"); 231 return; 232 } 233 234 s = getcursong(); 235 if (!s) { 236 dprintf(fd, "ERR playlist is empty\n"); 237 return; 238 } 239 240 stopsong(s); 241 dprintf(fd, "OK\n"); 242 } 243 244 static void cmdadd(int fd, char *arg) { 245 if (!arg[0]) { 246 dprintf(fd, "ERR expected file path\n"); 247 return; 248 } 249 250 if (!addplaylist(arg)) { 251 dprintf(fd, "ERR cannot add song to playlist\n"); 252 return; 253 } 254 255 dprintf(fd, "OK\n"); 256 } 257 258 static void cmdclear(int fd, char *arg) { 259 if (arg[0]) { 260 dprintf(fd, "ERR unexpected argument\n"); 261 return; 262 } 263 264 stopsong(getcursong()); 265 clearplaylist(); 266 dprintf(fd, "OK\n"); 267 } 268 269 static void cmdremove(int fd, char *arg) { 270 Song *s; 271 const char *errstr; 272 int id; 273 274 if (arg[0]) { 275 id = strtonum(arg, 0, INT_MAX, &errstr); 276 if (errstr) { 277 dprintf(fd, "ERR invalid song id\n"); 278 return; 279 } 280 s = findsongid(id); 281 if (!s) { 282 dprintf(fd, "ERR cannot find song with given id\n"); 283 return; 284 } 285 } else { 286 s = getcursong(); 287 stopsong(s); 288 } 289 290 if (rmplaylist(s->id) < 0) { 291 dprintf(fd, "ERR failed to remove song\n"); 292 return; 293 } 294 dprintf(fd, "OK\n"); 295 } 296 297 static void cmdplaylist(int fd, char *arg) { 298 if (arg[0]) { 299 dprintf(fd, "ERR unexpected argument\n"); 300 return; 301 } 302 dumpplaylist(fd); 303 dprintf(fd, "OK\n"); 304 } 305 306 static void cmdclose(int fd, char *arg) { 307 if (arg[0]) { 308 dprintf(fd, "ERR unexpected argument\n"); 309 return; 310 } 311 312 dprintf(fd, "OK\n"); 313 FD_CLR(fd, &master); 314 close(fd); 315 } 316 317 static void cmdkill(int fd, char *arg) { 318 if (arg[0]) { 319 dprintf(fd, "ERR unexpected argument\n"); 320 return; 321 } 322 dprintf(fd, "OK\n"); 323 raise(SIGTERM); 324 } 325 326 static void cmdping(int fd, char *arg) { 327 if (arg[0]) { 328 dprintf(fd, "ERR unexpected argument\n"); 329 return; 330 } 331 dprintf(fd, "pong\nOK\n"); 332 } 333 334 static void cmdversion(int fd, char *arg) { 335 if (arg[0]) { 336 dprintf(fd, "ERR unexpected argument\n"); 337 return; 338 } 339 dprintf(fd, "version 0.0\nOK\n"); 340 } 341 342 static void cmdenable(int fd, char *arg) { 343 if (!arg[0]) { 344 dprintf(fd, "ERR expected output name\n"); 345 return; 346 } 347 if (enableoutput(arg) < 0) { 348 dprintf(fd, "ERR failed to enable output %s\n", arg); 349 return; 350 } 351 dprintf(fd, "OK\n"); 352 } 353 354 static void cmddisable(int fd, char *arg) { 355 if (!arg[0]) { 356 dprintf(fd, "ERR expected output name\n"); 357 return; 358 } 359 if (disableoutput(arg) < 0) { 360 dprintf(fd, "ERR failed to disable output %s\n", arg); 361 return; 362 } 363 dprintf(fd, "OK\n"); 364 } 365 366 static void cmdwait(int fd, char *arg) { 367 if (!arg[0]) { 368 dprintf(fd, "ERR expected event name\n"); 369 return; 370 } 371 372 if (addsubscribername(fd, arg) < 0) { 373 dprintf(fd, "ERR unknown event type\n"); 374 return; 375 } 376 dprintf(fd, "OK\n"); 377 } 378 379 static Cmd cmds[] = { 380 {"repeat", cmdrepeat}, {"random", cmdrandom}, {"status", cmdstatus}, 381 {"volume", cmdvolume}, {"next", cmdnext}, {"pause", cmdpause}, 382 {"play", cmdplay}, {"prev", cmdprev}, {"stop", cmdstop}, 383 {"add", cmdadd}, {"clear", cmdclear}, {"remove", cmdremove}, 384 {"playlist", cmdplaylist}, {"close", cmdclose}, {"kill", cmdkill}, 385 {"ping", cmdping}, {"version", cmdversion}, {"enable", cmdenable}, 386 {"disable", cmddisable}, {"wait", cmdwait}, 387 }; 388 389 /* shamelessly taken from isakmpd ui.c */ 390 int docmd(int clifd) { 391 static char *buf = 0; 392 static char *p; 393 static size_t sz; 394 static size_t resid; 395 ssize_t n; 396 size_t cmdlen, i; 397 char *new_buf; 398 int c; 399 400 /* If no buffer, set it up. */ 401 if (!buf) { 402 sz = BUFSIZ; 403 buf = malloc(sz); 404 if (!buf) 405 err(1, "malloc"); 406 p = buf; 407 resid = sz; 408 } 409 /* If no place left in the buffer reallocate twice as large. */ 410 if (!resid) { 411 new_buf = realloc(buf, sz * 2); 412 if (!new_buf) 413 err(1, "realloc"); 414 buf = new_buf; 415 p = buf + sz; 416 resid = sz; 417 sz *= 2; 418 } 419 n = read(clifd, p, resid); 420 if (n <= 0) 421 return -1; 422 resid -= n; 423 while (n--) { 424 /* 425 * When we find a newline, cut off the line and feed it to the 426 * command processor. Then move the rest up-front. 427 */ 428 if (*p == '\n') { 429 *p = '\0'; 430 for (i = 0; i < LEN(cmds); i++) { 431 cmdlen = strlen(cmds[i].name); 432 if (!strncmp(cmds[i].name, buf, cmdlen) && 433 (buf[cmdlen] == '\0' || isspace(buf[cmdlen]))) { 434 /* strip leading whitespace */ 435 for (c = cmdlen; buf[c] && isspace(buf[c]); c++) 436 ; 437 cmds[i].fn(clifd, &buf[c]); 438 break; 439 } 440 } 441 if (i == LEN(cmds)) 442 dprintf(clifd, "ERR unknown command\n"); 443 memmove(buf, p + 1, n); 444 p = buf; 445 resid = sz - n; 446 continue; 447 } 448 p++; 449 } 450 return 0; 451 }