Mercurial > hg
changeset 27473:34a37a2e03e6
osutil: implement recvmsg() of SCM_RIGHTS for chg command server
It will be used to attach client's stdio files to a background chg command
server.
The socket module of Python 2.x doesn't provide recvmsg(). This could be
implemented by using ctypes, but it would be less portable than the C version
because the handling of socket ancillary data heavily depends on preprocessor.
Also, some length fields are wrongly typed in the Linux kernel.
author | Yuya Nishihara <yuya@tcha.org> |
---|---|
date | Thu, 17 Dec 2015 23:41:46 +0900 |
parents | 3dea4eae4eeb |
children | e517a89c24e1 |
files | mercurial/osutil.c |
diffstat | 1 files changed, 67 insertions(+), 0 deletions(-) [+] |
line wrap: on
line diff
--- a/mercurial/osutil.c Mon Dec 21 14:52:18 2015 -0600 +++ b/mercurial/osutil.c Thu Dec 17 23:41:46 2015 +0900 @@ -11,6 +11,7 @@ #include <Python.h> #include <fcntl.h> #include <stdio.h> +#include <stdlib.h> #include <string.h> #include <errno.h> @@ -19,6 +20,7 @@ #include <io.h> #else #include <dirent.h> +#include <sys/socket.h> #include <sys/stat.h> #include <sys/types.h> #include <unistd.h> @@ -648,6 +650,69 @@ return NULL; } +/* + * recvfds() simply does not release GIL during blocking io operation because + * command server is known to be single-threaded. + */ + +static ssize_t recvfdstobuf(int sockfd, int **rfds, void *cbuf, size_t cbufsize) +{ + char dummy[1]; + struct iovec iov = {dummy, sizeof(dummy)}; + struct msghdr msgh = {0}; + struct cmsghdr *cmsg; + + msgh.msg_iov = &iov; + msgh.msg_iovlen = 1; + msgh.msg_control = cbuf; + msgh.msg_controllen = (socklen_t)cbufsize; + if (recvmsg(sockfd, &msgh, 0) < 0) + return -1; + + for (cmsg = CMSG_FIRSTHDR(&msgh); cmsg; + cmsg = CMSG_NXTHDR(&msgh, cmsg)) { + if (cmsg->cmsg_level != SOL_SOCKET || + cmsg->cmsg_type != SCM_RIGHTS) + continue; + *rfds = (int *)CMSG_DATA(cmsg); + return (cmsg->cmsg_len - CMSG_LEN(0)) / sizeof(int); + } + + *rfds = cbuf; + return 0; +} + +static PyObject *recvfds(PyObject *self, PyObject *args) +{ + int sockfd; + int *rfds = NULL; + ssize_t rfdscount, i; + char cbuf[256]; + PyObject *rfdslist = NULL; + + if (!PyArg_ParseTuple(args, "i", &sockfd)) + return NULL; + + rfdscount = recvfdstobuf(sockfd, &rfds, cbuf, sizeof(cbuf)); + if (rfdscount < 0) + return PyErr_SetFromErrno(PyExc_OSError); + + rfdslist = PyList_New(rfdscount); + if (!rfdslist) + goto bail; + for (i = 0; i < rfdscount; i++) { + PyObject *obj = PyInt_FromLong(rfds[i]); + if (!obj) + goto bail; + PyList_SET_ITEM(rfdslist, i, obj); + } + return rfdslist; + +bail: + Py_XDECREF(rfdslist); + return NULL; +} + #endif /* ndef _WIN32 */ static PyObject *listdir(PyObject *self, PyObject *args, PyObject *kwargs) @@ -816,6 +881,8 @@ {"statfiles", (PyCFunction)statfiles, METH_VARARGS | METH_KEYWORDS, "stat a series of files or symlinks\n" "Returns None for non-existent entries and entries of other types.\n"}, + {"recvfds", (PyCFunction)recvfds, METH_VARARGS, + "receive list of file descriptors via socket\n"}, #endif #ifdef __APPLE__ {