ambrop7 13 лет назад
Родитель
Сommit
1ebc9e1fa0

+ 53 - 16
CMakeLists.txt

@@ -19,21 +19,41 @@ macro (build_switch name text default)
     list(APPEND BUILD_COMPONENTS "${name}")
 endmacro ()
 
+# detect Emscripten
+if (CMAKE_C_COMPILER MATCHES "/emcc$")
+    set(EMSCRIPTEN ON)
+else ()
+    set(EMSCRIPTEN OFF)
+endif ()
+
+if (EMSCRIPTEN)
+    set(ON_IF_NOT_EMSCRIPTEN OFF)
+else ()
+    set(ON_IF_NOT_EMSCRIPTEN ON)
+endif()
+
+if (CMAKE_SYSTEM_NAME STREQUAL "Linux" AND NOT EMSCRIPTEN)
+    set(ON_IF_LINUX ON)
+else ()
+    set(ON_IF_LINUX OFF)
+endif()
+
+if (CMAKE_SYSTEM_NAME STREQUAL "Linux" OR EMSCRIPTEN)
+    set(ON_IF_LINUX_OR_EMSCRIPTEN ON)
+else ()
+    set(ON_IF_LINUX_OR_EMSCRIPTEN OFF)
+endif ()
+
 # define build defaults
 build_switch(EXAMPLES "build example programs" ON)
 build_switch(TESTS "build some other example programs" ON)
-build_switch(SERVER "build badvpn-server" ON)
-build_switch(CLIENT "build badvpn-client" ON)
-build_switch(FLOODER "build badvpn-flooder" ON)
-build_switch(TUN2SOCKS "build badvpn-tun2socks" ON)
-build_switch(UDPGW "build badvpn-udpgw" ON)
-if (CMAKE_SYSTEM_NAME STREQUAL "Linux")
-    build_switch(NCD "build badvpn-ncd" ON)
-    build_switch(TUNCTL "build badvpn-tunctl" ON)
-else ()
-    build_switch(NCD "build badvpn-ncd" OFF)
-    build_switch(TUNCTL "build badvpn-tunctl" OFF)
-endif ()
+build_switch(SERVER "build badvpn-server" ${ON_IF_NOT_EMSCRIPTEN})
+build_switch(CLIENT "build badvpn-client" ${ON_IF_NOT_EMSCRIPTEN})
+build_switch(FLOODER "build badvpn-flooder" ${ON_IF_NOT_EMSCRIPTEN})
+build_switch(TUN2SOCKS "build badvpn-tun2socks" ${ON_IF_NOT_EMSCRIPTEN})
+build_switch(UDPGW "build badvpn-udpgw" ${ON_IF_NOT_EMSCRIPTEN})
+build_switch(NCD "build badvpn-ncd" ${ON_IF_LINUX_OR_EMSCRIPTEN})
+build_switch(TUNCTL "build badvpn-tunctl" ${ON_IF_LINUX})
 build_switch(DOSTEST "build dostest-server and dostest-attacker" OFF)
 
 if (BUILD_NCD AND NOT (CMAKE_SYSTEM_NAME STREQUAL "Linux"))
@@ -58,7 +78,11 @@ if (DEFINED BREACTOR_BACKEND)
         message(FATAL_ERROR "unknown reactor backend specified")
     endif ()
 else ()
-    set(BREACTOR_BACKEND "badvpn")
+    if (EMSCRIPTEN)
+        set(BREACTOR_BACKEND "emscripten")
+    else ()
+        set(BREACTOR_BACKEND "badvpn")
+    endif ()
 endif ()
 
 if (BREACTOR_BACKEND STREQUAL "badvpn")
@@ -69,6 +93,8 @@ elseif (BREACTOR_BACKEND STREQUAL "glib")
     endif ()
     find_package(GLIB2 REQUIRED)
     add_definitions(-DBADVPN_BREACTOR_GLIB)
