1 /*
2  * Python bindings for wpa_ctrl (wpa_supplicant/hostapd control interface)
3  * Copyright (c) 2013, Jouni Malinen <j@w1.fi>
4  *
5  * This software may be distributed under the terms of the BSD license.
6  * See README for more details.
7  */
8 
9 #include <Python.h>
10 #include <structmember.h>
11 
12 #include "wpa_ctrl.h"
13 
14 
15 struct wpaspy_obj {
16 	PyObject_HEAD
17 	struct wpa_ctrl *ctrl;
18 	int attached;
19 };
20 
21 static PyObject *wpaspy_error;
22 
23 
wpaspy_open(struct wpaspy_obj * self,PyObject * args)24 static int wpaspy_open(struct wpaspy_obj *self, PyObject *args)
25 {
26 	const char *path;
27 
28 	if (!PyArg_ParseTuple(args, "s", &path))
29 		return -1;
30 	self->ctrl = wpa_ctrl_open(path);
31 	if (self->ctrl == NULL)
32 		return -1;
33 	self->attached = 0;
34 	return 0;
35 }
36 
37 
wpaspy_close(struct wpaspy_obj * self)38 static void wpaspy_close(struct wpaspy_obj *self)
39 {
40 	if (self->ctrl) {
41 		if (self->attached)
42 			wpa_ctrl_detach(self->ctrl);
43 		wpa_ctrl_close(self->ctrl);
44 		self->ctrl = NULL;
45 	}
46 
47 	PyObject_Del(self);
48 }
49 
50 
wpaspy_request(struct wpaspy_obj * self,PyObject * args)51 static PyObject * wpaspy_request(struct wpaspy_obj *self, PyObject *args)
52 {
53 	const char *cmd;
54 	char buf[4096];
55 	size_t buflen;
56 	int ret;
57 
58 	if (!PyArg_ParseTuple(args, "s", &cmd))
59 		return NULL;
60 
61 	buflen = sizeof(buf) - 1;
62 	ret = wpa_ctrl_request(self->ctrl, cmd, strlen(cmd), buf, &buflen,
63 			       NULL);
64 	if (ret == -2) {
65 		PyErr_SetString(wpaspy_error, "Request timed out");
66 		return NULL;
67 	}
68 	if (ret) {
69 		PyErr_SetString(wpaspy_error, "Request failed");
70 		return NULL;
71 	}
72 
73 	buf[buflen] = '\0';
74 	return Py_BuildValue("s", buf);
75 }
76 
77 
wpaspy_attach(struct wpaspy_obj * self)78 static PyObject * wpaspy_attach(struct wpaspy_obj *self)
79 {
80 	int ret;
81 
82 	if (self->attached)
83 		Py_RETURN_NONE;
84 
85 	ret = wpa_ctrl_attach(self->ctrl);
86 	if (ret) {
87 		PyErr_SetString(wpaspy_error, "Attach failed");
88 		return NULL;
89 	}
90 	Py_RETURN_NONE;
91 }
92 
93 
wpaspy_detach(struct wpaspy_obj * self)94 static PyObject * wpaspy_detach(struct wpaspy_obj *self)
95 {
96 	int ret;
97 
98 	if (!self->attached)
99 		Py_RETURN_NONE;
100 
101 	ret = wpa_ctrl_detach(self->ctrl);
102 	if (ret) {
103 		PyErr_SetString(wpaspy_error, "Detach failed");
104 		return NULL;
105 	}
106 	Py_RETURN_NONE;
107 }
108 
109 
wpaspy_pending(struct wpaspy_obj * self)110 static PyObject * wpaspy_pending(struct wpaspy_obj *self)
111 {
112 	switch (wpa_ctrl_pending(self->ctrl)) {
113 	case 1:
114 		Py_RETURN_TRUE;
115 	case 0:
116 		Py_RETURN_FALSE;
117 	default:
118 		PyErr_SetString(wpaspy_error, "wpa_ctrl_pending failed");
119 		break;
120 	}
121 
122 	return NULL;
123 }
124 
125 
wpaspy_recv(struct wpaspy_obj * self)126 static PyObject * wpaspy_recv(struct wpaspy_obj *self)
127 {
128 	int ret;
129 	char buf[4096];
130 	size_t buflen;
131 
132 	buflen = sizeof(buf) - 1;
133 	Py_BEGIN_ALLOW_THREADS
134 	ret = wpa_ctrl_recv(self->ctrl, buf, &buflen);
135 	Py_END_ALLOW_THREADS
136 
137 	if (ret) {
138 		PyErr_SetString(wpaspy_error, "wpa_ctrl_recv failed");
139 		return NULL;
140 	}
141 
142 	buf[buflen] = '\0';
143 	return Py_BuildValue("s", buf);
144 }
145 
146 
147 static PyMethodDef wpaspy_methods[] = {
148 	{
149 		"request", (PyCFunction) wpaspy_request, METH_VARARGS,
150 		"Send a control interface command and return response"
151 	},
152 	{
153 		"attach", (PyCFunction) wpaspy_attach, METH_NOARGS,
154 		"Attach as an event monitor"
155 	},
156 	{
157 		"detach", (PyCFunction) wpaspy_detach, METH_NOARGS,
158 		"Detach an event monitor"
159 	},
160 	{
161 		"pending", (PyCFunction) wpaspy_pending, METH_NOARGS,
162 		"Check whether any events are pending"
163 	},
164 	{
165 		"recv", (PyCFunction) wpaspy_recv, METH_NOARGS,
166 		"Received pending event"
167 	},
168 	{ NULL, NULL, 0, NULL }
169 };
170 
171 static PyMemberDef wpaspy_members[] = {
172 	{
173 		"attached", T_INT, offsetof(struct wpaspy_obj, attached),
174 		READONLY,
175 		"Whether instance is attached as event monitor"
176 	},
177 	{ NULL }
178 };
179 
180 static PyTypeObject wpaspy_ctrl = {
181 	PyObject_HEAD_INIT(NULL)
182 	.tp_name = "wpaspy.Ctrl",
183 	.tp_basicsize = sizeof(struct wpaspy_obj),
184 	.tp_getattro = PyObject_GenericGetAttr,
185 	.tp_setattro = PyObject_GenericSetAttr,
186 	.tp_flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE,
187 	.tp_methods = wpaspy_methods,
188 	.tp_members = wpaspy_members,
189 	.tp_init = (initproc) wpaspy_open,
190 	.tp_dealloc = (destructor) wpaspy_close,
191 	.tp_new = PyType_GenericNew,
192 };
193 
194 
195 #if PY_MAJOR_VERSION < 3
196 static PyMethodDef module_methods[] = {
197 	{ NULL, NULL, 0, NULL }
198 };
199 
200 
initwpaspy(void)201 PyMODINIT_FUNC initwpaspy(void)
202 {
203 	PyObject *mod;
204 
205 	PyType_Ready(&wpaspy_ctrl);
206 	mod = Py_InitModule("wpaspy", module_methods);
207 	wpaspy_error = PyErr_NewException("wpaspy.error", NULL, NULL);
208 
209 	Py_INCREF(&wpaspy_ctrl);
210 	Py_INCREF(wpaspy_error);
211 
212 	PyModule_AddObject(mod, "Ctrl", (PyObject *) &wpaspy_ctrl);
213 	PyModule_AddObject(mod, "error", wpaspy_error);
214 }
215 #else
216 static struct PyModuleDef wpaspy_def = {
217 	PyModuleDef_HEAD_INIT,
218 	"wpaspy",
219 };
220 
221 
initwpaspy(void)222 PyMODINIT_FUNC initwpaspy(void)
223 {
224 	PyObject *mod;
225 
226 	mod = PyModule_Create(&wpaspy_def);
227 	if (!mod)
228 		return NULL;
229 
230 	wpaspy_error = PyErr_NewException("wpaspy.error", NULL, NULL);
231 
232 	Py_INCREF(&wpaspy_ctrl);
233 	Py_INCREF(wpaspy_error);
234 
235 	if (PyModule_AddObject(mod, "Ctrl", (PyObject *) &wpaspy_ctrl) < 0 ||
236 	    PyModule_AddObject(mod, "error", wpaspy_error) < 0) {
237 		Py_DECREF(&wpaspy_ctrl);
238 		Py_DECREF(wpaspy_error);
239 		Py_DECREF(mod);
240 		mod = NULL;
241 	}
242 
243 	return mod;
244 }
245 #endif
246