From bf9f0d6338e5043a1816ff9995261c6fd032a8fe Mon Sep 17 00:00:00 2001 From: ValKmjolnir Date: Wed, 12 Jun 2024 01:25:40 +0800 Subject: [PATCH 1/4] :sparkles: add subprocess.fork/kill --- src/nasal_codegen.h | 4 +- src/nasal_import.cpp | 9 ++- src/natives/subprocess.cpp | 112 +++++++++++++++++++++++++++++++++++++ src/natives/subprocess.h | 3 + std/dylib.nas | 2 +- std/subprocess.nas | 10 +++- 6 files changed, 136 insertions(+), 4 deletions(-) diff --git a/src/nasal_codegen.h b/src/nasal_codegen.h index ab742c9..175fb06 100644 --- a/src/nasal_codegen.h +++ b/src/nasal_codegen.h @@ -62,7 +62,9 @@ private: // subprocess "__subprocess_popen", "__subprocess_pclose", - "__subprocess_read_stdout" + "__subprocess_read_stdout", + "__subprocess_fork", + "__subprocess_kill" }; // file mapper for file -> index diff --git a/src/nasal_import.cpp b/src/nasal_import.cpp index 4093516..77a206e 100644 --- a/src/nasal_import.cpp +++ b/src/nasal_import.cpp @@ -9,8 +9,15 @@ namespace nasal { linker::linker(): show_path_flag(false), this_file("") { + const auto env_get_path = getenv("PATH"); + if (!env_get_path) { + err.warn("link", "cannot get env \"PATH\"."); + envpath = {}; + return; + } + const auto seperator = util::is_windows()? ';':':'; - const auto PATH = std::string(getenv("PATH")); + const auto PATH = std::string(env_get_path); usize last = 0, position = PATH.find(seperator, 0); while(position!=std::string::npos) { std::string dirpath = PATH.substr(last, position-last); diff --git a/src/natives/subprocess.cpp b/src/natives/subprocess.cpp index f6974e0..91a63a5 100644 --- a/src/natives/subprocess.cpp +++ b/src/natives/subprocess.cpp @@ -4,9 +4,17 @@ #ifdef _MSC_VER #define popen _popen #define pclose _pclose +#define perror _perror +#endif + +#ifndef _MSC_VER +#include +#include #endif #include +#include +#include namespace nasal { @@ -59,10 +67,114 @@ var builtin_subprocess_read_stdout(context* ctx, gc* ngc) { return res; } +var builtin_subprocess_fork(context* ctx, gc* ngc) { + auto cmd = ctx->localr[1]; + if (!cmd.is_vec()) { + return nas_err("subprocess::fork", "expect a string as the command"); + } + +#ifdef WIN32 + // STARTUPINFO si; + // PROCESS_INFORMATION pi; + + // // init STARTUPINFO + // ZeroMemory(&si, sizeof(si)); + // si.cb = sizeof(si); + // si.dwFlags = STARTF_USESHOWWINDOW; + // si.wShowWindow = SW_SHOW; + + // // init PROCESS_INFORMATION + // ZeroMemory(&pi, sizeof(pi)); + + // auto len = MultiByteToWideChar(CP_UTF8, 0, cmd.str().c_str(), -1, nullptr, 0); + + // auto res = CreateProcess( + // nullptr, + // cmd.str().c_str(), + // nullptr, + // nullptr, + // false, + // 0, + // nullptr, + // nullptr, + // &si, + // &pi + // ); +#else + // create argv + char** argv = new char*[cmd.vec().elems.size()+1]; + for(const auto& v : cmd.vec().elems) { + if (!v.is_str()) { + return nas_err("subprocess::fork", "non-string arguments"); + } + } + for(usize i = 0; ilocalr[1]; + if (!pid.is_num()) { + return nas_err("subprocess::kill", "need numeral pid"); + } + + int status; + pid_t result = waitpid(pid.num(), &status, WNOHANG); + // child process running + if (result==0) { + kill(pid.num(), SIGTERM); + } +#endif + return nil; +} + nasal_builtin_table subprocess_native[] = { {"__subprocess_popen", builtin_subprocess_popen}, {"__subprocess_pclose", builtin_subprocess_pclose}, {"__subprocess_read_stdout", builtin_subprocess_read_stdout}, + {"__subprocess_fork", builtin_subprocess_fork}, + {"__subprocess_kill", builtin_subprocess_kill}, {nullptr, nullptr} }; diff --git a/src/natives/subprocess.h b/src/natives/subprocess.h index 23029a4..144b63d 100644 --- a/src/natives/subprocess.h +++ b/src/natives/subprocess.h @@ -11,6 +11,9 @@ void subprocess_destructor(void*); var builtin_subprocess_popen(context*, gc*); var builtin_subprocess_pclose(context*, gc*); var builtin_subprocess_read_stdout(context*, gc*); + +var builtin_subprocess_fork(context*, gc*); +var builtin_subprocess_kill(context*, gc*); extern nasal_builtin_table subprocess_native[]; } \ No newline at end of file diff --git a/std/dylib.nas b/std/dylib.nas index a808653..a63b0a0 100644 --- a/std/dylib.nas +++ b/std/dylib.nas @@ -13,7 +13,7 @@ var dlopen = func(libname) { if (io.exists(libname)) return __dlopen(libname); # find dynamic lib through PATH - var envpath = split(os.platform()=="windows"? ";":":",unix.getenv("PATH")); + var envpath = split(os.platform()=="windows"? ";":":", unix.getenv("PATH")); # first find ./module append(envpath, "."); var path = os.platform()=="windows"? "\\module\\":"/module/"; diff --git a/std/subprocess.nas b/std/subprocess.nas index e82cf8b..c8b5abf 100644 --- a/std/subprocess.nas +++ b/std/subprocess.nas @@ -8,4 +8,12 @@ var pclose = func(subproc) { var read = func(subproc) { return __subprocess_read_stdout(subproc); -} \ No newline at end of file +} + +var fork = func(vec) { + return __subprocess_fork(vec); +} + +var kill = func(pid) { + return __subprocess_kill(pid); +} From ef00209018c4690dd864b2309ae9a2faa15945c5 Mon Sep 17 00:00:00 2001 From: ValKmjolnir Date: Wed, 12 Jun 2024 22:08:02 +0800 Subject: [PATCH 2/4] :bug: fix compilation error on macOS --- src/natives/subprocess.cpp | 22 ++++++---------------- 1 file changed, 6 insertions(+), 16 deletions(-) diff --git a/src/natives/subprocess.cpp b/src/natives/subprocess.cpp index 91a63a5..6ef5f81 100644 --- a/src/natives/subprocess.cpp +++ b/src/natives/subprocess.cpp @@ -9,12 +9,16 @@ #ifndef _MSC_VER #include +#endif + +#ifndef WIN32 #include #endif #include #include #include +#include namespace nasal { @@ -113,17 +117,6 @@ var builtin_subprocess_fork(context* ctx, gc* ngc) { } argv[cmd.vec().elems.size()] = nullptr; - // use the same env of parent process - char** env = nullptr; - if (getenv("PATH")) { - env = new char*[2]; - env[0] = strdup((std::string("PATH=") + getenv("PATH")).c_str()); - env[1] = nullptr; - } else { - env = new char*[1]; - env[0] = nullptr; - } - // create child process auto pid = fork(); if (pid < 0) { @@ -131,7 +124,7 @@ var builtin_subprocess_fork(context* ctx, gc* ngc) { } // child process if (pid==0) { - execve("nasal", argv, env); + execve(argv[0], argv, environ); _exit(-1); } @@ -140,10 +133,7 @@ var builtin_subprocess_fork(context* ctx, gc* ngc) { delete argv[i]; } delete[] argv; - for(usize i = 0; env[i]; ++i) { - delete env[i]; - } - delete[] env; + return var::num(pid); #endif From 40ca6c19bf5c845f744efcaa5624a8a8f5a8bac9 Mon Sep 17 00:00:00 2001 From: ValKmjolnir Date: Thu, 13 Jun 2024 00:43:17 +0800 Subject: [PATCH 3/4] :sparkles: finish subprocess::create/terminate --- src/nasal_codegen.h | 7 +- src/natives/subprocess.cpp | 200 ++++++++++++++++++++----------------- src/natives/subprocess.h | 25 +++-- std/subprocess.nas | 21 +--- 4 files changed, 133 insertions(+), 120 deletions(-) diff --git a/src/nasal_codegen.h b/src/nasal_codegen.h index 175fb06..8b4d123 100644 --- a/src/nasal_codegen.h +++ b/src/nasal_codegen.h @@ -60,11 +60,8 @@ private: "__pipe", "__fork", "__waitpid", "__chdir", "__environ", "__getcwd", "__getenv", // subprocess - "__subprocess_popen", - "__subprocess_pclose", - "__subprocess_read_stdout", - "__subprocess_fork", - "__subprocess_kill" + "__subprocess_create", + "__subprocess_terminate" }; // file mapper for file -> index diff --git a/src/natives/subprocess.cpp b/src/natives/subprocess.cpp index 6ef5f81..c89275d 100644 --- a/src/natives/subprocess.cpp +++ b/src/natives/subprocess.cpp @@ -17,101 +17,106 @@ #include #include +#include +#include #include #include namespace nasal { -const auto subprocess_object_name = "subprocess"; +const auto subproc_desc_name = "subprocess_descriptor"; -void subprocess_destructor(void* ptr) { - pclose(static_cast(ptr)); +void subprocess_desc_dtor(void* ptr) { +#ifdef WIN32 + auto pi = &static_cast(ptr)->pi; + + WaitForSingleObject(pi->hProcess, 0); + TerminateProcess(pi->hProcess, 0); + CloseHandle(pi->hProcess); + CloseHandle(pi->hThread); +#else + auto pid = static_cast(ptr)->pid; + + int status; + pid_t result = waitpid(pid, &status, WNOHANG); + + // child process running + if (result==0) { + kill(pid, SIGTERM); + } +#endif } -var builtin_subprocess_popen(context* ctx, gc* ngc) { - auto command = ctx->localr[1]; - if (!command.is_str()) { - return nas_err("subprocess::popen", "expect a string as the command"); - } - - auto fd = popen(command.str().c_str(), "r"); - if (fd == nullptr) { - return nas_err("subprocess::popen", "failed to create sub-process"); - } - - var ret = ngc->alloc(vm_type::vm_ghost); - ret.ghost().set(subprocess_object_name, subprocess_destructor, nullptr, fd); - return ret; -} - -var builtin_subprocess_pclose(context* ctx, gc* ngc) { - auto fd = ctx->localr[1]; - if (!fd.object_check(subprocess_object_name)) { - return nas_err("subprocess::pclose", "not a valid subprocess"); - } - auto res = pclose(static_cast(fd.ghost().pointer)); - fd.ghost().pointer = nullptr; - fd.ghost().clear(); - return var::num(res); -} - -var builtin_subprocess_read_stdout(context* ctx, gc* ngc) { - auto fd = ctx->localr[1]; - if (!fd.object_check(subprocess_object_name)) { - return nas_err("subprocess::pclose", "not a valid subprocess"); - } - char buffer[1025]; - auto res = ngc->alloc(vm_type::vm_str); - while(fgets( - buffer, - sizeof(buffer)-1, - static_cast(fd.ghost().pointer))!=nullptr) { - res.str() += buffer; - } - return res; -} - -var builtin_subprocess_fork(context* ctx, gc* ngc) { +var builtin_subprocess_create(context* ctx, gc* ngc) { auto cmd = ctx->localr[1]; if (!cmd.is_vec()) { - return nas_err("subprocess::fork", "expect a string as the command"); + return nas_err("subprocess::create", "expect a string as the command"); } + for(const auto& v : cmd.vec().elems) { + if (!v.is_str()) { + return nas_err("subprocess::create", "non-string arguments"); + } + } + + auto obj = ngc->alloc(vm_type::vm_ghost); + obj.ghost().set( + subproc_desc_name, + subprocess_desc_dtor, + nullptr, + new subprocess_descriptor + ); + #ifdef WIN32 - // STARTUPINFO si; - // PROCESS_INFORMATION pi; + auto si = &static_cast(obj.ghost().pointer)->si; + auto pi = &static_cast(obj.ghost().pointer)->pi; - // // init STARTUPINFO - // ZeroMemory(&si, sizeof(si)); - // si.cb = sizeof(si); - // si.dwFlags = STARTF_USESHOWWINDOW; - // si.wShowWindow = SW_SHOW; + // init STARTUPINFO + ZeroMemory(si, sizeof(STARTUPINFOW)); + si->cb = sizeof(STARTUPINFOW); + si->dwFlags = STARTF_USESHOWWINDOW; + si->wShowWindow = SW_SHOW; - // // init PROCESS_INFORMATION - // ZeroMemory(&pi, sizeof(pi)); + // init PROCESS_INFORMATION + ZeroMemory(pi, sizeof(PROCESS_INFORMATION)); - // auto len = MultiByteToWideChar(CP_UTF8, 0, cmd.str().c_str(), -1, nullptr, 0); + auto command = cmd.vec().elems[0].str(); + for(usize i = 1; i(obj.ghost().pointer)->pid = pid; for(usize i = 0; argv[i]; ++i) { delete argv[i]; } delete[] argv; - return var::num(pid); #endif - return nil; + return obj; } -var builtin_subprocess_kill(context* ctx, gc* ngc) { -#ifdef WIN32 - // TODO -#else - auto pid = ctx->localr[1]; - if (!pid.is_num()) { - return nas_err("subprocess::kill", "need numeral pid"); +var builtin_subprocess_terminate(context* ctx, gc* ngc) { + auto obj = ctx->localr[1]; + if (!obj.object_check(subproc_desc_name)) { + return nas_err("subprocess::terminate", + "need correct subprocess descriptor" + ); } +#ifdef WIN32 + auto pi = &static_cast(obj.ghost().pointer)->pi; + + WaitForSingleObject(pi->hProcess, 0); + TerminateProcess(pi->hProcess, 0); + CloseHandle(pi->hProcess); + CloseHandle(pi->hThread); +#else + auto pid = static_cast(obj.ghost().pointer)->pid; + int status; - pid_t result = waitpid(pid.num(), &status, WNOHANG); + pid_t result = waitpid(pid, &status, WNOHANG); + // child process running if (result==0) { - kill(pid.num(), SIGTERM); + kill(pid, SIGTERM); } #endif + return nil; } nasal_builtin_table subprocess_native[] = { - {"__subprocess_popen", builtin_subprocess_popen}, - {"__subprocess_pclose", builtin_subprocess_pclose}, - {"__subprocess_read_stdout", builtin_subprocess_read_stdout}, - {"__subprocess_fork", builtin_subprocess_fork}, - {"__subprocess_kill", builtin_subprocess_kill}, + {"__subprocess_create", builtin_subprocess_create}, + {"__subprocess_terminate", builtin_subprocess_terminate}, {nullptr, nullptr} }; diff --git a/src/natives/subprocess.h b/src/natives/subprocess.h index 144b63d..4e25f19 100644 --- a/src/natives/subprocess.h +++ b/src/natives/subprocess.h @@ -4,16 +4,29 @@ #include "nasal_gc.h" #include "natives/builtin.h" +#ifndef _MSC_VER +#include +#endif + +#ifndef WIN32 +#include +#endif + namespace nasal { -void subprocess_destructor(void*); +struct subprocess_descriptor { +#ifndef WIN32 + pid_t pid; +#else + STARTUPINFOW si; + PROCESS_INFORMATION pi; +#endif +}; -var builtin_subprocess_popen(context*, gc*); -var builtin_subprocess_pclose(context*, gc*); -var builtin_subprocess_read_stdout(context*, gc*); +void subprocess_desc_dtor(void*); -var builtin_subprocess_fork(context*, gc*); -var builtin_subprocess_kill(context*, gc*); +var builtin_subprocess_create(context*, gc*); +var builtin_subprocess_terminate(context*, gc*); extern nasal_builtin_table subprocess_native[]; } \ No newline at end of file diff --git a/std/subprocess.nas b/std/subprocess.nas index c8b5abf..f256ca8 100644 --- a/std/subprocess.nas +++ b/std/subprocess.nas @@ -1,19 +1,8 @@ -var popen = func(cmd) { - return __subprocess_popen(cmd); + +var create = func(vec) { + return __subprocess_create(vec); } -var pclose = func(subproc) { - return __subprocess_pclose(subproc); -} - -var read = func(subproc) { - return __subprocess_read_stdout(subproc); -} - -var fork = func(vec) { - return __subprocess_fork(vec); -} - -var kill = func(pid) { - return __subprocess_kill(pid); +var terminate = func(pid) { + return __subprocess_terminate(pid); } From 9752b8823e8aa89d0a9e057c0231a4e1ba246b7e Mon Sep 17 00:00:00 2001 From: ValKmjolnir Date: Thu, 13 Jun 2024 01:00:18 +0800 Subject: [PATCH 4/4] :bug: subprocess::terminate return correct value --- src/natives/subprocess.cpp | 10 ++++++++-- test/watchdog.nas | 14 ++++++-------- 2 files changed, 14 insertions(+), 10 deletions(-) diff --git a/src/natives/subprocess.cpp b/src/natives/subprocess.cpp index c89275d..a04038e 100644 --- a/src/natives/subprocess.cpp +++ b/src/natives/subprocess.cpp @@ -158,7 +158,11 @@ var builtin_subprocess_terminate(context* ctx, gc* ngc) { auto pi = &static_cast(obj.ghost().pointer)->pi; WaitForSingleObject(pi->hProcess, 0); - TerminateProcess(pi->hProcess, 0); + TerminateProcess(pi->hProcess, -1); + + DWORD res; + GetExitCodeProcess(pi->hProcess, &res); + CloseHandle(pi->hProcess); CloseHandle(pi->hThread); #else @@ -171,9 +175,11 @@ var builtin_subprocess_terminate(context* ctx, gc* ngc) { if (result==0) { kill(pid, SIGTERM); } + + auto res = WIFEXITED(status)? WEXITSTATUS(status):-1; #endif - return nil; + return var::num(res); } nasal_builtin_table subprocess_native[] = { diff --git a/test/watchdog.nas b/test/watchdog.nas index 5c3c32b..6b0658b 100644 --- a/test/watchdog.nas +++ b/test/watchdog.nas @@ -70,7 +70,7 @@ if (size(argv)==2) { var modified_time = io.fstat(filename).st_mtime; println(os_time(), info_hd(), "\e[1mwatching ", filename, " ..\e[0m"); while(1) { - unix.sleep(5); + unix.sleep(1); if (!io.exists(filename)) { println( os_time(), @@ -91,7 +91,6 @@ while(1) { foreach(var i; args) { cmd ~= " " ~ i; } - cmd ~= " 2>&1"; println( os_time(), info_hd(), @@ -100,12 +99,11 @@ while(1) { "\"\e[0m" ); - var subproc = subprocess.popen(cmd); - var stdout_info = subprocess.read(subproc); - var ret = subprocess.pclose(subproc); - if (size(stdout_info)>0) { - println(stdout_info); - } + var subproc = subprocess.create(["nasal", filename]~args); + + unix.sleep(2); + var ret = subprocess.terminate(subproc); + println( os_time(), ret!=0? err_hd():info_hd(),