+elseif (BREACTOR_BACKEND STREQUAL "emscripten")
+    add_definitions(-DBADVPN_BREACTOR_EMSCRIPTEN)
 endif ()
 
 include_directories(
@@ -138,7 +164,10 @@ else ()
 
     link_libraries(rt)
 
-    if (CMAKE_SYSTEM_NAME STREQUAL "Linux")
+    if (EMSCRIPTEN)
+        add_definitions(-DBADVPN_EMSCRIPTEN)
+        add_definitions(-DBADVPN_NO_PROCESS -DBADVPN_NO_UDEV -DBADVPN_NO_RANDOM)
+    elseif (CMAKE_SYSTEM_NAME STREQUAL "Linux")
         add_definitions(-DBADVPN_LINUX)
 
         check_include_files(sys/signalfd.h HAVE_SYS_SIGNALFD_H)
@@ -192,6 +221,12 @@ else ()
     endif ()
 endif ()
 
+# check for syslog
+check_include_files(syslog.h HAVE_SYSLOG_H)
+if (HAVE_SYSLOG_H)
+    add_definitions(-DBADVPN_USE_SYSLOG)
+endif ()
+
 # add preprocessor definitions
 if (BIG_ENDIAN)
     add_definitions(-DBADVPN_BIG_ENDIAN)
@@ -243,7 +278,7 @@ endif ()
 if (BUILD_CLIENT OR BUILD_FLOODER)
     add_subdirectory(server_connection)
 endif ()
-if (BUILD_NCD)
+if (BUILD_NCD AND NOT EMSCRIPTEN)
     set(BUILDING_DHCPCLIENT 1)
     set(BUILDING_ARPPROBE 1)
     set(BUILDING_UDEVMONITOR 1)
@@ -301,7 +336,9 @@ endif ()
 # ncd
 if (BUILD_NCD)
     add_subdirectory(ncd)
-    add_subdirectory(ncd-request)
+    if (NOT EMSCRIPTEN)
+        add_subdirectory(ncd-request)
+    endif ()
 endif ()
 
 # dostest

+ 1 - 1
base/CMakeLists.txt

@@ -1,6 +1,6 @@
 set(BASE_ADDITIONAL_SOURCES)
 
-if (NOT WIN32)
+if (HAVE_SYSLOG_H)
     list(APPEND BASE_ADDITIONAL_SOURCES BLog_syslog.c)
 endif ()
 

+ 18 - 7
examples/CMakeLists.txt

@@ -1,13 +1,17 @@
-add_executable(btimer_example btimer_example.c)
-target_link_libraries(btimer_example system)
+if (NOT EMSCRIPTEN)
+    add_executable(btimer_example btimer_example.c)
+    target_link_libraries(btimer_example system)
+endif ()
 
 if (BUILDING_PREDICATE)
     add_executable(predicate_test predicate_test.c)
     target_link_libraries(predicate_test predicate)
 endif ()
 
-add_executable(fairqueue_test fairqueue_test.c)
-target_link_libraries(fairqueue_test system flow)
+if (NOT EMSCRIPTEN)
+    add_executable(fairqueue_test fairqueue_test.c)
+    target_link_libraries(fairqueue_test system flow)
+endif ()
 
 add_executable(indexedlist_test indexedlist_test.c)
 
@@ -35,8 +39,10 @@ if (BUILD_NCD)
     add_executable(ncd_value_parser_test ncd_value_parser_test.c)
     target_link_libraries(ncd_value_parser_test ncdvalparser ncdvalgenerator)
 
-    add_executable(ncdinterfacemonitor_test ncdinterfacemonitor_test.c)
-    target_link_libraries(ncdinterfacemonitor_test ncdinterfacemonitor)
+    if (NOT EMSCRIPTEN)
+        add_executable(ncdinterfacemonitor_test ncdinterfacemonitor_test.c)
+        target_link_libraries(ncdinterfacemonitor_test ncdinterfacemonitor)
+    endif ()
 
     add_executable(ncdval_test ncdval_test.c)
     target_link_libraries(ncdval_test ncdval)
@@ -53,7 +59,7 @@ if (BUILDING_UDEVMONITOR)
     target_link_libraries(ncdudevmanager_test udevmonitor)
 endif ()
 
-if (NOT WIN32)
+if (NOT WIN32 AND NOT EMSCRIPTEN)
     add_executable(bprocess_example bprocess_example.c)
     target_link_libraries(bprocess_example system)
 
@@ -84,3 +90,8 @@ if (BUILDING_RANDOM)
 endif ()
 
 add_executable(cavl_test cavl_test.c)
+
+if (EMSCRIPTEN)
+    add_executable(emscripten_test emscripten_test.c)
+    target_link_libraries(emscripten_test system)
+endif ()

+ 71 - 0
examples/emscripten_test.c

@@ -0,0 +1,71 @@
+/**
+ * @file emscripten_test.c
+ * @author Ambroz Bizjak <ambrop7@gmail.com>
+ * 
+ * @section LICENSE
+ * 
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the author nor the
+ *    names of its contributors may be used to endorse or promote products
+ *    derived from this software without specific prior written permission.
+ * 
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <inttypes.h>
+
+#include <emscripten/emscripten.h>
+
+#include <misc/debug.h>
+#include <system/BTime.h>
+#include <system/BReactor.h>
+
+BReactor reactor;
+BTimer timer;
+BPending job;
+
+static void timer_handler (void *unused)
+{
+    printf("timer_handler %"PRIu64"\n", btime_gettime());
+    
+    BPending_Set(&job);
+    BReactor_SetTimer(&reactor, &timer);
+}
+
+static void job_handler (void *unused)
+{
+    printf("job_handler %"PRIu64"\n", btime_gettime());
+}
+
+int main ()
+{
+    BTime_Init();
+    
+    BReactor_EmscriptenInit(&reactor);
+    
+    BTimer_Init(&timer, 500, timer_handler, NULL);
+    BReactor_SetTimer(&reactor, &timer);
+    
+    BPending_Init(&job, BReactor_PendingGroup(&reactor), job_handler, NULL);
+    BPending_Set(&job);
+    
+    BReactor_EmscriptenSync(&reactor);
+    return 0;
+}

+ 91 - 61
ncd/CMakeLists.txt

@@ -1,23 +1,70 @@
 include_directories(${CMAKE_CURRENT_BINARY_DIR})
 
 set(NCD_ADDITIONAL_SOURCES)
+set(NCD_ADDITIONAL_LIBS)
+
+if (NOT EMSCRIPTEN)
+    if (BADVPN_USE_LINUX_RFKILL)
+        list(APPEND NCD_ADDITIONAL_SOURCES
+            NCDRfkillMonitor.c
+            modules/net_backend_rfkill.c
+        )
+    endif ()
 
-if (BADVPN_USE_LINUX_RFKILL)
-    list(APPEND NCD_ADDITIONAL_SOURCES
-        NCDRfkillMonitor.c
-        modules/net_backend_rfkill.c
-    )
-endif ()
+    if (BADVPN_USE_LINUX_INPUT)
+        list(APPEND NCD_ADDITIONAL_SOURCES
+            modules/sys_evdev.c
+        )
+    endif ()
 
-if (BADVPN_USE_LINUX_INPUT)
-    list(APPEND NCD_ADDITIONAL_SOURCES
-        modules/sys_evdev.c
+    if (BADVPN_USE_INOTIFY)
+        list(APPEND NCD_ADDITIONAL_SOURCES
+            modules/sys_watch_directory.c
+        )
+    endif ()
+
+    add_library(ncdinterfacemonitor
+        NCDInterfaceMonitor.c
     )
-endif ()
+    target_link_libraries(ncdinterfacemonitor base system)
+    
+    add_library(ncdrequest
+        NCDRequestClient.c
+    )
+    target_link_libraries(ncdrequest base system ncdvalgenerator ncdvalparser)
 
-if (BADVPN_USE_INOTIFY)
     list(APPEND NCD_ADDITIONAL_SOURCES
-        modules/sys_watch_directory.c
+        NCDIfConfig.c
+        modules/command_template.c
+        modules/event_template.c
+        modules/regex_match.c
+        modules/run.c
+        modules/runonce.c
+        modules/daemon.c
+        modules/net_backend_waitdevice.c
+        modules/net_backend_waitlink.c
+        modules/net_backend_badvpn.c
+        modules/net_backend_wpa_supplicant.c
+        modules/net_up.c
+        modules/net_dns.c
+        modules/net_iptables.c
+        modules/net_ipv4_addr.c
+        modules/net_ipv4_route.c
+        modules/net_ipv4_dhcp.c
+        modules/net_ipv4_arp_probe.c
+        modules/net_watch_interfaces.c
+        modules/sys_watch_input.c
+        modules/sys_watch_usb.c
+        modules/sys_request_server.c
+        modules/net_ipv6_wait_dynamic_addr.c
+        modules/sys_request_client.c
+        modules/reboot.c
+        modules/net_ipv6_addr.c
+        modules/net_ipv6_route.c
+    )
+    
+    list(APPEND NCD_ADDITIONAL_LIBS
+        dhcpclient arpprobe ncdinterfacemonitor ncdrequest udevmonitor badvpn_random
     )
 endif ()
 
@@ -59,16 +106,6 @@ add_library(ncdsugar
 )
 target_link_libraries(ncdsugar ncdast)
 
-add_library(ncdrequest
-    NCDRequestClient.c
-)
-target_link_libraries(ncdrequest base system ncdvalgenerator ncdvalparser)
-
-add_library(ncdinterfacemonitor
-    NCDInterfaceMonitor.c
-)
-target_link_libraries(ncdinterfacemonitor base system)
-
 add_library(ncdvalcons
     NCDValCons.c
 )
@@ -78,15 +115,12 @@ add_library(ncdinterpreter
     NCDInterpreter.c
     NCDModule.c
     NCDModuleIndex.c
-    NCDIfConfig.c
     NCDObject.c
     NCDInterpProcess.c
     NCDInterpProg.c
     NCDPlaceholderDb.c
     NCDMethodIndex.c
     BEventLock.c
-    modules/command_template.c
-    modules/event_template.c
     modules/var.c
     modules/list.c
     modules/depend.c
@@ -96,14 +130,10 @@ add_library(ncdinterpreter
     modules/concatv.c
     modules/if.c
     modules/strcmp.c
-    modules/regex_match.c
     modules/logical.c
     modules/sleep.c
     modules/print.c
     modules/blocker.c
-    modules/run.c
-    modules/runonce.c
-    modules/daemon.c
     modules/spawn.c
     modules/call.c
     modules/imperative.c
@@ -118,23 +148,6 @@ add_library(ncdinterpreter
     modules/to_string.c
     modules/value.c
     modules/try.c
-    modules/net_backend_waitdevice.c
-    modules/net_backend_waitlink.c
-    modules/net_backend_badvpn.c
-    modules/net_backend_wpa_supplicant.c
-    modules/net_up.c
-    modules/net_dns.c
-    modules/net_iptables.c
-    modules/net_ipv4_addr.c
-    modules/net_ipv4_route.c
-    modules/net_ipv4_dhcp.c
-    modules/net_ipv4_arp_probe.c
-    modules/net_watch_interfaces.c
-    modules/sys_watch_input.c
-    modules/sys_watch_usb.c
-    modules/sys_request_server.c
-    modules/net_ipv6_wait_dynamic_addr.c
-    modules/sys_request_client.c
     modules/exit.c
     modules/getargs.c
     modules/arithmetic.c
@@ -145,25 +158,16 @@ add_library(ncdinterpreter
     modules/implode.c
     modules/call2.c
     modules/assert.c
-    modules/reboot.c
     modules/explode.c
-    modules/net_ipv6_addr.c
-    modules/net_ipv6_route.c
     modules/net_ipv4_addr_in_network.c
     modules/net_ipv6_addr_in_network.c
     ${NCD_ADDITIONAL_SOURCES}
 )
 target_link_libraries(ncdinterpreter
-    system flow flowextra dhcpclient arpprobe ncdval ncdstringindex ncdvalgenerator
-    ncdvalparser ncdconfigparser ncdsugar udevmonitor ncdinterfacemonitor ncdrequest
-    badvpn_random
+    system flow flowextra ncdval ncdstringindex ncdvalgenerator ncdvalparser
+    ncdconfigparser ncdsugar ${NCD_ADDITIONAL_LIBS}
 )
 
-add_executable(badvpn-ncd
-    ncd.c
-)
-target_link_libraries(badvpn-ncd ncdinterpreter)
-
 if (BADVPN_USE_LINUX_INPUT)
     string(REPLACE " " ";" FLAGS_LIST "${CMAKE_C_FLAGS}")
     execute_process(COMMAND ${CMAKE_C_COMPILER} ${FLAGS_LIST} -E ${CMAKE_CURRENT_SOURCE_DIR}/include_linux_input.c
@@ -190,7 +194,33 @@ if (BADVPN_USE_LINUX_INPUT)
     endif ()
 endif ()
 
-install(
-    TARGETS badvpn-ncd
-    RUNTIME DESTINATION bin
-)
+if (NOT EMSCRIPTEN)
+    add_executable(badvpn-ncd ncd.c)
+    target_link_libraries(badvpn-ncd ncdinterpreter)
+    
+    install(
+        TARGETS badvpn-ncd
+        RUNTIME DESTINATION bin
+    )
+endif ()
+
+if (EMSCRIPTEN)
+    add_executable(emncd emncd.c)
+    target_link_libraries(emncd ncdinterpreter)
+    
+    add_custom_command(
+        OUTPUT emncd.bc
+        DEPENDS emncd
+        COMMAND cp emncd emncd.bc
+    )
+    
+    add_custom_command(
+        OUTPUT emncd.js
+        DEPENDS emncd.bc
+        COMMAND
+        ${CMAKE_C_COMPILER} emncd.bc -o emncd.js -O2
+        -s EXPORTED_FUNCTIONS=\"['_breactor_timer_cb','_main','_emncd_start','_emncd_stop']\"
+    )
+    
+    add_custom_target(emncd_js ALL DEPENDS ${CMAKE_CURRENT_BINARY_DIR}/emncd.js)
+endif ()

+ 12 - 0
ncd/NCDInterpreter.c

@@ -129,9 +129,15 @@ int NCDInterpreter_Init (NCDInterpreter *o, const char *program, size_t program_
     ASSERT(params.handler_finished);
     ASSERT(params.num_extra_args >= 0);
     ASSERT(params.reactor);
+#ifndef BADVPN_NO_PROCESS
     ASSERT(params.manager);
+#endif
+#ifndef BADVPN_NO_UDEV
     ASSERT(params.umanager);
+#endif
+#ifndef BADVPN_NO_RANDOM
     ASSERT(params.random2);
+#endif
     
     // set params
     o->params = params;
@@ -196,9 +202,15 @@ int NCDInterpreter_Init (NCDInterpreter *o, const char *program, size_t program_
     // Don't initialize any callback at this point as these must not be called
     // from globalinit functions of modules.
     o->module_iparams.reactor = params.reactor;
+#ifndef BADVPN_NO_PROCESS
     o->module_iparams.manager = params.manager;
+#endif
+#ifndef BADVPN_NO_UDEV
     o->module_iparams.umanager = params.umanager;
+#endif
+#ifndef BADVPN_NO_RANDOM
     o->module_iparams.random2 = params.random2;
+#endif
     o->module_iparams.string_index = &o->string_index;
     
     // init modules

+ 16 - 3
ncd/NCDInterpreter.h

@@ -36,9 +36,6 @@
 #include <base/DebugObject.h>
 #include <system/BTime.h>
 #include <system/BReactor.h>
-#include <system/BProcess.h>
-#include <udevmonitor/NCDUdevManager.h>
-#include <random/BRandom2.h>
 #include <ncd/NCDStringIndex.h>
 #include <ncd/NCDMethodIndex.h>
 #include <ncd/NCDModuleIndex.h>
@@ -48,6 +45,16 @@
 #include <ncd/NCDModule.h>
 #include <structure/LinkedList1.h>
 
+#ifndef BADVPN_NO_PROCESS
+#include <system/BProcess.h>
+#endif
+#ifndef BADVPN_NO_UDEV
+#include <udevmonitor/NCDUdevManager.h>
+#endif
+#ifndef BADVPN_NO_RANDOM
+#include <random/BRandom2.h>
+#endif
+
 typedef void (*NCDInterpreter_handler_finished) (void *user, int exit_code);
 
 struct NCDInterpreter_params {
@@ -62,9 +69,15 @@ struct NCDInterpreter_params {
     
     // possibly shared resources
     BReactor *reactor;
+#ifndef BADVPN_NO_PROCESS
     BProcessManager *manager;
+#endif
+#ifndef BADVPN_NO_UDEV
     NCDUdevManager *umanager;
+#endif
+#ifndef BADVPN_NO_RANDOM
     BRandom2 *random2;
+#endif
 };
 
 typedef struct {

+ 15 - 2
ncd/NCDModule.h

@@ -33,11 +33,18 @@
 #include <misc/debug.h>
 #include <system/BReactor.h>
 #include <base/BLog.h>
+#include <ncd/NCDObject.h>
+#include <ncd/NCDStringIndex.h>
+
+#ifndef BADVPN_NO_PROCESS
 #include <system/BProcess.h>
+#endif
+#ifndef BADVPN_NO_UDEV
 #include <udevmonitor/NCDUdevManager.h>
+#endif
+#ifndef BADVPN_NO_RANDOM
 #include <random/BRandom2.h>
-#include <ncd/NCDObject.h>
-#include <ncd/NCDStringIndex.h>
+#endif
 
 #define NCDMODULE_EVENT_UP 1
 #define NCDMODULE_EVENT_DOWN 2
@@ -283,18 +290,24 @@ struct NCDModuleInst_iparams {
      * Reactor we live in.
      */
     BReactor *reactor;
+#ifndef BADVPN_NO_PROCESS
     /**
      * Process manager.
      */
     BProcessManager *manager;
+#endif
+#ifndef BADVPN_NO_UDEV
     /**
      * Udev manager.
      */
     NCDUdevManager *umanager;
+#endif
+#ifndef BADVPN_NO_RANDOM
     /**
      * Random number generator.
      */
     BRandom2 *random2;
+#endif
     /**
      * String index which keeps a mapping between strings and string identifiers.
      */

+ 122 - 0
ncd/emncd.c

@@ -0,0 +1,122 @@
+/**
+ * @file emncd.c
+ * @author Ambroz Bizjak <ambrop7@gmail.com>
+ * 
+ * @section LICENSE
+ * 
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the author nor the
+ *    names of its contributors may be used to endorse or promote products
+ *    derived from this software without specific prior written permission.
+ * 
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <stdlib.h>
+#include <string.h>
+
+#include <misc/version.h>
+#include <misc/debug.h>
+#include <base/BLog.h>
+#include <system/BTime.h>
+#include <system/BReactor.h>
+#include <ncd/NCDInterpreter.h>
+
+#include <generated/blog_channel_ncd.h>
+
+static BReactor reactor;
+static int running;
+static NCDInterpreter interpreter;
+
+static void interpreter_handler_finished (void *user, int exit_code)
+{
+    ASSERT(running)
+    
+    fprintf(stderr, "--- interpreter terminated ---\n");
+    
+    NCDInterpreter_Free(&interpreter);
+    
+    running = 0;
+}
+
+__attribute__((used))
+int main ()
+{
+    BLog_InitStderr();
+    
+    fprintf(stderr, "--- initializing emncd version "GLOBAL_VERSION" ---\n");
+    
+    BTime_Init();
+    
+    BReactor_EmscriptenInit(&reactor);
+    
+    running = 0;
+    
+    return 0;
+}
+
+__attribute__((used))
+void emncd_start (const char *program, int loglevel)
+{
+    ASSERT(program);
+    ASSERT(loglevel >= 0);
+    ASSERT(loglevel <= BLOG_DEBUG);
+    
+    if (running) {
+        fprintf(stderr, "--- cannot start, interpreter is already running! ---\n");
+        return;
+    }
+    
+    fprintf(stderr, "--- starting interpreter ---\n");
+    
+    for (int i = 0; i < BLOG_NUM_CHANNELS; i++) {
+        BLog_SetChannelLoglevel(i, loglevel);
+    }
+    
+    struct NCDInterpreter_params params;
+    params.handler_finished = interpreter_handler_finished;
+    params.user = NULL;
+    params.retry_time = 5000;
+    params.extra_args = NULL;
+    params.num_extra_args = 0;
+    params.reactor = &reactor;
+    
+    if (!NCDInterpreter_Init(&interpreter, program, strlen(program), params)) {
+        fprintf(stderr, "--- failed to initialize the interpreter (syntax error?) ---\n");
+        return;
+    }
+    
+    running = 1;
+    
+    BReactor_EmscriptenSync(&reactor);
+}
+
+__attribute__((used))
+void emncd_stop (void)
+{
+    if (!running) {
+        fprintf(stderr, "--- cannot request termination, interpreter is not running! ---\n");
+        return;
+    }
+    
+    fprintf(stderr, "--- requesting interpreter termination ---\n");
+    
+    NCDInterpreter_RequestShutdown(&interpreter, 0);
+    
+    BReactor_EmscriptenSync(&reactor);
+}

+ 320 - 0
ncd/emncd.html

@@ -0,0 +1,320 @@
+<!DOCTYPE html>
+<html>
+<head>
+<title>NCD in Javascript</title>
+<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
+</head>
+<body>
+
+<script src="emncd.js"></script>
+
+<script>
+
+var cfunc_emncd_start = Module.cwrap('emncd_start', 'null', ['string']);
+var cfunc_emncd_stop = Module.cwrap('emncd_stop', 'null', []);
+
+function on_start ()
+{
+    var code = document.getElementById('codetext').value;
+    
+    var loglevel = 0;
+    for (i = 0; i <= 5; i++) {
+        if (document.getElementById('loglevel' + i).checked) {
+            loglevel = i;
+        }
+    }
+
+    cfunc_emncd_start(code, loglevel);
+}
+
+function on_stop ()
+{
+    cfunc_emncd_stop();
+}
+
+function load_example (num)
+{
+    document.getElementById('codetext').value = document.getElementById('example' + num).value;
+}
+
+</script>
+
+This is a quick port of my <a href="http://code.google.com/p/badvpn/wiki/NCD">NCD programming language</a>
+to Javascript using the <a href="https://github.com/kripken/emscripten">Emscripten</a> compiler.
+<br />
+
+Please open the Javascript console so you can see the output (Chrome: CTRL+Shift+J. Firefox: CTRL+Shift+K).
+<br />
+
+<textarea id="example1" style="display:none;">
+process hello {
+    println("hello, world");
+    exit("0");
+}
+</textarea>
+
+<textarea id="example2" style="display:none;">
+process foo {
+    println("Starting up, please wait...");
+    rprintln("Goodbye World!");
+
+    sleep("500", "300"); # sleeps 500ms on init and 300ms on deinit
+
+    println("Hello World!");
+    rprintln("Shutting down, please wait...");
+}
+</textarea>
+
+<textarea id="example3" style="display:none;">
+process hello {
+    var("0") ctr;
+
+    blocker() blk;
+    blk->up();
+    blk->use();
+
+    num_modulo(ctr, "3") m_three;
+    num_modulo(ctr, "5") m_five;
+    num_equal(m_three, "0") d_three;
+    num_equal(m_five, "0") d_five;
+    and(d_three, d_five) d_three_five;
+
+    If (d_three_five) {
+        var("fizzbuzz") text;
+    } Elif (d_three) {
+        var("fizz") text;
+    } Elif (d_five) {
+        var("buzz") text;
+    } else {
+        var(ctr) text;
+    } branch;
+
+    println(branch.text);
+
+    num_add(ctr, "1") i;
+    num_modulo(i, "20") i;
+    ctr->set(i);
+
+    sleep("100", "0");
+    blk->downup();
+
+}
+</textarea>
+
+<textarea id="example4" style="display:none;">
+process main {
+    var({"0", "1", "3", "2", "2", "3", "1", "1", "6", "end"}) list;
+    value(["1":"one", "2":"two", "3":"three"]) map;
+
+    call("replace", {"_caller.list", "_caller.map"}) replace;
+
+    to_string(replace.result) str;
+    println(str);
+    exit("0");
+}
+
+template replace {
+    alias(_arg0) list;
+    alias(_arg1) map;
+
+    value({}) new_list;
+    Foreach (list As elem) {
+        map->try_get(elem) y;
+        If (y.exists) {
+            new_list->insert(new_list.length, y);
+        } Else {
+            new_list->insert(new_list.length, elem);
+        };
+    };
+
+    alias("new_list") result;
+}
+</textarea>
+
+<textarea id="example5" style="display:none;">
+process hello {
+    println("Hello, NCD in Javascript!");
+    println("Will now wait 2 seconds.");
+    rprintln("Goodbye.");
+
+    sleep("2000", "3000");
+    rprintln("Waiting 3 seconds before dying.");
+
+    call("func", {"FirstArg", "SecondArg"});
+    
+    var({"one", "two", "three"}) list;
+    to_string(list) str;
+    println(str);
+    
+    Foreach (list As elem) {
+        println("start ", elem);
+        rprintln("stop ", elem);
+    };
+
+    println("We're finished. Press \"Request termination\" to unwind us.");
+    rprintln("Terminating...");
+}
+
+template func {
+    println("func here, my args are: ", _arg0, " ", _arg1);
+    rprintln("func going away");
+}
+</textarea>
+
+<textarea id="example6" style="display:none;">
+process main {
+    # Turing machine specification.
+    var("B") blank;
+    var([
+        {"0", "0"}:{"0", "0", "right"},
+        {"0", "1"}:{"1", "x", "right"},
+        {"1", "1"}:{"1", "1", "right"},
+        {"1", "0"}:{"2", "0", "right"},
+        {"2", "0"}:{"2", "0", "right"},
+        {"2", "1"}:{"3", "1", "right"},
+        {"3", "1"}:{"3", "1", "right"},
+        {"3", "0"}:{"4", "1", "left"},
+        {"3", "B"}:{"4", "1", "left"},
+        {"4", "1"}:{"4", "1", "left"},
+        {"4", "0"}:{"5", "0", "left"},
+        {"5", "0"}:{"5", "0", "left"},
+        {"5", "1"}:{"6", "1", "left"},
+        {"5", "x"}:{"h", "x", "stay"},
+        {"6", "1"}:{"6", "1", "left"},
+        {"6", "x"}:{"0", "x", "right"},
+        {"6", "0"}:{"0", "0", "right"}
+    ]) rules;
+    var("0") initial_state;
+    var({}) initial_tape_left;
+    var({
+        "1", "1", "1", "1", "0", "0", "1", "1", "1", "1"
+    }) initial_tape_right;
+
+    # Perform the computation, stopping when no rule matches.
+    call("turing", {blank, rules, initial_state, initial_tape_left, initial_tape_right}) results;
+
+    # Print results.
+    to_string(results.tape_left) tape_left;
+    to_string(results.tape_right) tape_right;
+    to_string({results.side, results.pos}) head_pos;
+    to_string(results.state) head_state;
+    println("Tape L: ", tape_left);
+    println("Tape R: ", tape_right);
+    println("Head position: ", head_pos);
+    println("Head state: ", head_state);
+
+    exit("0");
+}
+
+template turing {
+    alias("_arg0") blank;
+    value(_arg1) rules;
+    alias("_arg2") initial_state;
+    alias("_arg3") initial_tape_left;
+    alias("_arg4") initial_tape_right;
+
+    # Head state.
+    var(initial_state) state;
+
+    # Tape. Positions go like this: ... L2 L1 L0 R0 R1 R2 ... 
+    value(initial_tape_left) tape_left;
+    value(initial_tape_right) tape_right;
+
+    # Make sure each side of the tape has at least one symbol so we can flip easily.
+    tape_left->insert(tape_left.length, blank);
+    tape_right->insert(tape_right.length, blank);
+
+    # Head position.
+    var("right") side;
+    var("0") pos;
+
+    # Enter loop.
+    blocker() loop_blk;
+    loop_blk->up();
+    loop_blk->use();
+
+    # Get symbol under head.
+    concat("tape_", side) tape_name;
+    alias(tape_name) cur_tape;
+    cur_tape->get(pos) symbol;
+
+    # Look for a matching rule.
+    rules->try_get({state, symbol}) rule;
+
+    If (rule.exists) {
+        # Extract directions from rule.
+        rule->get("0") new_state;
+        rule->get("1") new_symbol;
+        rule->get("2") move;
+
+        # Change head state.
+        state->set(new_state);
+
+        # Replace symbol under head.
+        cur_tape->remove(pos);
+        cur_tape->insert(pos, new_symbol);
+
+        # Branch based on how we move.
+        strcmp(move, side) is_outside;
+        strcmp(move, "stay") is_stay;
+        strcmp(pos, "0") is_zero;
+        If (is_outside) {
+            # Increment position.
+            num_add(pos, "1") new_pos;
+            pos->set(new_pos);
+
+            # If the new position is out of range, extend tape.
+            strcmp(pos, cur_tape.length) need_extend;
+            If (need_extend) {
+                cur_tape->insert(pos, blank);
+            };
+        } elif (is_stay) {
+            # Nop.
+            getargs();
+        } elif (is_zero) {
+            # Flip side, leave pos at zero.
+            side->set(move);
+        } else {
+            # Decrement position.
+            num_subtract(pos, "1") new_pos;
+            pos->set(new_pos);
+        };
+
+        # Continue loop.
+        loop_blk->downup();
+    };
+}
+</textarea>
+
+Examples:
+<button onclick="load_example(1)">Hello World</button>
+<button onclick="load_example(2)">Sleep</button>
+<button onclick="load_example(3)">FizzBuzz</button>
+<button onclick="load_example(4)">Replace</button>
+<button onclick="load_example(5)">Foreach, call</button>
+<button onclick="load_example(6)">Turing Machine</button>
+<br />
+
+<textarea rows=30 cols=80 id="codetext">
+process hello {
+    println("hello, world");
+    exit("0");
+}
+</textarea>
+<br />
+
+Loglevel: 
+<input type="radio" name="loglevel" id="loglevel0"> None
+<input type="radio" name="loglevel" id="loglevel1"> Error
+<input type="radio" name="loglevel" id="loglevel2" checked> Warning
+<input type="radio" name="loglevel" id="loglevel3"> Notice
+<input type="radio" name="loglevel" id="loglevel4"> Info
+<input type="radio" name="loglevel" id="loglevel5"> Debug <br />
+
+<button onclick="on_start()">Start NCD</button>
+<button onclick="on_stop()">Request termination</button>
+<br />
+Note: if you get the interpreter stuck and unable to terminate, just refresh the page
+
+</body>
+</html>

+ 38 - 34
ncd/modules/modules.h

@@ -43,14 +43,10 @@ extern const struct NCDModuleGroup ncdmodule_concat;
 extern const struct NCDModuleGroup ncdmodule_concatv;
 extern const struct NCDModuleGroup ncdmodule_if;
 extern const struct NCDModuleGroup ncdmodule_strcmp;
-extern const struct NCDModuleGroup ncdmodule_regex_match;
 extern const struct NCDModuleGroup ncdmodule_logical;
 extern const struct NCDModuleGroup ncdmodule_sleep;
 extern const struct NCDModuleGroup ncdmodule_print;
 extern const struct NCDModuleGroup ncdmodule_blocker;
-extern const struct NCDModuleGroup ncdmodule_run;
-extern const struct NCDModuleGroup ncdmodule_runonce;
-extern const struct NCDModuleGroup ncdmodule_daemon;
 extern const struct NCDModuleGroup ncdmodule_spawn;
 extern const struct NCDModuleGroup ncdmodule_call;
 extern const struct NCDModuleGroup ncdmodule_imperative;
@@ -65,6 +61,24 @@ extern const struct NCDModuleGroup ncdmodule_from_string;
 extern const struct NCDModuleGroup ncdmodule_to_string;
 extern const struct NCDModuleGroup ncdmodule_value;
 extern const struct NCDModuleGroup ncdmodule_try;
+extern const struct NCDModuleGroup ncdmodule_exit;
+extern const struct NCDModuleGroup ncdmodule_getargs;
+extern const struct NCDModuleGroup ncdmodule_arithmetic;
+extern const struct NCDModuleGroup ncdmodule_parse;
+extern const struct NCDModuleGroup ncdmodule_valuemetic;
+extern const struct NCDModuleGroup ncdmodule_file;
+extern const struct NCDModuleGroup ncdmodule_netmask;
+extern const struct NCDModuleGroup ncdmodule_implode;
+extern const struct NCDModuleGroup ncdmodule_call2;
+extern const struct NCDModuleGroup ncdmodule_assert;
+extern const struct NCDModuleGroup ncdmodule_explode;
+extern const struct NCDModuleGroup ncdmodule_net_ipv4_addr_in_network;
+extern const struct NCDModuleGroup ncdmodule_net_ipv6_addr_in_network;
+#ifndef BADVPN_EMSCRIPTEN
+extern const struct NCDModuleGroup ncdmodule_regex_match;
+extern const struct NCDModuleGroup ncdmodule_run;
+extern const struct NCDModuleGroup ncdmodule_runonce;
+extern const struct NCDModuleGroup ncdmodule_daemon;
 extern const struct NCDModuleGroup ncdmodule_net_backend_waitdevice;
 extern const struct NCDModuleGroup ncdmodule_net_backend_waitlink;
 extern const struct NCDModuleGroup ncdmodule_net_backend_badvpn;
@@ -91,22 +105,10 @@ extern const struct NCDModuleGroup ncdmodule_sys_watch_directory;
 extern const struct NCDModuleGroup ncdmodule_sys_request_server;
 extern const struct NCDModuleGroup ncdmodule_net_ipv6_wait_dynamic_addr;
 extern const struct NCDModuleGroup ncdmodule_sys_request_client;
-extern const struct NCDModuleGroup ncdmodule_exit;
-extern const struct NCDModuleGroup ncdmodule_getargs;
-extern const struct NCDModuleGroup ncdmodule_arithmetic;
-extern const struct NCDModuleGroup ncdmodule_parse;
-extern const struct NCDModuleGroup ncdmodule_valuemetic;
-extern const struct NCDModuleGroup ncdmodule_file;
-extern const struct NCDModuleGroup ncdmodule_netmask;
-extern const struct NCDModuleGroup ncdmodule_implode;
-extern const struct NCDModuleGroup ncdmodule_call2;
-extern const struct NCDModuleGroup ncdmodule_assert;
 extern const struct NCDModuleGroup ncdmodule_reboot;
-extern const struct NCDModuleGroup ncdmodule_explode;
 extern const struct NCDModuleGroup ncdmodule_net_ipv6_addr;
 extern const struct NCDModuleGroup ncdmodule_net_ipv6_route;
-extern const struct NCDModuleGroup ncdmodule_net_ipv4_addr_in_network;
-extern const struct NCDModuleGroup ncdmodule_net_ipv6_addr_in_network;
+#endif
 
 static const struct NCDModuleGroup *ncd_modules[] = {
     &ncdmodule_var,
@@ -118,14 +120,10 @@ static const struct NCDModuleGroup *ncd_modules[] = {
     &ncdmodule_concatv,
     &ncdmodule_if,
     &ncdmodule_strcmp,
-    &ncdmodule_regex_match,
     &ncdmodule_logical,
     &ncdmodule_sleep,
     &ncdmodule_print,
     &ncdmodule_blocker,
-    &ncdmodule_run,
-    &ncdmodule_runonce,
-    &ncdmodule_daemon,
     &ncdmodule_spawn,
     &ncdmodule_call,
     &ncdmodule_imperative,
@@ -140,6 +138,24 @@ static const struct NCDModuleGroup *ncd_modules[] = {
     &ncdmodule_to_string,
     &ncdmodule_value,
     &ncdmodule_try,
+    &ncdmodule_exit,
+    &ncdmodule_getargs,
+    &ncdmodule_arithmetic,
+    &ncdmodule_parse,
+    &ncdmodule_valuemetic,
+    &ncdmodule_file,
+    &ncdmodule_netmask,
+    &ncdmodule_implode,
+    &ncdmodule_call2,
+    &ncdmodule_assert,
+    &ncdmodule_explode,
+    &ncdmodule_net_ipv4_addr_in_network,
+    &ncdmodule_net_ipv6_addr_in_network,
+#ifndef BADVPN_EMSCRIPTEN
+    &ncdmodule_regex_match,
+    &ncdmodule_run,
+    &ncdmodule_runonce,
+    &ncdmodule_daemon,
     &ncdmodule_net_backend_waitdevice,
     &ncdmodule_net_backend_waitlink,
     &ncdmodule_net_backend_badvpn,
@@ -166,22 +182,10 @@ static const struct NCDModuleGroup *ncd_modules[] = {
     &ncdmodule_sys_request_server,
     &ncdmodule_net_ipv6_wait_dynamic_addr,
     &ncdmodule_sys_request_client,
-    &ncdmodule_exit,
-    &ncdmodule_getargs,
-    &ncdmodule_arithmetic,
-    &ncdmodule_parse,
-    &ncdmodule_valuemetic,
-    &ncdmodule_file,
-    &ncdmodule_netmask,
-    &ncdmodule_implode,
-    &ncdmodule_call2,
-    &ncdmodule_assert,
     &ncdmodule_reboot,
-    &ncdmodule_explode,
     &ncdmodule_net_ipv6_addr,
     &ncdmodule_net_ipv6_route,
-    &ncdmodule_net_ipv4_addr_in_network,
-    &ncdmodule_net_ipv6_addr_in_network,
+#endif
     NULL
 };
 

+ 15 - 5
ncd/modules/sleep.c

@@ -38,8 +38,10 @@
 #include <string.h>
 #include <stdio.h>
 #include <inttypes.h>
+#include <limits.h>
 
 #include <ncd/NCDModule.h>
+#include <ncd/value_utils.h>
 
 #include <generated/blog_channel_ncd_sleep.h>
 
@@ -80,19 +82,27 @@ static void func_new (void *vo, NCDModuleInst *i, const struct NCDModuleInst_new
         ModuleLog(o->i, BLOG_ERROR, "wrong arity");
         goto fail0;
     }
-    if (!NCDVal_IsStringNoNulls(ms_start_arg) || !NCDVal_IsStringNoNulls(ms_stop_arg)) {
+    if (!NCDVal_IsString(ms_start_arg) || !NCDVal_IsString(ms_stop_arg)) {
         ModuleLog(o->i, BLOG_ERROR, "wrong type");
         goto fail0;
     }
-    if (sscanf(NCDVal_StringValue(ms_start_arg), "%"SCNi64, &o->ms_start) != 1) {
-        ModuleLog(o->i, BLOG_ERROR, "wrong time");
+    
+    uintmax_t ms_start;
+    uintmax_t ms_stop;
+    
+    if (!ncd_read_uintmax(ms_start_arg, &ms_start) || ms_start > INT64_MAX) {
+        ModuleLog(o->i, BLOG_ERROR, "wrong start time");
         goto fail0;
     }
-    if (sscanf(NCDVal_StringValue(ms_stop_arg), "%"SCNi64, &o->ms_stop) != 1) {
-        ModuleLog(o->i, BLOG_ERROR, "wrong time");
+    
+    if (!ncd_read_uintmax(ms_stop_arg, &ms_stop) || ms_stop > INT64_MAX) {
+        ModuleLog(o->i, BLOG_ERROR, "wrong stop time");
         goto fail0;
     }
     
+    o->ms_start = ms_start;
+    o->ms_stop = ms_stop;
+    
     // init timer
     BTimer_Init(&o->timer, 0, timer_handler, o);
     

+ 3 - 1
system/BReactor.h

@@ -1,4 +1,4 @@
-#if defined(BADVPN_BREACTOR_BADVPN) + defined(BADVPN_BREACTOR_GLIB) != 1
+#if defined(BADVPN_BREACTOR_BADVPN) + defined(BADVPN_BREACTOR_GLIB) + defined(BADVPN_BREACTOR_EMSCRIPTEN) != 1
 #error No reactor backend or too many reactor backens
 #endif
 
@@ -6,4 +6,6 @@
 #include "BReactor_badvpn.h"
 #elif defined(BADVPN_BREACTOR_GLIB)
 #include "BReactor_glib.h"
+#elif defined(BADVPN_BREACTOR_EMSCRIPTEN)
+#include "BReactor_emscripten.h"
 #endif

+ 176 - 0
system/BReactor_emscripten.c

@@ -0,0 +1,176 @@
+/**
+ * @file BReactor_emscripten.c
+ * @author Ambroz Bizjak <ambrop7@gmail.com>
+ * 
+ * @section LICENSE
+ * 
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the author nor the
+ *    names of its contributors may be used to endorse or promote products
+ *    derived from this software without specific prior written permission.
+ * 
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <stdio.h>
+#include <inttypes.h>
+
+#include <emscripten/emscripten.h>
+
+#include "BReactor_emscripten.h"
+
+#include <misc/debug.h>
+
+#include <generated/blog_channel_BReactor.h>
+
+static void assert_timer (BTimer *bt, BReactor *reactor)
+{
+    ASSERT(bt->active == 0 || bt->active == 1);
+    ASSERT(bt->active == 0 || bt->reactor);
+    ASSERT(bt->active == 0 || (!reactor || bt->reactor == reactor));
+}
+
+static void dispatch_pending (BReactor *o)
+{
+    while (BPendingGroup_HasJobs(&o->pending_jobs)) {
+        BPendingGroup_ExecuteJob(&o->pending_jobs);
+    }
+}
+
+__attribute__((used))
+void breactor_timer_cb (BReactor *reactor, BTimer *bt)
+{
+    assert_timer(bt, reactor);
+    ASSERT(bt->active);
+    ASSERT(!BPendingGroup_HasJobs(&reactor->pending_jobs));
+    
+    bt->active = 0;
+    
+    bt->handler(bt->handler_pointer);
+    dispatch_pending(reactor);
+}
+
+static void small_timer_handler (void *vbt)
+{
+    BSmallTimer *bt = vbt;
+    
+    bt->handler(bt);
+}
+
+void BTimer_Init (BTimer *bt, btime_t msTime, BTimer_handler handler, void *user)
+{
+    bt->msTime = msTime;
+    bt->handler = handler;
+    bt->handler_pointer = user;
+    bt->active = 0;
+}
+
+int BTimer_IsRunning (BTimer *bt)
+{
+    assert_timer(bt, NULL);
+    
+    return bt->active;
+}
+
+void BSmallTimer_Init (BSmallTimer *bt, BSmallTimer_handler handler)
+{
+    BTimer_Init(&bt->timer, 0, small_timer_handler, bt);
+    bt->handler = handler;
+}
+
+int BSmallTimer_IsRunning (BSmallTimer *bt)
+{
+    return BTimer_IsRunning(&bt->timer);
+}
+
+void BReactor_EmscriptenInit (BReactor *bsys)
+{
+    BPendingGroup_Init(&bsys->pending_jobs);
+    
+    DebugObject_Init(&bsys->d_obj);
+}
+
+void BReactor_EmscriptenFree (BReactor *bsys)
+{
+    DebugObject_Free(&bsys->d_obj);
+    ASSERT(!BPendingGroup_HasJobs(&bsys->pending_jobs));
+}
+
+void BReactor_EmscriptenSync (BReactor *bsys)
+{
+    DebugObject_Access(&bsys->d_obj);
+    
+    dispatch_pending(bsys);
+}
+
+BPendingGroup * BReactor_PendingGroup (BReactor *bsys)
+{
+    DebugObject_Access(&bsys->d_obj);
+    
+    return &bsys->pending_jobs;
+}
+
+void BReactor_SetTimer (BReactor *bsys, BTimer *bt)
+{
+    BReactor_SetTimerAfter(bsys, bt, bt->msTime);
+}
+
+void BReactor_SetTimerAfter (BReactor *bsys, BTimer *bt, btime_t after)
+{
+    DebugObject_Access(&bsys->d_obj);
+    assert_timer(bt, bsys);
+    
+    if (bt->active) {
+        BReactor_RemoveTimer(bsys, bt);
+    }
+    
+    char cmd[120];
+    sprintf(cmd, "setTimeout(function(){Module.ccall('breactor_timer_cb','null',['number','number'],[%d,%d]);},%"PRIi64")", (int)bsys, (int)bt, after);
+    
+    bt->active = 1;
+    bt->timerid = emscripten_run_script_int(cmd);
+    bt->reactor = bsys;
+}
+
+void BReactor_RemoveTimer (BReactor *bsys, BTimer *bt)
+{
+    DebugObject_Access(&bsys->d_obj);
+    assert_timer(bt, bsys);
+    
+    if (!bt->active) {
+        return;
+    }
+    
+    char cmd[30];
+    sprintf(cmd, "clearTimeout(%d)", bt->timerid);
+    
+    emscripten_run_script(cmd);
+    bt->active = 0;
+}
+
+void BReactor_SetSmallTimer (BReactor *bsys, BSmallTimer *bt, int mode, btime_t time)
+{
+    ASSERT(mode == BTIMER_SET_RELATIVE)
+    
+    BReactor_SetTimerAfter(bsys, &bt->timer, time);
+}
+
+void BReactor_RemoveSmallTimer (BReactor *bsys, BSmallTimer *bt)
+{
+    BReactor_RemoveTimer(bsys, &bt->timer);
+}

+ 87 - 0
system/BReactor_emscripten.h

@@ -0,0 +1,87 @@
+/**
+ * @file BReactor_emscripten.h
+ * @author Ambroz Bizjak <ambrop7@gmail.com>
+ * 
+ * @section LICENSE
+ * 
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the author nor the
+ *    names of its contributors may be used to endorse or promote products
+ *    derived from this software without specific prior written permission.
+ * 
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef BADVPN_SYSTEM_BREACTOR_H
+#define BADVPN_SYSTEM_BREACTOR_H
+
+#include <stdint.h>
+
+#include <base/DebugObject.h>
+#include <base/BPending.h>
+#include <system/BTime.h>
+
+#define BTIMER_SET_RELATIVE 2
+
+typedef struct BReactor_s BReactor;
+
+typedef void (*BTimer_handler) (void *user);
+
+typedef struct BTimer_t {
+    btime_t msTime;
+    BTimer_handler handler;
+    void *handler_pointer;
+    uint8_t active;
+    int timerid;
+    BReactor *reactor;
+} BTimer;
+
+void BTimer_Init (BTimer *bt, btime_t msTime, BTimer_handler handler, void *user);
+int BTimer_IsRunning (BTimer *bt);
+
+struct BSmallTimer_t;
+
+typedef void (*BSmallTimer_handler) (struct BSmallTimer_t *timer);
+
+typedef struct BSmallTimer_t {
+    BTimer timer;
+    BSmallTimer_handler handler;
+} BSmallTimer;
+
+void BSmallTimer_Init (BSmallTimer *bt, BSmallTimer_handler handler);
+int BSmallTimer_IsRunning (BSmallTimer *bt);
+
+struct BReactor_s {
+    BPendingGroup pending_jobs;
+    DebugObject d_obj;
+};
+
+void BReactor_EmscriptenInit (BReactor *bsys);
+void BReactor_EmscriptenFree (BReactor *bsys);
+void BReactor_EmscriptenSync (BReactor *bsys);
+
+BPendingGroup * BReactor_PendingGroup (BReactor *bsys);
+
+void BReactor_SetTimer (BReactor *bsys, BTimer *bt);
+void BReactor_SetTimerAfter (BReactor *bsys, BTimer *bt, btime_t after);
+void BReactor_RemoveTimer (BReactor *bsys, BTimer *bt);
+
+void BReactor_SetSmallTimer (BReactor *bsys, BSmallTimer *bt, int mode, btime_t time);
+void BReactor_RemoveSmallTimer (BReactor *bsys, BSmallTimer *bt);
+
+#endif

+ 16 - 4
system/BTime.h

@@ -34,8 +34,10 @@
 #ifndef BADVPN_SYSTEM_BTIME_H
 #define BADVPN_SYSTEM_BTIME_H
 
-#ifdef BADVPN_USE_WINAPI
+#if defined(BADVPN_USE_WINAPI)
 #include <windows.h>
+#elif defined(BADVPN_EMSCRIPTEN)
+#include <emscripten/emscripten.h>
 #else
 #include <time.h>
 #include <sys/time.h>
@@ -57,8 +59,10 @@ struct _BTime_global {
     #ifndef NDEBUG
     int initialized; // initialized statically
     #endif
-    #ifdef BADVPN_USE_WINAPI
+    #if defined(BADVPN_USE_WINAPI)
     LARGE_INTEGER start_time;
+    #elif defined(BADVPN_EMSCRIPTEN)
+    btime_t start_time;
     #else
     btime_t start_time;
     int use_gettimeofday;
@@ -71,10 +75,14 @@ static void BTime_Init (void)
 {
     ASSERT(!btime_global.initialized)
     
-    #ifdef BADVPN_USE_WINAPI
+    #if defined(BADVPN_USE_WINAPI)
     
     ASSERT_FORCE(QueryPerformanceCounter(&btime_global.start_time))
     
+    #elif defined(BADVPN_EMSCRIPTEN)
+    
+    btime_global.start_time = emscripten_get_now();
+    
     #else
     
     struct timespec ts;
@@ -102,7 +110,7 @@ static btime_t btime_gettime (void)
 {
     ASSERT(btime_global.initialized)
     
-    #ifdef BADVPN_USE_WINAPI
+    #if defined(BADVPN_USE_WINAPI)
     
     LARGE_INTEGER count;
     LARGE_INTEGER freq;
@@ -110,6 +118,10 @@ static btime_t btime_gettime (void)
     ASSERT_FORCE(QueryPerformanceFrequency(&freq))
     return (((count.QuadPart - btime_global.start_time.QuadPart) * 1000) / freq.QuadPart);
     
+    #elif defined(BADVPN_EMSCRIPTEN)
+    
+    return (btime_t)emscripten_get_now() - btime_global.start_time;
+    
     #else
     
     if (btime_global.use_gettimeofday) {

+ 22 - 15
system/CMakeLists.txt

@@ -1,22 +1,29 @@
 set(BSYSTEM_ADDITIONAL_LIBS)
 set(BSYSTEM_ADDITIONAL_SOURCES)
 
-if (WIN32)
-    list(APPEND BSYSTEM_ADDITIONAL_LIBS ws2_32 mswsock)
+if (NOT EMSCRIPTEN)
     list(APPEND BSYSTEM_ADDITIONAL_SOURCES
-        BConnection_win.c
-        BDatagram_win.c
+        BSignal.c
+        BNetwork.c
     )
-endif ()
 
-if (NOT WIN32)
-    list(APPEND BSYSTEM_ADDITIONAL_SOURCES
-        BUnixSignal.c
-        BConnection_unix.c
-        BDatagram_unix.c
-        BProcess.c
-        BInputProcess.c
-    )
+    if (WIN32)
+        list(APPEND BSYSTEM_ADDITIONAL_LIBS ws2_32 mswsock)
+        list(APPEND BSYSTEM_ADDITIONAL_SOURCES
+            BConnection_win.c
+            BDatagram_win.c
+        )
+    endif ()
+
+    if (NOT WIN32)
+        list(APPEND BSYSTEM_ADDITIONAL_SOURCES
+            BUnixSignal.c
+            BConnection_unix.c
+            BDatagram_unix.c
+            BProcess.c
+            BInputProcess.c
+        )
+    endif ()
 endif ()
 
 if (BREACTOR_BACKEND STREQUAL "badvpn")
@@ -24,12 +31,12 @@ if (BREACTOR_BACKEND STREQUAL "badvpn")
 elseif (BREACTOR_BACKEND STREQUAL "glib")
     list(APPEND BSYSTEM_ADDITIONAL_SOURCES BReactor_glib.c)
     list(APPEND BSYSTEM_ADDITIONAL_LIBS ${GLIB2_LIBRARIES})
+elseif (BREACTOR_BACKEND STREQUAL "emscripten")
+    list(APPEND BSYSTEM_ADDITIONAL_SOURCES BReactor_emscripten.c)
 endif ()
 
 add_library(system
-    BSignal.c
     BTime.c
-    BNetwork.c
     ${BSYSTEM_ADDITIONAL_SOURCES}
 )
 target_link_libraries(system base flow ${BSYSTEM_ADDITIONAL_LIBS})