2 * Copyright (C) 2011-2014 Felix Fietkau <nbd@openwrt.org>
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU Lesser General Public License version 2.1
6 * as published by the Free Software Foundation
8 * This program is distributed in the hope that it will be useful,
9 * but WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 * GNU General Public License for more details.
14 #include <sys/socket.h>
16 #include <sys/param.h>
21 #define USES_EXTERNAL_BUFFER ~0U
23 static struct ubus_msg_buf *ubus_msg_ref(struct ubus_msg_buf *ub)
25 struct ubus_msg_buf *new_ub;
26 if (ub->refcount == USES_EXTERNAL_BUFFER) {
27 new_ub = ubus_msg_new(ub->data, ub->len, false);
30 memcpy(&new_ub->hdr, &ub->hdr, sizeof(struct ubus_msghdr));
39 struct ubus_msg_buf *ubus_msg_new(void *data, int len, bool shared)
41 struct ubus_msg_buf *ub;
42 int buflen = sizeof(*ub);
47 ub = calloc(1, buflen);
54 ub->refcount = USES_EXTERNAL_BUFFER;
58 ub->data = (void *) (ub + 1);
60 memcpy(ub + 1, data, len);
67 void ubus_msg_free(struct ubus_msg_buf *ub)
69 switch (ub->refcount) {
71 case USES_EXTERNAL_BUFFER:
83 ssize_t ubus_msg_writev(int fd, struct ubus_msg_buf *ub, size_t offset)
85 uint8_t fd_buf[CMSG_SPACE(sizeof(int))] = { 0 };
86 static struct iovec iov[2];
87 struct msghdr msghdr = { 0 };
88 struct ubus_msghdr hdr;
94 msghdr.msg_iovlen = ARRAY_SIZE(iov);
95 msghdr.msg_control = fd_buf;
96 msghdr.msg_controllen = sizeof(fd_buf);
98 cmsg = CMSG_FIRSTHDR(&msghdr);
99 cmsg->cmsg_type = SCM_RIGHTS;
100 cmsg->cmsg_level = SOL_SOCKET;
101 cmsg->cmsg_len = CMSG_LEN(sizeof(int));
103 pfd = (int *) CMSG_DATA(cmsg);
104 msghdr.msg_controllen = cmsg->cmsg_len;
107 if (ub->fd < 0 || offset) {
108 msghdr.msg_control = NULL;
109 msghdr.msg_controllen = 0;
112 if (offset < sizeof(ub->hdr)) {
113 hdr.version = ub->hdr.version;
114 hdr.type = ub->hdr.type;
115 hdr.seq = cpu_to_be16(ub->hdr.seq);
116 hdr.peer = cpu_to_be32(ub->hdr.peer);
118 iov[0].iov_base = ((char *) &hdr) + offset;
119 iov[0].iov_len = sizeof(hdr) - offset;
120 iov[1].iov_base = (char *) ub->data;
121 iov[1].iov_len = ub->len;
123 offset -= sizeof(ub->hdr);
124 iov[0].iov_base = ((char *) ub->data) + offset;
125 iov[0].iov_len = ub->len - offset;
126 msghdr.msg_iovlen = 1;
130 ret = sendmsg(fd, &msghdr, 0);
131 } while (ret < 0 && errno == EINTR);
136 static void ubus_msg_enqueue(struct ubus_client *cl, struct ubus_msg_buf *ub)
138 if (cl->tx_queue[cl->txq_tail])
141 cl->tx_queue[cl->txq_tail] = ubus_msg_ref(ub);
142 cl->txq_tail = (cl->txq_tail + 1) % ARRAY_SIZE(cl->tx_queue);
145 /* takes the msgbuf reference */
146 void ubus_msg_send(struct ubus_client *cl, struct ubus_msg_buf *ub)
150 if (ub->hdr.type != UBUS_MSG_MONITOR)
151 ubusd_monitor_message(cl, ub, true);
153 if (!cl->tx_queue[cl->txq_cur]) {
154 written = ubus_msg_writev(cl->sock.fd, ub, 0);
159 if (written >= (ssize_t) (ub->len + sizeof(ub->hdr)))
162 cl->txq_ofs = written;
164 /* get an event once we can write to the socket again */
165 uloop_fd_add(&cl->sock, ULOOP_READ | ULOOP_WRITE | ULOOP_EDGE_TRIGGER);
167 ubus_msg_enqueue(cl, ub);