BadVPN – Blame information for rev 1

Subversion Repositories:
Rev:
Rev Author Line No. Line
1 office 1 /**
2 * @file sys_watch_input.c
3 * @author Ambroz Bizjak <ambrop7@gmail.com>
4 *
5 * @section LICENSE
6 *
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions are met:
9 * 1. Redistributions of source code must retain the above copyright
10 * notice, this list of conditions and the following disclaimer.
11 * 2. Redistributions in binary form must reproduce the above copyright
12 * notice, this list of conditions and the following disclaimer in the
13 * documentation and/or other materials provided with the distribution.
14 * 3. Neither the name of the author nor the
15 * names of its contributors may be used to endorse or promote products
16 * derived from this software without specific prior written permission.
17 *
18 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
19 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
20 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
21 * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
22 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
23 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
24 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
25 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
26 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
27 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
28 *
29 * @section DESCRIPTION
30 *
31 * Input device watcher.
32 *
33 * Synopsis: sys.watch_input(string devnode_type)
34 * Description: reports input device events. Transitions up when an event is detected, and
35 * goes down waiting for the next event when sys.watch_input::nextevent() is called.
36 * On startup, "added" events are reported for existing input devices.
37 * Arguments:
38 * string devnode_type - device node type, for example "event", "mouse" or "js".
39 * Variables:
40 * string event_type - what happened with the input device: "added" or "removed"
41 * string devname - device node path
42 * string device_type - input device type, "tablet", "joystick", "touchscreen", "mouse",
43 * "touchpad", "key", "keyboard" or "unknown"
44 *
45 * Synopsis: sys.watch_input::nextevent()
46 * Description: makes the watch_input module transition down in order to report the next event.
47 */
48  
49 #include <stdlib.h>
50 #include <string.h>
51 #include <ctype.h>
52  
53 #include <misc/debug.h>
54 #include <misc/offset.h>
55 #include <structure/LinkedList1.h>
56 #include <udevmonitor/NCDUdevManager.h>
57 #include <ncd/modules/event_template.h>
58  
59 #include <ncd/module_common.h>
60  
61 #include <generated/blog_channel_ncd_sys_watch_input.h>
62  
63 struct device {
64 char *devname;
65 char *devpath;
66 BStringMap removed_map;
67 LinkedList1Node devices_list_node;
68 };
69  
70 struct instance {
71 NCDModuleInst *i;
72 MemRef devnode_type;
73 NCDUdevClient client;
74 LinkedList1 devices_list;
75 event_template templ;
76 };
77  
78 static void templ_func_free (struct instance *o, int is_error);
79  
80 static struct device * find_device_by_devname (struct instance *o, const char *devname)
81 {
82 LinkedList1Node *list_node = LinkedList1_GetFirst(&o->devices_list);
83 while (list_node) {
84 struct device *device = UPPER_OBJECT(list_node, struct device, devices_list_node);
85 if (!strcmp(device->devname, devname)) {
86 return device;
87 }
88 list_node = LinkedList1Node_Next(list_node);
89 }
90  
91 return NULL;
92 }
93  
94 static struct device * find_device_by_devpath (struct instance *o, const char *devpath)
95 {
96 LinkedList1Node *list_node = LinkedList1_GetFirst(&o->devices_list);
97 while (list_node) {
98 struct device *device = UPPER_OBJECT(list_node, struct device, devices_list_node);
99 if (!strcmp(device->devpath, devpath)) {
100 return device;
101 }
102 list_node = LinkedList1Node_Next(list_node);
103 }
104  
105 return NULL;
106 }
107  
108 static void free_device (struct instance *o, struct device *device, int have_removed_map)
109 {
110 // remove from devices list
111 LinkedList1_Remove(&o->devices_list, &device->devices_list_node);
112  
113 // free removed map
114 if (have_removed_map) {
115 BStringMap_Free(&device->removed_map);
116 }
117  
118 // free devpath
119 free(device->devpath);
120  
121 // free devname
122 free(device->devname);
123  
124 // free structure
125 free(device);
126 }
127  
128 static int make_event_map (struct instance *o, int added, const char *devname, const char *device_type, BStringMap *out_map)
129 {
130 // init map
131 BStringMap map;
132 BStringMap_Init(&map);
133  
134 // set type
135 if (!BStringMap_Set(&map, "event_type", (added ? "added" : "removed"))) {
136 ModuleLog(o->i, BLOG_ERROR, "BStringMap_Set failed");
137 goto fail1;
138 }
139  
140 // set devname
141 if (!BStringMap_Set(&map, "devname", devname)) {
142 ModuleLog(o->i, BLOG_ERROR, "BStringMap_Set failed");
143 goto fail1;
144 }
145  
146 // set device type
147 if (!BStringMap_Set(&map, "device_type", device_type)) {
148 ModuleLog(o->i, BLOG_ERROR, "BStringMap_Set failed");
149 goto fail1;
150 }
151  
152 *out_map = map;
153 return 1;
154  
155 fail1:
156 BStringMap_Free(&map);
157 return 0;
158 }
159  
160 static int devname_is_type (const char *devname, MemRef devname_type)
161 {
162 // skip digits at the end
163 size_t i;
164 for (i = strlen(devname); i > 0; i--) {
165 if (!isdigit(devname[i - 1])) {
166 break;
167 }
168 }
169  
170 // check if devname_type precedes skipped digits
171 for (size_t j = devname_type.len; j > 0; j--) {
172 if (!(i > 0 && devname[i - 1] == devname_type.ptr[j - 1])) {
173 return 0;
174 }
175 i--;
176 }
177  
178 // check if slash precedes devname_type
179 if (!(i > 0 && devname[i - 1] == '/')) {
180 return 0;
181 }
182  
183 return 1;
184 }
185  
186 static void queue_event (struct instance *o, BStringMap map)
187 {
188 // pass event to template
189 int was_empty;
190 event_template_queue(&o->templ, map, &was_empty);
191  
192 // if event queue was empty, stop receiving udev events
193 if (was_empty) {
194 NCDUdevClient_Pause(&o->client);
195 }
196 }
197  
198 static void add_device (struct instance *o, const char *devname, const char *devpath, const char *device_type)
199 {
200 ASSERT(!find_device_by_devname(o, devname))
201 ASSERT(!find_device_by_devpath(o, devpath))
202  
203 // allocate structure
204 struct device *device = malloc(sizeof(*device));
205 if (!device) {
206 ModuleLog(o->i, BLOG_ERROR, "malloc failed");
207 goto fail0;
208 }
209  
210 // init devname
211 if (!(device->devname = strdup(devname))) {
212 ModuleLog(o->i, BLOG_ERROR, "strdup failed");
213 goto fail1;
214 }
215  
216 // init devpath
217 if (!(device->devpath = strdup(devpath))) {
218 ModuleLog(o->i, BLOG_ERROR, "strdup failed");
219 goto fail2;
220 }
221  
222 // init removed map
223 if (!make_event_map(o, 0, devname, device_type, &device->removed_map)) {
224 ModuleLog(o->i, BLOG_ERROR, "make_event_map failed");
225 goto fail3;
226 }
227  
228 // init added map
229 BStringMap added_map;
230 if (!make_event_map(o, 1, devname, device_type, &added_map)) {
231 ModuleLog(o->i, BLOG_ERROR, "make_event_map failed");
232 goto fail4;
233 }
234  
235 // insert to devices list
236 LinkedList1_Append(&o->devices_list, &device->devices_list_node);
237  
238 // queue event
239 queue_event(o, added_map);
240  
241 return;
242  
243 fail4:
244 BStringMap_Free(&device->removed_map);
245 fail3:
246 free(device->devpath);
247 fail2:
248 free(device->devname);
249 fail1:
250 free(device);
251 fail0:
252 ModuleLog(o->i, BLOG_ERROR, "failed to add device %s", devname);
253 }
254  
255 static void remove_device (struct instance *o, struct device *device)
256 {
257 queue_event(o, device->removed_map);
258 free_device(o, device, 0);
259 }
260  
261 static void next_event (struct instance *o)
262 {
263 ASSERT(event_template_is_enabled(&o->templ))
264  
265 // order template to finish the current event
266 int is_empty;
267 event_template_dequeue(&o->templ, &is_empty);
268  
269 // if template has no events, continue udev events
270 if (is_empty) {
271 NCDUdevClient_Continue(&o->client);
272 }
273 }
274  
275 static void client_handler (struct instance *o, char *devpath, int have_map, BStringMap map)
276 {
277 // lookup existing device with this devpath
278 struct device *ex_device = find_device_by_devpath(o, devpath);
279 // lookup cache entry
280 const BStringMap *cache_map = NCDUdevManager_Query(o->i->params->iparams->umanager, devpath);
281  
282 if (!cache_map) {
283 if (ex_device) {
284 remove_device(o, ex_device);
285 }
286 goto out;
287 }
288  
289 const char *subsystem = BStringMap_Get(cache_map, "SUBSYSTEM");
290 const char *devname = BStringMap_Get(cache_map, "DEVNAME");
291  
292 if (!(subsystem && !strcmp(subsystem, "input") && devname && devname_is_type(devname, o->devnode_type))) {
293 if (ex_device) {
294 remove_device(o, ex_device);
295 }
296 goto out;
297 }
298  
299 if (ex_device && strcmp(ex_device->devname, devname)) {
300 remove_device(o, ex_device);
301 ex_device = NULL;
302 }
303  
304 if (!ex_device) {
305 struct device *ex_devname_device = find_device_by_devname(o, devname);
306 if (ex_devname_device) {
307 remove_device(o, ex_devname_device);
308 }
309  
310 // determine type
311 const char *device_type = "unknown";
312 if (BStringMap_Get(cache_map, "ID_INPUT_TABLET")) {
313 device_type = "tablet";
314 }
315 else if (BStringMap_Get(cache_map, "ID_INPUT_JOYSTICK")) {
316 device_type = "joystick";
317 }
318 else if (BStringMap_Get(cache_map, "ID_INPUT_TOUCHSCREEN")) {
319 device_type = "touchscreen";
320 }
321 else if (BStringMap_Get(cache_map, "ID_INPUT_MOUSE")) {
322 device_type = "mouse";
323 }
324 else if (BStringMap_Get(cache_map, "ID_INPUT_TOUCHPAD")) {
325 device_type = "touchpad";
326 }
327 else if (BStringMap_Get(cache_map, "ID_INPUT_KEY")) {
328 device_type = "key";
329 }
330 else if (BStringMap_Get(cache_map, "ID_INPUT_KEYBOARD")) {
331 device_type = "keyboard";
332 }
333  
334 add_device(o, devname, devpath, device_type);
335 }
336  
337 out:
338 free(devpath);
339 if (have_map) {
340 BStringMap_Free(&map);
341 }
342 }
343  
344 static void func_new (void *vo, NCDModuleInst *i, const struct NCDModuleInst_new_params *params)
345 {
346 struct instance *o = vo;
347 o->i = i;
348  
349 // check arguments
350 NCDValRef devnode_type_arg;
351 if (!NCDVal_ListRead(params->args, 1, &devnode_type_arg)) {
352 ModuleLog(o->i, BLOG_ERROR, "wrong arity");
353 goto fail0;
354 }
355 if (!NCDVal_IsString(devnode_type_arg)) {
356 ModuleLog(o->i, BLOG_ERROR, "wrong type");
357 goto fail0;
358 }
359 o->devnode_type = NCDVal_StringMemRef(devnode_type_arg);
360  
361 // init client
362 NCDUdevClient_Init(&o->client, o->i->params->iparams->umanager, o, (NCDUdevClient_handler)client_handler);
363  
364 // init devices list
365 LinkedList1_Init(&o->devices_list);
366  
367 event_template_new(&o->templ, o->i, BLOG_CURRENT_CHANNEL, 3, o, (event_template_func_free)templ_func_free);
368 return;
369  
370 fail0:
371 NCDModuleInst_Backend_DeadError(i);
372 }
373  
374 static void templ_func_free (struct instance *o, int is_error)
375 {
376 // free devices
377 LinkedList1Node *list_node;
378 while (list_node = LinkedList1_GetFirst(&o->devices_list)) {
379 struct device *device = UPPER_OBJECT(list_node, struct device, devices_list_node);
380 free_device(o, device, 1);
381 }
382  
383 // free client
384 NCDUdevClient_Free(&o->client);
385  
386 if (is_error) {
387 NCDModuleInst_Backend_DeadError(o->i);
388 } else {
389 NCDModuleInst_Backend_Dead(o->i);
390 }
391 }
392  
393 static void func_die (void *vo)
394 {
395 struct instance *o = vo;
396 event_template_die(&o->templ);
397 }
398  
399 static int func_getvar (void *vo, const char *name, NCDValMem *mem, NCDValRef *out)
400 {
401 struct instance *o = vo;
402 return event_template_getvar(&o->templ, name, mem, out);
403 }
404  
405 static void nextevent_func_new (void *unused, NCDModuleInst *i, const struct NCDModuleInst_new_params *params)
406 {
407 // check arguments
408 if (!NCDVal_ListRead(params->args, 0)) {
409 ModuleLog(i, BLOG_ERROR, "wrong arity");
410 goto fail0;
411 }
412  
413 // get method object
414 struct instance *mo = NCDModuleInst_Backend_GetUser((NCDModuleInst *)params->method_user);
415  
416 // make sure we are currently reporting an event
417 if (!event_template_is_enabled(&mo->templ)) {
418 ModuleLog(i, BLOG_ERROR, "not reporting an event");
419 goto fail0;
420 }
421  
422 // signal up.
423 // Do it before finishing the event so our process does not advance any further if
424 // we would be killed the event provider going down.
425 NCDModuleInst_Backend_Up(i);
426  
427 // wait for next event
428 next_event(mo);
429 return;
430  
431 fail0:
432 NCDModuleInst_Backend_DeadError(i);
433 }
434  
435 static struct NCDModule modules[] = {
436 {
437 .type = "sys.watch_input",
438 .func_new2 = func_new,
439 .func_die = func_die,
440 .func_getvar = func_getvar,
441 .alloc_size = sizeof(struct instance)
442 }, {
443 .type = "sys.watch_input::nextevent",
444 .func_new2 = nextevent_func_new
445 }, {
446 .type = NULL
447 }
448 };
449  
450 const struct NCDModuleGroup ncdmodule_sys_watch_input = {
451 .modules = modules
452 };