commit 494b7acad7b09b0dadcc5a7241920e1f9a41916a
parent e16156d22e3a0acecb586ebc69c3375a821216b8
Author: Jacob R. Edwards <jacobouno@protonmail.com>
Date: Tue, 29 Jun 2021 22:04:15 -0700
Use common buffer code for request and response structures
Diffstat:
9 files changed, 185 insertions(+), 127 deletions(-)
diff --git a/aps/aps.c b/aps/aps.c
@@ -64,6 +64,16 @@ sigchld(int sig)
}
int
+aps_drop(struct aps *aps, int fd)
+{
+ --aps->nfds;
+ aps->pfds[fd].fd = -1;
+ respfree(aps, fd);
+ buffree(aps->clients[fd].request);
+ return sclose(fd);
+}
+
+int
aps_accept(struct aps *aps)
{
int s;
@@ -78,23 +88,17 @@ aps_accept(struct aps *aps)
return r;
while ((s = accept4(aps->con.s, NULL, NULL, 0)) >= 0) {
- aps->pfds[s].fd = s;
++aps->nfds;
+ aps->pfds[s].fd = s;
+ if ((aps->clients[s].request = bufnew()) == NULL ||
+ respnew(aps, s))
+ aps_drop(aps, s);
}
return errno != EWOULDBLOCK;
}
int
-aps_drop(struct aps *aps, int fd)
-{
- aps->pfds[fd].fd = -1;
- resp_free(aps, fd);
- --aps->nfds;
- return sclose(fd);
-}
-
-int
aps_handle(struct aps *aps, int fd)
{
if (aps->pfds[fd].revents & (POLLERR | POLLHUP | POLLNVAL)) {
@@ -103,7 +107,7 @@ aps_handle(struct aps *aps, int fd)
if (aps_command(aps, fd))
return 1;
} else if (aps->pfds[fd].revents & POLLOUT) {
- if (aps->clients[fd].response.lock && resp_send(aps, fd))
+ if (aps->clients[fd].resp->lock && respond(aps, fd))
return 1;
} else {
die("unhandled poll revent");
diff --git a/aps/aps.h b/aps/aps.h
@@ -1,5 +1,8 @@
#include <ap.h>
+struct aps;
+
+#include "buf.h"
#include "player.h"
#include "queue.h"
#include "response.h"
@@ -12,11 +15,8 @@ struct aps {
int playing;
struct apcon con;
struct client {
- struct request {
- char buf[AP_LINE_MAX];
- int len;
- } request;
- struct response response;
+ struct buf *request;
+ struct response *resp;
} clients[OPEN_MAX];
struct player player;
struct pollfd pfds[OPEN_MAX];
diff --git a/aps/buf.c b/aps/buf.c
@@ -0,0 +1,67 @@
+/* Copyright 2021 Jacob R. Edwards
+ *
+ * ap -- audio player
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ */
+
+#include <errno.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "buf.h"
+#include "util.h"
+
+struct buf *
+bufnew(void)
+{
+ return calloc(1, sizeof(struct buf));
+}
+
+void
+buffree(struct buf *buf)
+{
+ if (buf == NULL)
+ return;
+ free(buf->data);
+ free(buf);
+}
+
+int
+bufresize(struct buf *buf, unsigned int newsize)
+{
+ char *tmp;
+
+ if (buf == NULL || newsize < buf->len)
+ ERET(EINVAL);
+
+ tmp = realloc(buf->data, newsize);
+ if (tmp == NULL)
+ return 1;
+
+ buf->data = tmp;
+ buf->size = newsize;
+ return 0;
+}
+
+int
+bufappend(struct buf *buf, void *data, unsigned int len)
+{
+ if ((buf == NULL || buf->size - buf->len < len) &&
+ bufresize(buf, (buf->size * 2) + len) != 0)
+ return 1;
+ memcpy(buf->data + buf->len, data, len);
+ buf->len += len;
+ return 0;
+}
diff --git a/aps/buf.h b/aps/buf.h
@@ -0,0 +1,10 @@
+struct buf {
+ unsigned int len;
+ unsigned int size;
+ void *data;
+};
+
+struct buf *bufnew(void);
+void buffree(struct buf *);
+int bufresize(struct buf *, unsigned int);
+int bufappend(struct buf *, void *, unsigned int);
diff --git a/aps/command.c b/aps/command.c
@@ -35,10 +35,9 @@
static char *earg = "Invalid number of arguments";
static char *enotfound = "Not found";
static char *eposition = "No position in queue";
-static char *etoofew = "Too few arguments";
int
-resp_finish(struct aps *aps, int s, char *error)
+finish(struct aps *aps, int s, char *error)
{
char buf[AP_LINE_MAX];
int len;
@@ -52,14 +51,15 @@ resp_finish(struct aps *aps, int s, char *error)
if (len >= sizeof(buf))
ERET(EMSGSIZE);
- if (resp_add(aps, s, buf, len))
+ if (bufappend(aps->clients[s].resp->buf, buf, len))
return 1;
- resp_end(aps, s);
+
+ aps->clients[s].resp->lock = 1;
return 0;
}
int
-resp_additem(struct aps *aps, int s, char *item)
+additem(struct aps *aps, int s, char *item)
{
char buf[AP_LINE_MAX];
int n;
@@ -67,7 +67,7 @@ resp_additem(struct aps *aps, int s, char *item)
n = snprintf(buf, sizeof(buf), "%s\n", item);
if (n >= sizeof(buf))
ERET(EMSGSIZE);
- return resp_add(aps, s, buf, n);
+ return bufappend(aps->clients[s].resp->buf, buf, n);
}
int
@@ -132,7 +132,7 @@ aps_list(struct aps *aps, int s, int argc, char **argv)
item = aps->queue.head;
while ((item = nextmatch(item_next, item, argv + 1, argc - 1, 1))) {
- if (resp_additem(aps, s, item->path))
+ if (additem(aps, s, item->path))
return strerror(errno);
item = item->next;
}
@@ -164,7 +164,6 @@ findpat(struct item *(*incr)(struct item *), struct item *item,
struct item *
find(struct aps *aps, int argc, char **argv, char **err)
{
- char *e;
struct item *(*incr)(struct item *);
struct item *item;
@@ -267,7 +266,7 @@ aps_name(struct aps *aps, int s, int argc, char **argv)
if (item == NULL)
return err;
- if (resp_additem(aps, s, item->path))
+ if (additem(aps, s, item->path))
return strerror(errno);
return NULL;
}
@@ -292,13 +291,40 @@ aps_next(struct aps *aps, int s, int argc, char **argv)
return aps_play(aps, -1, aps->nextlen, aps->next);
}
+char *
+aps_read(struct aps *aps, int s)
+{
+ char *end;
+ int len;
+ struct buf *buf;
+
+ buf = aps->clients[s].request;
+ if (buf->len == buf->size && bufresize(buf, MAX(buf->size, 128) * 2)) {
+ buf->len = 0;
+ /* TODO: handle error */
+ finish(aps, s, strerror(errno));
+ return NULL;
+ }
+
+ len = recv(s, buf->data + buf->len, buf->size - buf->len, 0);
+ if (len == -1)
+ return NULL;
+ buf->len += len;
+
+ end = memchr(buf->data + (buf->len - len), '\n', len);
+ if (end == NULL)
+ return NULL; /* not a full command yet */
+ *end = 0;
+
+ return end + 1;
+}
+
int
aps_command(struct aps *aps, int s)
{
- char *end;
- char buf[AP_LINE_MAX];
- int argc;
- struct client *c;
+ char *next;
+ char bufbuf[AP_LINE_MAX];
+ struct buf *buf;
char *argv[AP_ARGV_MAX];
int i;
int len;
@@ -318,38 +344,27 @@ aps_command(struct aps *aps, int s)
{ "toggle", aps_toggle }
};
- c = &aps->clients[s];
- if (c->request.len == sizeof(c->request.buf)) {
- c->request.len = 0;
- return resp_finish(aps, s, strerror(EMSGSIZE));
- }
+ if ((next = aps_read(aps, s)) == NULL)
+ return 0;
- len = recv(s, c->request.buf + c->request.len, LEN(c->request.buf) - c->request.len, 0);
- if (len == -1)
- return 1;
-
- end = memchr(c->request.buf + c->request.len, '\n', len);
- c->request.len += len;
- if (end == NULL)
- return 0; /* not a full command yet */
+ buf = aps->clients[s].request;
+ len = next - (char *)buf->data;
+ memcpy(bufbuf, buf->data, len);
+ memmove(buf->data, buf->data + len, buf->len - len);
+ buf->len -= len;
- *end = 0;
- len = end - c->request.buf + 1;
- memcpy(buf, c->request.buf, len);
- memmove(c->request.buf, c->request.buf + len, c->request.len - len);
- c->request.len -= len;
-
- argc = split(argv, LEN(argv), buf, AP_ARG_SEPS);
- if (!argc)
- return resp_finish(aps, s, "No command");
- if (argc < 0)
- return resp_finish(aps, s, strerror(E2BIG));
+ len = split(argv, LEN(argv), bufbuf, AP_ARG_SEPS);
+ if (len == 0)
+ return finish(aps, s, "No command");
+ if (len < 0)
+ return finish(aps, s, earg);
for (i = 0; i < LEN(commands); ++i) {
- if (strcmp(*argv, commands[i].name) == 0)
- return resp_finish(aps, s,
- commands[i].func(aps, s, argc, argv));
+ if (strcmp(*argv, commands[i].name) == 0) {
+ return finish(aps, s,
+ commands[i].func(aps, s, len, argv));
+ }
}
- return resp_finish(aps, s, "Command not found");
+ return finish(aps, s, "Command not found");
}
diff --git a/aps/mkfile b/aps/mkfile
@@ -1,7 +1,7 @@
# Copyright 2021 Jacob R. Edwards
name = aps
-src = aps.c command.c player.c queue.c response.c split.c
+src = aps.c buf.c command.c player.c queue.c response.c split.c
obj = ${src:%.c=%.o}
cppflags = -I../lib
diff --git a/aps/response.c b/aps/response.c
@@ -21,91 +21,55 @@
#include <errno.h>
#include <limits.h>
#include <poll.h>
-#include <stdio.h>
#include <stdlib.h>
-#include <string.h>
#include "aps.h"
#include "util.h"
+#define RESP (aps->clients[fd].resp)
+
int
-resp_enlarge(struct aps *aps, int fd, unsigned int more)
+respnew(struct aps *aps, int fd)
{
- struct response *resp;
- unsigned int newsiz;
- void *tmp;
-
- resp = &aps->clients[fd].response;
- if (resp->len + more < resp->len)
- ERET(EOVERFLOW);
-
- newsiz = (resp->siz ? resp->siz * 2 : BUFSIZ);
- while (newsiz < resp->len + more) {
- if (newsiz *= 2 < resp->siz)
- ERET(EOVERFLOW);
- }
-
- tmp = realloc(resp->buf, newsiz);
- if (tmp == NULL) {
- resp_free(aps, fd);
+ if ((RESP = calloc(1, sizeof(struct response))) == NULL)
+ return 1;
+ if ((RESP->buf = bufnew()) == NULL) {
+ free(RESP);
return 1;
}
-
- resp->buf = tmp;
- resp->siz = newsiz;
return 0;
}
-int
-resp_add(struct aps *aps, int fd, void *data, unsigned int len)
+void
+respfree(struct aps *aps, int fd)
{
- struct response *resp;
-
- resp = &aps->clients[fd].response;
- if (resp->len + len > resp->siz && resp_enlarge(aps, fd, len))
- return 1;
-
- memcpy(resp->buf + resp->len, data, len);
- resp->len += len;
- return 0;
+ if (RESP == NULL)
+ return;
+ buffree(RESP->buf);
+ free(RESP);
+ RESP = NULL;
}
int
-resp_send(struct aps *aps, int fd)
+respond(struct aps *aps, int fd)
{
ssize_t n;
struct response *resp;
- resp = &aps->clients[fd].response;
- if (!resp->lock || resp->len - resp->sent == 0)
+ resp = RESP;
+ if (!resp->lock || resp->buf->len - resp->sent == 0)
return 1;
-
- n = send(fd, resp->buf + resp->sent, resp->len - resp->sent,
+ n = send(fd, resp->buf->data + resp->sent, resp->buf->len - resp->sent,
MSG_NOSIGNAL);
- switch (n) {
- case -1:
+
+ if (n < 0)
return 1;
- case 0:
+ if (n == 0)
ERET(ECONNRESET);
- default:
- if (resp->sent += n == resp->len)
- resp_free(aps, fd);
- return 0;
+ if (resp->sent += n == resp->buf->len) {
+ resp->buf->len = 0;
+ resp->sent = 0;
+ resp->lock = 0;
}
-}
-
-void
-resp_end(struct aps *aps, int fd)
-{
- aps->clients[fd].response.lock = 1;
-}
-
-void
-resp_free(struct aps *aps, int fd)
-{
- struct response *resp;
-
- resp = &aps->clients[fd].response;
- free(resp->buf);
- memset(resp, 0, sizeof(*resp));
+ return 0;
}
diff --git a/aps/response.h b/aps/response.h
@@ -1,12 +1,9 @@
struct response {
int lock;
- unsigned int len;
+ struct buf *buf;
unsigned int sent;
- unsigned int siz;
- void *buf;
};
-int resp_add(struct aps *, int, void *, unsigned int);
-int resp_send(struct aps *, int);
-void resp_end(struct aps *, int);
-void resp_free(struct aps *, int);
+int respnew(struct aps *, int);
+void respfree(struct aps *, int);
+int respond(struct aps *, int);
diff --git a/aps/util.h b/aps/util.h
@@ -1,3 +1,4 @@
#define ERET(E) { errno = E; return 1; }
#define ERROR(S) (write(2, S, sizeof(S) - 1) != sizeof(S) - 1)
#define LEN(X) (sizeof(X) / sizeof(*X))
+#define MAX(A,B) (A > B ? A : B)