accept.c 3.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136
  1. // SPDX-License-Identifier: GPL-2.0 or GPL-3.0
  2. // Copyright © 2018-2019 Ariadne Devos
  3. /* sHT -- accept connections from passive sockets (Unix) */
  4. #include "worker.h"
  5. #include <sHT/stream.h>
  6. #include <sHT/accept.h>
  7. #include <sHT/bugs.h>
  8. #include <sHT/compiler.h>
  9. #include <sHT/test.h>
  10. #include <errno.h>
  11. #include <stddef.h>
  12. #include <sys/epoll.h>
  13. #include <sys/socket.h>
  14. static const struct sHT_bug_option sHT_accept_bugs[] =
  15. { { .code = EBADFD, .msg = "passive socket not a fd" },
  16. { .code = EINVAL, .msg = "socket not listening" },
  17. { .code = ENOTSOCK, .msg = "not a socket" } };
  18. static int
  19. sHT_sys_accept(int fd, int flags)
  20. {
  21. int new_fd = accept4(fd, NULL, NULL, flags);
  22. if (sHT_lt0(new_fd))
  23. new_fd = -errno;
  24. return new_fd;
  25. }
  26. __attribute__((nonnull (1, 2)))
  27. static _Bool
  28. sHT_try_accept(struct sHT_worker *worker, struct sHT_task_accept *task)
  29. {
  30. /* It is called a subtask or child task, but sHT has no concept
  31. of any such DAG. */
  32. struct sHT_task_stream *subtask = sHT_accept_subtask(worker, task);
  33. if (sHT_null_p(subtask))
  34. return 1;
  35. int fd;
  36. retry:
  37. /* XXX: use errno correctly */
  38. fd = sHT_sys_accept(task->accept.fd, SOCK_NONBLOCK | SOCK_CLOEXEC);
  39. if (!sHT_lt0(fd)) {
  40. /* Only set this field. It is up to sHT_accept_subtask and
  41. sHT_accept_logic to fill in other fields. */
  42. subtask->stream.fd = fd;
  43. sHT_accept_logic(worker, task, subtask);
  44. return 0;
  45. }
  46. /* TODO: possibly unportable, assumes Linux
  47. Probably, only a few cases should be added. */
  48. int err = errno;
  49. switch(err) {
  50. #if EAGAIN != EWOULDBLOCK
  51. case EAGAIN: /* fallthrough */
  52. #endif
  53. case EWOULDBLOCK:
  54. sHT_hide_var(err);
  55. task->task.epollflags &= ~EPOLLIN;
  56. sHT_accept_abort(worker, task, subtask);
  57. return 1;
  58. case EMFILE:
  59. sHT_hide_var(err);
  60. worker->flags |= sHT_WORKER_LIMIT_FDS;
  61. return 1;
  62. case ENFILE:
  63. sHT_hide_var(err);
  64. /* not that we can really do anything about it,
  65. but provide a diagnostic anyway */
  66. worker->flags |= sHT_WORKER_SYSTEM_FDS;
  67. return 1;
  68. case ENOBUFS: /* fallthrough */
  69. case ENOMEM:
  70. sHT_hide_var(err);
  71. worker->flags |= sHT_WORKER_OOM;
  72. return 1;
  73. case EINTR:
  74. sHT_hide_var(err);
  75. goto retry;
  76. case EPROTO: /* fallthrough */
  77. case ECONNABORTED: /* fallthrough */
  78. /* firewall rules */
  79. case EPERM: /* fallthrough */
  80. sHT_hide_var(err);
  81. return 1;
  82. /* Linux accept4(2): Various Linux kernels can return other
  83. errors ... */
  84. case ENOSR: /* fallthrough */
  85. case ESOCKTNOSUPPORT: /* fallthrough */
  86. case EPROTONOSUPPORT: /* fallthrough */
  87. case ETIMEDOUT:
  88. sHT_hide_var(err);
  89. return 1;
  90. /* From Linux accept4(2) Error handling: */
  91. case ENETDOWN: /* fallthrough */
  92. case ENOPROTOOPT: /* fallthrough */
  93. case EHOSTDOWN: /* fallthrough */
  94. case ENONET: /* fallthrough */
  95. case EHOSTUNREACH: /* fallthrough */
  96. case EOPNOTSUPP: /* fallthrough */
  97. case ENETUNREACH:
  98. sHT_hide_var(err);
  99. return 1;
  100. default:
  101. sHT_hide_var(err);
  102. sHT_bug(sHT_accept_bugs, err);
  103. return 0;
  104. }
  105. }
  106. void
  107. sHT_accept_task(struct sHT_worker *worker, struct sHT_task_accept *task)
  108. {
  109. if (!sHT_and_any(task->task.epollflags, EPOLLIN))
  110. /* TODO: other values */
  111. return;
  112. /* Something you should now when debugging:
  113. if N connections are active,
  114. you have allocated enough tasks, papers, etc. to handle N
  115. connections,
  116. but there is a connection waiting,
  117. sHT_try_accept will nevertheless try to accept it,
  118. but as there are not enough resources, sHT_TASK_* will be set.
  119. So make sure that, in test cases, you allow an error message in
  120. this case (which I did not). */
  121. for (int i = 0; sHT_gt(task->accept.max_accept_iterations, i); i++) {
  122. _Bool err = sHT_try_accept(worker, task);
  123. if (sHT_nonzero_p(err))
  124. return;
  125. }
  126. }