123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136 |
- // SPDX-License-Identifier: GPL-2.0 or GPL-3.0
- // Copyright © 2018-2019 Ariadne Devos
- /* sHT -- accept connections from passive sockets (Unix) */
- #include "worker.h"
- #include <sHT/stream.h>
- #include <sHT/accept.h>
- #include <sHT/bugs.h>
- #include <sHT/compiler.h>
- #include <sHT/test.h>
- #include <errno.h>
- #include <stddef.h>
- #include <sys/epoll.h>
- #include <sys/socket.h>
- static const struct sHT_bug_option sHT_accept_bugs[] =
- { { .code = EBADFD, .msg = "passive socket not a fd" },
- { .code = EINVAL, .msg = "socket not listening" },
- { .code = ENOTSOCK, .msg = "not a socket" } };
- static int
- sHT_sys_accept(int fd, int flags)
- {
- int new_fd = accept4(fd, NULL, NULL, flags);
- if (sHT_lt0(new_fd))
- new_fd = -errno;
- return new_fd;
- }
- __attribute__((nonnull (1, 2)))
- static _Bool
- sHT_try_accept(struct sHT_worker *worker, struct sHT_task_accept *task)
- {
- /* It is called a subtask or child task, but sHT has no concept
- of any such DAG. */
- struct sHT_task_stream *subtask = sHT_accept_subtask(worker, task);
- if (sHT_null_p(subtask))
- return 1;
- int fd;
- retry:
- /* XXX: use errno correctly */
- fd = sHT_sys_accept(task->accept.fd, SOCK_NONBLOCK | SOCK_CLOEXEC);
- if (!sHT_lt0(fd)) {
- /* Only set this field. It is up to sHT_accept_subtask and
- sHT_accept_logic to fill in other fields. */
- subtask->stream.fd = fd;
- sHT_accept_logic(worker, task, subtask);
- return 0;
- }
- /* TODO: possibly unportable, assumes Linux
- Probably, only a few cases should be added. */
- int err = errno;
- switch(err) {
- #if EAGAIN != EWOULDBLOCK
- case EAGAIN: /* fallthrough */
- #endif
- case EWOULDBLOCK:
- sHT_hide_var(err);
- task->task.epollflags &= ~EPOLLIN;
- sHT_accept_abort(worker, task, subtask);
- return 1;
- case EMFILE:
- sHT_hide_var(err);
- worker->flags |= sHT_WORKER_LIMIT_FDS;
- return 1;
- case ENFILE:
- sHT_hide_var(err);
- /* not that we can really do anything about it,
- but provide a diagnostic anyway */
- worker->flags |= sHT_WORKER_SYSTEM_FDS;
- return 1;
- case ENOBUFS: /* fallthrough */
- case ENOMEM:
- sHT_hide_var(err);
- worker->flags |= sHT_WORKER_OOM;
- return 1;
- case EINTR:
- sHT_hide_var(err);
- goto retry;
- case EPROTO: /* fallthrough */
- case ECONNABORTED: /* fallthrough */
- /* firewall rules */
- case EPERM: /* fallthrough */
- sHT_hide_var(err);
- return 1;
- /* Linux accept4(2): Various Linux kernels can return other
- errors ... */
- case ENOSR: /* fallthrough */
- case ESOCKTNOSUPPORT: /* fallthrough */
- case EPROTONOSUPPORT: /* fallthrough */
- case ETIMEDOUT:
- sHT_hide_var(err);
- return 1;
- /* From Linux accept4(2) Error handling: */
- case ENETDOWN: /* fallthrough */
- case ENOPROTOOPT: /* fallthrough */
- case EHOSTDOWN: /* fallthrough */
- case ENONET: /* fallthrough */
- case EHOSTUNREACH: /* fallthrough */
- case EOPNOTSUPP: /* fallthrough */
- case ENETUNREACH:
- sHT_hide_var(err);
- return 1;
- default:
- sHT_hide_var(err);
- sHT_bug(sHT_accept_bugs, err);
- return 0;
- }
- }
- void
- sHT_accept_task(struct sHT_worker *worker, struct sHT_task_accept *task)
- {
- if (!sHT_and_any(task->task.epollflags, EPOLLIN))
- /* TODO: other values */
- return;
- /* Something you should now when debugging:
- if N connections are active,
- you have allocated enough tasks, papers, etc. to handle N
- connections,
- but there is a connection waiting,
- sHT_try_accept will nevertheless try to accept it,
- but as there are not enough resources, sHT_TASK_* will be set.
- So make sure that, in test cases, you allow an error message in
- this case (which I did not). */
- for (int i = 0; sHT_gt(task->accept.max_accept_iterations, i); i++) {
- _Bool err = sHT_try_accept(worker, task);
- if (sHT_nonzero_p(err))
- return;
- }
- }
|