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 new_ub->fd = ub->fd; 32 return new_ub; 33 } 34 35 ub->refcount++; 36 return ub; 37 } 38 39 struct ubus_msg_buf *ubus_msg_new(void *data, int len, bool shared) 40 { 41 struct ubus_msg_buf *ub; 42 int buflen = sizeof(*ub); 43 44 if (!shared) 45 buflen += len; 46 47 ub = calloc(1, buflen); 48 if (!ub) 49 return NULL; 50 51 ub->fd = -1; 52 53 if (shared) { 54 ub->refcount = USES_EXTERNAL_BUFFER; 55 ub->data = data; 56 } else { 57 ub->refcount = 1; 58 ub->data = (void *) (ub + 1); 59 if (data) 60 memcpy(ub + 1, data, len); 61 } 62 63 ub->len = len; 64 return ub; 65 } 66 67 void ubus_msg_free(struct ubus_msg_buf *ub) 68 { 69 switch (ub->refcount) { 70 case 1: 71 case USES_EXTERNAL_BUFFER: 72 if (ub->fd >= 0) 73 close(ub->fd); 74 75 free(ub); 76 break; 77 default: 78 ub->refcount--; 79 break; 80 } 81 } 82 83 ssize_t ubus_msg_writev(int fd, struct ubus_msg_buf *ub, size_t offset) 84 { 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; 89 struct cmsghdr *cmsg; 90 ssize_t ret; 91 int *pfd; 92 93 msghdr.msg_iov = iov; 94 msghdr.msg_iovlen = ARRAY_SIZE(iov); 95 msghdr.msg_control = fd_buf; 96 msghdr.msg_controllen = sizeof(fd_buf); 97 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)); 102 103 pfd = (int *) CMSG_DATA(cmsg); 104 msghdr.msg_controllen = cmsg->cmsg_len; 105 106 *pfd = ub->fd; 107 if (ub->fd < 0 || offset) { 108 msghdr.msg_control = NULL; 109 msghdr.msg_controllen = 0; 110 } 111 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); 117 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; 122 } else { 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; 127 } 128 129 do { 130 ret = sendmsg(fd, &msghdr, 0); 131 } while (ret < 0 && errno == EINTR); 132 133 return ret; 134 } 135 136 void ubus_msg_list_free(struct ubus_msg_buf_list *ubl) 137 { 138 list_del_init(&ubl->list); 139 ubus_msg_free(ubl->msg); 140 free(ubl); 141 } 142 143 static void ubus_msg_enqueue(struct ubus_client *cl, struct ubus_msg_buf *ub) 144 { 145 struct ubus_msg_buf_list *ubl; 146 147 if (cl->txq_len + ub->len > UBUS_CLIENT_MAX_TXQ_LEN) 148 return; 149 150 ubl = calloc(1, sizeof(struct ubus_msg_buf_list)); 151 if (!ubl) 152 return; 153 154 INIT_LIST_HEAD(&ubl->list); 155 ubl->msg = ubus_msg_ref(ub); 156 157 list_add_tail(&ubl->list, &cl->tx_queue); 158 cl->txq_len += ub->len; 159 } 160 161 /* takes the msgbuf reference */ 162 void ubus_msg_send(struct ubus_client *cl, struct ubus_msg_buf *ub) 163 { 164 ssize_t written; 165 166 if (ub->hdr.type != UBUS_MSG_MONITOR) 167 ubusd_monitor_message(cl, ub, true); 168 169 if (list_empty(&cl->tx_queue)) { 170 written = ubus_msg_writev(cl->sock.fd, ub, 0); 171 172 if (written < 0) 173 written = 0; 174 175 if (written >= (ssize_t) (ub->len + sizeof(ub->hdr))) 176 return; 177 178 cl->txq_ofs = written; 179 cl->txq_len = -written; 180 181 /* get an event once we can write to the socket again */ 182 uloop_fd_add(&cl->sock, ULOOP_READ | ULOOP_WRITE | ULOOP_EDGE_TRIGGER); 183 } 184 ubus_msg_enqueue(cl, ub); 185 } 186
This page was automatically generated by LXR 0.3.1. • OpenWrt