s3-msg: Rename msg_channel_init_destructor
[kai/samba.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 "ctdb_packet.h"
24
25 struct ctdb_packet_context {
26         int fd;
27         DATA_BLOB in, out;
28 };
29
30 /*
31  * Close the underlying fd
32  */
33 static int ctdb_packet_context_destructor(struct ctdb_packet_context *ctx)
34 {
35         return close(ctx->fd);
36 }
37
38 /*
39  * Initialize a ctdb_packet context. The fd is given to the ctdb_packet context, meaning
40  * that it is automatically closed when the ctdb_packet context is freed.
41  */
42 struct ctdb_packet_context *ctdb_packet_init(TALLOC_CTX *mem_ctx, int fd)
43 {
44         struct ctdb_packet_context *result;
45
46         if (!(result = talloc_zero(mem_ctx, struct ctdb_packet_context))) {
47                 return NULL;
48         }
49
50         result->fd = fd;
51         talloc_set_destructor(result, ctdb_packet_context_destructor);
52         return result;
53 }
54
55 /*
56  * Pull data from the fd
57  */
58 NTSTATUS ctdb_packet_fd_read(struct ctdb_packet_context *ctx)
59 {
60         int res, available;
61         size_t new_size;
62         uint8 *in;
63
64         res = ioctl(ctx->fd, FIONREAD, &available);
65
66         if (res == -1) {
67                 DEBUG(10, ("ioctl(FIONREAD) failed: %s\n", strerror(errno)));
68                 return map_nt_error_from_unix(errno);
69         }
70
71         SMB_ASSERT(available >= 0);
72
73         if (available == 0) {
74                 return NT_STATUS_END_OF_FILE;
75         }
76
77         new_size = ctx->in.length + available;
78
79         if (new_size < ctx->in.length) {
80                 DEBUG(0, ("integer wrap\n"));
81                 return NT_STATUS_NO_MEMORY;
82         }
83
84         if (!(in = talloc_realloc(ctx, ctx->in.data, uint8, new_size))) {
85                 DEBUG(10, ("talloc failed\n"));
86                 return NT_STATUS_NO_MEMORY;
87         }
88
89         ctx->in.data = in;
90
91         res = recv(ctx->fd, in + ctx->in.length, available, 0);
92
93         if (res < 0) {
94                 DEBUG(10, ("recv failed: %s\n", strerror(errno)));
95                 return map_nt_error_from_unix(errno);
96         }
97
98         if (res == 0) {
99                 return NT_STATUS_END_OF_FILE;
100         }
101
102         ctx->in.length += res;
103
104         return NT_STATUS_OK;
105 }
106
107 NTSTATUS ctdb_packet_fd_read_sync_timeout(struct ctdb_packet_context *ctx, int timeout)
108 {
109         int res, revents;
110
111         res = poll_one_fd(ctx->fd, POLLIN|POLLHUP, timeout, &revents);
112         if (res == 0) {
113                 DEBUG(10, ("poll timed out\n"));
114                 return NT_STATUS_IO_TIMEOUT;
115         }
116
117         if (res == -1) {
118                 DEBUG(10, ("poll returned %s\n", strerror(errno)));
119                 return map_nt_error_from_unix(errno);
120         }
121         if ((revents & (POLLIN|POLLHUP|POLLERR)) == 0) {
122                 DEBUG(10, ("socket not readable\n"));
123                 return NT_STATUS_IO_TIMEOUT;
124         }
125
126         return ctdb_packet_fd_read(ctx);
127 }
128
129 bool ctdb_packet_handler(struct ctdb_packet_context *ctx,
130                     bool (*full_req)(const uint8_t *buf,
131                                      size_t available,
132                                      size_t *length,
133                                      void *priv),
134                     NTSTATUS (*callback)(uint8_t *buf, size_t length,
135                                          void *priv),
136                     void *priv, NTSTATUS *status)
137 {
138         size_t length;
139         uint8_t *buf;
140
141         if (!full_req(ctx->in.data, ctx->in.length, &length, priv)) {
142                 return False;
143         }
144
145         if (length > ctx->in.length) {
146                 *status = NT_STATUS_INTERNAL_ERROR;
147                 return true;
148         }
149
150         if (length == ctx->in.length) {
151                 buf = ctx->in.data;
152                 ctx->in.data = NULL;
153                 ctx->in.length = 0;
154         } else {
155                 buf = (uint8_t *)talloc_memdup(ctx, ctx->in.data, length);
156                 if (buf == NULL) {
157                         *status = NT_STATUS_NO_MEMORY;
158                         return true;
159                 }
160
161                 memmove(ctx->in.data, ctx->in.data + length,
162                         ctx->in.length - length);
163                 ctx->in.length -= length;
164         }
165
166         *status = callback(buf, length, priv);
167         return True;
168 }
169
170 /*
171  * How many bytes of outgoing data do we have pending?
172  */
173 size_t ctdb_packet_outgoing_bytes(struct ctdb_packet_context *ctx)
174 {
175         return ctx->out.length;
176 }
177
178 /*
179  * Push data to the fd
180  */
181 NTSTATUS ctdb_packet_fd_write(struct ctdb_packet_context *ctx)
182 {
183         ssize_t sent;
184
185         sent = send(ctx->fd, ctx->out.data, ctx->out.length, 0);
186
187         if (sent == -1) {
188                 DEBUG(0, ("send failed: %s\n", strerror(errno)));
189                 return map_nt_error_from_unix(errno);
190         }
191
192         memmove(ctx->out.data, ctx->out.data + sent,
193                 ctx->out.length - sent);
194         ctx->out.length -= sent;
195
196         return NT_STATUS_OK;
197 }
198
199 /*
200  * Sync flush all outgoing bytes
201  */
202 NTSTATUS ctdb_packet_flush(struct ctdb_packet_context *ctx)
203 {
204         while (ctx->out.length != 0) {
205                 NTSTATUS status = ctdb_packet_fd_write(ctx);
206                 if (!NT_STATUS_IS_OK(status)) {
207                         return status;
208                 }
209         }
210         return NT_STATUS_OK;
211 }
212
213 /*
214  * Send a list of DATA_BLOBs
215  *
216  * Example:  ctdb_packet_send(ctx, 2, data_blob_const(&size, sizeof(size)),
217  *                       data_blob_const(buf, size));
218  */
219 NTSTATUS ctdb_packet_send(struct ctdb_packet_context *ctx, int num_blobs, ...)
220 {
221         va_list ap;
222         int i;
223         size_t len;
224         uint8 *out;
225
226         len = ctx->out.length;
227
228         va_start(ap, num_blobs);
229         for (i=0; i<num_blobs; i++) {
230                 size_t tmp;
231                 DATA_BLOB blob = va_arg(ap, DATA_BLOB);
232
233                 tmp = len + blob.length;
234                 if (tmp < len) {
235                         DEBUG(0, ("integer overflow\n"));
236                         va_end(ap);
237                         return NT_STATUS_NO_MEMORY;
238                 }
239                 len = tmp;
240         }
241         va_end(ap);
242
243         if (len == 0) {
244                 return NT_STATUS_OK;
245         }
246
247         if (!(out = talloc_realloc(ctx, ctx->out.data, uint8, len))) {
248                 DEBUG(0, ("talloc failed\n"));
249                 return NT_STATUS_NO_MEMORY;
250         }
251
252         ctx->out.data = out;
253
254         va_start(ap, num_blobs);
255         for (i=0; i<num_blobs; i++) {
256                 DATA_BLOB blob = va_arg(ap, DATA_BLOB);
257
258                 memcpy(ctx->out.data+ctx->out.length, blob.data, blob.length);
259                 ctx->out.length += blob.length;
260         }
261         va_end(ap);
262
263         SMB_ASSERT(ctx->out.length == len);
264         return NT_STATUS_OK;
265 }
266
267 /*
268  * Get the ctdb_packet context's file descriptor
269  */
270 int ctdb_packet_get_fd(struct ctdb_packet_context *ctx)
271 {
272         return ctx->fd;
273 }