ap

Queue manager meant to be used as an audio player
git clone git://jacobedwards.org/ap
Log | Files | Refs | README | LICENSE

aps.c (6170B)


      1 /*
      2  * Copyright 2021-2023 Jacob R. Edwards
      3  *
      4  * ap -- audio player
      5  *
      6  * This program is free software: you can redistribute it and/or modify
      7  * it under the terms of the GNU General Public License as published by
      8  * the Free Software Foundation, either version 3 of the License, or
      9  * (at your option) any later version.
     10  *
     11  * This program is distributed in the hope that it will be useful,
     12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
     13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
     14  * GNU General Public License for more details.
     15  *
     16  * You should have received a copy of the GNU General Public License
     17  * along with this program.  If not, see <https://www.gnu.org/licenses/>.
     18  */
     19 
     20 #include <sys/socket.h>
     21 #include <sys/stat.h>
     22 
     23 #include <ctype.h>
     24 #include <errno.h>
     25 #include <limits.h>
     26 #include <poll.h>
     27 #include <regex.h>
     28 #include <stdlib.h>
     29 #include <string.h>
     30 #include <unistd.h>
     31 
     32 #include "aps.h"
     33 #include "arg.h"
     34 #include "buf.h"
     35 #include "bug.h"
     36 #include "command.h"
     37 #include "find.h"
     38 #include "log.h"
     39 #include "queue.h"
     40 #include "split.h"
     41 #include "util.h"
     42 
     43 struct aps *
     44 aps_open(char *path, char **player)
     45 {
     46 	int i;
     47 	struct aps *aps;
     48 
     49 	aps = calloc(1, sizeof(*aps));
     50 	if (aps == NULL)
     51 		return NULL;
     52 
     53 	for (i = 0; i < LEN(aps->pfds); ++i)
     54 		aps->pfds[i].fd = -1;
     55 
     56 	aps->player = pnew(player);
     57 	if (aps->player == NULL) {
     58 		aps_close(aps);
     59 		return NULL;
     60 	}
     61 
     62 	aps->con = apopen(path, bind, SOCK_NONBLOCK);
     63 	if (aps->con == NULL || listen(aps->con->sock, 10)) {
     64 		aps_close(aps);
     65 		return NULL;
     66 	}
     67 	aps->pfds[aps->con->sock].fd = aps->con->sock;
     68 	aps->pfds[aps->con->sock].events = POLLIN;
     69 
     70 	aps->timeout = INFTIM;
     71 
     72 	return aps;
     73 }
     74 
     75 void
     76 aps_close(struct aps *aps)
     77 {
     78 	int i;
     79 
     80 	if (aps == NULL)
     81 		return;
     82 
     83 	while (aps->queue)
     84 		queue_remove(aps, aps->queue);
     85 	pfree(aps->player);
     86 
     87 	for (i = 0; i < LEN(aps->clients); ++i)
     88 		if (aps->clients[i].request)
     89 			aps_drop(aps, i);
     90 	apclose(aps->con);
     91 
     92 	free(aps);
     93 }
     94 
     95 int
     96 aps_drop(struct aps *aps, int fd)
     97 {
     98 	aps_log(aps, fd, INFO, logsubjects[LogConn], "dropping client");
     99 	--aps->nfds;
    100 	aps->pfds[fd].fd = -1;
    101 	buffree(aps->clients[fd].response);
    102 	aps->clients[fd].response = NULL;
    103 	buffree(aps->clients[fd].request);
    104 	aps->clients[fd].request = NULL;
    105 	return sclose(fd);
    106 }
    107 
    108 int
    109 aps_addfd(struct aps *aps, int fd)
    110 {
    111 	if ((aps->clients[fd].request = bufnew()) == NULL)
    112 		return 1;
    113 	if ((aps->clients[fd].response = bufnew()) == NULL) {
    114 		buffree(aps->clients[fd].request);
    115 		return 1;
    116 	}
    117 
    118 	++aps->nfds;
    119 	aps->pfds[fd].fd = fd;
    120 	aps->pfds[fd].events = POLLIN;
    121 	return 0;
    122 }
    123 
    124 int
    125 aps_accept(struct aps *aps)
    126 {
    127 	int s;
    128 
    129 	while ((s = accept4(aps->con->sock, NULL, NULL, 0)) >= 0) {
    130 		if (aps_addfd(aps, s))
    131 			aps_log(aps, s, ERROR, logsubjects[LogConn], "unable to add client");
    132 		else
    133 			aps_log(aps, s, INFO, logsubjects[LogConn], "client added");
    134 	}
    135 
    136 	return errno != EWOULDBLOCK;
    137 }
    138 
    139 int
    140 aps_respond(struct aps *aps, int fd)
    141 {
    142 	ssize_t n;
    143 	struct buf *buf;
    144 
    145 	buf = aps->clients[fd].response;
    146 	n = send(fd, buf->data, buf->len, MSG_NOSIGNAL);
    147 	switch (n) {
    148 	case 0:
    149 		errno = ECONNRESET;
    150 		/* no break */
    151 	case -1:
    152 		return 1;
    153 	default:
    154 		if ((buf->len - n) == 0) {
    155 			buf->len = 0;
    156 			aps->pfds[fd].events = POLLIN;
    157 		} else {
    158 			bufshift(buf, n);
    159 		}
    160 		return 0;
    161 	}
    162 }
    163 
    164 int
    165 aps_handle(struct aps *aps, int fd)
    166 {
    167 	if (aps->pfds[fd].revents & (POLLERR | POLLHUP | POLLNVAL)) {
    168 		return 1;
    169 	} else if (aps->pfds[fd].revents & POLLIN) {
    170 		if (aps_command(aps, fd))
    171 			return 1;
    172 	} else if (aps->pfds[fd].revents & POLLOUT) {
    173 		if (aps_respond(aps, fd))
    174 			return 1;
    175 	} else {
    176 		bug("impossible poll event");
    177 	}
    178 
    179 	return 0;
    180 }
    181 
    182 int
    183 aps_named(struct aps *aps)
    184 {
    185 	struct stat sb;
    186 
    187 	return !stat(aps->con->path, &sb);
    188 }
    189 
    190 int
    191 aps_update(struct aps *aps)
    192 {
    193 	int i;
    194 	int re;
    195 
    196 	if ((re = poll(aps->pfds, LEN(aps->pfds), aps->timeout)) <= 0)
    197 		return re;
    198 
    199 	if (aps->pfds[aps->con->sock].revents) {
    200 		if ((aps->pfds[aps->con->sock].revents & POLLIN) && aps_accept(aps))
    201 			return 1;
    202 		aps->pfds[aps->con->sock].revents = 0;
    203 		--re;
    204 	}
    205 
    206 	for (i = 0; re > 0 && i < LEN(aps->pfds); ++i) {
    207 		if (aps->pfds[i].revents) {
    208 			if (aps_handle(aps, i))
    209 				aps_drop(aps, i);
    210 			--re;
    211 		}
    212 	}
    213 
    214 	if (!aps_named(aps) && !aps->nfds)
    215 		aps->close = 1;
    216 
    217 	return 0;
    218 }
    219 
    220 int
    221 aps_read(struct aps *aps, int fd, char **command)
    222 {
    223 	ssize_t len;
    224 
    225 	len = bufread(aps->clients[fd].request, fd, 4096);
    226 	if (len <= 0)
    227 		return -1;
    228 	return bufgetline(aps->clients[fd].request, command) < 0;
    229 }
    230 
    231 int
    232 aps_command(struct aps *aps, int fd)
    233 {
    234 	char *(*com)(struct aps *, int, int, char **);
    235 	char *buf, **argv;
    236 	char *status;
    237 	unsigned int i;
    238 
    239 	buf = NULL;
    240 	if (aps_read(aps, fd, &buf))
    241 		return -1;
    242 	if (buf == NULL)
    243 		return 0;
    244 
    245 	aps_log(aps, fd, DEBUG, logsubjects[LogCommand], "raw '%s'", buf);
    246 
    247 	argv = split(buf, NULL);
    248 	if (argv == NULL) {
    249 		status = "Unable to split arguments";
    250 		goto exit;
    251 	}
    252 	if (*argv == NULL) {
    253 		status = "No command";
    254 		goto exit;
    255 	}
    256 
    257 	aps_log(aps, fd, INFO, logsubjects[LogCommand], "running '%s' command", *argv);
    258 
    259 	for (com = NULL, i = 0; com == NULL && i < aps->ncoms; ++i)
    260 		if (strcmp(*argv, aps->coms[i].name) == 0)
    261 			com = aps->coms[i].func;
    262 
    263 	if (com == NULL)
    264 		status = "Command not found";
    265 	else
    266 		status = com(aps, fd, arglen(argv + 1), argv + 1);
    267 
    268 exit:
    269 	free(argv);
    270 	free(buf);
    271 	return aps_bufstatus(aps, fd, status);
    272 }
    273 
    274 void
    275 aps_errordrop(struct aps *aps, int fd, char *why)
    276 {
    277 	/* One could send(2) the error aswell. */
    278 	aps_log(aps, fd, ERROR, logsubjects[LogConn], why);
    279 	aps_drop(aps, fd);
    280 }
    281 
    282 char *
    283 aps_seek(struct aps *aps, char *pattern)
    284 {
    285 	char *err;
    286 	struct item *item;
    287 	struct search search;
    288 
    289 	if (!aps->queue)
    290 		return "Empty queue";
    291 
    292 	if ((err = prepsearch(&search, aps->queue, pattern)))
    293 		return err;
    294 
    295 	item = findnext(&search);
    296 	if (item == aps->queue)
    297 		item = findnext(&search);
    298 	stopsearch(&search);
    299 
    300 	if (item == NULL)
    301 		return "No match";
    302 	queue_set(aps, item);
    303 	return NULL;
    304 }
    305 
    306 int
    307 aps_play(struct aps *aps, struct item *item)
    308 {
    309 	queue_set(aps, item);
    310 	return pplay(aps->player, item->path);
    311 }
    312 
    313 int
    314 aps_unname(struct aps *aps)
    315 {
    316 	if (unlink(aps->con->path) == 0)
    317 		return 0;
    318 	aps_log(aps, 0, WARN, NULL, "%s: unable to remove socket", aps->con->path);
    319 	return 1;
    320 }