mirror of
https://github.com/neovim/neovim.git
synced 2024-12-29 14:41:06 -07:00
API: nvim_get_proc_children()
ref https://github.com/libuv/libuv/pull/836
This commit is contained in:
parent
de86f82483
commit
dbad797edd
@ -33,6 +33,7 @@
|
||||
#include "nvim/syntax.h"
|
||||
#include "nvim/getchar.h"
|
||||
#include "nvim/os/input.h"
|
||||
#include "nvim/os/process.h"
|
||||
#include "nvim/viml/parser/expressions.h"
|
||||
#include "nvim/viml/parser/parser.h"
|
||||
#include "nvim/ui.h"
|
||||
@ -1478,3 +1479,36 @@ Array nvim_list_uis(void)
|
||||
{
|
||||
return ui_array();
|
||||
}
|
||||
|
||||
/// Gets the immediate children of process `pid`.
|
||||
///
|
||||
/// @return Array of child process ids, or empty array if process not found.
|
||||
Array nvim_get_proc_children(Integer pid, Error *err)
|
||||
FUNC_API_SINCE(4)
|
||||
{
|
||||
Array proc_array = ARRAY_DICT_INIT;
|
||||
int *proc_list = NULL;
|
||||
|
||||
if (pid <= 0 || pid > INT_MAX) {
|
||||
api_set_error(err, kErrorTypeException, "Invalid pid: %d", pid);
|
||||
goto end;
|
||||
}
|
||||
|
||||
size_t proc_count;
|
||||
int rv = os_proc_children((int)pid, &proc_list, &proc_count);
|
||||
if (rv == 1) {
|
||||
goto end; // Process not found; return empty list.
|
||||
} else if (rv != 0) {
|
||||
api_set_error(err, kErrorTypeException,
|
||||
"Failed to get process children. pid=%d error=%d", pid, rv);
|
||||
goto end;
|
||||
}
|
||||
|
||||
for (size_t i = 0; i < proc_count; i++) {
|
||||
ADD(proc_array, INTEGER_OBJ(proc_list[i]));
|
||||
}
|
||||
|
||||
end:
|
||||
xfree(proc_list);
|
||||
return proc_array;
|
||||
}
|
||||
|
@ -6,6 +6,17 @@
|
||||
# include <tlhelp32.h> // for CreateToolhelp32Snapshot
|
||||
#endif
|
||||
|
||||
#if defined(__FreeBSD__) // XXX: OpenBSD, NetBSD ?
|
||||
# include <string.h>
|
||||
# include <sys/types.h>
|
||||
# include <sys/user.h>
|
||||
#endif
|
||||
|
||||
#if defined(__APPLE__) || defined(BSD)
|
||||
# include <sys/sysctl.h>
|
||||
# include <pwd.h>
|
||||
#endif
|
||||
|
||||
#include "nvim/log.h"
|
||||
#include "nvim/os/process.h"
|
||||
#include "nvim/os/os.h"
|
||||
@ -16,8 +27,7 @@
|
||||
#endif
|
||||
|
||||
#ifdef WIN32
|
||||
/// Kills process `pid` and its descendants recursively.
|
||||
bool os_proc_tree_kill_rec(HANDLE process, int sig)
|
||||
static bool os_proc_tree_kill_rec(HANDLE process, int sig)
|
||||
{
|
||||
if (process == NULL) {
|
||||
return false;
|
||||
@ -35,7 +45,7 @@ bool os_proc_tree_kill_rec(HANDLE process, int sig)
|
||||
|
||||
do {
|
||||
if (pe.th32ParentProcessID == pid) {
|
||||
HANDLE ph = OpenProcess(PROCESS_ALL_ACCESS, FALSE, pe.th32ProcessID);
|
||||
HANDLE ph = OpenProcess(PROCESS_ALL_ACCESS, false, pe.th32ProcessID);
|
||||
if (ph != NULL) {
|
||||
os_proc_tree_kill_rec(ph, sig);
|
||||
CloseHandle(ph);
|
||||
@ -50,13 +60,14 @@ bool os_proc_tree_kill_rec(HANDLE process, int sig)
|
||||
theend:
|
||||
return (bool)TerminateProcess(process, (unsigned int)sig);
|
||||
}
|
||||
/// Kills process `pid` and its descendants recursively.
|
||||
bool os_proc_tree_kill(int pid, int sig)
|
||||
{
|
||||
assert(sig >= 0);
|
||||
assert(sig == SIGTERM || sig == SIGKILL);
|
||||
if (pid > 0) {
|
||||
ILOG("terminating process tree: %d", pid);
|
||||
HANDLE h = OpenProcess(PROCESS_ALL_ACCESS, FALSE, (DWORD)pid);
|
||||
HANDLE h = OpenProcess(PROCESS_ALL_ACCESS, false, (DWORD)pid);
|
||||
return os_proc_tree_kill_rec(h, sig);
|
||||
} else {
|
||||
ELOG("invalid pid: %d", pid);
|
||||
@ -85,3 +96,87 @@ bool os_proc_tree_kill(int pid, int sig)
|
||||
return false;
|
||||
}
|
||||
#endif
|
||||
|
||||
/// Gets the process ids of the immediate children of process `ppid`.
|
||||
///
|
||||
/// @param ppid Process to inspect.
|
||||
/// @param[out,allocated] proc_list Child process ids.
|
||||
/// @param[out] proc_count Number of child processes.
|
||||
/// @return 0 on success, 1 if process not found, 2 on other error.
|
||||
int os_proc_children(int ppid, int **proc_list, size_t *proc_count)
|
||||
{
|
||||
//
|
||||
// psutil is a good reference for cross-platform syscall voodoo:
|
||||
// https://github.com/giampaolo/psutil/tree/master/psutil/arch
|
||||
//
|
||||
|
||||
int *temp = NULL;
|
||||
*proc_list = NULL;
|
||||
*proc_count = 0;
|
||||
|
||||
#if defined(__APPLE__) || defined(BSD)
|
||||
# if defined(__APPLE__)
|
||||
# define KP_PID(o) o.kp_proc.p_pid
|
||||
# define KP_PPID(o) o.kp_eproc.e_ppid
|
||||
# elif defined(__FreeBSD__)
|
||||
# define KP_PID(o) o.ki_pid
|
||||
# define KP_PPID(o) o.ki_ppid
|
||||
# else
|
||||
# define KP_PID(o) o.p_pid
|
||||
# define KP_PPID(o) o.p_ppid
|
||||
# endif
|
||||
static int name[] = { CTL_KERN, KERN_PROC, KERN_PROC_ALL, 0 };
|
||||
|
||||
// Get total process count.
|
||||
size_t len = 0;
|
||||
int rv = sysctl(name, ARRAY_SIZE(name) - 1, NULL, &len, NULL, 0);
|
||||
if (rv) {
|
||||
return 2;
|
||||
}
|
||||
|
||||
// Get ALL processes.
|
||||
struct kinfo_proc *p_list = xmalloc(len);
|
||||
rv = sysctl(name, ARRAY_SIZE(name) - 1, p_list, &len, NULL, 0);
|
||||
if (rv) {
|
||||
xfree(p_list);
|
||||
return 2;
|
||||
}
|
||||
|
||||
// Collect processes whose parent matches `ppid`.
|
||||
bool exists = false;
|
||||
size_t p_count = len / sizeof(*p_list);
|
||||
for (size_t i = 0; i < p_count; i++) {
|
||||
exists = exists || KP_PID(p_list[i]) == ppid;
|
||||
if (KP_PPID(p_list[i]) == ppid) {
|
||||
temp = xrealloc(temp, (*proc_count + 1) * sizeof(*temp));
|
||||
temp[*proc_count] = KP_PID(p_list[i]);
|
||||
(*proc_count)++;
|
||||
}
|
||||
}
|
||||
xfree(p_list);
|
||||
if (!exists) {
|
||||
return 1; // Process not found.
|
||||
}
|
||||
|
||||
#elif defined(__linux__)
|
||||
char proc_p[256] = { 0 };
|
||||
// Collect processes whose parent matches `ppid`.
|
||||
// Rationale: children are defined in thread with same ID of process.
|
||||
snprintf(proc_p, sizeof(proc_p), "/proc/%d/task/%d/children", ppid, ppid);
|
||||
FILE *fp = fopen(proc_p, "r");
|
||||
if (fp == NULL) {
|
||||
return 1; // Process not found.
|
||||
}
|
||||
int match_pid;
|
||||
while (fscanf(fp, "%d", &match_pid) > 0) {
|
||||
temp = xrealloc(temp, (*proc_count + 1) * sizeof(*temp));
|
||||
temp[*proc_count] = match_pid;
|
||||
(*proc_count)++;
|
||||
}
|
||||
fclose(fp);
|
||||
#endif
|
||||
|
||||
*proc_list = temp;
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -1,6 +1,8 @@
|
||||
#ifndef NVIM_OS_PROCESS_H
|
||||
#define NVIM_OS_PROCESS_H
|
||||
|
||||
#include <stddef.h>
|
||||
|
||||
#ifdef INCLUDE_GENERATED_DECLARATIONS
|
||||
# include "os/process.h.generated.h"
|
||||
#endif
|
||||
|
53
test/functional/api/proc_spec.lua
Normal file
53
test/functional/api/proc_spec.lua
Normal file
@ -0,0 +1,53 @@
|
||||
local helpers = require('test.functional.helpers')(after_each)
|
||||
|
||||
local clear, eq = helpers.clear, helpers.eq
|
||||
local funcs = helpers.funcs
|
||||
local nvim_argv = helpers.nvim_argv
|
||||
local request = helpers.request
|
||||
local retry = helpers.retry
|
||||
|
||||
describe('api', function()
|
||||
before_each(clear)
|
||||
|
||||
describe('nvim_get_proc_children', function()
|
||||
it('returns child process ids', function()
|
||||
local this_pid = funcs.getpid()
|
||||
|
||||
local job1 = funcs.jobstart(nvim_argv)
|
||||
retry(nil, nil, function()
|
||||
eq(1, #request('nvim_get_proc_children', this_pid))
|
||||
end)
|
||||
|
||||
local job2 = funcs.jobstart(nvim_argv)
|
||||
retry(nil, nil, function()
|
||||
eq(2, #request('nvim_get_proc_children', this_pid))
|
||||
end)
|
||||
|
||||
funcs.jobstop(job1)
|
||||
retry(nil, nil, function()
|
||||
eq(1, #request('nvim_get_proc_children', this_pid))
|
||||
end)
|
||||
|
||||
funcs.jobstop(job2)
|
||||
retry(nil, nil, function()
|
||||
eq(0, #request('nvim_get_proc_children', this_pid))
|
||||
end)
|
||||
end)
|
||||
|
||||
it('validates input', function()
|
||||
local status, rv = pcall(request, "nvim_get_proc_children", -1)
|
||||
eq(false, status)
|
||||
eq("Invalid pid: -1", string.match(rv, "Invalid.*"))
|
||||
|
||||
status, rv = pcall(request, "nvim_get_proc_children", 0)
|
||||
eq(false, status)
|
||||
eq("Invalid pid: 0", string.match(rv, "Invalid.*"))
|
||||
|
||||
-- Assume PID 99999999 does not exist.
|
||||
status, rv = pcall(request, "nvim_get_proc_children", 99999999)
|
||||
eq(true, status)
|
||||
eq({}, rv)
|
||||
end)
|
||||
end)
|
||||
|
||||
end)
|
Loading…
Reference in New Issue
Block a user