• source navigation  • diff markup  • identifier search  • freetext search  • 

Sources/ubus/ubusd.c

  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