2 Unix SMB/CIFS implementation.
4 Copyright (C) Volker Lendecke 2008
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.
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.
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/>.
23 * Discriminator for async_syscall_state
25 enum async_syscall_type {
27 ASYNC_SYSCALL_SENDALL,
29 ASYNC_SYSCALL_RECVALL,
34 * Holder for syscall arguments and the result
37 struct async_syscall_state {
38 enum async_syscall_type syscall_type;
48 struct param_sendall {
61 struct param_recvall {
68 struct param_connect {
70 * connect needs to be done on a nonblocking
71 * socket. Keep the old flags around
75 const struct sockaddr *address;
76 socklen_t address_len;
81 ssize_t result_ssize_t;
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
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.
101 static 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)
106 struct async_req *result;
107 struct async_syscall_state *state;
109 result = async_req_new(mem_ctx, ev);
110 if (result == NULL) {
114 state = talloc(result, struct async_syscall_state);
120 state->syscall_type = type;
122 result->private_data = state;
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
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
145 static struct async_req *async_fde_syscall_new(
147 struct event_context *ev,
148 enum async_syscall_type type,
151 void (*fde_cb)(struct event_context *ev,
152 struct fd_event *fde, uint16_t flags,
154 struct async_syscall_state **pstate)
156 struct async_req *result;
157 struct async_syscall_state *state;
159 result = async_syscall_new(mem_ctx, ev, type, &state);
160 if (result == NULL) {
164 state->fde = event_add_fd(ev, state, fd, fde_flags, fde_cb, result);
165 if (state->fde == NULL) {
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
180 ssize_t async_syscall_result_ssize_t(struct async_req *req, int *perrno)
182 struct async_syscall_state *state = talloc_get_type_abort(
183 req->private_data, struct async_syscall_state);
185 *perrno = state->sys_errno;
186 return state->result.result_ssize_t;
190 * Retrieve a size_t typed result from an async syscall
191 * @param[in] req The syscall that has just finished
192 * @param[out] perrno Where to put the syscall's errno
193 * @retval The return value from the asynchronously called syscall
196 size_t async_syscall_result_size_t(struct async_req *req, int *perrno)
198 struct async_syscall_state *state = talloc_get_type_abort(
199 req->private_data, struct async_syscall_state);
201 *perrno = state->sys_errno;
202 return state->result.result_size_t;
206 * Retrieve a int typed result from an async syscall
207 * @param[in] req The syscall that has just finished
208 * @param[out] perrno Where to put the syscall's errno
209 * @retval The return value from the asynchronously called syscall
212 ssize_t async_syscall_result_int(struct async_req *req, int *perrno)
214 struct async_syscall_state *state = talloc_get_type_abort(
215 req->private_data, struct async_syscall_state);
217 *perrno = state->sys_errno;
218 return state->result.result_int;
222 * fde event handler for the "send" syscall
223 * @param[in] ev The event context that sent us here
224 * @param[in] fde The file descriptor event associated with the send
225 * @param[in] flags Can only be EVENT_FD_WRITE here
226 * @param[in] priv private data, "struct async_req *" in this case
229 static void async_send_callback(struct event_context *ev,
230 struct fd_event *fde, uint16_t flags,
233 struct async_req *req = talloc_get_type_abort(
234 priv, struct async_req);
235 struct async_syscall_state *state = talloc_get_type_abort(
236 req->private_data, struct async_syscall_state);
237 struct param_send *p = &state->param.param_send;
239 SMB_ASSERT(state->syscall_type == ASYNC_SYSCALL_SEND);
241 state->result.result_ssize_t = send(p->fd, p->buffer, p->length,
243 state->sys_errno = errno;
245 TALLOC_FREE(state->fde);
251 * Async version of send(2)
252 * @param[in] mem_ctx The memory context to hang the result off
253 * @param[in] ev The event context to work from
254 * @param[in] fd The socket to send to
255 * @param[in] buffer The buffer to send
256 * @param[in] length How many bytes to send
257 * @param[in] flags flags passed to send(2)
259 * This function is a direct counterpart of send(2)
262 struct async_req *async_send(TALLOC_CTX *mem_ctx, struct event_context *ev,
263 int fd, const void *buffer, size_t length,
266 struct async_req *result;
267 struct async_syscall_state *state;
269 result = async_fde_syscall_new(
270 mem_ctx, ev, ASYNC_SYSCALL_SEND,
271 fd, EVENT_FD_WRITE, async_send_callback,
273 if (result == NULL) {
277 state->param.param_send.fd = fd;
278 state->param.param_send.buffer = buffer;
279 state->param.param_send.length = length;
280 state->param.param_send.flags = flags;
286 * fde event handler for the "sendall" syscall group
287 * @param[in] ev The event context that sent us here
288 * @param[in] fde The file descriptor event associated with the send
289 * @param[in] flags Can only be EVENT_FD_WRITE here
290 * @param[in] priv private data, "struct async_req *" in this case
293 static void async_sendall_callback(struct event_context *ev,
294 struct fd_event *fde, uint16_t flags,
297 struct async_req *req = talloc_get_type_abort(
298 priv, struct async_req);
299 struct async_syscall_state *state = talloc_get_type_abort(
300 req->private_data, struct async_syscall_state);
301 struct param_sendall *p = &state->param.param_sendall;
303 SMB_ASSERT(state->syscall_type == ASYNC_SYSCALL_SENDALL);
305 state->result.result_ssize_t = send(p->fd, (char *)p->buffer + p->sent,
306 p->length - p->sent, p->flags);
307 state->sys_errno = errno;
309 if (state->result.result_ssize_t == -1) {
310 async_req_error(req, map_nt_error_from_unix(state->sys_errno));
314 if (state->result.result_ssize_t == 0) {
315 async_req_error(req, NT_STATUS_END_OF_FILE);
319 p->sent += state->result.result_ssize_t;
320 SMB_ASSERT(p->sent <= p->length);
322 if (p->sent == p->length) {
323 TALLOC_FREE(state->fde);
329 * @brief Send all bytes to a socket
330 * @param[in] mem_ctx The memory context to hang the result off
331 * @param[in] ev The event context to work from
332 * @param[in] fd The socket to send to
333 * @param[in] buffer The buffer to send
334 * @param[in] length How many bytes to send
335 * @param[in] flags flags passed to send(2)
337 * async_sendall calls send(2) as long as it is necessary to send all of the
341 struct async_req *sendall_send(TALLOC_CTX *mem_ctx, struct event_context *ev,
342 int fd, const void *buffer, size_t length,
345 struct async_req *result;
346 struct async_syscall_state *state;
348 result = async_fde_syscall_new(
349 mem_ctx, ev, ASYNC_SYSCALL_SENDALL,
350 fd, EVENT_FD_WRITE, async_sendall_callback,
352 if (result == NULL) {
356 state->param.param_sendall.fd = fd;
357 state->param.param_sendall.buffer = buffer;
358 state->param.param_sendall.length = length;
359 state->param.param_sendall.flags = flags;
360 state->param.param_sendall.sent = 0;
365 NTSTATUS sendall_recv(struct async_req *req)
367 SMB_ASSERT(req->state >= ASYNC_REQ_DONE);
368 if (req->state == ASYNC_REQ_ERROR) {
375 * fde event handler for the "recv" syscall
376 * @param[in] ev The event context that sent us here
377 * @param[in] fde The file descriptor event associated with the recv
378 * @param[in] flags Can only be EVENT_FD_READ here
379 * @param[in] priv private data, "struct async_req *" in this case
382 static void async_recv_callback(struct event_context *ev,
383 struct fd_event *fde, uint16_t flags,
386 struct async_req *req = talloc_get_type_abort(
387 priv, struct async_req);
388 struct async_syscall_state *state = talloc_get_type_abort(
389 req->private_data, struct async_syscall_state);
390 struct param_recv *p = &state->param.param_recv;
392 SMB_ASSERT(state->syscall_type == ASYNC_SYSCALL_RECV);
394 state->result.result_ssize_t = recv(p->fd, p->buffer, p->length,
396 state->sys_errno = errno;
398 TALLOC_FREE(state->fde);
404 * Async version of recv(2)
405 * @param[in] mem_ctx The memory context to hang the result off
406 * @param[in] ev The event context to work from
407 * @param[in] fd The socket to recv from
408 * @param[in] buffer The buffer to recv into
409 * @param[in] length How many bytes to recv
410 * @param[in] flags flags passed to recv(2)
412 * This function is a direct counterpart of recv(2)
415 struct async_req *async_recv(TALLOC_CTX *mem_ctx, struct event_context *ev,
416 int fd, void *buffer, size_t length,
419 struct async_req *result;
420 struct async_syscall_state *state;
422 result = async_fde_syscall_new(
423 mem_ctx, ev, ASYNC_SYSCALL_RECV,
424 fd, EVENT_FD_READ, async_recv_callback,
427 if (result == NULL) {
431 state->param.param_recv.fd = fd;
432 state->param.param_recv.buffer = buffer;
433 state->param.param_recv.length = length;
434 state->param.param_recv.flags = flags;
440 * fde event handler for the "recvall" syscall group
441 * @param[in] ev The event context that sent us here
442 * @param[in] fde The file descriptor event associated with the recv
443 * @param[in] flags Can only be EVENT_FD_READ here
444 * @param[in] priv private data, "struct async_req *" in this case
447 static void async_recvall_callback(struct event_context *ev,
448 struct fd_event *fde, uint16_t flags,
451 struct async_req *req = talloc_get_type_abort(
452 priv, struct async_req);
453 struct async_syscall_state *state = talloc_get_type_abort(
454 req->private_data, struct async_syscall_state);
455 struct param_recvall *p = &state->param.param_recvall;
457 SMB_ASSERT(state->syscall_type == ASYNC_SYSCALL_RECVALL);
459 state->result.result_ssize_t = recv(p->fd,
460 (char *)p->buffer + p->received,
461 p->length - p->received, p->flags);
462 state->sys_errno = errno;
464 if (state->result.result_ssize_t == -1) {
465 async_req_error(req, map_nt_error_from_unix(state->sys_errno));
469 if (state->result.result_ssize_t == 0) {
470 async_req_error(req, NT_STATUS_END_OF_FILE);
474 p->received += state->result.result_ssize_t;
475 SMB_ASSERT(p->received <= p->length);
477 if (p->received == p->length) {
478 TALLOC_FREE(state->fde);
484 * Receive a specified number of bytes from a socket
485 * @param[in] mem_ctx The memory context to hang the result off
486 * @param[in] ev The event context to work from
487 * @param[in] fd The socket to recv from
488 * @param[in] buffer The buffer to recv into
489 * @param[in] length How many bytes to recv
490 * @param[in] flags flags passed to recv(2)
492 * async_recvall will call recv(2) until "length" bytes are received
495 struct async_req *recvall_send(TALLOC_CTX *mem_ctx, struct event_context *ev,
496 int fd, void *buffer, size_t length,
499 struct async_req *result;
500 struct async_syscall_state *state;
502 result = async_fde_syscall_new(
503 mem_ctx, ev, ASYNC_SYSCALL_RECVALL,
504 fd, EVENT_FD_READ, async_recvall_callback,
506 if (result == NULL) {
510 state->param.param_recvall.fd = fd;
511 state->param.param_recvall.buffer = buffer;
512 state->param.param_recvall.length = length;
513 state->param.param_recvall.flags = flags;
514 state->param.param_recvall.received = 0;
519 NTSTATUS recvall_recv(struct async_req *req)
521 SMB_ASSERT(req->state >= ASYNC_REQ_DONE);
522 if (req->state == ASYNC_REQ_ERROR) {
529 * fde event handler for connect(2)
530 * @param[in] ev The event context that sent us here
531 * @param[in] fde The file descriptor event associated with the connect
532 * @param[in] flags Indicate read/writeability of the socket
533 * @param[in] priv private data, "struct async_req *" in this case
536 static void async_connect_callback(struct event_context *ev,
537 struct fd_event *fde, uint16_t flags,
540 struct async_req *req = talloc_get_type_abort(
541 priv, struct async_req);
542 struct async_syscall_state *state = talloc_get_type_abort(
543 req->private_data, struct async_syscall_state);
544 struct param_connect *p = &state->param.param_connect;
546 SMB_ASSERT(state->syscall_type == ASYNC_SYSCALL_CONNECT);
548 TALLOC_FREE(state->fde);
551 * Stevens, Network Programming says that if there's a
552 * successful connect, the socket is only writable. Upon an
553 * error, it's both readable and writable.
555 if ((flags & (EVENT_FD_READ|EVENT_FD_WRITE))
556 == (EVENT_FD_READ|EVENT_FD_WRITE)) {
558 socklen_t err_len = sizeof(sockerr);
560 if (getsockopt(p->fd, SOL_SOCKET, SO_ERROR,
561 (void *)&sockerr, &err_len) == 0) {
565 state->sys_errno = errno;
567 DEBUG(10, ("connect returned %s\n", strerror(errno)));
569 sys_fcntl_long(p->fd, F_SETFL, p->old_sockflags);
571 async_req_error(req, map_nt_error_from_unix(state->sys_errno));
575 sys_fcntl_long(p->fd, F_SETFL, p->old_sockflags);
577 state->result.result_int = 0;
578 state->sys_errno = 0;
584 * @brief async version of connect(2)
585 * @param[in] mem_ctx The memory context to hang the result off
586 * @param[in] ev The event context to work from
587 * @param[in] fd The socket to recv from
588 * @param[in] address Where to connect?
589 * @param[in] address_len Length of *address
590 * @retval The async request
592 * This function sets the socket into non-blocking state to be able to call
593 * connect in an async state. This will be reset when the request is finished.
596 struct async_req *async_connect(TALLOC_CTX *mem_ctx, struct event_context *ev,
597 int fd, const struct sockaddr *address,
598 socklen_t address_len)
600 struct async_req *result;
601 struct async_syscall_state *state;
602 struct param_connect *p;
604 result = async_syscall_new(mem_ctx, ev, ASYNC_SYSCALL_CONNECT, &state);
605 if (result == NULL) {
608 p = &state->param.param_connect;
611 * We have to set the socket to nonblocking for async connect(2). Keep
612 * the old sockflags around.
615 p->old_sockflags = sys_fcntl_long(fd, F_GETFL, 0);
617 if (p->old_sockflags == -1) {
618 if (async_post_status(result, map_nt_error_from_unix(errno))) {
625 set_blocking(fd, true);
627 state->result.result_int = connect(fd, address, address_len);
629 if (state->result.result_int == 0) {
630 state->sys_errno = 0;
631 if (async_post_status(result, NT_STATUS_OK)) {
634 sys_fcntl_long(fd, F_SETFL, p->old_sockflags);
640 * A number of error messages show that something good is progressing
641 * and that we have to wait for readability.
643 * If none of them are present, bail out.
646 if (!(errno == EINPROGRESS || errno == EALREADY ||
650 errno == EAGAIN || errno == EINTR)) {
652 state->sys_errno = errno;
654 if (async_post_status(result, map_nt_error_from_unix(errno))) {
657 sys_fcntl_long(fd, F_SETFL, p->old_sockflags);
662 state->fde = event_add_fd(ev, state, fd,
663 EVENT_FD_READ | EVENT_FD_WRITE,
664 async_connect_callback, result);
665 if (state->fde == NULL) {
666 sys_fcntl_long(fd, F_SETFL, p->old_sockflags);
670 result->private_data = state;
672 state->param.param_connect.fd = fd;
673 state->param.param_connect.address = address;
674 state->param.param_connect.address_len = address_len;