FileNo.c 8.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275
  1. /* vim: set expandtab ts=4 sw=4: */
  2. /*
  3. * You may redistribute this program and/or modify it under the terms of
  4. * the GNU General Public License as published by the Free Software Foundation,
  5. * either version 3 of the License, or (at your option) any later version.
  6. *
  7. * This program is distributed in the hope that it will be useful,
  8. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  9. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  10. * GNU General Public License for more details.
  11. *
  12. * You should have received a copy of the GNU General Public License
  13. * along with this program. If not, see <https://www.gnu.org/licenses/>.
  14. */
  15. #include "util/events/libuv/UvWrapper.h"
  16. #include "benc/String.h"
  17. #include "memory/Allocator.h"
  18. #include "util/events/FileNo.h"
  19. #include "util/events/libuv/EventBase_pvt.h"
  20. #include "util/log/Log.h"
  21. #include "util/Identity.h"
  22. #include <inttypes.h>
  23. #include <stdio.h>
  24. // This structure used to be public and accessable.
  25. struct FileNo
  26. {
  27. /** The name of the file eg: "/tmp/cjdns_fileno_foo" */
  28. const char* const pipePath;
  29. void* userData;
  30. enum FileNo_Type type;
  31. struct EventBase* const base;
  32. struct Allocator* alloc;
  33. struct Log* logger;
  34. };
  35. struct FileNoContext
  36. {
  37. struct FileNo_Promise pub;
  38. struct FileNo* fileno;
  39. Identity
  40. };
  41. struct FileNo_pvt
  42. {
  43. struct FileNo pub;
  44. uv_pipe_t server;
  45. uv_pipe_t peer;
  46. /** Job to close the handles when the allocator is freed */
  47. struct Allocator_OnFreeJob* closeHandlesOnFree;
  48. int isActive;
  49. Identity
  50. };
  51. static void onClose(uv_handle_t* handle)
  52. {
  53. struct FileNo_pvt* fileno = Identity_check((struct FileNo_pvt*)handle->data);
  54. handle->data = NULL;
  55. if (fileno->closeHandlesOnFree && !fileno->server.data && !fileno->peer.data) {
  56. Allocator_onFreeComplete((struct Allocator_OnFreeJob*) fileno->closeHandlesOnFree);
  57. }
  58. }
  59. #if FileNo_PADDING_AMOUNT < 8
  60. #error
  61. #endif
  62. #define ALLOC(buff) (((struct Allocator**) &(buff[-(8 + (((uintptr_t)buff) % 8))]))[0])
  63. static void incoming(uv_pipe_t* stream,
  64. ssize_t nread,
  65. const uv_buf_t* buf,
  66. uv_handle_type pending)
  67. {
  68. // Grab out the allocator which was placed there by allocate()
  69. struct Allocator* alloc = buf->base ? ALLOC(buf->base) : NULL;
  70. Assert_true(pending == UV_UNKNOWN_HANDLE);
  71. if (nread < 0) {
  72. uv_close((uv_handle_t*) stream, onClose);
  73. #ifndef win32
  74. } else {
  75. struct FileNo_pvt* fileno = Identity_check((struct FileNo_pvt*) stream->data);
  76. if (fileno->peer.accepted_fd != -1) {
  77. struct FileNoContext* fctx = (struct FileNoContext*) fileno->pub.userData;
  78. if (fctx->pub.callback) {
  79. fctx->pub.callback(&fctx->pub, fileno->peer.accepted_fd);
  80. }
  81. }
  82. #endif
  83. }
  84. if (alloc) {
  85. Allocator_free(alloc);
  86. }
  87. }
  88. static void allocate(uv_handle_t* handle, size_t size, uv_buf_t* buf)
  89. {
  90. struct FileNo_pvt* pipe = Identity_check((struct FileNo_pvt*) handle->data);
  91. size = FileNo_BUFFER_CAP;
  92. size_t fullSize = size + FileNo_PADDING_AMOUNT;
  93. struct Allocator* child = Allocator_child(pipe->pub.alloc);
  94. char* buff = Allocator_malloc(child, fullSize);
  95. buff += FileNo_PADDING_AMOUNT;
  96. ALLOC(buff) = child;
  97. buf->base = buff;
  98. buf->len = size;
  99. }
  100. static void connected(uv_connect_t* req, int status)
  101. {
  102. uv_stream_t* link = req->handle;
  103. struct FileNo_pvt* fileno = Identity_check((struct FileNo_pvt*) link->data);
  104. Log_debug(fileno->pub.logger, "FileNo [%s] established connection", fileno->pub.pipePath);
  105. int ret;
  106. if (status) {
  107. Log_info(fileno->pub.logger, "uv_pipe_connect() failed for pipe [%s] [%s]",
  108. fileno->pub.pipePath, uv_strerror(status) );
  109. uv_close((uv_handle_t*) &fileno->peer, onClose);
  110. } else if ((ret = uv_read2_start((uv_stream_t*)&fileno->peer, allocate, incoming))) {
  111. Log_info(fileno->pub.logger, "uv_read2_start() failed for pipe [%s] [%s]",
  112. fileno->pub.pipePath, uv_strerror(ret));
  113. uv_close((uv_handle_t*) &fileno->peer, onClose);
  114. } else {
  115. fileno->isActive = 1;
  116. }
  117. }
  118. static void listenCallback(uv_stream_t* server, int status)
  119. {
  120. struct FileNo_pvt* fileno = Identity_check((struct FileNo_pvt*) server->data);
  121. if (fileno->isActive) {
  122. // first connection wins.
  123. return;
  124. }
  125. if (status == -1) {
  126. Log_info(fileno->pub.logger, "failed to accept pipe connection [%s] [%s]",
  127. fileno->pub.pipePath, uv_strerror(status) );
  128. return;
  129. }
  130. Log_info(fileno->pub.logger, "Try accept pipe connection [%s]",
  131. fileno->pub.pipePath);
  132. int ret = uv_accept(server, (uv_stream_t*) &fileno->peer);
  133. if (ret) {
  134. Log_warn(fileno->pub.logger, "uv_accept() failed: pipe [%s] [%s]",
  135. fileno->pub.pipePath, uv_strerror(ret) );
  136. uv_close((uv_handle_t*) &fileno->peer, onClose);
  137. } else {
  138. uv_connect_t req = { .handle = (uv_stream_t*) &fileno->peer };
  139. connected(&req, 0);
  140. }
  141. }
  142. static int closeHandlesOnFree(struct Allocator_OnFreeJob* job)
  143. {
  144. struct FileNo_pvt* fileno = Identity_check((struct FileNo_pvt*)job->userData);
  145. fileno->closeHandlesOnFree = job;
  146. int skip = 2;
  147. if (fileno->server.data) {
  148. uv_close((uv_handle_t*) &fileno->server, NULL);
  149. skip--;
  150. }
  151. if (fileno->peer.data) {
  152. uv_close((uv_handle_t*) &fileno->peer, NULL);
  153. skip--;
  154. }
  155. if (skip == 2) {
  156. return 0;
  157. }
  158. return Allocator_ONFREE_ASYNC;
  159. }
  160. static struct FileNo* FileNo_new(const char* path,
  161. struct EventBase* eb,
  162. struct Except* eh,
  163. struct Log* logger,
  164. struct Allocator* userAlloc)
  165. {
  166. struct EventBase_pvt* ctx = EventBase_privatize(eb);
  167. struct Allocator* alloc = Allocator_child(userAlloc);
  168. String* pathStr = String_new(path, alloc);
  169. struct FileNo_pvt* out = Allocator_clone(alloc, (&(struct FileNo_pvt) {
  170. .pub = {
  171. .pipePath = pathStr->bytes,
  172. .base = eb,
  173. .logger = logger,
  174. .alloc = alloc
  175. }
  176. }));
  177. int ret = uv_pipe_init(ctx->loop, &out->peer, 1);
  178. if (ret) {
  179. Except_throw(eh, "uv_pipe_init() failed [%s]", uv_strerror(ret));
  180. }
  181. ret = uv_pipe_init(ctx->loop, &out->server, 1);
  182. if (ret) {
  183. Except_throw(eh, "uv_pipe_init() failed [%s]", uv_strerror(ret));
  184. }
  185. Allocator_onFree(alloc, closeHandlesOnFree, out);
  186. out->peer.data = out;
  187. out->server.data = out;
  188. Identity_set(out);
  189. uv_fs_t req;
  190. uv_fs_unlink(out->peer.loop, &req, out->pub.pipePath, NULL);
  191. ret = uv_pipe_bind(&out->server, out->pub.pipePath);
  192. if (!ret) {
  193. ret = uv_listen((uv_stream_t*) &out->server, 1, listenCallback);
  194. if (ret) {
  195. Except_throw(eh, "uv_listen() failed [%s] for pipe [%s]",
  196. uv_strerror(ret), out->pub.pipePath);
  197. }
  198. return &out->pub;
  199. }
  200. Except_throw(eh, "uv_pipe_bind() failed [%s] for pipe [%s]",
  201. uv_strerror(ret), out->pub.pipePath);
  202. return &out->pub;
  203. }
  204. static struct FileNo_Promise* FileNo_newFile(const char* path,
  205. struct EventBase* eb,
  206. struct Except* eh,
  207. struct Log* logger,
  208. struct Allocator* alloc)
  209. {
  210. struct FileNo* fn = FileNo_new(path, eb, eh, logger, alloc);
  211. struct FileNoContext* fctx = Allocator_clone(fn->alloc, (&(struct FileNoContext) {
  212. .pub = {
  213. .alloc = fn->alloc
  214. },
  215. .fileno = fn
  216. }));
  217. Identity_set(fctx);
  218. fn->userData = fctx;
  219. return &fctx->pub;
  220. }
  221. struct FileNo_Promise* FileNo_import(const char* path,
  222. struct EventBase* eb,
  223. struct Except* eh,
  224. struct Log* logger,
  225. struct Allocator* alloc)
  226. {
  227. return FileNo_newFile(path, eb, eh, logger, alloc);
  228. }