sendfilemodule.c 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406
  1. /*
  2. * $Id: sendfilemodule.c 101 2012-01-10 23:27:51Z g.rodola@gmail.com $
  3. */
  4. /*
  5. * pysendfile
  6. *
  7. * A Python module interface to sendfile(2)
  8. *
  9. * Original author:
  10. * Copyright (C) 2005 Ben Woolley <user tautolog at gmail>
  11. *
  12. * The AIX support code is:
  13. * Copyright (C) 2008,2009 Niklas Edmundsson <nikke@acc.umu.se>
  14. *
  15. * Rewritten from scratch and maintained by Giampaolo Rodola'
  16. * Copyright (C) 2009,2012 <g.rodola@gmail.com>
  17. *
  18. *
  19. * The MIT License
  20. *
  21. * Copyright (c) <2011> <Ben Woolley, Niklas Edmundsson, Giampaolo Rodola'>
  22. *
  23. * Permission is hereby granted, free of charge, to any person obtaining a copy
  24. * of this software and associated documentation files (the "Software"), to deal
  25. * in the Software without restriction, including without limitation the rights
  26. * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
  27. * copies of the Software, and to permit persons to whom the Software is
  28. * furnished to do so, subject to the following conditions:
  29. *
  30. * The above copyright notice and this permission notice shall be included in
  31. * all copies or substantial portions of the Software.
  32. *
  33. * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  34. * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  35. * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
  36. * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
  37. * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
  38. * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
  39. * THE SOFTWARE.
  40. */
  41. #include <Python.h>
  42. #include <stdlib.h>
  43. // force a compilation error if platform is not supported
  44. #if !defined(__FreeBSD__) && \
  45. !defined(__DragonFly__) && \
  46. !defined(__APPLE__) && \
  47. !defined(_AIX) && \
  48. !defined(__linux__) && \
  49. !defined(__sun)
  50. #error platfom not supported
  51. #endif
  52. static int
  53. _parse_off_t(PyObject* arg, void* addr)
  54. {
  55. #if !defined(HAVE_LARGEFILE_SUPPORT)
  56. *((off_t*)addr) = PyLong_AsLong(arg);
  57. #else
  58. *((off_t*)addr) = PyLong_AsLongLong(arg);
  59. #endif
  60. if (PyErr_Occurred())
  61. return 0;
  62. return 1;
  63. }
  64. /* --- begin FreeBSD / Dragonfly / OSX --- */
  65. #if defined(__FreeBSD__) || defined(__DragonFly__) || defined(__APPLE__)
  66. #include <sys/types.h>
  67. #include <sys/socket.h>
  68. #include <sys/uio.h>
  69. // needed on OSX - Python < 2.6
  70. #if defined(__APPLE__) && defined(_POSIX_C_SOURCE)
  71. struct sf_hdtr {
  72. struct iovec *headers;
  73. int hdr_cnt;
  74. struct iovec *trailers;
  75. int trl_cnt;
  76. };
  77. #endif
  78. static PyObject *
  79. method_sendfile(PyObject *self, PyObject *args, PyObject *kwdict)
  80. {
  81. int fd;
  82. int sock;
  83. int flags = 0;
  84. off_t offset;
  85. Py_ssize_t ret;
  86. Py_ssize_t nbytes;
  87. char * head = NULL;
  88. char * tail = NULL;
  89. Py_ssize_t head_len = 0;
  90. Py_ssize_t tail_len = 0;
  91. off_t sent;
  92. struct sf_hdtr hdtr;
  93. PyObject *offobj;
  94. static char *keywords[] = {"out", "in", "offset", "nbytes", "header",
  95. "trailer", "flags", NULL};
  96. if (!PyArg_ParseTupleAndKeywords(args, kwdict,
  97. "iiOn|s#s#i:sendfile",
  98. keywords, &fd, &sock, &offobj, &nbytes,
  99. &head, &head_len, &tail, &tail_len,
  100. &flags)) {
  101. return NULL;
  102. }
  103. if (!_parse_off_t(offobj, &offset))
  104. return NULL;
  105. #ifdef __APPLE__
  106. sent = nbytes;
  107. #endif
  108. if (head_len != 0 || tail_len != 0) {
  109. struct iovec ivh = {head, head_len};
  110. struct iovec ivt = {tail, tail_len};
  111. hdtr.headers = &ivh;
  112. hdtr.hdr_cnt = 1;
  113. hdtr.trailers = &ivt;
  114. hdtr.trl_cnt = 1;
  115. Py_BEGIN_ALLOW_THREADS
  116. #ifdef __APPLE__
  117. sent += head_len;
  118. ret = sendfile(sock, fd, offset, &sent, &hdtr, flags);
  119. #else
  120. ret = sendfile(sock, fd, offset, nbytes, &hdtr, &sent, flags);
  121. #endif
  122. Py_END_ALLOW_THREADS
  123. }
  124. else {
  125. Py_BEGIN_ALLOW_THREADS
  126. #ifdef __APPLE__
  127. ret = sendfile(sock, fd, offset, &sent, NULL, flags);
  128. #else
  129. ret = sendfile(sock, fd, offset, nbytes, NULL, &sent, flags);
  130. #endif
  131. Py_END_ALLOW_THREADS
  132. }
  133. if (ret < 0) {
  134. if ((errno == EAGAIN) || (errno == EBUSY) || (errno == EWOULDBLOCK)) {
  135. if (sent != 0) {
  136. // some data has been sent
  137. errno = 0;
  138. goto done;
  139. }
  140. else {
  141. // no data has been sent; upper application is supposed
  142. // to retry on EAGAIN / EBUSY / EWOULDBLOCK
  143. PyErr_SetFromErrno(PyExc_OSError);
  144. return NULL;
  145. }
  146. }
  147. PyErr_SetFromErrno(PyExc_OSError);
  148. return NULL;
  149. }
  150. goto done;
  151. done:
  152. #if defined(HAVE_LARGEFILE_SUPPORT)
  153. return Py_BuildValue("L", sent);
  154. #else
  155. return Py_BuildValue("l", sent);
  156. #endif
  157. }
  158. /* --- end OSX / FreeBSD / Dragonfly --- */
  159. /* --- begin AIX --- */
  160. #elif defined(_AIX)
  161. #include <sys/socket.h>
  162. static PyObject *
  163. method_sendfile(PyObject *self, PyObject *args)
  164. {
  165. int out_fd, in_fd;
  166. off_t offset;
  167. size_t nbytes;
  168. char *hdr=NULL, *trail=NULL;
  169. int hdrsize, trailsize;
  170. ssize_t sts=0;
  171. struct sf_parms sf_iobuf;
  172. int rc;
  173. if (!PyArg_ParseTuple(args, "iiLk|s#s#",
  174. &out_fd, &in_fd, &offset, &nbytes, &hdr, &hdrsize,
  175. &trail, &trailsize))
  176. return NULL;
  177. if(hdr != NULL) {
  178. sf_iobuf.header_data = hdr;
  179. sf_iobuf.header_length = hdrsize;
  180. }
  181. else {
  182. sf_iobuf.header_data = NULL;
  183. sf_iobuf.header_length = 0;
  184. }
  185. if(trail != NULL) {
  186. sf_iobuf.trailer_data = trail;
  187. sf_iobuf.trailer_length = trailsize;
  188. }
  189. else {
  190. sf_iobuf.trailer_data = NULL;
  191. sf_iobuf.trailer_length = 0;
  192. }
  193. sf_iobuf.file_descriptor = in_fd;
  194. sf_iobuf.file_offset = offset;
  195. sf_iobuf.file_bytes = nbytes;
  196. Py_BEGIN_ALLOW_THREADS;
  197. do {
  198. sf_iobuf.bytes_sent = 0; /* Really needed? */
  199. rc = send_file(&out_fd, &sf_iobuf, SF_DONT_CACHE);
  200. sts += sf_iobuf.bytes_sent;
  201. } while( rc == 1 || (rc == -1 && errno == EINTR) );
  202. Py_END_ALLOW_THREADS;
  203. offset = sf_iobuf.file_offset;
  204. if (rc == -1) {
  205. PyErr_SetFromErrno(PyExc_OSError);
  206. return NULL;
  207. }
  208. else {
  209. #if defined(HAVE_LARGEFILE_SUPPORT)
  210. return Py_BuildValue("L", sts);
  211. #else
  212. return Py_BuildValue("l", sts);
  213. #endif
  214. }
  215. }
  216. /* --- end AIX --- */
  217. /* --- start Linux --- */
  218. #elif defined (__linux__)
  219. #include <sys/sendfile.h>
  220. static PyObject *
  221. method_sendfile(PyObject *self, PyObject *args, PyObject *kwdict)
  222. {
  223. int out_fd, in_fd;
  224. off_t offset;
  225. Py_ssize_t nbytes;
  226. Py_ssize_t sent;
  227. PyObject *offobj;
  228. if (!PyArg_ParseTuple(args, "iiOn", &out_fd, &in_fd, &offobj, &nbytes)) {
  229. return NULL;
  230. }
  231. if (offobj == Py_None) {
  232. Py_BEGIN_ALLOW_THREADS;
  233. sent = sendfile(out_fd, in_fd, NULL, nbytes);
  234. Py_END_ALLOW_THREADS;
  235. }
  236. else {
  237. if (!_parse_off_t(offobj, &offset))
  238. return NULL;
  239. Py_BEGIN_ALLOW_THREADS;
  240. sent = sendfile(out_fd, in_fd, &offset, nbytes);
  241. Py_END_ALLOW_THREADS;
  242. }
  243. if (sent == -1)
  244. return PyErr_SetFromErrno(PyExc_OSError);
  245. return Py_BuildValue("n", sent);
  246. }
  247. /* --- end Linux --- */
  248. /* --- begin SUN OS --- */
  249. #elif defined(__sun)
  250. #include <sys/sendfile.h>
  251. static PyObject *
  252. method_sendfile(PyObject *self, PyObject *args, PyObject *kwdict)
  253. {
  254. int out_fd;
  255. int in_fd;
  256. off_t offset;
  257. Py_ssize_t nbytes;
  258. Py_ssize_t sent;
  259. PyObject *offobj;
  260. if (!PyArg_ParseTuple(args, "iiOn", &out_fd, &in_fd, &offobj, &nbytes)) {
  261. return NULL;
  262. }
  263. if (!_parse_off_t(offobj, &offset))
  264. return NULL;
  265. sent = sendfile(out_fd, in_fd, &offset, nbytes);
  266. if (sent == -1)
  267. return PyErr_SetFromErrno(PyExc_OSError);
  268. return Py_BuildValue("n", sent);
  269. }
  270. #else
  271. /* --- end SUN OS --- */
  272. #error platfom not supported
  273. #endif
  274. /* --- module initialization --- */
  275. struct module_state {
  276. PyObject *error;
  277. };
  278. #if PY_MAJOR_VERSION >= 3
  279. #define GETSTATE(m) ((struct module_state*)PyModule_GetState(m))
  280. #else
  281. #define GETSTATE(m) (&_state)
  282. static struct module_state _state;
  283. #endif
  284. static PyMethodDef sendfile_methods[] = {
  285. {"sendfile", (PyCFunction)method_sendfile, METH_VARARGS | METH_KEYWORDS,
  286. "sendfile(out, in, offset, nbytes, header=\"\", trailer=\"\", flags=0)\n\n"
  287. "Copy nbytes bytes from file descriptor in (a regular file) to\n"
  288. "file descriptor out (a socket) starting at offset.\n"
  289. "Return the number of bytes just being sent. When the end of\n"
  290. "file is reached return 0.\n"
  291. "On Linux, if offset is given as None, the bytes are read from\n"
  292. "the current position of in and the position of in is updated.\n"
  293. "headers and trailers are strings that are written before and\n"
  294. "after the data from in is written. In cross platform applications\n"
  295. "their usage is discouraged (socket.send() or socket.sendall()\n"
  296. "can be used instead).\n"
  297. "On Solaris, out may be the file descriptor of a regular file\n"
  298. "or the file descriptor of a socket. On all other platforms,\n"
  299. "out must be the file descriptor of an open socket.\n"
  300. "flags argument is only supported on FreeBSD.\n"
  301. },
  302. {NULL, NULL}
  303. };
  304. #if PY_MAJOR_VERSION >= 3
  305. static int sendfile_traverse(PyObject *m, visitproc visit, void *arg) {
  306. Py_VISIT(GETSTATE(m)->error);
  307. return 0;
  308. }
  309. static int sendfile_clear(PyObject *m) {
  310. Py_CLEAR(GETSTATE(m)->error);
  311. return 0;
  312. }
  313. static struct PyModuleDef moduledef = {
  314. PyModuleDef_HEAD_INIT,
  315. "sendfile",
  316. NULL,
  317. sizeof(struct module_state),
  318. sendfile_methods,
  319. NULL,
  320. sendfile_traverse,
  321. sendfile_clear,
  322. NULL
  323. };
  324. #define INITERROR return NULL
  325. PyObject *
  326. PyInit_sendfile(void)
  327. #else
  328. #define INITERROR return
  329. void
  330. initsendfile(void)
  331. #endif
  332. {
  333. #if PY_MAJOR_VERSION >= 3
  334. PyObject *module = PyModule_Create(&moduledef);
  335. #else
  336. PyObject *module = Py_InitModule("sendfile", sendfile_methods);
  337. #endif
  338. #ifdef SF_NODISKIO
  339. PyModule_AddIntConstant(module, "SF_NODISKIO", SF_NODISKIO);
  340. #endif
  341. #ifdef SF_MNOWAIT
  342. PyModule_AddIntConstant(module, "SF_MNOWAIT", SF_MNOWAIT);
  343. #endif
  344. #ifdef SF_SYNC
  345. PyModule_AddIntConstant(module, "SF_SYNC", SF_SYNC);
  346. #endif
  347. if (module == NULL)
  348. INITERROR;
  349. struct module_state *st = GETSTATE(module);
  350. st->error = PyErr_NewException("sendfile.Error", NULL, NULL);
  351. if (st->error == NULL) {
  352. Py_DECREF(module);
  353. INITERROR;
  354. }
  355. #if PY_MAJOR_VERSION >= 3
  356. return module;
  357. #endif
  358. }