1 /* 2 * Copyright (C) 2011-2014 Felix Fietkau <nbd@openwrt.org> 3 * 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 7 * 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. 12 */ 13 14 #include <sys/socket.h> 15 #ifdef FreeBSD 16 #include <sys/param.h> 17 #endif 18 19 #include "ubusd.h" 20 21 #define USES_EXTERNAL_BUFFER ~0U 22 23 static struct ubus_msg_buf *ubus_msg_ref(struct ubus_msg_buf *ub) 24 { 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); 28 if (!new_ub) 29 return NULL; 30 memcpy(&new_ub->hdr, &ub->hdr, sizeof(struct ubus_msghdr)); 31 /* 32 * Transfer fd ownership to the copy: the external message is 33 * freed right after being queued, which would close the fd 34 * before the queued copy has sent it. 35 */ 36 new_ub->fd = ub->fd; 37 ub->fd = -1; 38 return new_ub; 39 } 40 41 ub->refcount++; 42 return ub; 43 } 44 45 struct ubus_msg_buf *ubus_msg_new(void *data, int len, bool shared) 46 { 47 struct ubus_msg_buf *ub; 48 int buflen = sizeof(*ub); 49 50 if (!shared) 51 buflen += len; 52 53 ub = calloc(1, buflen); 54 if (!ub) 55 return NULL; 56 57 ub->fd = -1; 58 59 if (shared) { 60 ub->refcount = USES_EXTERNAL_BUFFER; 61 ub->data = data; 62 } else { 63 ub->refcount = 1; 64 ub->data = (void *) (ub + 1); 65 if (data) 66 memcpy(ub + 1, data, len); 67 } 68 69 ub->len = len; 70 return ub; 71 } 72 73 void ubus_msg_free(struct ubus_msg_buf *ub) 74 { 75 switch (ub->refcount) { 76 case 1: 77 case USES_EXTERNAL_BUFFER: 78 if (ub->fd >= 0) 79 close(ub->fd); 80 81 free(ub); 82 break; 83 default: 84 ub->refcount--; 85 break; 86 } 87 } 88 89 ssize_t ubus_msg_writev(int fd, struct ubus_msg_buf *ub, size_t offset) 90 { 91 uint8_t fd_buf[CMSG_SPACE(sizeof(int))] = { 0 }; 92 static struct iovec iov[2]; 93 struct msghdr msghdr = { 0 }; 94 struct ubus_msghdr hdr; 95 struct cmsghdr *cmsg; 96 ssize_t ret; 97 int *pfd; 98 99 msghdr.msg_iov = iov; 100 msghdr.msg_iovlen = ARRAY_SIZE(iov); 101 msghdr.msg_control = fd_buf; 102 msghdr.msg_controllen = sizeof(fd_buf); 103 104 cmsg = CMSG_FIRSTHDR(&msghdr); 105 cmsg->cmsg_type = SCM_RIGHTS; 106 cmsg->cmsg_level = SOL_SOCKET; 107 cmsg->cmsg_len = CMSG_LEN(sizeof(int)); 108 109 pfd = (int *) CMSG_DATA(cmsg); 110 msghdr.msg_controllen = cmsg->cmsg_len; 111 112 *pfd = ub->fd; 113 if (ub->fd < 0 || offset) { 114 msghdr.msg_control = NULL; 115 msghdr.msg_controllen = 0; 116 } 117 118 if (offset < sizeof(ub->hdr)) { 119 hdr.version = ub->hdr.version; 120 hdr.type = ub->hdr.type; 121 hdr.seq = cpu_to_be16(ub->hdr.seq); 122 hdr.peer = cpu_to_be32(ub->hdr.peer); 123 124 iov[0].iov_base = ((char *) &hdr) + offset; 125 iov[0].iov_len = sizeof(hdr) - offset; 126 iov[1].iov_base = (char *) ub->data; 127 iov[1].iov_len = ub->len; 128 } else { 129 offset -= sizeof(ub->hdr); 130 iov[0].iov_base = ((char *) ub->data) + offset; 131 iov[0].iov_len = ub->len - offset; 132 msghdr.msg_iovlen = 1; 133 } 134 135 do { 136 ret = sendmsg(fd, &msghdr, 0); 137 } while (ret < 0 && errno == EINTR); 138 139 return ret; 140 } 141 142 void ubus_msg_list_free(struct ubus_msg_buf_list *ubl) 143 { 144 list_del_init(&ubl->list); 145 ubus_msg_free(ubl->msg); 146 free(ubl); 147 } 148 149 static bool ubus_msg_enqueue(struct ubus_client *cl, struct ubus_msg_buf *ub) 150 { 151 struct ubus_msg_buf_list *ubl; 152 153 if (cl->txq_len + sizeof(ub->hdr) + ub->len > UBUS_CLIENT_MAX_TXQ_LEN) 154 return false; 155 156 ubl = calloc(1, sizeof(struct ubus_msg_buf_list)); 157 if (!ubl) 158 return false; 159 160 INIT_LIST_HEAD(&ubl->list); 161 ubl->msg = ubus_msg_ref(ub); 162 if (!ubl->msg) { 163 free(ubl); 164 return false; 165 } 166 167 list_add_tail(&ubl->list, &cl->tx_queue); 168 cl->txq_len += ub->len + sizeof(ub->hdr); 169 return true; 170 } 171 172 /* takes the msgbuf reference */ 173 void ubus_msg_send(struct ubus_client *cl, struct ubus_msg_buf *ub) 174 { 175 ssize_t written; 176 bool partial = false; 177 178 /* 179 * Once a message has been left half-written the stream framing is 180 * broken and the client is flagged for disconnect, which only happens 181 * when control returns to the event loop. Until then the tx queue is 182 * empty again, so a further send would take the direct-write branch 183 * below and splice a fresh message onto the abandoned one, leaving the 184 * peer to parse garbage. Drop anything queued for a dying client. 185 */ 186 if (cl->sock.eof) 187 return; 188 189 if (ub->hdr.type != UBUS_MSG_MONITOR) 190 ubusd_monitor_message(cl, ub, true); 191 192 if (list_empty(&cl->tx_queue)) { 193 written = ubus_msg_writev(cl->sock.fd, ub, 0); 194 195 if (written < 0) 196 written = 0; 197 198 if (written >= (ssize_t) (ub->len + sizeof(ub->hdr))) 199 return; 200 201 partial = written > 0; 202 cl->txq_ofs = written; 203 cl->txq_len = -written; 204 205 /* get an event once we can write to the socket again */ 206 uloop_fd_add(&cl->sock, ULOOP_READ | ULOOP_WRITE | ULOOP_EDGE_TRIGGER); 207 } 208 209 if (ubus_msg_enqueue(cl, ub)) 210 return; 211 212 /* 213 * Dropping a whole message keeps the stream framing intact, but a 214 * message that is already partially on the wire cannot be completed 215 * anymore; disconnect the client on the next event. 216 */ 217 if (partial) 218 cl->sock.eof = true; 219 } 220
This page was automatically generated by LXR 0.3.1. • OpenWrt