From dbad797edd4636f830abd7ade1138a1a27ac30d2 Mon Sep 17 00:00:00 2001 From: "Justin M. Keyes" Date: Sun, 11 Mar 2018 21:47:12 +0100 Subject: API: nvim_get_proc_children() ref https://github.com/libuv/libuv/pull/836 --- src/nvim/api/vim.c | 34 ++++++++++++++++++++++++++++++++++ 1 file changed, 34 insertions(+) (limited to 'src/nvim/api') diff --git a/src/nvim/api/vim.c b/src/nvim/api/vim.c index dad67c5e4b..850f892c18 100644 --- a/src/nvim/api/vim.c +++ b/src/nvim/api/vim.c @@ -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; +} -- cgit From 12af7016e23e7b7f507dc99a1b73e64d0bb5ccf3 Mon Sep 17 00:00:00 2001 From: "Justin M. Keyes" Date: Wed, 14 Mar 2018 23:26:37 +0100 Subject: nvim_get_proc_children: fallback to shell MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit /proc/…/children may be unavailable because of an unset kernel option. Fallback to `pgrep` invoked in a shell. --- src/nvim/api/vim.c | 24 +++++++++++++++++------- 1 file changed, 17 insertions(+), 7 deletions(-) (limited to 'src/nvim/api') diff --git a/src/nvim/api/vim.c b/src/nvim/api/vim.c index 850f892c18..9f1f395c28 100644 --- a/src/nvim/api/vim.c +++ b/src/nvim/api/vim.c @@ -1482,7 +1482,7 @@ Array nvim_list_uis(void) /// Gets the immediate children of process `pid`. /// -/// @return Array of child process ids, or empty array if process not found. +/// @return Array of child process ids. Empty array if process not found. Array nvim_get_proc_children(Integer pid, Error *err) FUNC_API_SINCE(4) { @@ -1490,17 +1490,27 @@ Array nvim_get_proc_children(Integer pid, Error *err) int *proc_list = NULL; if (pid <= 0 || pid > INT_MAX) { - api_set_error(err, kErrorTypeException, "Invalid pid: %d", pid); + api_set_error(err, kErrorTypeException, "Invalid pid: %" PRId64, 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); + if (rv != 0) { + // syscall failed (possibly because of kernel options), try shelling out. + Array a = ARRAY_DICT_INIT; + ADD(a, INTEGER_OBJ(pid)); + String s = cstr_to_string("return vim._os_proc_children(select(1, ...))"); + Object o = nvim_execute_lua(s, a, err); + api_free_string(s); + api_free_array(a); + if (o.type == kObjectTypeArray) { + proc_array = o.data.array; + } else if (!ERROR_SET(err)) { + api_set_error(err, kErrorTypeException, + "Failed to get process children. pid=%" PRId64 " error=%d", + pid, rv); + } goto end; } -- cgit From a034d4b69d6032b3431c10b8a11c998551700fc2 Mon Sep 17 00:00:00 2001 From: "Justin M. Keyes" Date: Fri, 16 Mar 2018 05:13:38 +0100 Subject: API: nvim_get_proc() TODO: "exepath" field (win32: QueryFullProcessImageName()) On unix-likes `ps` is used because the platform-specific APIs are a nightmare. For reference, below is a (incomplete) attempt: diff --git a/src/nvim/os/process.c b/src/nvim/os/process.c index 09769925aca5..99afbbf290c1 100644 --- a/src/nvim/os/process.c +++ b/src/nvim/os/process.c @@ -208,3 +210,60 @@ int os_proc_children(int ppid, int **proc_list, size_t *proc_count) return 0; } +/// Gets various properties of the process identified by `pid`. +/// +/// @param pid Process to inspect. +/// @return Map of process properties, empty on error. +Dictionary os_proc_info(int pid) +{ + Dictionary pinfo = ARRAY_DICT_INIT; +#ifdef WIN32 + +#elif defined(__APPLE__) + char buf[PROC_PIDPATHINFO_MAXSIZE]; + if (proc_pidpath(pid, buf, sizeof(buf))) { + name = getName(buf); + PUT(pinfo, "exepath", STRING_OBJ(cstr_to_string(buf))); + return name; + } else { + ILOG("proc_pidpath() failed for pid: %d", pid); + } +#elif defined(BSD) +# if defined(__FreeBSD__) +# define KP_COMM(o) o.ki_comm +# else +# define KP_COMM(o) o.p_comm +# endif + struct kinfo_proc *proc = kinfo_getproc(pid); + if (proc) { + PUT(pinfo, "name", cstr_to_string(KP_COMM(proc))); + xfree(proc); + } else { + ILOG("kinfo_getproc() failed for pid: %d", pid); + } + +#elif defined(__linux__) + char fname[256] = { 0 }; + char buf[MAXPATHL]; + snprintf(fname, sizeof(fname), "/proc/%d/comm", pid); + FILE *fp = fopen(fname, "r"); + // FileDescriptor *f = file_open_new(&error, fname, kFileReadOnly, 0); + // ptrdiff_t file_read(FileDescriptor *const fp, char *const ret_buf, + // const size_t size) + if (fp == NULL) { + ILOG("fopen() of /proc/%d/comm failed", pid); + } else { + size_t n = fread(buf, sizeof(char), sizeof(buf) - 1, fp); + if (n == 0) { + WLOG("fread() of /proc/%d/comm failed", pid); + } else { + size_t end = MIN(sizeof(buf) - 1, n); + end = (end > 0 && buf[end - 1] == '\n') ? end - 1 : end; + buf[end] = '\0'; + PUT(pinfo, "name", STRING_OBJ(cstr_to_string(buf))); + } + } + fclose(fp); +#endif + return pinfo; +} --- src/nvim/api/vim.c | 50 +++++++++++++++++++++++++++++++++++++++++++++----- 1 file changed, 45 insertions(+), 5 deletions(-) (limited to 'src/nvim/api') diff --git a/src/nvim/api/vim.c b/src/nvim/api/vim.c index 9f1f395c28..962081cc23 100644 --- a/src/nvim/api/vim.c +++ b/src/nvim/api/vim.c @@ -1482,11 +1482,11 @@ Array nvim_list_uis(void) /// Gets the immediate children of process `pid`. /// -/// @return Array of child process ids. Empty array if process not found. +/// @return Array of child process ids, empty if process not found. Array nvim_get_proc_children(Integer pid, Error *err) FUNC_API_SINCE(4) { - Array proc_array = ARRAY_DICT_INIT; + Array rvobj = ARRAY_DICT_INIT; int *proc_list = NULL; if (pid <= 0 || pid > INT_MAX) { @@ -1498,6 +1498,7 @@ Array nvim_get_proc_children(Integer pid, Error *err) int rv = os_proc_children((int)pid, &proc_list, &proc_count); if (rv != 0) { // syscall failed (possibly because of kernel options), try shelling out. + DLOG("fallback to vim._os_proc_children()"); Array a = ARRAY_DICT_INIT; ADD(a, INTEGER_OBJ(pid)); String s = cstr_to_string("return vim._os_proc_children(select(1, ...))"); @@ -1505,7 +1506,7 @@ Array nvim_get_proc_children(Integer pid, Error *err) api_free_string(s); api_free_array(a); if (o.type == kObjectTypeArray) { - proc_array = o.data.array; + rvobj = o.data.array; } else if (!ERROR_SET(err)) { api_set_error(err, kErrorTypeException, "Failed to get process children. pid=%" PRId64 " error=%d", @@ -1515,10 +1516,49 @@ Array nvim_get_proc_children(Integer pid, Error *err) } for (size_t i = 0; i < proc_count; i++) { - ADD(proc_array, INTEGER_OBJ(proc_list[i])); + ADD(rvobj, INTEGER_OBJ(proc_list[i])); } end: xfree(proc_list); - return proc_array; + return rvobj; +} + +/// Gets info describing process `pid`. +/// +/// @return Map of process properties, or NIL if process not found. +Object nvim_get_proc(Integer pid, Error *err) + FUNC_API_SINCE(4) +{ + Object rvobj = OBJECT_INIT; + rvobj.data.dictionary = (Dictionary)ARRAY_DICT_INIT; + rvobj.type = kObjectTypeDictionary; + + if (pid <= 0 || pid > INT_MAX) { + api_set_error(err, kErrorTypeException, "Invalid pid: %" PRId64, pid); + return NIL; + } +#ifdef WIN32 + rvobj.data.dictionary = os_proc_info((int)pid); + if (rvobj.data.dictionary.size == 0) { // Process not found. + return NIL; + } +#else + // Cross-platform process info APIs are miserable, so use `ps` instead. + Array a = ARRAY_DICT_INIT; + ADD(a, INTEGER_OBJ(pid)); + String s = cstr_to_string("return vim._os_proc_info(select(1, ...))"); + Object o = nvim_execute_lua(s, a, err); + api_free_string(s); + api_free_array(a); + if (o.type == kObjectTypeArray && o.data.array.size == 0) { + return NIL; // Process not found. + } else if (o.type == kObjectTypeDictionary) { + rvobj.data.dictionary = o.data.dictionary; + } else if (!ERROR_SET(err)) { + api_set_error(err, kErrorTypeException, + "Failed to get process info. pid=%" PRId64, pid); + } +#endif + return rvobj; } -- cgit