| 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 | }
|