OILS / pyext / fanos.c View on Github | oilshell.org

119 lines, 73 significant
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.
14static 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
24static PyObject *io_error;
25static PyObject *fanos_error;
26
27static PyObject *
28func_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
69static PyObject *
70func_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
101static 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
111void 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}