source: branches/samba-3.3.x/source/lib/async_sock.c

Last change on this file was 206, checked in by Herwig Bauernfeind, 16 years ago

Import Samba 3.3 branch at 3.0.0 level (psmedley's port)

File size: 17.7 KB
Line 
1/*
2 Unix SMB/CIFS implementation.
3 async socket syscalls
4 Copyright (C) Volker Lendecke 2008
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
22/**
23 * Discriminator for async_syscall_state
24 */
25enum async_syscall_type {
26 ASYNC_SYSCALL_SEND,
27 ASYNC_SYSCALL_SENDALL,
28 ASYNC_SYSCALL_RECV,
29 ASYNC_SYSCALL_RECVALL,
30 ASYNC_SYSCALL_CONNECT
31};
32
33/**
34 * Holder for syscall arguments and the result
35 */
36
37struct async_syscall_state {
38 enum async_syscall_type syscall_type;
39 struct fd_event *fde;
40
41 union {
42 struct param_send {
43 int fd;
44 const void *buffer;
45 size_t length;
46 int flags;
47 } param_send;
48 struct param_sendall {
49 int fd;
50 const void *buffer;
51 size_t length;
52 int flags;
53 size_t sent;
54 } param_sendall;
55 struct param_recv {
56 int fd;
57 void *buffer;
58 size_t length;
59 int flags;
60 } param_recv;
61 struct param_recvall {
62 int fd;
63 void *buffer;
64 size_t length;
65 int flags;
66 size_t received;
67 } param_recvall;
68 struct param_connect {
69 /**
70 * connect needs to be done on a nonblocking
71 * socket. Keep the old flags around
72 */
73 long old_sockflags;
74 int fd;
75 const struct sockaddr *address;
76 socklen_t address_len;
77 } param_connect;
78 } param;
79
80 union {
81 ssize_t result_ssize_t;
82 size_t result_size_t;
83 int result_int;
84 } result;
85 int sys_errno;
86};
87
88/**
89 * @brief Create a new async syscall req
90 * @param[in] mem_ctx The memory context to hang the result off
91 * @param[in] ev The event context to work from
92 * @param[in] type Which syscall will this be
93 * @param[in] pstate Where to put the newly created private_data state
94 * @retval The new request
95 *
96 * This is a helper function to prepare a new struct async_req with an
97 * associated struct async_syscall_state. The async_syscall_state will be put
98 * into the async_req as private_data.
99 */
100
101static struct async_req *async_syscall_new(TALLOC_CTX *mem_ctx,
102 struct event_context *ev,
103 enum async_syscall_type type,
104 struct async_syscall_state **pstate)
105{
106 struct async_req *result;
107 struct async_syscall_state *state;
108
109 result = async_req_new(mem_ctx, ev);
110 if (result == NULL) {
111 return NULL;
112 }
113
114 state = talloc(result, struct async_syscall_state);
115 if (state == NULL) {
116 TALLOC_FREE(result);
117 return NULL;
118 }
119
120 state->syscall_type = type;
121
122 result->private_data = state;
123
124 *pstate = state;
125
126 return result;
127}
128
129/**
130 * @brief Create a new async syscall req based on a fd
131 * @param[in] mem_ctx The memory context to hang the result off
132 * @param[in] ev The event context to work from
133 * @param[in] type Which syscall will this be
134 * @param[in] fd The file descriptor we work on
135 * @param[in] fde_flags EVENT_FD_READ/WRITE -- what are we interested in?
136 * @param[in] fde_cb The callback function for the file descriptor event
137 * @param[in] pstate Where to put the newly created private_data state
138 * @retval The new request
139 *
140 * This is a helper function to prepare a new struct async_req with an
141 * associated struct async_syscall_state and an associated file descriptor
142 * event.
143 */
144
145static struct async_req *async_fde_syscall_new(
146 TALLOC_CTX *mem_ctx,
147 struct event_context *ev,
148 enum async_syscall_type type,
149 int fd,
150 uint16_t fde_flags,
151 void (*fde_cb)(struct event_context *ev,
152 struct fd_event *fde, uint16_t flags,
153 void *priv),
154 struct async_syscall_state **pstate)
155{
156 struct async_req *result;
157 struct async_syscall_state *state;
158
159 result = async_syscall_new(mem_ctx, ev, type, &state);
160 if (result == NULL) {
161 return NULL;
162 }
163
164 state->fde = event_add_fd(ev, state, fd, fde_flags, fde_cb, result);
165 if (state->fde == NULL) {
166 TALLOC_FREE(result);
167 return NULL;
168 }
169 *pstate = state;
170 return result;
171}
172
173/**
174 * Retrieve a ssize_t typed result from an async syscall
175 * @param[in] req The syscall that has just finished
176 * @param[out] perrno Where to put the syscall's errno
177 * @retval The return value from the asynchronously called syscall
178 */
179
180ssize_t async_syscall_result_ssize_t(struct async_req **req, int *perrno)
181{
182 struct async_syscall_state *state = talloc_get_type_abort(
183 (*req)->private_data, struct async_syscall_state);
184
185 int sys_errno = state->sys_errno;
186 ssize_t result = state->result.result_ssize_t;
187
188 TALLOC_FREE(*req);
189
190 *perrno = sys_errno;
191 return result;
192}
193
194/**
195 * Retrieve a size_t typed result from an async syscall
196 * @param[in] req The syscall that has just finished
197 * @param[out] perrno Where to put the syscall's errno
198 * @retval The return value from the asynchronously called syscall
199 */
200
201size_t async_syscall_result_size_t(struct async_req **req, int *perrno)
202{
203 struct async_syscall_state *state = talloc_get_type_abort(
204 (*req)->private_data, struct async_syscall_state);
205
206 int sys_errno = state->sys_errno;
207 size_t result = state->result.result_ssize_t;
208
209 TALLOC_FREE(*req);
210
211 *perrno = sys_errno;
212 return result;
213}
214
215/**
216 * Retrieve a int typed result from an async syscall
217 * @param[in] req The syscall that has just finished
218 * @param[out] perrno Where to put the syscall's errno
219 * @retval The return value from the asynchronously called syscall
220 */
221
222ssize_t async_syscall_result_int(struct async_req **req, int *perrno)
223{
224 struct async_syscall_state *state = talloc_get_type_abort(
225 (*req)->private_data, struct async_syscall_state);
226
227 int sys_errno = state->sys_errno;
228 int result = state->result.result_ssize_t;
229
230 TALLOC_FREE(*req);
231
232 *perrno = sys_errno;
233 return result;
234}
235
236/**
237 * fde event handler for the "send" syscall
238 * @param[in] ev The event context that sent us here
239 * @param[in] fde The file descriptor event associated with the send
240 * @param[in] flags Can only be EVENT_FD_WRITE here
241 * @param[in] priv private data, "struct async_req *" in this case
242 */
243
244static void async_send_callback(struct event_context *ev,
245 struct fd_event *fde, uint16_t flags,
246 void *priv)
247{
248 struct async_req *req = talloc_get_type_abort(
249 priv, struct async_req);
250 struct async_syscall_state *state = talloc_get_type_abort(
251 req->private_data, struct async_syscall_state);
252 struct param_send *p = &state->param.param_send;
253
254 SMB_ASSERT(state->syscall_type == ASYNC_SYSCALL_SEND);
255
256 state->result.result_ssize_t = send(p->fd, p->buffer, p->length,
257 p->flags);
258 state->sys_errno = errno;
259
260 TALLOC_FREE(state->fde);
261
262 async_req_done(req);
263}
264
265/**
266 * Async version of send(2)
267 * @param[in] mem_ctx The memory context to hang the result off
268 * @param[in] ev The event context to work from
269 * @param[in] fd The socket to send to
270 * @param[in] buffer The buffer to send
271 * @param[in] length How many bytes to send
272 * @param[in] flags flags passed to send(2)
273 *
274 * This function is a direct counterpart of send(2)
275 */
276
277struct async_req *async_send(TALLOC_CTX *mem_ctx, struct event_context *ev,
278 int fd, const void *buffer, size_t length,
279 int flags)
280{
281 struct async_req *result;
282 struct async_syscall_state *state;
283
284 result = async_fde_syscall_new(
285 mem_ctx, ev, ASYNC_SYSCALL_SEND,
286 fd, EVENT_FD_WRITE, async_send_callback,
287 &state);
288 if (result == NULL) {
289 return NULL;
290 }
291
292 state->param.param_send.fd = fd;
293 state->param.param_send.buffer = buffer;
294 state->param.param_send.length = length;
295 state->param.param_send.flags = flags;
296
297 return result;
298}
299
300/**
301 * fde event handler for the "sendall" syscall group
302 * @param[in] ev The event context that sent us here
303 * @param[in] fde The file descriptor event associated with the send
304 * @param[in] flags Can only be EVENT_FD_WRITE here
305 * @param[in] priv private data, "struct async_req *" in this case
306 */
307
308static void async_sendall_callback(struct event_context *ev,
309 struct fd_event *fde, uint16_t flags,
310 void *priv)
311{
312 struct async_req *req = talloc_get_type_abort(
313 priv, struct async_req);
314 struct async_syscall_state *state = talloc_get_type_abort(
315 req->private_data, struct async_syscall_state);
316 struct param_sendall *p = &state->param.param_sendall;
317
318 SMB_ASSERT(state->syscall_type == ASYNC_SYSCALL_SENDALL);
319
320 state->result.result_ssize_t = send(p->fd, (char *)p->buffer + p->sent,
321 p->length - p->sent, p->flags);
322 state->sys_errno = errno;
323
324 if (state->result.result_ssize_t == -1) {
325 async_req_error(req, map_nt_error_from_unix(state->sys_errno));
326 return;
327 }
328
329 if (state->result.result_ssize_t == 0) {
330 async_req_error(req, NT_STATUS_END_OF_FILE);
331 return;
332 }
333
334 p->sent += state->result.result_ssize_t;
335 SMB_ASSERT(p->sent <= p->length);
336
337 if (p->sent == p->length) {
338 TALLOC_FREE(state->fde);
339 async_req_done(req);
340 }
341}
342
343/**
344 * @brief Send all bytes to a socket
345 * @param[in] mem_ctx The memory context to hang the result off
346 * @param[in] ev The event context to work from
347 * @param[in] fd The socket to send to
348 * @param[in] buffer The buffer to send
349 * @param[in] length How many bytes to send
350 * @param[in] flags flags passed to send(2)
351 *
352 * async_sendall calls send(2) as long as it is necessary to send all of the
353 * "length" bytes
354 */
355
356struct async_req *async_sendall(TALLOC_CTX *mem_ctx, struct event_context *ev,
357 int fd, const void *buffer, size_t length,
358 int flags)
359{
360 struct async_req *result;
361 struct async_syscall_state *state;
362
363 result = async_fde_syscall_new(
364 mem_ctx, ev, ASYNC_SYSCALL_SENDALL,
365 fd, EVENT_FD_WRITE, async_sendall_callback,
366 &state);
367 if (result == NULL) {
368 return NULL;
369 }
370
371 state->param.param_sendall.fd = fd;
372 state->param.param_sendall.buffer = buffer;
373 state->param.param_sendall.length = length;
374 state->param.param_sendall.flags = flags;
375 state->param.param_sendall.sent = 0;
376
377 return result;
378}
379
380/**
381 * fde event handler for the "recv" syscall
382 * @param[in] ev The event context that sent us here
383 * @param[in] fde The file descriptor event associated with the recv
384 * @param[in] flags Can only be EVENT_FD_READ here
385 * @param[in] priv private data, "struct async_req *" in this case
386 */
387
388static void async_recv_callback(struct event_context *ev,
389 struct fd_event *fde, uint16_t flags,
390 void *priv)
391{
392 struct async_req *req = talloc_get_type_abort(
393 priv, struct async_req);
394 struct async_syscall_state *state = talloc_get_type_abort(
395 req->private_data, struct async_syscall_state);
396 struct param_recv *p = &state->param.param_recv;
397
398 SMB_ASSERT(state->syscall_type == ASYNC_SYSCALL_RECV);
399
400 state->result.result_ssize_t = recv(p->fd, p->buffer, p->length,
401 p->flags);
402 state->sys_errno = errno;
403
404 TALLOC_FREE(state->fde);
405
406 async_req_done(req);
407}
408
409/**
410 * Async version of recv(2)
411 * @param[in] mem_ctx The memory context to hang the result off
412 * @param[in] ev The event context to work from
413 * @param[in] fd The socket to recv from
414 * @param[in] buffer The buffer to recv into
415 * @param[in] length How many bytes to recv
416 * @param[in] flags flags passed to recv(2)
417 *
418 * This function is a direct counterpart of recv(2)
419 */
420
421struct async_req *async_recv(TALLOC_CTX *mem_ctx, struct event_context *ev,
422 int fd, void *buffer, size_t length,
423 int flags)
424{
425 struct async_req *result;
426 struct async_syscall_state *state;
427
428 result = async_fde_syscall_new(
429 mem_ctx, ev, ASYNC_SYSCALL_RECV,
430 fd, EVENT_FD_READ, async_recv_callback,
431 &state);
432
433 if (result == NULL) {
434 return NULL;
435 }
436
437 state->param.param_recv.fd = fd;
438 state->param.param_recv.buffer = buffer;
439 state->param.param_recv.length = length;
440 state->param.param_recv.flags = flags;
441
442 return result;
443}
444
445/**
446 * fde event handler for the "recvall" syscall group
447 * @param[in] ev The event context that sent us here
448 * @param[in] fde The file descriptor event associated with the recv
449 * @param[in] flags Can only be EVENT_FD_READ here
450 * @param[in] priv private data, "struct async_req *" in this case
451 */
452
453static void async_recvall_callback(struct event_context *ev,
454 struct fd_event *fde, uint16_t flags,
455 void *priv)
456{
457 struct async_req *req = talloc_get_type_abort(
458 priv, struct async_req);
459 struct async_syscall_state *state = talloc_get_type_abort(
460 req->private_data, struct async_syscall_state);
461 struct param_recvall *p = &state->param.param_recvall;
462
463 SMB_ASSERT(state->syscall_type == ASYNC_SYSCALL_RECVALL);
464
465 state->result.result_ssize_t = recv(p->fd,
466 (char *)p->buffer + p->received,
467 p->length - p->received, p->flags);
468 state->sys_errno = errno;
469
470 if (state->result.result_ssize_t == -1) {
471 async_req_error(req, map_nt_error_from_unix(state->sys_errno));
472 return;
473 }
474
475 if (state->result.result_ssize_t == 0) {
476 async_req_error(req, NT_STATUS_END_OF_FILE);
477 return;
478 }
479
480 p->received += state->result.result_ssize_t;
481 SMB_ASSERT(p->received <= p->length);
482
483 if (p->received == p->length) {
484 TALLOC_FREE(state->fde);
485 async_req_done(req);
486 }
487}
488
489/**
490 * Receive a specified number of bytes from a socket
491 * @param[in] mem_ctx The memory context to hang the result off
492 * @param[in] ev The event context to work from
493 * @param[in] fd The socket to recv from
494 * @param[in] buffer The buffer to recv into
495 * @param[in] length How many bytes to recv
496 * @param[in] flags flags passed to recv(2)
497 *
498 * async_recvall will call recv(2) until "length" bytes are received
499 */
500
501struct async_req *async_recvall(TALLOC_CTX *mem_ctx, struct event_context *ev,
502 int fd, void *buffer, size_t length,
503 int flags)
504{
505 struct async_req *result;
506 struct async_syscall_state *state;
507
508 result = async_fde_syscall_new(
509 mem_ctx, ev, ASYNC_SYSCALL_RECVALL,
510 fd, EVENT_FD_READ, async_recvall_callback,
511 &state);
512 if (result == NULL) {
513 return NULL;
514 }
515
516 state->param.param_recvall.fd = fd;
517 state->param.param_recvall.buffer = buffer;
518 state->param.param_recvall.length = length;
519 state->param.param_recvall.flags = flags;
520 state->param.param_recvall.received = 0;
521
522 return result;
523}
524
525/**
526 * fde event handler for connect(2)
527 * @param[in] ev The event context that sent us here
528 * @param[in] fde The file descriptor event associated with the connect
529 * @param[in] flags Indicate read/writeability of the socket
530 * @param[in] priv private data, "struct async_req *" in this case
531 */
532
533static void async_connect_callback(struct event_context *ev,
534 struct fd_event *fde, uint16_t flags,
535 void *priv)
536{
537 struct async_req *req = talloc_get_type_abort(
538 priv, struct async_req);
539 struct async_syscall_state *state = talloc_get_type_abort(
540 req->private_data, struct async_syscall_state);
541 struct param_connect *p = &state->param.param_connect;
542
543 SMB_ASSERT(state->syscall_type == ASYNC_SYSCALL_CONNECT);
544
545 TALLOC_FREE(state->fde);
546
547 /*
548 * Stevens, Network Programming says that if there's a
549 * successful connect, the socket is only writable. Upon an
550 * error, it's both readable and writable.
551 */
552 if ((flags & (EVENT_FD_READ|EVENT_FD_WRITE))
553 == (EVENT_FD_READ|EVENT_FD_WRITE)) {
554 int sockerr;
555 socklen_t err_len = sizeof(sockerr);
556
557 if (getsockopt(p->fd, SOL_SOCKET, SO_ERROR,
558 (void *)&sockerr, &err_len) == 0) {
559 errno = sockerr;
560 }
561
562 state->sys_errno = errno;
563
564 DEBUG(10, ("connect returned %s\n", strerror(errno)));
565
566 sys_fcntl_long(p->fd, F_SETFL, p->old_sockflags);
567
568 async_req_error(req, map_nt_error_from_unix(state->sys_errno));
569 return;
570 }
571
572 sys_fcntl_long(p->fd, F_SETFL, p->old_sockflags);
573
574 state->result.result_int = 0;
575 state->sys_errno = 0;
576
577 async_req_done(req);
578}
579
580/**
581 * @brief async version of connect(2)
582 * @param[in] mem_ctx The memory context to hang the result off
583 * @param[in] ev The event context to work from
584 * @param[in] fd The socket to recv from
585 * @param[in] address Where to connect?
586 * @param[in] address_len Length of *address
587 * @retval The async request
588 *
589 * This function sets the socket into non-blocking state to be able to call
590 * connect in an async state. This will be reset when the request is finished.
591 */
592
593struct async_req *async_connect(TALLOC_CTX *mem_ctx, struct event_context *ev,
594 int fd, const struct sockaddr *address,
595 socklen_t address_len)
596{
597 struct async_req *result;
598 struct async_syscall_state *state;
599 struct param_connect *p;
600
601 result = async_syscall_new(mem_ctx, ev, ASYNC_SYSCALL_CONNECT, &state);
602 if (result == NULL) {
603 return NULL;
604 }
605 p = &state->param.param_connect;
606
607 /**
608 * We have to set the socket to nonblocking for async connect(2). Keep
609 * the old sockflags around.
610 */
611
612 p->old_sockflags = sys_fcntl_long(fd, F_GETFL, 0);
613
614 if (p->old_sockflags == -1) {
615 if (async_post_status(result, map_nt_error_from_unix(errno))) {
616 return result;
617 }
618 TALLOC_FREE(result);
619 return NULL;
620 }
621
622 set_blocking(fd, true);
623
624 state->result.result_int = connect(fd, address, address_len);
625
626 if (state->result.result_int == 0) {
627 state->sys_errno = 0;
628 if (async_post_status(result, NT_STATUS_OK)) {
629 return result;
630 }
631 sys_fcntl_long(fd, F_SETFL, p->old_sockflags);
632 TALLOC_FREE(result);
633 return NULL;
634 }
635
636 /**
637 * A number of error messages show that something good is progressing
638 * and that we have to wait for readability.
639 *
640 * If none of them are present, bail out.
641 */
642
643 if (!(errno == EINPROGRESS || errno == EALREADY ||
644#ifdef EISCONN
645 errno == EISCONN ||
646#endif
647 errno == EAGAIN || errno == EINTR)) {
648
649 state->sys_errno = errno;
650
651 if (async_post_status(result, map_nt_error_from_unix(errno))) {
652 return result;
653 }
654 sys_fcntl_long(fd, F_SETFL, p->old_sockflags);
655 TALLOC_FREE(result);
656 return NULL;
657 }
658
659 state->fde = event_add_fd(ev, state, fd,
660 EVENT_FD_READ | EVENT_FD_WRITE,
661 async_connect_callback, state);
662 if (state->fde == NULL) {
663 sys_fcntl_long(fd, F_SETFL, p->old_sockflags);
664 TALLOC_FREE(result);
665 return NULL;
666 }
667
668 state->param.param_connect.fd = fd;
669 state->param.param_connect.address = address;
670 state->param.param_connect.address_len = address_len;
671
672 return result;
673}
674
Note: See TracBrowser for help on using the repository browser.