| 1 | // Python wrapper for FANOS library in cpp/fanos_shared.h
 | 
| 2 | 
 | 
| 3 | #include <assert.h>
 | 
| 4 | #include <stdarg.h>  // va_list, etc.
 | 
| 5 | #include <stdio.h>  // vfprintf
 | 
| 6 | #include <stdlib.h>
 | 
| 7 | #include <sys/socket.h>
 | 
| 8 | 
 | 
| 9 | #include <Python.h>
 | 
| 10 | 
 | 
| 11 | #include "cpp/fanos_shared.h"
 | 
| 12 | 
 | 
| 13 | // Log messages to stderr.
 | 
| 14 | static void debug(const char* fmt, ...) {
 | 
| 15 | #if 0
 | 
| 16 |   va_list args;
 | 
| 17 |   va_start(args, fmt);
 | 
| 18 |   vfprintf(stderr, fmt, args);
 | 
| 19 |   va_end(args);
 | 
| 20 |   fprintf(stderr, "\n");
 | 
| 21 | #endif
 | 
| 22 | }
 | 
| 23 | 
 | 
| 24 | static PyObject *io_error;
 | 
| 25 | static PyObject *fanos_error;
 | 
| 26 | 
 | 
| 27 | static PyObject *
 | 
| 28 | func_recv(PyObject *self, PyObject *args) {
 | 
| 29 |   int sock_fd;
 | 
| 30 |   PyObject* fd_out;
 | 
| 31 | 
 | 
| 32 |   if (!PyArg_ParseTuple(args, "iO", &sock_fd, &fd_out)) {
 | 
| 33 |     return NULL;
 | 
| 34 |   }
 | 
| 35 | 
 | 
| 36 |   debug("fanos.recv %d\n", sock_fd);
 | 
| 37 | 
 | 
| 38 |   struct FanosError err = {0};
 | 
| 39 |   struct FanosResult res = {NULL, FANOS_INVALID_LEN};
 | 
| 40 |   int fds[FANOS_NUM_FDS] = { -1, -1, -1 };
 | 
| 41 | 
 | 
| 42 |   fanos_recv(sock_fd, fds, &res, &err);
 | 
| 43 | 
 | 
| 44 |   if (err.err_code != 0) {
 | 
| 45 |     // uses the errno global, which is apparently still set (see tests)
 | 
| 46 |     debug("func_recv errno = %d", err.err_code);
 | 
| 47 |     return PyErr_SetFromErrno(io_error);
 | 
| 48 |   }
 | 
| 49 |   if (err.value_err != NULL) {
 | 
| 50 |     PyErr_SetString(fanos_error, err.value_err);
 | 
| 51 |     return NULL;
 | 
| 52 |   }
 | 
| 53 | 
 | 
| 54 |   if (res.len == FANOS_EOF) {
 | 
| 55 |     Py_RETURN_NONE;  // EOF sentinel
 | 
| 56 |   }
 | 
| 57 |   for (int i = 0; i < 3; i++) {
 | 
| 58 |     if (fds[i] != -1 && PyList_Append(fd_out, PyInt_FromLong(fds[i])) != 0) {
 | 
| 59 |       PyErr_SetString(PyExc_RuntimeError, "Couldn't append()");
 | 
| 60 |       return NULL;
 | 
| 61 |     }
 | 
| 62 |   }
 | 
| 63 | 
 | 
| 64 |   PyObject *ret = PyString_FromStringAndSize(res.data, res.len);
 | 
| 65 |   free(res.data);
 | 
| 66 |   return ret;
 | 
| 67 | }
 | 
| 68 | 
 | 
| 69 | static PyObject *
 | 
| 70 | func_send(PyObject *self, PyObject *args) {
 | 
| 71 |   int sock_fd;
 | 
| 72 |   char *blob;
 | 
| 73 |   int blob_len;
 | 
| 74 |   int fds[FANOS_NUM_FDS] = { -1, -1, -1 };
 | 
| 75 | 
 | 
| 76 |   // 3 optional file descriptors
 | 
| 77 |   if (!PyArg_ParseTuple(args, "is#|iii",
 | 
| 78 |                         &sock_fd, &blob, &blob_len,
 | 
| 79 |                         &fds[0], &fds[1], &fds[2])) {
 | 
| 80 |     return NULL;
 | 
| 81 |   }
 | 
| 82 | 
 | 
| 83 |   debug("SEND fd %d blob %s\n", sock_fd, blob);
 | 
| 84 |   debug("%d %d %d\n", fds[0], fds[1], fds[2]);
 | 
| 85 | 
 | 
| 86 |   struct FanosError err = {0};
 | 
| 87 |   fanos_send(sock_fd, blob, blob_len, fds, &err);
 | 
| 88 |   if (err.err_code != 0) {
 | 
| 89 |     // uses the errno global, which is apparently still set (see tests)
 | 
| 90 |     debug("func_send errno %d", err.err_code);
 | 
| 91 |     return PyErr_SetFromErrno(io_error);
 | 
| 92 |   }
 | 
| 93 |   if (err.value_err != NULL) {
 | 
| 94 |     PyErr_SetString(fanos_error, err.value_err);
 | 
| 95 |     return NULL;
 | 
| 96 |   }
 | 
| 97 | 
 | 
| 98 |   Py_RETURN_NONE;
 | 
| 99 | }
 | 
| 100 | 
 | 
| 101 | static PyMethodDef methods[] = {
 | 
| 102 |   // Receive message and FDs from socket.
 | 
| 103 |   {"recv", func_recv, METH_VARARGS, ""},
 | 
| 104 | 
 | 
| 105 |   // Send a message across a socket.
 | 
| 106 |   {"send", func_send, METH_VARARGS, ""},
 | 
| 107 | 
 | 
| 108 |   {NULL, NULL},
 | 
| 109 | };
 | 
| 110 | 
 | 
| 111 | void initfanos(void) {
 | 
| 112 |   Py_InitModule("fanos", methods);
 | 
| 113 | 
 | 
| 114 |   // error with errno
 | 
| 115 |   io_error = PyErr_NewException("fanos.IOError", PyExc_IOError, NULL);
 | 
| 116 | 
 | 
| 117 |   // other protocol errors
 | 
| 118 |   fanos_error = PyErr_NewException("fanos.ValueError", PyExc_ValueError, NULL);
 | 
| 119 | }
 |