From: Petr Štetiar Date: Thu, 12 Dec 2019 09:05:48 +0000 (+0100) Subject: refactor ubusd.c into reusable ubusd_library X-Git-Url: https://git.librecmc.org/?a=commitdiff_plain;h=c413be9b376c685e4a5b04b1d0d9d716dfbeb460;p=oweals%2Fubus.git refactor ubusd.c into reusable ubusd_library In order to allow reusability in unit testing & fuzzing. Signed-off-by: Petr Štetiar --- diff --git a/CMakeLists.txt b/CMakeLists.txt index 866b3ab..dc6e428 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -41,8 +41,9 @@ TARGET_LINK_LIBRARIES(ubus ${ubox_library}) find_library(json NAMES json-c json) -ADD_EXECUTABLE(ubusd ubusd.c ubusd_id.c ubusd_obj.c ubusd_proto.c ubusd_event.c ubusd_acl.c ubusd_monitor.c) -TARGET_LINK_LIBRARIES(ubusd ${ubox_library} ${blob_library} ${json}) +ADD_LIBRARY(ubusd_library STATIC ubusd.c ubusd_proto.c ubusd_id.c ubusd_obj.c ubusd_event.c ubusd_acl.c ubusd_monitor.c) +ADD_EXECUTABLE(ubusd ubusd_main.c) +TARGET_LINK_LIBRARIES(ubusd ubusd_library ${ubox_library} ${blob_library} ${json}) ADD_EXECUTABLE(cli cli.c) SET_TARGET_PROPERTIES(cli PROPERTIES OUTPUT_NAME ubus) diff --git a/libubus-internal.h b/libubus-internal.h index c4067d3..8cf99b3 100644 --- a/libubus-internal.h +++ b/libubus-internal.h @@ -18,6 +18,7 @@ extern struct blob_buf b; extern const struct ubus_method watch_method; struct blob_attr **ubus_parse_msg(struct blob_attr *msg); +bool ubus_validate_hdr(struct ubus_msghdr *hdr); void ubus_handle_data(struct uloop_fd *u, unsigned int events); int ubus_send_msg(struct ubus_context *ctx, uint32_t seq, struct blob_attr *msg, int cmd, uint32_t peer, int fd); diff --git a/libubus-io.c b/libubus-io.c index 120fb60..81c1cd1 100644 --- a/libubus-io.c +++ b/libubus-io.c @@ -215,7 +215,7 @@ static int recv_retry(struct ubus_context *ctx, struct iovec *iov, bool wait, in return total; } -static bool ubus_validate_hdr(struct ubus_msghdr *hdr) +bool ubus_validate_hdr(struct ubus_msghdr *hdr) { struct blob_attr *data = (struct blob_attr *) (hdr + 1); diff --git a/ubusd.c b/ubusd.c index c020ff4..0d43977 100644 --- a/ubusd.c +++ b/ubusd.c @@ -12,21 +12,9 @@ */ #include -#include -#include #ifdef FreeBSD #include #endif -#include -#include -#include -#include -#include - -#include -#include -#include -#include #include "ubusd.h" @@ -92,7 +80,7 @@ void ubus_msg_free(struct ubus_msg_buf *ub) } } -static ssize_t ubus_msg_writev(int fd, struct ubus_msg_buf *ub, size_t offset) +ssize_t ubus_msg_writev(int fd, struct ubus_msg_buf *ub, size_t offset) { static struct iovec iov[2]; static struct { @@ -177,258 +165,3 @@ void ubus_msg_send(struct ubus_client *cl, struct ubus_msg_buf *ub) } ubus_msg_enqueue(cl, ub); } - -static struct ubus_msg_buf *ubus_msg_head(struct ubus_client *cl) -{ - return cl->tx_queue[cl->txq_cur]; -} - -static void ubus_msg_dequeue(struct ubus_client *cl) -{ - struct ubus_msg_buf *ub = ubus_msg_head(cl); - - if (!ub) - return; - - ubus_msg_free(ub); - cl->txq_ofs = 0; - cl->tx_queue[cl->txq_cur] = NULL; - cl->txq_cur = (cl->txq_cur + 1) % ARRAY_SIZE(cl->tx_queue); -} - -static void handle_client_disconnect(struct ubus_client *cl) -{ - while (ubus_msg_head(cl)) - ubus_msg_dequeue(cl); - - ubusd_monitor_disconnect(cl); - ubusd_proto_free_client(cl); - if (cl->pending_msg_fd >= 0) - close(cl->pending_msg_fd); - uloop_fd_delete(&cl->sock); - close(cl->sock.fd); - free(cl); -} - -static void client_cb(struct uloop_fd *sock, unsigned int events) -{ - struct ubus_client *cl = container_of(sock, struct ubus_client, sock); - struct ubus_msg_buf *ub; - static struct iovec iov; - static struct { - int fd; - struct cmsghdr h; - } fd_buf = { - .h = { - .cmsg_type = SCM_RIGHTS, - .cmsg_level = SOL_SOCKET, - .cmsg_len = sizeof(fd_buf), - } - }; - struct msghdr msghdr = { - .msg_iov = &iov, - .msg_iovlen = 1, - }; - - /* first try to tx more pending data */ - while ((ub = ubus_msg_head(cl))) { - ssize_t written; - - written = ubus_msg_writev(sock->fd, ub, cl->txq_ofs); - if (written < 0) { - switch(errno) { - case EINTR: - case EAGAIN: - break; - default: - goto disconnect; - } - break; - } - - cl->txq_ofs += written; - if (cl->txq_ofs < ub->len + sizeof(ub->hdr)) - break; - - ubus_msg_dequeue(cl); - } - - /* prevent further ULOOP_WRITE events if we don't have data - * to send anymore */ - if (!ubus_msg_head(cl) && (events & ULOOP_WRITE)) - uloop_fd_add(sock, ULOOP_READ | ULOOP_EDGE_TRIGGER); - -retry: - if (!sock->eof && cl->pending_msg_offset < (int) sizeof(cl->hdrbuf)) { - int offset = cl->pending_msg_offset; - int bytes; - - fd_buf.fd = -1; - - iov.iov_base = ((char *) &cl->hdrbuf) + offset; - iov.iov_len = sizeof(cl->hdrbuf) - offset; - - if (cl->pending_msg_fd < 0) { - msghdr.msg_control = &fd_buf; - msghdr.msg_controllen = sizeof(fd_buf); - } else { - msghdr.msg_control = NULL; - msghdr.msg_controllen = 0; - } - - bytes = recvmsg(sock->fd, &msghdr, 0); - if (bytes < 0) - goto out; - - if (fd_buf.fd >= 0) - cl->pending_msg_fd = fd_buf.fd; - - cl->pending_msg_offset += bytes; - if (cl->pending_msg_offset < (int) sizeof(cl->hdrbuf)) - goto out; - - if (blob_pad_len(&cl->hdrbuf.data) > UBUS_MAX_MSGLEN) - goto disconnect; - - cl->pending_msg = ubus_msg_new(NULL, blob_raw_len(&cl->hdrbuf.data), false); - if (!cl->pending_msg) - goto disconnect; - - cl->hdrbuf.hdr.seq = be16_to_cpu(cl->hdrbuf.hdr.seq); - cl->hdrbuf.hdr.peer = be32_to_cpu(cl->hdrbuf.hdr.peer); - - memcpy(&cl->pending_msg->hdr, &cl->hdrbuf.hdr, sizeof(cl->hdrbuf.hdr)); - memcpy(cl->pending_msg->data, &cl->hdrbuf.data, sizeof(cl->hdrbuf.data)); - } - - ub = cl->pending_msg; - if (ub) { - int offset = cl->pending_msg_offset - sizeof(ub->hdr); - int len = blob_raw_len(ub->data) - offset; - int bytes = 0; - - if (len > 0) { - bytes = read(sock->fd, (char *) ub->data + offset, len); - if (bytes <= 0) - goto out; - } - - if (bytes < len) { - cl->pending_msg_offset += bytes; - goto out; - } - - /* accept message */ - ub->fd = cl->pending_msg_fd; - cl->pending_msg_fd = -1; - cl->pending_msg_offset = 0; - cl->pending_msg = NULL; - ubusd_monitor_message(cl, ub, false); - ubusd_proto_receive_message(cl, ub); - goto retry; - } - -out: - if (!sock->eof || ubus_msg_head(cl)) - return; - -disconnect: - handle_client_disconnect(cl); -} - -static bool get_next_connection(int fd) -{ - struct ubus_client *cl; - int client_fd; - - client_fd = accept(fd, NULL, 0); - if (client_fd < 0) { - switch (errno) { - case ECONNABORTED: - case EINTR: - return true; - default: - return false; - } - } - - cl = ubusd_proto_new_client(client_fd, client_cb); - if (cl) - uloop_fd_add(&cl->sock, ULOOP_READ | ULOOP_EDGE_TRIGGER); - else - close(client_fd); - - return true; -} - -static void server_cb(struct uloop_fd *fd, unsigned int events) -{ - bool next; - - do { - next = get_next_connection(fd->fd); - } while (next); -} - -static struct uloop_fd server_fd = { - .cb = server_cb, -}; - -static int usage(const char *progname) -{ - fprintf(stderr, "Usage: %s []\n" - "Options: \n" - " -A : Set the path to ACL files\n" - " -s : Set the unix domain socket to listen on\n" - "\n", progname); - return 1; -} - -static void sighup_handler(int sig) -{ - ubusd_acl_load(); -} - -int main(int argc, char **argv) -{ - const char *ubus_socket = UBUS_UNIX_SOCKET; - int ret = 0; - int ch; - - signal(SIGPIPE, SIG_IGN); - signal(SIGHUP, sighup_handler); - - openlog("ubusd", LOG_PID, LOG_DAEMON); - uloop_init(); - - while ((ch = getopt(argc, argv, "A:s:")) != -1) { - switch (ch) { - case 's': - ubus_socket = optarg; - break; - case 'A': - ubusd_acl_dir = optarg; - break; - default: - return usage(argv[0]); - } - } - - unlink(ubus_socket); - umask(0111); - server_fd.fd = usock(USOCK_UNIX | USOCK_SERVER | USOCK_NONBLOCK, ubus_socket, NULL); - if (server_fd.fd < 0) { - perror("usock"); - ret = -1; - goto out; - } - uloop_fd_add(&server_fd, ULOOP_READ | ULOOP_EDGE_TRIGGER); - ubusd_acl_load(); - - uloop_run(); - unlink(ubus_socket); - -out: - uloop_done(); - return ret; -} diff --git a/ubusd.h b/ubusd.h index 4d87920..867cde9 100644 --- a/ubusd.h +++ b/ubusd.h @@ -70,6 +70,7 @@ extern const char *ubusd_acl_dir; struct ubus_msg_buf *ubus_msg_new(void *data, int len, bool shared); void ubus_msg_send(struct ubus_client *cl, struct ubus_msg_buf *ub); +ssize_t ubus_msg_writev(int fd, struct ubus_msg_buf *ub, size_t offset); void ubus_msg_free(struct ubus_msg_buf *ub); struct blob_attr **ubus_parse_msg(struct blob_attr *msg); diff --git a/ubusd_main.c b/ubusd_main.c new file mode 100644 index 0000000..81868c1 --- /dev/null +++ b/ubusd_main.c @@ -0,0 +1,271 @@ +/* + * Copyright (C) 2011-2014 Felix Fietkau + * + * SPDX-License-Identifier: LGPL-2.1-only + */ + +#include +#include +#ifdef FreeBSD +#include +#endif +#include + +#include + +#include "ubusd.h" + +static struct ubus_msg_buf *ubus_msg_head(struct ubus_client *cl) +{ + return cl->tx_queue[cl->txq_cur]; +} + +static void ubus_msg_dequeue(struct ubus_client *cl) +{ + struct ubus_msg_buf *ub = ubus_msg_head(cl); + + if (!ub) + return; + + ubus_msg_free(ub); + cl->txq_ofs = 0; + cl->tx_queue[cl->txq_cur] = NULL; + cl->txq_cur = (cl->txq_cur + 1) % ARRAY_SIZE(cl->tx_queue); +} + +static void handle_client_disconnect(struct ubus_client *cl) +{ + while (ubus_msg_head(cl)) + ubus_msg_dequeue(cl); + + ubusd_monitor_disconnect(cl); + ubusd_proto_free_client(cl); + if (cl->pending_msg_fd >= 0) + close(cl->pending_msg_fd); + uloop_fd_delete(&cl->sock); + close(cl->sock.fd); + free(cl); +} + +static void client_cb(struct uloop_fd *sock, unsigned int events) +{ + struct ubus_client *cl = container_of(sock, struct ubus_client, sock); + struct ubus_msg_buf *ub; + static struct iovec iov; + static struct { + int fd; + struct cmsghdr h; + } fd_buf = { + .h = { + .cmsg_type = SCM_RIGHTS, + .cmsg_level = SOL_SOCKET, + .cmsg_len = sizeof(fd_buf), + } + }; + struct msghdr msghdr = { + .msg_iov = &iov, + .msg_iovlen = 1, + }; + + /* first try to tx more pending data */ + while ((ub = ubus_msg_head(cl))) { + ssize_t written; + + written = ubus_msg_writev(sock->fd, ub, cl->txq_ofs); + if (written < 0) { + switch(errno) { + case EINTR: + case EAGAIN: + break; + default: + goto disconnect; + } + break; + } + + cl->txq_ofs += written; + if (cl->txq_ofs < ub->len + sizeof(ub->hdr)) + break; + + ubus_msg_dequeue(cl); + } + + /* prevent further ULOOP_WRITE events if we don't have data + * to send anymore */ + if (!ubus_msg_head(cl) && (events & ULOOP_WRITE)) + uloop_fd_add(sock, ULOOP_READ | ULOOP_EDGE_TRIGGER); + +retry: + if (!sock->eof && cl->pending_msg_offset < (int) sizeof(cl->hdrbuf)) { + int offset = cl->pending_msg_offset; + int bytes; + + fd_buf.fd = -1; + + iov.iov_base = ((char *) &cl->hdrbuf) + offset; + iov.iov_len = sizeof(cl->hdrbuf) - offset; + + if (cl->pending_msg_fd < 0) { + msghdr.msg_control = &fd_buf; + msghdr.msg_controllen = sizeof(fd_buf); + } else { + msghdr.msg_control = NULL; + msghdr.msg_controllen = 0; + } + + bytes = recvmsg(sock->fd, &msghdr, 0); + if (bytes < 0) + goto out; + + if (fd_buf.fd >= 0) + cl->pending_msg_fd = fd_buf.fd; + + cl->pending_msg_offset += bytes; + if (cl->pending_msg_offset < (int) sizeof(cl->hdrbuf)) + goto out; + + if (blob_pad_len(&cl->hdrbuf.data) > UBUS_MAX_MSGLEN) + goto disconnect; + + cl->pending_msg = ubus_msg_new(NULL, blob_raw_len(&cl->hdrbuf.data), false); + if (!cl->pending_msg) + goto disconnect; + + cl->hdrbuf.hdr.seq = be16_to_cpu(cl->hdrbuf.hdr.seq); + cl->hdrbuf.hdr.peer = be32_to_cpu(cl->hdrbuf.hdr.peer); + + memcpy(&cl->pending_msg->hdr, &cl->hdrbuf.hdr, sizeof(cl->hdrbuf.hdr)); + memcpy(cl->pending_msg->data, &cl->hdrbuf.data, sizeof(cl->hdrbuf.data)); + } + + ub = cl->pending_msg; + if (ub) { + int offset = cl->pending_msg_offset - sizeof(ub->hdr); + int len = blob_raw_len(ub->data) - offset; + int bytes = 0; + + if (len > 0) { + bytes = read(sock->fd, (char *) ub->data + offset, len); + if (bytes <= 0) + goto out; + } + + if (bytes < len) { + cl->pending_msg_offset += bytes; + goto out; + } + + /* accept message */ + ub->fd = cl->pending_msg_fd; + cl->pending_msg_fd = -1; + cl->pending_msg_offset = 0; + cl->pending_msg = NULL; + ubusd_monitor_message(cl, ub, false); + ubusd_proto_receive_message(cl, ub); + goto retry; + } + +out: + if (!sock->eof || ubus_msg_head(cl)) + return; + +disconnect: + handle_client_disconnect(cl); +} + +static bool get_next_connection(int fd) +{ + struct ubus_client *cl; + int client_fd; + + client_fd = accept(fd, NULL, 0); + if (client_fd < 0) { + switch (errno) { + case ECONNABORTED: + case EINTR: + return true; + default: + return false; + } + } + + cl = ubusd_proto_new_client(client_fd, client_cb); + if (cl) + uloop_fd_add(&cl->sock, ULOOP_READ | ULOOP_EDGE_TRIGGER); + else + close(client_fd); + + return true; +} + +static void server_cb(struct uloop_fd *fd, unsigned int events) +{ + bool next; + + do { + next = get_next_connection(fd->fd); + } while (next); +} + +static struct uloop_fd server_fd = { + .cb = server_cb, +}; + +static int usage(const char *progname) +{ + fprintf(stderr, "Usage: %s []\n" + "Options: \n" + " -A : Set the path to ACL files\n" + " -s : Set the unix domain socket to listen on\n" + "\n", progname); + return 1; +} + +static void sighup_handler(int sig) +{ + ubusd_acl_load(); +} + +int main(int argc, char **argv) +{ + const char *ubus_socket = UBUS_UNIX_SOCKET; + int ret = 0; + int ch; + + signal(SIGPIPE, SIG_IGN); + signal(SIGHUP, sighup_handler); + + openlog("ubusd", LOG_PID, LOG_DAEMON); + uloop_init(); + + while ((ch = getopt(argc, argv, "A:s:")) != -1) { + switch (ch) { + case 's': + ubus_socket = optarg; + break; + case 'A': + ubusd_acl_dir = optarg; + break; + default: + return usage(argv[0]); + } + } + + unlink(ubus_socket); + umask(0111); + server_fd.fd = usock(USOCK_UNIX | USOCK_SERVER | USOCK_NONBLOCK, ubus_socket, NULL); + if (server_fd.fd < 0) { + perror("usock"); + ret = -1; + goto out; + } + uloop_fd_add(&server_fd, ULOOP_READ | ULOOP_EDGE_TRIGGER); + ubusd_acl_load(); + + uloop_run(); + unlink(ubus_socket); + +out: + uloop_done(); + return ret; +}