ambrop7 пре 15 година
родитељ
комит
37020bce35

+ 6 - 0
CMakeLists.txt

@@ -78,6 +78,12 @@ else ()
             add_definitions(-DBADVPN_USE_LINUX_RFKILL)
             set(BADVPN_USE_LINUX_RFKILL 1)
         endif ()
+
+        check_include_files(linux/input.h HAVE_LINUX_INPUT_H)
+        if (HAVE_LINUX_INPUT_H)
+            add_definitions(-DBADVPN_USE_LINUX_INPUT)
+            set(BADVPN_USE_LINUX_INPUT 1)
+        endif ()
     elseif (CMAKE_SYSTEM_NAME MATCHES "FreeBSD")
         add_definitions(-DBADVPN_FREEBSD)
 

+ 1 - 0
blog_channels.txt

@@ -28,6 +28,7 @@ ncd_net_iptables 4
 ncd_net_ipv4_addr 4
 ncd_net_ipv4_dhcp 4
 ncd_net_ipv4_route 4
+ncd_sys_evdev 4
 StreamPeerIO 4
 DatagramPeerIO 4
 BReactor 3

+ 4 - 0
generated/blog_channel_ncd_sys_evdev.h

@@ -0,0 +1,4 @@
+#ifdef BLOG_CURRENT_CHANNEL
+#undef BLOG_CURRENT_CHANNEL
+#endif
+#define BLOG_CURRENT_CHANNEL BLOG_CHANNEL_ncd_sys_evdev

+ 29 - 28
generated/blog_channels_defines.h

@@ -28,31 +28,32 @@
 #define BLOG_CHANNEL_ncd_net_ipv4_addr 27
 #define BLOG_CHANNEL_ncd_net_ipv4_dhcp 28
 #define BLOG_CHANNEL_ncd_net_ipv4_route 29
-#define BLOG_CHANNEL_StreamPeerIO 30
-#define BLOG_CHANNEL_DatagramPeerIO 31
-#define BLOG_CHANNEL_BReactor 32
-#define BLOG_CHANNEL_BSignal 33
-#define BLOG_CHANNEL_FragmentProtoAssembler 34
-#define BLOG_CHANNEL_BPredicate 35
-#define BLOG_CHANNEL_ServerConnection 36
-#define BLOG_CHANNEL_Listener 37
-#define BLOG_CHANNEL_DataProto 38
-#define BLOG_CHANNEL_FrameDecider 39
-#define BLOG_CHANNEL_BSocksClient 40
-#define BLOG_CHANNEL_BDHCPClientCore 41
-#define BLOG_CHANNEL_BDHCPClient 42
-#define BLOG_CHANNEL_NCDIfConfig 43
-#define BLOG_CHANNEL_BUnixSignal 44
-#define BLOG_CHANNEL_BProcess 45
-#define BLOG_CHANNEL_StreamSocketSink 46
-#define BLOG_CHANNEL_StreamSocketSource 47
-#define BLOG_CHANNEL_DatagramSocketSink 48
-#define BLOG_CHANNEL_DatagramSocketSource 49
-#define BLOG_CHANNEL_PRStreamSink 50
-#define BLOG_CHANNEL_PRStreamSource 51
-#define BLOG_CHANNEL_BSocketPRFileDesc 52
-#define BLOG_CHANNEL_PacketProtoDecoder 53
-#define BLOG_CHANNEL_DPRelay 54
-#define BLOG_CHANNEL_BThreadWork 55
-#define BLOG_CHANNEL_DPReceive 56
-#define BLOG_NUM_CHANNELS 57
+#define BLOG_CHANNEL_ncd_sys_evdev 30
+#define BLOG_CHANNEL_StreamPeerIO 31
+#define BLOG_CHANNEL_DatagramPeerIO 32
+#define BLOG_CHANNEL_BReactor 33
+#define BLOG_CHANNEL_BSignal 34
+#define BLOG_CHANNEL_FragmentProtoAssembler 35
+#define BLOG_CHANNEL_BPredicate 36
+#define BLOG_CHANNEL_ServerConnection 37
+#define BLOG_CHANNEL_Listener 38
+#define BLOG_CHANNEL_DataProto 39
+#define BLOG_CHANNEL_FrameDecider 40
+#define BLOG_CHANNEL_BSocksClient 41
+#define BLOG_CHANNEL_BDHCPClientCore 42
+#define BLOG_CHANNEL_BDHCPClient 43
+#define BLOG_CHANNEL_NCDIfConfig 44
+#define BLOG_CHANNEL_BUnixSignal 45
+#define BLOG_CHANNEL_BProcess 46
+#define BLOG_CHANNEL_StreamSocketSink 47
+#define BLOG_CHANNEL_StreamSocketSource 48
+#define BLOG_CHANNEL_DatagramSocketSink 49
+#define BLOG_CHANNEL_DatagramSocketSource 50
+#define BLOG_CHANNEL_PRStreamSink 51
+#define BLOG_CHANNEL_PRStreamSource 52
+#define BLOG_CHANNEL_BSocketPRFileDesc 53
+#define BLOG_CHANNEL_PacketProtoDecoder 54
+#define BLOG_CHANNEL_DPRelay 55
+#define BLOG_CHANNEL_BThreadWork 56
+#define BLOG_CHANNEL_DPReceive 57
+#define BLOG_NUM_CHANNELS 58

