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