• 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                 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