This is an automated email from the ASF dual-hosted git repository. vatamane pushed a commit to branch remove-spawnkiller-crutch in repository https://gitbox.apache.org/repos/asf/couchdb.git
commit bf27b2c1e9269c3a5248a0ed1ee30a1cc1ad44c7 Author: Nick Vatamaniuc <vatam...@apache.org> AuthorDate: Fri Sep 13 16:02:10 2024 -0400 Remove spawnkillable os_pid is available since at least 19.0 and we never noticed [1] This should make our releses easily relocatable to paths which have spaces. [1] https://github.com/erlang/otp/blob/3ff36ef6f6f20749b5a03b3667206ede0fdb5791/erts/doc/src/erlang_port_info.md?plain=1#L27 --- src/couch/priv/spawnkillable/couchspawnkillable.sh | 20 --- .../priv/spawnkillable/couchspawnkillable_win.c | 145 --------------------- src/couch/rebar.config.script | 15 +-- src/couch/src/couch_os_process.erl | 24 ++-- 4 files changed, 16 insertions(+), 188 deletions(-) diff --git a/src/couch/priv/spawnkillable/couchspawnkillable.sh b/src/couch/priv/spawnkillable/couchspawnkillable.sh deleted file mode 100755 index f8d042e36..000000000 --- a/src/couch/priv/spawnkillable/couchspawnkillable.sh +++ /dev/null @@ -1,20 +0,0 @@ -#! /bin/sh -e - -# Licensed under the Apache License, Version 2.0 (the "License"); you may not -# use this file except in compliance with the License. You may obtain a copy of -# the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT -# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the -# License for the specific language governing permissions and limitations under -# the License. - -# The purpose of this script is to echo an OS specific command before launching -# the actual process. This provides a way for Erlang to hard-kill its external -# processes. - -echo "kill -9 $$" -exec $* diff --git a/src/couch/priv/spawnkillable/couchspawnkillable_win.c b/src/couch/priv/spawnkillable/couchspawnkillable_win.c deleted file mode 100644 index 067823159..000000000 --- a/src/couch/priv/spawnkillable/couchspawnkillable_win.c +++ /dev/null @@ -1,145 +0,0 @@ -// Licensed under the Apache License, Version 2.0 (the "License"); you may not -// use this file except in compliance with the License. You may obtain a copy of -// the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT -// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the -// License for the specific language governing permissions and limitations under -// the License. - -// Do what 2 lines of shell script in couchspawnkillable does... -// * Create a new suspended process with the same (duplicated) standard -// handles as us. -// * Write a line to stdout, consisting of the path to ourselves, plus -// '--kill {pid}' where {pid} is the PID of the newly created process. -// * Un-suspend the new process. -// * Wait for the process to terminate. -// * Terminate with the child's exit-code. - -// Later, couch will call us with --kill and the PID, so we dutifully -// terminate the specified PID. - -#include <stdlib.h> -#include "windows.h" - -char *get_child_cmdline(int argc, char **argv) -{ - // make a new command-line, but skipping me. - // XXX - todo - spaces etc in args??? - int i; - char *p, *cmdline; - int nchars = 0; - int nthis = 1; - for (i=1;i<argc;i++) - nchars += strlen(argv[i])+1; - cmdline = p = malloc(nchars+1); - if (!cmdline) - return NULL; - for (i=1;i<argc;i++) { - nthis = strlen(argv[i]); - strncpy(p, argv[i], nthis); - p[nthis] = ' '; - p += nthis+1; - } - // Replace the last space we added above with a '\0' - cmdline[nchars-1] = '\0'; - return cmdline; -} - -// create the child process, returning 0, or the exit-code we will -// terminate with. -int create_child(int argc, char **argv, PROCESS_INFORMATION *pi) -{ - char buf[1024]; - DWORD dwcreate; - STARTUPINFO si; - char *cmdline; - if (argc < 2) - return 1; - cmdline = get_child_cmdline(argc, argv); - if (!cmdline) - return 2; - - memset(&si, 0, sizeof(si)); - si.cb = sizeof(si); - // depending on how *our* parent is started, we may or may not have - // a valid stderr stream - so although we try and duplicate it, only - // failing to duplicate stdin and stdout are considered fatal. - if (!DuplicateHandle(GetCurrentProcess(), - GetStdHandle(STD_INPUT_HANDLE), - GetCurrentProcess(), - &si.hStdInput, - 0, - TRUE, // inheritable - DUPLICATE_SAME_ACCESS) || - !DuplicateHandle(GetCurrentProcess(), - GetStdHandle(STD_OUTPUT_HANDLE), - GetCurrentProcess(), - &si.hStdOutput, - 0, - TRUE, // inheritable - DUPLICATE_SAME_ACCESS)) { - return 3; - } - DuplicateHandle(GetCurrentProcess(), - GetStdHandle(STD_ERROR_HANDLE), - GetCurrentProcess(), - &si.hStdError, - 0, - TRUE, // inheritable - DUPLICATE_SAME_ACCESS); - - si.dwFlags = STARTF_USESTDHANDLES; - dwcreate = CREATE_SUSPENDED; - if (!CreateProcess( NULL, cmdline, - NULL, - NULL, - TRUE, // inherit handles - dwcreate, - NULL, // environ - NULL, // cwd - &si, - pi)) - return 4; - return 0; -} - -// and here we go... -int main(int argc, char **argv) -{ - char out_buf[1024]; - int rc; - DWORD cbwritten; - DWORD exitcode; - PROCESS_INFORMATION pi; - if (argc==3 && strcmp(argv[1], "--kill")==0) { - HANDLE h = OpenProcess(PROCESS_TERMINATE, 0, atoi(argv[2])); - if (!h) - return 1; - if (!TerminateProcess(h, 0)) - return 2; - CloseHandle(h); - return 0; - } - // spawn the new suspended process - rc = create_child(argc, argv, &pi); - if (rc) - return rc; - // Write the 'terminate' command, which includes this PID, back to couch. - // *sob* - what about spaces etc? - sprintf_s(out_buf, sizeof(out_buf), "%s --kill %d\n", - argv[0], pi.dwProcessId); - WriteFile(GetStdHandle(STD_OUTPUT_HANDLE), out_buf, strlen(out_buf), - &cbwritten, NULL); - // Let the child process go... - ResumeThread(pi.hThread); - // Wait for the process to terminate so we can reflect the exit code - // back to couch. - WaitForSingleObject(pi.hProcess, INFINITE); - if (!GetExitCodeProcess(pi.hProcess, &exitcode)) - return 6; - return exitcode; -} diff --git a/src/couch/rebar.config.script b/src/couch/rebar.config.script index 5efe238c2..7034f29ce 100644 --- a/src/couch/rebar.config.script +++ b/src/couch/rebar.config.script @@ -236,7 +236,7 @@ SpidermonkeySpecs = case WithSpidermonkey of false -> [] end. -BaseSpecs = SpidermonkeySpecs ++ [ +PortSpecs = SpidermonkeySpecs ++ [ % ejson_compare {"darwin", ComparePath, CompareSrc, [{env, IcuEnv ++ IcuDarwinEnv}]}, {"linux", ComparePath, CompareSrc, [{env, IcuEnv}]}, @@ -244,25 +244,12 @@ BaseSpecs = SpidermonkeySpecs ++ [ {"win32", ComparePath, CompareSrc, [{env, IcuWinEnv}]} ]. -SpawnSpec = [ - {"priv/couchspawnkillable", ["priv/spawnkillable/*.c"]} -]. - %% hack required until switch to enc/rebar3 PortEnvOverrides = [ {"win32", "EXE_LINK_CXX_TEMPLATE", "$LINKER $PORT_IN_FILES $LDFLAGS $EXE_LDFLAGS /OUT:$PORT_OUT_FILE"} ]. -PortSpecs = case os:type() of - {win32, _} -> - BaseSpecs ++ SpawnSpec; - _ -> - {ok, CSK} = file:read_file("priv/spawnkillable/couchspawnkillable.sh"), - ok = CopyIfDifferent("priv/couchspawnkillable", CSK), - os:cmd("chmod +x priv/couchspawnkillable"), - BaseSpecs -end. PlatformDefines = [ {platform_define, "win32", 'WINDOWS'} ]. diff --git a/src/couch/src/couch_os_process.erl b/src/couch/src/couch_os_process.erl index 225ce3d53..59ceeca13 100644 --- a/src/couch/src/couch_os_process.erl +++ b/src/couch/src/couch_os_process.erl @@ -130,31 +130,30 @@ pick_command1(_) -> % gen_server API init([Command]) -> - PrivDir = couch_util:priv_dir(), - Spawnkiller = "\"" ++ filename:join(PrivDir, "couchspawnkillable") ++ "\"", V = config:get("query_server_config", "os_process_idle_limit", "300"), IdleLimit = list_to_integer(V) * 1000, LogLevel = log_level(os:getenv("COUCHDB_IO_LOG_LEVEL")), T0 = erlang:monotonic_time(), + Port = open_port({spawn, Command}, ?PORT_OPTIONS), + {os_pid, OsPid} = erlang:port_info(Port, os_pid), OsProc = #os_proc{ command = Command, - port = open_port({spawn, Spawnkiller ++ " " ++ Command}, ?PORT_OPTIONS), + port = Port, idle = IdleLimit, log_level = LogLevel }, - KillCmd = iolist_to_binary(readline(OsProc)), T1 = erlang:monotonic_time(), DtUSec = erlang:convert_time_unit(T1 - T0, native, microsecond), bump_time_stat(spawn_proc, DtUSec), Pid = self(), [CmdLog | _] = string:split(Command, " "), CmdLog1 = filename:basename(CmdLog), - log(OsProc, "OS Process ~p Started :: ~p", [OsProc#os_proc.port, CmdLog1]), + log(OsProc, "OS Process pid:~p port:~p Started :: ~p", [OsPid, Port, CmdLog1]), couch_stats:increment_counter([couchdb, query_server, process_starts]), spawn(fun() -> % this ensure the real os process is killed when this process dies. erlang:monitor(process, Pid), - killer(?b2l(KillCmd)) + killer(OsPid) end), {ok, OsProc, IdleLimit}. @@ -216,12 +215,19 @@ handle_info(Msg, #os_proc{idle = Idle} = OsProc) -> log(OsProc, "OS Process ~p Unknown info :: ~p", [OsProc#os_proc.port, Msg]), {noreply, OsProc, Idle}. -killer(KillCmd) -> +kill_command(OsPid) -> + OsPid1 = integer_to_list(OsPid), + case os:type() of + {win32, _} -> "taskkill /f /pid " ++ OsPid1; + {_, _} -> "kill -9 " ++ OsPid1 + end. + +killer(OsPid) when is_integer(OsPid), OsPid > 0 -> receive _ -> - os:cmd(KillCmd) + os:cmd(kill_command(OsPid)) after 1000 -> - killer(KillCmd) + killer(OsPid) end. bump_cmd_time_stat(Cmd, USec) when is_list(Cmd), is_integer(USec) ->