lua: workaround false positive dereference of null pointer
[project/ubus.git] / 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 static struct iovec iov[2];
86 static struct {
87 int fd;
88 struct cmsghdr h;
89 } fd_buf = {
90 .h = {
91 .cmsg_len = sizeof(fd_buf),
92 .cmsg_level = SOL_SOCKET,
93 .cmsg_type = SCM_RIGHTS,
94 },
95 };
96 struct msghdr msghdr = {
97 .msg_iov = iov,
98 .msg_iovlen = ARRAY_SIZE(iov),
99 .msg_control = &fd_buf,
100 .msg_controllen = sizeof(fd_buf),
101 };
102 struct ubus_msghdr hdr;
103 ssize_t ret;
104
105 fd_buf.fd = ub->fd;
106 if (ub->fd < 0 || offset) {
107 msghdr.msg_control = NULL;
108 msghdr.msg_controllen = 0;
109 }
110
111 if (offset < sizeof(ub->hdr)) {
112 hdr.version = ub->hdr.version;
113 hdr.type = ub->hdr.type;
114 hdr.seq = cpu_to_be16(ub->hdr.seq);
115 hdr.peer = cpu_to_be32(ub->hdr.peer);
116
117 iov[0].iov_base = ((char *) &hdr) + offset;
118 iov[0].iov_len = sizeof(hdr) - offset;
119 iov[1].iov_base = (char *) ub->data;
120 iov[1].iov_len = ub->len;
121 } else {
122 offset -= sizeof(ub->hdr);
123 iov[0].iov_base = ((char *) ub->data) + offset;
124 iov[0].iov_len = ub->len - offset;
125 msghdr.msg_iovlen = 1;
126 }
127
128 do {
129 ret = sendmsg(fd, &msghdr, 0);
130 } while (ret < 0 && errno == EINTR);
131
132 return ret;
133 }
134
135 static void ubus_msg_enqueue(struct ubus_client *cl, struct ubus_msg_buf *ub)
136 {
137 if (cl->tx_queue[cl->txq_tail])
138 return;
139
140 cl->tx_queue[cl->txq_tail] = ubus_msg_ref(ub);
141 cl->txq_tail = (cl->txq_tail + 1) % ARRAY_SIZE(cl->tx_queue);
142 }
143
144 /* takes the msgbuf reference */
145 void ubus_msg_send(struct ubus_client *cl, struct ubus_msg_buf *ub)
146 {
147 ssize_t written;
148
149 if (ub->hdr.type != UBUS_MSG_MONITOR)
150 ubusd_monitor_message(cl, ub, true);
151
152 if (!cl->tx_queue[cl->txq_cur]) {
153 written = ubus_msg_writev(cl->sock.fd, ub, 0);
154
155 if (written < 0)
156 written = 0;
157
158 if (written >= (ssize_t) (ub->len + sizeof(ub->hdr)))
159 return;
160
161 cl->txq_ofs = written;
162
163 /* get an event once we can write to the socket again */
164 uloop_fd_add(&cl->sock, ULOOP_READ | ULOOP_WRITE | ULOOP_EDGE_TRIGGER);
165 }
166 ubus_msg_enqueue(cl, ub);
167 }