s3-talloc Change TALLOC_REALLOC_ARRAY() to talloc_realloc()
[sfrench/samba-autobuild/.git] / source3 / lib / ctdb_packet.c
1 /*
2    Unix SMB/CIFS implementation.
3    CTDB Packet handling
4    Copyright (C) Volker Lendecke 2007
5
6    This program is free software; you can redistribute it and/or modify
7    it under the terms of the GNU General Public License as published by
8    the Free Software Foundation; either version 3 of the License, or
9    (at your option) any later version.
10
11    This program is distributed in the hope that it will be useful,
12    but WITHOUT ANY WARRANTY; without even the implied warranty of
13    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14    GNU General Public License for more details.
15
16    You should have received a copy of the GNU General Public License
17    along with this program.  If not, see <http://www.gnu.org/licenses/>.
18 */
19
20 #include "includes.h"
21 #include "../lib/util/select.h"
22 #include "system/filesys.h"
23 #include "system/select.h"
24 #include "ctdb_packet.h"
25
26 struct ctdb_packet_context {
27         int fd;
28         DATA_BLOB in, out;
29 };
30
31 /*
32  * Close the underlying fd
33  */
34 static int ctdb_packet_context_destructor(struct ctdb_packet_context *ctx)
35 {
36         return close(ctx->fd);
37 }
38
39 /*
40  * Initialize a ctdb_packet context. The fd is given to the ctdb_packet context, meaning
41  * that it is automatically closed when the ctdb_packet context is freed.
42  */
43 struct ctdb_packet_context *ctdb_packet_init(TALLOC_CTX *mem_ctx, int fd)
44 {
45         struct ctdb_packet_context *result;
46
47         if (!(result = TALLOC_ZERO_P(mem_ctx, struct ctdb_packet_context))) {
48                 return NULL;
49         }
50
51         result->fd = fd;
52         talloc_set_destructor(result, ctdb_packet_context_destructor);
53         return result;
54 }
55
56 /*
57  * Pull data from the fd
58  */
59 NTSTATUS ctdb_packet_fd_read(struct ctdb_packet_context *ctx)
60 {
61         int res, available;
62         size_t new_size;
63         uint8 *in;
64
65         res = ioctl(ctx->fd, FIONREAD, &available);
66
67         if (res == -1) {
68                 DEBUG(10, ("ioctl(FIONREAD) failed: %s\n", strerror(errno)));
69                 return map_nt_error_from_unix(errno);
70         }
71
72         SMB_ASSERT(available >= 0);
73
74         if (available == 0) {
75                 return NT_STATUS_END_OF_FILE;
76         }
77
78         new_size = ctx->in.length + available;
79
80         if (new_size < ctx->in.length) {
81                 DEBUG(0, ("integer wrap\n"));
82                 return NT_STATUS_NO_MEMORY;
83         }
84
85         if (!(in = talloc_realloc(ctx, ctx->in.data, uint8, new_size))) {
86                 DEBUG(10, ("talloc failed\n"));
87                 return NT_STATUS_NO_MEMORY;
88         }
89
90         ctx->in.data = in;
91
92         res = recv(ctx->fd, in + ctx->in.length, available, 0);
93
94         if (res < 0) {
95                 DEBUG(10, ("recv failed: %s\n", strerror(errno)));
96                 return map_nt_error_from_unix(errno);
97         }
98
99         if (res == 0) {
100                 return NT_STATUS_END_OF_FILE;
101         }
102
103         ctx->in.length += res;
104
105         return NT_STATUS_OK;
106 }
107
108 NTSTATUS ctdb_packet_fd_read_sync_timeout(struct ctdb_packet_context *ctx, int timeout)
109 {
110         int res, revents;
111
112         res = poll_one_fd(ctx->fd, POLLIN|POLLHUP, timeout, &revents);
113         if (res == 0) {
114                 DEBUG(10, ("poll timed out\n"));
115                 return NT_STATUS_IO_TIMEOUT;
116         }
117
118         if (res == -1) {
119                 DEBUG(10, ("poll returned %s\n", strerror(errno)));
120                 return map_nt_error_from_unix(errno);
121         }
122         if ((revents & (POLLIN|POLLHUP|POLLERR)) == 0) {
123                 DEBUG(10, ("socket not readable\n"));
124                 return NT_STATUS_IO_TIMEOUT;
125         }
126
127         return ctdb_packet_fd_read(ctx);
128 }
129
130 bool ctdb_packet_handler(struct ctdb_packet_context *ctx,
131                     bool (*full_req)(const uint8_t *buf,
132                                      size_t available,
133                                      size_t *length,
134                                      void *priv),
135                     NTSTATUS (*callback)(uint8_t *buf, size_t length,
136                                          void *priv),
137                     void *priv, NTSTATUS *status)
138 {
139         size_t length;
140         uint8_t *buf;
141
142         if (!full_req(ctx->in.data, ctx->in.length, &length, priv)) {
143                 return False;
144         }
145
146         if (length > ctx->in.length) {
147                 *status = NT_STATUS_INTERNAL_ERROR;
148                 return true;
149         }
150
151         if (length == ctx->in.length) {
152                 buf = ctx->in.data;
153                 ctx->in.data = NULL;
154                 ctx->in.length = 0;
155         } else {
156                 buf = (uint8_t *)TALLOC_MEMDUP(ctx, ctx->in.data, length);
157                 if (buf == NULL) {
158                         *status = NT_STATUS_NO_MEMORY;
159                         return true;
160                 }
161
162                 memmove(ctx->in.data, ctx->in.data + length,
163                         ctx->in.length - length);
164                 ctx->in.length -= length;
165         }
166
167         *status = callback(buf, length, priv);
168         return True;
169 }
170
171 /*
172  * How many bytes of outgoing data do we have pending?
173  */
174 size_t ctdb_packet_outgoing_bytes(struct ctdb_packet_context *ctx)
175 {
176         return ctx->out.length;
177 }
178
179 /*
180  * Push data to the fd
181  */
182 NTSTATUS ctdb_packet_fd_write(struct ctdb_packet_context *ctx)
183 {
184         ssize_t sent;
185
186         sent = send(ctx->fd, ctx->out.data, ctx->out.length, 0);
187
188         if (sent == -1) {
189                 DEBUG(0, ("send failed: %s\n", strerror(errno)));
190                 return map_nt_error_from_unix(errno);
191         }
192
193         memmove(ctx->out.data, ctx->out.data + sent,
194                 ctx->out.length - sent);
195         ctx->out.length -= sent;
196
197         return NT_STATUS_OK;
198 }
199
200 /*
201  * Sync flush all outgoing bytes
202  */
203 NTSTATUS ctdb_packet_flush(struct ctdb_packet_context *ctx)
204 {
205         while (ctx->out.length != 0) {
206                 NTSTATUS status = ctdb_packet_fd_write(ctx);
207                 if (!NT_STATUS_IS_OK(status)) {
208                         return status;
209                 }
210         }
211         return NT_STATUS_OK;
212 }
213
214 /*
215  * Send a list of DATA_BLOBs
216  *
217  * Example:  ctdb_packet_send(ctx, 2, data_blob_const(&size, sizeof(size)),
218  *                       data_blob_const(buf, size));
219  */
220 NTSTATUS ctdb_packet_send(struct ctdb_packet_context *ctx, int num_blobs, ...)
221 {
222         va_list ap;
223         int i;
224         size_t len;
225         uint8 *out;
226
227         len = ctx->out.length;
228
229         va_start(ap, num_blobs);
230         for (i=0; i<num_blobs; i++) {
231                 size_t tmp;
232                 DATA_BLOB blob = va_arg(ap, DATA_BLOB);
233
234                 tmp = len + blob.length;
235                 if (tmp < len) {
236                         DEBUG(0, ("integer overflow\n"));
237                         va_end(ap);
238                         return NT_STATUS_NO_MEMORY;
239                 }
240                 len = tmp;
241         }
242         va_end(ap);
243
244         if (len == 0) {
245                 return NT_STATUS_OK;
246         }
247
248         if (!(out = talloc_realloc(ctx, ctx->out.data, uint8, len))) {
249                 DEBUG(0, ("talloc failed\n"));
250                 return NT_STATUS_NO_MEMORY;
251         }
252
253         ctx->out.data = out;
254
255         va_start(ap, num_blobs);
256         for (i=0; i<num_blobs; i++) {
257                 DATA_BLOB blob = va_arg(ap, DATA_BLOB);
258
259                 memcpy(ctx->out.data+ctx->out.length, blob.data, blob.length);
260                 ctx->out.length += blob.length;
261         }
262         va_end(ap);
263
264         SMB_ASSERT(ctx->out.length == len);
265         return NT_STATUS_OK;
266 }
267
268 /*
269  * Get the ctdb_packet context's file descriptor
270  */
271 int ctdb_packet_get_fd(struct ctdb_packet_context *ctx)
272 {
273         return ctx->fd;
274 }