+ 1 - 0
generated/blog_channels_list.h

@@ -28,6 +28,7 @@
 {.name = "ncd_net_ipv4_addr", .loglevel = 4},
 {.name = "ncd_net_ipv4_dhcp", .loglevel = 4},
 {.name = "ncd_net_ipv4_route", .loglevel = 4},
+{.name = "ncd_sys_evdev", .loglevel = 4},
 {.name = "StreamPeerIO", .loglevel = 4},
 {.name = "DatagramPeerIO", .loglevel = 4},
 {.name = "BReactor", .loglevel = 3},

+ 34 - 0
ncd/CMakeLists.txt

@@ -1,4 +1,7 @@
+include_directories(${CMAKE_CURRENT_BINARY_DIR})
+
 set(NCD_ADDITIONAL_SOURCES)
+
 if (BADVPN_USE_LINUX_RFKILL)
     list(APPEND NCD_ADDITIONAL_SOURCES
         NCDRfkillMonitor.c
@@ -6,6 +9,12 @@ if (BADVPN_USE_LINUX_RFKILL)
     )
 endif ()
 
+if (BADVPN_USE_LINUX_INPUT)
+    list(APPEND NCD_ADDITIONAL_SOURCES
+        modules/sys_evdev.c
+    )
+endif ()
+
 add_executable(badvpn-ncd
     ncd.c
     NCDValue.c
@@ -41,6 +50,31 @@ add_executable(badvpn-ncd
 )
 target_link_libraries(badvpn-ncd system dhcpclient ncdconfig process)
 
+if (BADVPN_USE_LINUX_INPUT)
+    execute_process(COMMAND ${CMAKE_C_COMPILER} ${CMAKE_C_FLAGS} -E ${CMAKE_CURRENT_SOURCE_DIR}/include_linux_input.h
+                    RESULT_VARIABLE LINUX_INPUT_PREPROCESS_RESULT
+                    OUTPUT_VARIABLE LINUX_INPUT_PREPROCESS_OUTPUT)
+    if (NOT LINUX_INPUT_PREPROCESS_RESULT EQUAL 0)
+        message(FATAL_ERROR "failed to preprocess linux/input.h include")
+    endif ()
+
+    string(REGEX MATCH "\"(/[^\"]+/linux/input.h)\"" LINUX_INPUT_MATCH ${LINUX_INPUT_PREPROCESS_OUTPUT})
+    if (NOT LINUX_INPUT_MATCH)
+        message(FATAL_ERROR "failed to match preprocessor output for path of linux/input.h")
+    endif ()
+    set(LINUX_INPUT_H_PATH ${CMAKE_MATCH_1})
+
+    message(STATUS "Generating linux_input_names.h from ${LINUX_INPUT_H_PATH}")
+
+    execute_process(COMMAND ${CMAKE_CURRENT_SOURCE_DIR}/parse_linux_input.sh 
+                            ${LINUX_INPUT_H_PATH}
+                            ${CMAKE_CURRENT_BINARY_DIR}/linux_input_names.h
+                    RESULT_VARIABLE LINUX_INPUT_PARSE_RESULT)
+    if (NOT LINUX_INPUT_PARSE_RESULT EQUAL 0)
+        message(FATAL_ERROR "failed to generate linux_input_names.h")
+    endif ()
+endif ()
+
 install(
     TARGETS badvpn-ncd
     RUNTIME DESTINATION bin

+ 1 - 0
ncd/include_linux_input.h

@@ -0,0 +1 @@
+#include <linux/input.h>

+ 6 - 0
ncd/modules/modules.h

@@ -54,6 +54,9 @@ extern const struct NCDModuleGroup ncdmodule_net_iptables;
 extern const struct NCDModuleGroup ncdmodule_net_ipv4_addr;
 extern const struct NCDModuleGroup ncdmodule_net_ipv4_route;
 extern const struct NCDModuleGroup ncdmodule_net_ipv4_dhcp;
+#ifdef BADVPN_USE_LINUX_INPUT
+extern const struct NCDModuleGroup ncdmodule_sys_evdev;
+#endif
 
 static const struct NCDModuleGroup *ncd_modules[] = {
     &ncdmodule_var,
@@ -83,6 +86,9 @@ static const struct NCDModuleGroup *ncd_modules[] = {
     &ncdmodule_net_ipv4_addr,
     &ncdmodule_net_ipv4_route,
     &ncdmodule_net_ipv4_dhcp,
+#ifdef BADVPN_USE_LINUX_INPUT
+    &ncdmodule_sys_evdev,
+#endif
     NULL
 };
 

+ 359 - 0
ncd/modules/sys_evdev.c

@@ -0,0 +1,359 @@
+/**
+ * @file sys_evdev.c
+ * @author Ambroz Bizjak <ambrop7@gmail.com>
+ * 
+ * @section LICENSE
+ * 
+ * This file is part of BadVPN.
+ * 
+ * BadVPN is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2
+ * as published by the Free Software Foundation.
+ * 
+ * BadVPN is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ * 
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ * 
+ * @section DESCRIPTION
+ * 
+ * Linux event device module.
+ * 
+ * Synopsis: sys.evdev(string device)
+ * Description: reports input events from a Linux event device. Transitions up when an event is
+ *   detected, and goes down waiting for the next event when sys.evdev::nextevent() is called.
+ * Variables:
+ *   string type - symbolic event type (e.g. EV_KEY, EV_REL, EV_ABS), corresponding to
+ *     (struct input_event).type, or "unknown"
+ *   string value - event value (signed integer), equal to (struct input_event).value
+ *   string code_numeric - numeric event code (unsigned integer), equal to
+ *     (struct input_event).code
+ *   string code - symbolic event code (e.g. KEY_ESC. KEY_1, KEY_2, BTN_LEFT), corrresponding
+ *     to (struct input_event).code, or "unknown"
+ * 
+ * Synopsis: sys.evdev::nextevent()
+ * Description: makes the evdev module transition down in order to report the next event.
+ */
+
+#include <stdlib.h>
+#include <string.h>
+#include <stdint.h>
+#include <inttypes.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <linux/input.h>
+
+#include <misc/nonblocking.h>
+
+#include <ncd/NCDModule.h>
+
+#include <generated/blog_channel_ncd_sys_evdev.h>
+
+#include "linux_input_names.h"
+
+#define ModuleLog(i, ...) NCDModuleInst_Backend_Log((i), BLOG_CURRENT_CHANNEL, __VA_ARGS__)
+
+struct instance {
+    NCDModuleInst *i;
+    int evdev_fd;
+    BFileDescriptor bfd;
+    int processing;
+    struct input_event event;
+};
+
+struct nextevent_instance {
+    NCDModuleInst *i;
+};
+
+#define MAKE_LOOKUP_FUNC(_name_) \
+static const char * evdev_##_name_##_to_str (uint16_t type) \
+{ \
+    if (type >= (sizeof(_name_##_names) / sizeof(_name_##_names[0])) || !_name_##_names[type]) { \
+        return "unknown"; \
+    } \
+    return _name_##_names[type]; \
+}
+
+MAKE_LOOKUP_FUNC(type)
+MAKE_LOOKUP_FUNC(key)
+MAKE_LOOKUP_FUNC(rel)
+MAKE_LOOKUP_FUNC(abs)
+MAKE_LOOKUP_FUNC(sw)
+MAKE_LOOKUP_FUNC(msc)
+MAKE_LOOKUP_FUNC(led)
+MAKE_LOOKUP_FUNC(rep)
+MAKE_LOOKUP_FUNC(snd)
+MAKE_LOOKUP_FUNC(ffstatus)
+
+static void device_handler (struct instance *o, int events)
+{
+    ASSERT(!o->processing)
+    
+    int res = read(o->evdev_fd, &o->event, sizeof(o->event));
+    if (res < 0) {
+        ModuleLog(o->i, BLOG_ERROR, "read failed");
+        return;
+    }
+    if (res != sizeof(o->event)) {
+        ModuleLog(o->i, BLOG_ERROR, "read wrong");
+        return;
+    }
+    
+    // stop reading
+    BReactor_SetFileDescriptorEvents(o->i->reactor, &o->bfd, 0);
+    
+    // set processing
+    o->processing = 1;
+    
+    // signal up
+    NCDModuleInst_Backend_Event(o->i, NCDMODULE_EVENT_UP);
+}
+
+static void device_nextevent (struct instance *o)
+{
+    ASSERT(o->processing)
+    
+    // start reading
+    BReactor_SetFileDescriptorEvents(o->i->reactor, &o->bfd, BREACTOR_READ);
+    
+    // set not processing
+    o->processing = 0;
+    
+    // signal down
+    NCDModuleInst_Backend_Event(o->i, NCDMODULE_EVENT_DOWN);
+}
+
+static void func_new (NCDModuleInst *i)
+{
+    // allocate instance
+    struct instance *o = malloc(sizeof(*o));
+    if (!o) {
+        ModuleLog(i, BLOG_ERROR, "failed to allocate instance");
+        goto fail0;
+    }
+    NCDModuleInst_Backend_SetUser(i, o);
+    
+    // init arguments
+    o->i = i;
+    
+    // check arguments
+    NCDValue *device_arg;
+    if (!NCDValue_ListRead(o->i->args, 1, &device_arg)) {
+        ModuleLog(o->i, BLOG_ERROR, "wrong arity");
+        goto fail1;
+    }
+    if (NCDValue_Type(device_arg) != NCDVALUE_STRING) {
+        ModuleLog(o->i, BLOG_ERROR, "wrong type");
+        goto fail1;
+    }
+    
+    // open device
+    if ((o->evdev_fd = open(NCDValue_StringValue(device_arg), O_RDONLY)) < 0) {
+        ModuleLog(o->i, BLOG_ERROR, "open failed");
+        goto fail1;
+    }
+    
+    // set non-blocking
+    if (!badvpn_set_nonblocking(o->evdev_fd)) {
+        ModuleLog(o->i, BLOG_ERROR, "badvpn_set_nonblocking failed");
+        goto fail2;
+    }
+    
+    // init BFileDescriptor
+    BFileDescriptor_Init(&o->bfd, o->evdev_fd, (BFileDescriptor_handler)device_handler, o);
+    if (!BReactor_AddFileDescriptor(o->i->reactor, &o->bfd)) {
+        ModuleLog(o->i, BLOG_ERROR, "BReactor_AddFileDescriptor failed");
+        goto fail2;
+    }
+    BReactor_SetFileDescriptorEvents(o->i->reactor, &o->bfd, BREACTOR_READ);
+    
+    // set not processing
+    o->processing = 0;
+    
+    return;
+    
+fail2:
+    ASSERT_FORCE(close(o->evdev_fd) == 0)
+fail1:
+    free(o);
+fail0:
+    NCDModuleInst_Backend_SetError(i);
+    NCDModuleInst_Backend_Event(i, NCDMODULE_EVENT_DEAD);
+}
+
+static void func_die (void *vo)
+{
+    struct instance *o = vo;
+    NCDModuleInst *i = o->i;
+    
+    // free BFileDescriptor
+    BReactor_RemoveFileDescriptor(o->i->reactor, &o->bfd);
+    
+    // close device
+    ASSERT_FORCE(close(o->evdev_fd) == 0)
+    
+    // free instance
+    free(o);
+    
+    NCDModuleInst_Backend_Event(i, NCDMODULE_EVENT_DEAD);
+}
+
+static int func_getvar (void *vo, const char *name, NCDValue *out)
+{
+    struct instance *o = vo;
+    ASSERT(o->processing)
+    
+    if (!strcmp(name, "type")) {
+        if (!NCDValue_InitString(out, evdev_type_to_str(o->event.type))) {
+            ModuleLog(o->i, BLOG_ERROR, "NCDValue_InitString failed");
+            return 0;
+        }
+        
+        return 1;
+    }
+    
+    if (!strcmp(name, "value")) {
+        char str[50];
+        snprintf(str, sizeof(str), "%"PRIi32, o->event.value);
+        if (!NCDValue_InitString(out, str)) {
+            ModuleLog(o->i, BLOG_ERROR, "NCDValue_InitString failed");
+            return 0;
+        }
+        
+        return 1;
+    }
+    
+    if (!strcmp(name, "code_numeric")) {
+        char str[50];
+        snprintf(str, sizeof(str), "%"PRIu16, o->event.code);
+        if (!NCDValue_InitString(out, str)) {
+            ModuleLog(o->i, BLOG_ERROR, "NCDValue_InitString failed");
+            return 0;
+        }
+        
+        return 1;
+    }
+    
+    if (!strcmp(name, "code")) {
+        const char *str = "unknown";
+        
+        #define MAKE_CASE(_evname_, _name_) \
+            case _evname_: \
+                str = evdev_##_name_##_to_str(o->event.code); \
+                break;
+        
+        switch (o->event.type) {
+            #ifdef EV_KEY
+            MAKE_CASE(EV_KEY, key)
+            #endif
+            #ifdef EV_REL
+            MAKE_CASE(EV_REL, rel)
+            #endif
+            #ifdef EV_ABS
+            MAKE_CASE(EV_ABS, abs)
+            #endif
+            #ifdef EV_SW
+            MAKE_CASE(EV_SW, sw)
+            #endif
+            #ifdef EV_MSC
+            MAKE_CASE(EV_MSC, msc)
+            #endif
+            #ifdef EV_LED
+            MAKE_CASE(EV_LED, led)
+            #endif
+            #ifdef EV_REP
+            MAKE_CASE(EV_REP, rep)
+            #endif
+            #ifdef EV_SND
+            MAKE_CASE(EV_SND, snd)
+            #endif
+            #ifdef EV_FF_STATUS
+            MAKE_CASE(EV_FF_STATUS, ffstatus)
+            #endif
+        }
+        
+        if (!NCDValue_InitString(out, str)) {
+            ModuleLog(o->i, BLOG_ERROR, "NCDValue_InitString failed");
+            return 0;
+        }
+        
+        return 1;
+    }
+
+    
+    return 0;
+}
+
+static void nextevent_func_new (NCDModuleInst *i)
+{
+    // allocate instance
+    struct nextevent_instance *o = malloc(sizeof(*o));
+    if (!o) {
+        ModuleLog(i, BLOG_ERROR, "failed to allocate instance");
+        goto fail0;
+    }
+    NCDModuleInst_Backend_SetUser(i, o);
+    
+    // init arguments
+    o->i = i;
+    
+    // check arguments
+    if (!NCDValue_ListRead(o->i->args, 0)) {
+        ModuleLog(o->i, BLOG_ERROR, "wrong arity");
+        goto fail1;
+    }
+    
+    // get method object
+    struct instance *mo = i->method_object->inst_user;
+    ASSERT(mo->processing)
+    
+    // wait for next event
+    device_nextevent(mo);
+    
+    // signal up
+    NCDModuleInst_Backend_Event(o->i, NCDMODULE_EVENT_UP);
+    
+    return;
+    
+fail1:
+    free(o);
+fail0:
+    NCDModuleInst_Backend_SetError(i);
+    NCDModuleInst_Backend_Event(i, NCDMODULE_EVENT_DEAD);
+}
+
+static void nextevent_func_die (void *vo)
+{
+    struct nextevent_instance *o = vo;
+    NCDModuleInst *i = o->i;
+    
+    // free instance
+    free(o);
+    
+    NCDModuleInst_Backend_Event(i, NCDMODULE_EVENT_DEAD);
+}
+
+static const struct NCDModule modules[] = {
+    {
+        .type = "sys.evdev",
+        .func_new = func_new,
+        .func_die = func_die,
+        .func_getvar = func_getvar
+    }, {
+        .type = "sys.evdev::nextevent",
+        .func_new = nextevent_func_new,
+        .func_die = nextevent_func_die
+    }, {
+        .type = NULL
+    }
+};
+
+const struct NCDModuleGroup ncdmodule_sys_evdev = {
+    .modules = modules
+};

+ 94 - 0
ncd/parse_linux_input.sh

@@ -0,0 +1,94 @@
+#!/bin/bash
+
+INPUT=$1
+OUTPUT=$2
+
+types=""
+keys=""
+rels=""
+abss=""
+sws=""
+mscs=""
+leds=""
+reps=""
+snds=""
+ffstatuss=""
+
+while read LINE; do
+    tab=$'\t'
+    space="[ ${tab}]"
+    regex="^#define ((EV|KEY|BTN|REL|ABS|SW|MSC|LED|REP|SND|FF_STATUS)_[A-Z0-9_]+)${space}"
+    if [[ $LINE =~ $regex ]]; then
+        type=${BASH_REMATCH[2]}
+        name=${BASH_REMATCH[1]}
+        if [[ $type = "EV" ]]; then
+            if [[ $name != "EV_VERSION" ]]; then
+                types="${types}    [${name}] = \"${name}\",
+"
+            fi
+        elif [[ $type = "KEY" ]] || [[ $type = "BTN" ]]; then
+            if [[ $name != "KEY_MIN_INTERESTING" ]]; then
+                keys="${keys}    [${name}] = \"${name}\",
+"
+            fi
+        elif [[ $type = "REL" ]]; then
+            rels="${rels}    [${name}] = \"${name}\",
+"
+        elif [[ $type = "ABS" ]]; then
+            abss="${abss}    [${name}] = \"${name}\",
+"
+        elif [[ $type = "SW" ]]; then
+            sws="${sws}    [${name}] = \"${name}\",
+"
+        elif [[ $type = "MSC" ]]; then
+            mscs="${mscs}    [${name}] = \"${name}\",
+"
+        elif [[ $type = "LED" ]]; then
+            leds="${leds}    [${name}] = \"${name}\",
+"
+        elif [[ $type = "REP" ]]; then
+            reps="${reps}    [${name}] = \"${name}\",
+"
+        elif [[ $type = "SND" ]]; then
+            snds="${snds}    [${name}] = \"${name}\",
+"
+        elif [[ $type = "FF_STATUS" ]]; then
+            ffstatuss="${ffstatuss}    [${name}] = \"${name}\",
+"
+        fi
+    fi
+done < "${INPUT}"
+
+(
+echo "
+static const char *type_names[] = {
+${types}};
+
+static const char *key_names[] = {
+${keys}};
+
+static const char *rel_names[] = {
+${rels}};
+
+static const char *abs_names[] = {
+${abss}};
+
+static const char *sw_names[] = {
+${sws}};
+
+static const char *msc_names[] = {
+${mscs}};
+
+static const char *led_names[] = {
+${leds}};
+
+static const char *rep_names[] = {
+${reps}};
+
+static const char *snd_names[] = {
+${snds}};
+
+static const char *ffstatus_names[] = {
+${ffstatuss}};
+"
+) >"${OUTPUT}"