@cryptotaxi247 / netdata-1 / commits / 4f34cf80e

Add helper to run external commands without additional privileges. (#20990)

Co-authored-by: Costa Tsaousis <costa@netdata.cloud>

Austin S. Hemmelgarn committed Sep 29, 2025 at 08:19 UTC 4f34cf80eef21419387030e2d1cd3ff5c3ab1d4a
11 files changed +404 -48
CMakeLists.txt
+55 -37
@@ -148,6 +148,27 @@ else()
148 message(FATAL_ERROR "Unknown/unsupported platform: ${CMAKE_SYSTEM_NAME} (Supported platforms: FreeBSD, Linux, macOS, Windows)")
149 endif()
150
151 +if(NOT NETDATA_RUNTIME_PREFIX STREQUAL "")
152 + string(REGEX REPLACE "/$" "" NETDATA_RUNTIME_PREFIX "${NETDATA_RUNTIME_PREFIX}")
153 +endif()
154 +
155 +set(CACHE_DIR "${NETDATA_RUNTIME_PREFIX}/var/cache/netdata")
156 +set(CONFIG_DIR "${NETDATA_RUNTIME_PREFIX}/etc/netdata")
157 +set(LIBCONFIG_DIR "${NETDATA_RUNTIME_PREFIX}/usr/lib/netdata/conf.d")
158 +set(LOG_DIR "${NETDATA_RUNTIME_PREFIX}/var/log/netdata")
159 +set(PLUGINS_DIR "${NETDATA_RUNTIME_PREFIX}/usr/libexec/netdata/plugins.d")
160 +set(VARLIB_DIR "${NETDATA_RUNTIME_PREFIX}/var/lib/netdata")
161 +set(NETDATA_BIN_DIR "${NETDATA_RUNTIME_PREFIX}/${BINDIR}")
162 +
163 +# A non-default value is only used when building Debian packages (/var/lib/netdata/www)
164 +if(NOT DEFINED WEB_DIR)
165 + set(WEB_DIR "usr/share/netdata/web")
166 +else()
167 + string(REGEX REPLACE "^/" "" WEB_DIR "${WEB_DIR}")
168 +endif()
169 +set(WEB_DEST "${WEB_DIR}")
170 +set(WEB_DIR "${NETDATA_RUNTIME_PREFIX}/${WEB_DEST}")
171 +
172 include(NetdataCompilerFlags)
173
174 check_c_compiler_flag("-fexceptions" HAVE_FEXCEPTIONS)
@@ -474,9 +495,6 @@ check_include_file("sys/socket.h" HAVE_SYS_SOCKET_H)
495 check_include_file("sys/wait.h" HAVE_SYS_WAIT_H)
496 check_include_file("sys/un.h" HAVE_SYS_UN_H)
497 check_include_file("spawn.h" HAVE_SPAWN_H)
477 -if(OS_LINUX)
478 - check_include_file("sys/capability.h" HAVE_SYS_CAPABILITY_H)
479 -endif()
498
499 #
500 # check symbols
@@ -490,6 +508,10 @@ check_symbol_exists(strerror_r "string.h" HAVE_STRERROR_R)
508 check_symbol_exists(finite "math.h" HAVE_FINITE)
509 check_symbol_exists(isfinite "math.h" HAVE_ISFINITE)
510 check_symbol_exists(dlsym "dlfcn.h" HAVE_DLSYM)
511 +check_symbol_exists(clearenv "stdlib.h" HAVE_CLEARENV)
512 +
513 +check_function_exists(setresuid HAVE_SETRESUID)
514 +check_function_exists(setresgid HAVE_SETRESGID)
515
516 check_function_exists(pthread_getthreadid_np HAVE_PTHREAD_GETTHREADID_NP)
517 check_function_exists(pthread_threadid_np HAVE_PTHREAD_THREADID_NP)
@@ -2471,14 +2493,35 @@ netdata_protoc_generate_cpp("${CMAKE_SOURCE_DIR}/src/aclk/aclk-schemas"
2493 list(APPEND ACLK_FILES ${ACLK_PROTO_BUILT_SRCS}
2494 ${ACLK_PROTO_BUILT_HDRS})
2495
2496 +#
2497 +# nd-run helper program
2498 +#
2499 +
2500 +if(OS_LINUX)
2501 + pkg_check_modules(CAP IMPORTED_TARGET libcap)
2502 +else()
2503 + set(CAP_FOUND False)
2504 +endif()
2505 +
2506 +if(CAP_FOUND)
2507 + set(HAVE_CAPABILITY True)
2508 +endif()
2509 +
2510 +set(NDRUN_FILES src/collectors/utils/nd-run.c)
2511 +add_executable(nd-run ${NDRUN_FILES})
2512 +if(CAP_FOUND)
2513 + target_link_libraries(nd-run PRIVATE PkgConfig::CAP)
2514 +endif()
2515 +target_include_directories(nd-run PRIVATE ${CONFIG_H_DIR})
2516 +install(TARGETS nd-run
2517 + COMPONENT netdata
2518 + DESTINATION "${BINDIR}")
2519 +
2520 #
2521 # build plugins
2522 #
2523
2524 if(ENABLE_PLUGIN_DEBUGFS)
2479 - # Check for libcap (optional)
2480 - pkg_check_modules(CAP QUIET libcap)
2481 -
2525 # Define debugfs.plugin source files
2526 set(DEBUGFS_PLUGIN_FILES
2527 src/collectors/debugfs.plugin/debugfs_plugin.c
@@ -2533,8 +2576,6 @@ add_executable(spawn-tester src/libnetdata/spawn_server/spawn-tester.c)
2576 target_link_libraries(spawn-tester libnetdata)
2577
2578 if(ENABLE_PLUGIN_APPS)
2536 - pkg_check_modules(CAP QUIET libcap)
2537 -
2579 set(APPS_PLUGIN_FILES
2580 src/collectors/apps.plugin/apps_plugin.c
2581 src/collectors/apps.plugin/apps_plugin.h
@@ -2577,10 +2618,6 @@ if(ENABLE_PLUGIN_APPS)
2618 endif()
2619 endif()
2620
2580 -if(CAP_FOUND)
2581 - set(HAVE_CAPABILITY True)
2582 -endif()
2583 -
2621 if(ENABLE_PLUGIN_FREEIPMI)
2622 pkg_check_modules(IPMI REQUIRED libipmimonitoring)
2623
@@ -3193,25 +3230,6 @@ endif()
3230 #
3231 # Generate config file
3232 #
3196 -if(NOT NETDATA_RUNTIME_PREFIX STREQUAL "")
3197 - string(REGEX REPLACE "/$" "" NETDATA_RUNTIME_PREFIX "${NETDATA_RUNTIME_PREFIX}")
3198 -endif()
3199 -
3200 -set(CACHE_DIR "${NETDATA_RUNTIME_PREFIX}/var/cache/netdata")
3201 -set(CONFIG_DIR "${NETDATA_RUNTIME_PREFIX}/etc/netdata")
3202 -set(LIBCONFIG_DIR "${NETDATA_RUNTIME_PREFIX}/usr/lib/netdata/conf.d")
3203 -set(LOG_DIR "${NETDATA_RUNTIME_PREFIX}/var/log/netdata")
3204 -set(PLUGINS_DIR "${NETDATA_RUNTIME_PREFIX}/usr/libexec/netdata/plugins.d")
3205 -set(VARLIB_DIR "${NETDATA_RUNTIME_PREFIX}/var/lib/netdata")
3206 -
3207 -# A non-default value is only used when building Debian packages (/var/lib/netdata/www)
3208 -if(NOT DEFINED WEB_DIR)
3209 - set(WEB_DIR "usr/share/netdata/web")
3210 -else()
3211 - string(REGEX REPLACE "^/" "" WEB_DIR "${WEB_DIR}")
3212 -endif()
3213 -set(WEB_DEST "${WEB_DIR}")
3214 -set(WEB_DIR "${NETDATA_RUNTIME_PREFIX}/${WEB_DEST}")
3233
3234 # Collect all compiler flags
3235 get_directory_property(NETDATA_COMPILE_OPTIONS COMPILE_OPTIONS)
@@ -3273,12 +3291,12 @@ install(DIRECTORY COMPONENT netdata DESTINATION ${WEB_DEST})
3291 set(libsysdir_POST "${NETDATA_RUNTIME_PREFIX}/usr/lib/netdata/system")
3292 set(pkglibexecdir_POST "${NETDATA_RUNTIME_PREFIX}/usr/libexec/netdata")
3293 set(localstatedir_POST "${NETDATA_RUNTIME_PREFIX}/var")
3276 -set(sbindir_POST "${NETDATA_RUNTIME_PREFIX}/${BINDIR}")
3277 -set(configdir_POST "${NETDATA_RUNTIME_PREFIX}/etc/netdata")
3278 -set(libconfigdir_POST "${NETDATA_RUNTIME_PREFIX}/usr/lib/netdata/conf.d")
3279 -set(cachedir_POST "${NETDATA_RUNTIME_PREFIX}/var/cache/netdata")
3280 -set(registrydir_POST "${NETDATA_RUNTIME_PREFIX}/var/lib/netdata/registry")
3281 -set(varlibdir_POST "${NETDATA_RUNTIME_PREFIX}/var/lib/netdata")
3294 +set(sbindir_POST "${NETDATA_BIN_DIR}")
3295 +set(configdir_POST "${CONFIG_DIR}")
3296 +set(libconfigdir_POST "${LIBCONFIG_DIR}")
3297 +set(cachedir_POST "${CACHE_DIR}")
3298 +set(varlibdir_POST "${VARLIB_DIR}")
3299 +set(registrydir_POST "${VARLIB_DIR}/registry")
3300 set(netdata_user_POST "${NETDATA_USER}")
3301 set(netdata_group_POST "${NETDATA_USER}")
3302
netdata.spec.in
+1
@@ -539,6 +539,7 @@ rm -rf "${RPM_BUILD_ROOT}"
539 %{_sbindir}/%{name}
540 %{_sbindir}/netdatacli
541 %{_sbindir}/nd-mcp
542 +%{_sbindir}/nd-run
543 %if %{_have_log2journal}
544 %{_sbindir}/log2journal
545 %endif
packaging/cmake/Modules/NetdataGoTools.cmake
+6 -2
@@ -2,11 +2,15 @@
2 # Macros and functions to assist in working with Go
3
4 if(CMAKE_BUILD_TYPE STREQUAL Debug)
5 - set(GO_LDFLAGS "-X github.com/netdata/netdata/go/plugins/pkg/buildinfo.Version=${NETDATA_VERSION_STRING}")
5 + set(GO_LDFLAGS "")
6 else()
7 - set(GO_LDFLAGS "-w -s -X github.com/netdata/netdata/go/plugins/pkg/buildinfo.Version=${NETDATA_VERSION_STRING}")
7 + set(GO_LDFLAGS "-w -s")
8 endif()
9
10 +set(GO_LDFLAGS "${GO_LDFLAGS} -X github.com/netdata/netdata/go/plugins/pkg/buildinfo.Version=${NETDATA_VERSION_STRING}")
11 +set(GO_LDFLAGS "${GO_LDFLAGS} -X github.com/netdata/netdata/go/plugins/pkg/buildinfo.NetdataBinDir=${NETDATA_BIN_DIR}")
12 +set(GO_LDFLAGS "${GO_LDFLAGS} -X github.com/netdata/netdata/go/plugins/pkg/buildinfo.PluginsDir=${PLUGINS_DIR}")
13 +
14 # add_go_target: Add a new target that needs to be built using the Go toolchain.
15 #
16 # Takes four arguments, the target name, the output artifact name, the
packaging/cmake/config.cmake.h.in
+5 -1
@@ -33,7 +33,6 @@
33 #cmakedefine HAVE_SYS_STATVFS_H
34 #cmakedefine HAVE_INTTYPES_H
35 #cmakedefine HAVE_STDINT_H
36 -#cmakedefine HAVE_SYS_CAPABILITY_H
36 #cmakedefine HAVE_ARPA_INET_H
37 #cmakedefine HAVE_NETINET_TCP_H
38 #cmakedefine HAVE_SYS_IOCTL_H
@@ -72,9 +71,14 @@
71 #cmakedefine HAVE_GETPRIORITY
72 #cmakedefine HAVE_SETENV
73 #cmakedefine HAVE_DLSYM
74 +#cmakedefine HAVE_CLEARENV
75 +
76 #cmakedefine HAVE_LIBCURL
77 #cmakedefine HAVE_LIBICONV
78
79 +#cmakedefine HAVE_SETRESUID
80 +#cmakedefine HAVE_SETRESGID
81 +
82 #cmakedefine HAVE_ARC4RANDOM_BUF
83 #cmakedefine HAVE_ARC4RANDOM_UNIFORM
84 #cmakedefine HAVE_RAND_S
src/collectors/apps.plugin/apps_plugin.c
+2 -2
@@ -603,7 +603,7 @@ static inline int am_i_running_as_root() {
603 return 0;
604 }
605
606 -#ifdef HAVE_SYS_CAPABILITY_H
606 +#ifdef HAVE_CAPABILITY
607 static inline int check_capabilities() {
608 cap_t caps = cap_get_proc();
609 if(!caps) {
@@ -718,7 +718,7 @@ int main(int argc, char **argv) {
718 #if !defined(OS_WINDOWS)
719 if(!check_capabilities() && !am_i_running_as_root() && !check_proc_1_io()) {
720 uid_t uid = getuid(), euid = geteuid();
721 -#ifdef HAVE_SYS_CAPABILITY_H
721 +#ifdef HAVE_CAPABILITY
722 netdata_log_error("apps.plugin should either run as root (now running with uid %u, euid %u) or have special capabilities. "
723 "Without these, apps.plugin cannot report disk I/O utilization of other processes. "
724 "To enable capabilities run: sudo setcap cap_dac_read_search,cap_sys_ptrace+ep %s; "
src/collectors/debugfs.plugin/debugfs_plugin.c
+2 -2
@@ -50,7 +50,7 @@ static struct debugfs_module {
50 {.name = NULL, .enabled = CONFIG_BOOLEAN_NO, .func = NULL}
51 };
52
53 -#ifdef HAVE_SYS_CAPABILITY_H
53 +#ifdef HAVE_CAPABILITY
54 static int debugfs_check_capabilities()
55 {
56 cap_t caps = cap_get_proc();
@@ -204,7 +204,7 @@ int main(int argc, char **argv)
204 // FIXME: remove debugfs_check_sys_permission() after https://github.com/netdata/netdata/issues/15048 is fixed
205 if (!debugfs_check_capabilities() && !debugfs_am_i_running_as_root() && !debugfs_check_sys_permission()) {
206 uid_t uid = getuid(), euid = geteuid();
207 -#ifdef HAVE_SYS_CAPABILITY_H
207 +#ifdef HAVE_CAPABILITY
208 netdata_log_error(
209 "debugfs.plugin should either run as root (now running with uid %u, euid %u) or have special capabilities. "
210 "Without these, debugfs.plugin cannot access /sys/kernel/debug. "
src/collectors/utils/nd-run.c new
+216
@@ -0,0 +1,216 @@
1 +#include <unistd.h>
2 +#include <errno.h>
3 +#include <sys/types.h>
4 +#include <grp.h>
5 +#include <pwd.h>
6 +#include <stdio.h>
7 +#include <stdlib.h>
8 +#include <string.h>
9 +
10 +#include "config.h"
11 +
12 +#ifdef HAVE_CAPABILITY
13 +#include <sys/capability.h>
14 +#endif
15 +
16 +#define FALLBACK_USER "nobody"
17 +
18 +void show_help() {
19 + fprintf(stdout, "\n");
20 + fprintf(stdout, "nd-run\n");
21 + fprintf(stdout, "\n");
22 + fprintf(stdout, "Copyright 2025 Netdata Inc.\n");
23 + fprintf(stdout, "\n");
24 + fprintf(stdout, "A helper to run a command as an unprivileged user without any extra privileges\n");
25 + fprintf(stdout, "\n");
26 + fprintf(stdout, "Defaults to running the command as '%s', but will fall back to '%s' if '%s' is not found on the system.\n", NETDATA_USER, FALLBACK_USER, NETDATA_USER);
27 + fprintf(stdout, "\n");
28 + fprintf(stdout, "If it's not possible to switch users, the command will run as the current user instead.\n");
29 + #ifdef HAVE_CAPABILITY
30 + fprintf(stdout, "\n");
31 + fprintf(stdout, "Regardless of whether it switched users, all capabilities will be dropped.\n");
32 + #endif
33 +}
34 +
35 +static void fatal(const char *msg) {
36 + perror(msg);
37 + exit(EXIT_FAILURE);
38 +}
39 +
40 +#ifdef HAVE_CAPABILITY
41 +static void clear_caps() {
42 + // Clear out all capabilities
43 + //
44 + // This does not require any special privileges since it is reducing
45 + // the process’s privileges.
46 + cap_t caps = cap_init();
47 +
48 + if (caps == NULL) fatal("cap_init");
49 +
50 + if (cap_clear(caps) == -1) {
51 + cap_free(caps);
52 + fatal("cap_clear");
53 + }
54 +
55 + if (cap_set_proc(caps) == -1) {
56 + cap_free(caps);
57 + fatal("cap_set_proc");
58 + }
59 +
60 + cap_free(caps);
61 +}
62 +#endif
63 +
64 +static void set_env_var(const char *name, const char *value) {
65 + // Set an environment variable if the specified value is not a NULL pointer.
66 + char buf[64];
67 +
68 + if (value == NULL) {
69 + return;
70 + }
71 +
72 + if (setenv(name, value, 1) != 0) {
73 + snprintf(buf, 64, "setenv %s", name);
74 + perror(buf);
75 + }
76 +}
77 +
78 +static void clean_environment(struct passwd *pw) {
79 + // Explicitly scrub the environment, only passing on a few things
80 + // we know are needed to make things work correctly.
81 +
82 + // First, save copies of the environment variables we want to keep.
83 + // We must copy them before clearing the environment, as getenv()
84 + // returns pointers into the environment block which will be invalidated.
85 + char *saved_path = NULL;
86 + char *saved_tz = NULL;
87 + char *saved_tzdir = NULL;
88 + char *saved_tmpdir = NULL;
89 + char *saved_pwd = NULL;
90 +
91 + const char *tmp;
92 + if ((tmp = getenv("PATH")) != NULL) {
93 + saved_path = strdup(tmp);
94 + if (!saved_path) fatal("strdup PATH");
95 + }
96 + if ((tmp = getenv("TZ")) != NULL) {
97 + saved_tz = strdup(tmp);
98 + if (!saved_tz) fatal("strdup TZ");
99 + }
100 + if ((tmp = getenv("TZDIR")) != NULL) {
101 + saved_tzdir = strdup(tmp);
102 + if (!saved_tzdir) fatal("strdup TZDIR");
103 + }
104 + if ((tmp = getenv("TMPDIR")) != NULL) {
105 + saved_tmpdir = strdup(tmp);
106 + if (!saved_tmpdir) fatal("strdup TMPDIR");
107 + }
108 + if ((tmp = getenv("PWD")) != NULL) {
109 + saved_pwd = strdup(tmp);
110 + if (!saved_pwd) fatal("strdup PWD");
111 + }
112 +
113 + // Now clear the environment
114 + #ifdef HAVE_CLEARENV
115 + clearenv();
116 + #else
117 + extern char **environ;
118 + environ = NULL;
119 + #endif
120 +
121 + // Set the new environment with our saved values
122 + set_env_var("USER", pw->pw_name);
123 + set_env_var("LOGNAME", pw->pw_name);
124 + set_env_var("HOME", pw->pw_dir);
125 + set_env_var("SHELL", "/bin/sh"); // Ignore user default shell
126 + set_env_var("LC_ALL", "C"); // Force C locale
127 + set_env_var("PATH", saved_path);
128 + set_env_var("PWD", saved_pwd);
129 + set_env_var("TZ", saved_tz);
130 + set_env_var("TZDIR", saved_tzdir);
131 + set_env_var("TMPDIR", (saved_tmpdir == NULL) ? "/tmp" : saved_tmpdir); // Use a sane default for TMPDIR if it wasn't set.
132 +
133 + // Free the saved copies
134 + free(saved_path);
135 + free(saved_tz);
136 + free(saved_tzdir);
137 + free(saved_tmpdir);
138 + free(saved_pwd);
139 +}
140 +
141 +int main(int argc, char *argv[]) {
142 + if (argc < 2) {
143 + show_help();
144 + return EXIT_FAILURE;
145 + }
146 +
147 + uid_t euid = geteuid();
148 +
149 + struct passwd *pw = getpwnam(NETDATA_USER);
150 + if (!pw) {
151 + fprintf(stderr, "User '%s' not found, falling back to '%s'\n",
152 + NETDATA_USER, FALLBACK_USER);
153 + pw = getpwnam(FALLBACK_USER);
154 + if (!pw) {
155 + fprintf(stderr, "Fallback user '%s' not found either\n", FALLBACK_USER);
156 + return EXIT_FAILURE;
157 + }
158 + }
159 +
160 + if (euid != pw->pw_uid) {
161 + fprintf(stderr, "Attempting to run as user: %s (UID=%d, GID=%d)\n", pw->pw_name, (int)pw->pw_uid, (int)pw->pw_gid);
162 +
163 + // Set supplementary groups for this user (must be done before dropping privs)
164 + if (initgroups(pw->pw_name, pw->pw_gid) != 0) {
165 + if (euid == 0) {
166 + perror("initgroups");
167 +
168 + if (setgroups(0, NULL) != 0) {
169 + fatal("setgroups");
170 + }
171 + } else if (errno != EPERM) {
172 + fatal("initgroups");
173 + }
174 + }
175 +
176 + // Drop GID then UID. Prefer setres* when available to also drop saved IDs.
177 + // Linux/BSD generally provide setresgid/setresuid; macOS does not.
178 + #ifdef HAVE_SETRESGID
179 + if (setresgid(pw->pw_gid, pw->pw_gid, pw->pw_gid) != 0) {
180 + if (euid == 0 || errno != EPERM) {
181 + fatal("setresgid");
182 + }
183 + }
184 + #else
185 + if (setgid(pw->pw_gid) != 0) {
186 + if (euid == 0 || errno != EPERM) {
187 + fatal("setgid");
188 + }
189 + }
190 + #endif
191 +
192 + #ifdef HAVE_SETRESUID
193 + if (setresuid(pw->pw_uid, pw->pw_uid, pw->pw_uid) != 0) {
194 + if (euid == 0 || errno != EPERM) {
195 + fatal("setresuid");
196 + }
197 + }
198 + #else
199 + if (setuid(pw->pw_uid) != 0) {
200 + if (euid == 0 || errno != EPERM) {
201 + fatal("setuid");
202 + }
203 + }
204 + #endif
205 + }
206 +
207 + #ifdef HAVE_CAPABILITY
208 + clear_caps();
209 + #endif
210 +
211 + clean_environment(pw);
212 +
213 + // Exec the requested command (replaces the current process on success)
214 + execvp(argv[1], &argv[1]);
215 + fatal("execvp"); // Only reached on error
216 +}
src/go/pkg/buildinfo/buildinfo.go
+9 -3
@@ -2,13 +2,19 @@
2
3 package buildinfo
4
5 -// Version stores the agent's version number. It's set during the build process using build flags.
5 +// The variables in this file are set during the build process using linker flags.
6 +
7 +// Version stores the agent's version number.
8 var Version = "v0.0.0"
9
10 // UserConfigDir stores the path to the user configuration directory.
9 -// This value is set during the build process using build flags.
11 var UserConfigDir = ""
12
13 // StockConfigDir stores the path to the stock (default) configuration directory.
13 -// This value is set during the build process using build flags.
14 var StockConfigDir = ""
15 +
16 +// PluginsDir stores the directory where pulgins were installed at build time.
17 +var PluginsDir = "/usr/libexec/netdata/plugins.d"
18 +
19 +// NetdataBinDir stores the directory where executables were installed at build time.
20 +var NetdataBinDir = "/usr/sbin"
src/go/plugin/go.d/pkg/README.md
+1
@@ -1,5 +1,6 @@
1 # Helper Packages
2
3 +- if you need to run an external command, please use [`ndexec`](https://github.com/netdata/netdata/tree/master/src/go/plugin/go.d/pkg/ndexec).
4 - if you need IP ranges consider to
5 use [`iprange`](/src/go/plugin/go.d/pkg/iprange).
6 - if you parse an application log files, then [`log`](https://github.com/netdata/netdata/tree/master/src/go/plugin/go.d/pkg/logs) is
src/go/plugin/go.d/pkg/ndexec/ndexec.go new
+106
@@ -0,0 +1,106 @@
1 +// SPDX-License-Identifier: GPL-3.0-or-later
2 +
3 +package ndexec
4 +
5 +import (
6 + "context"
7 + "fmt"
8 + "os/exec"
9 + "path/filepath"
10 + "time"
11 +
12 + "github.com/netdata/netdata/go/plugins/logger"
13 + "github.com/netdata/netdata/go/plugins/pkg/buildinfo"
14 +)
15 +
16 +// CommandUnprivileged runs a command without any extra privileges and
17 +// returns the exec.Cmd instance.
18 +//
19 +// ctx is a context.Context to use to run the command. logger is a Logger
20 +// instance to use to log the command to be executed. timeout indicates
21 +// the timeout for the command. arg is a list of the command arguments,
22 +// with the first string in the slice being the command to run.
23 +//
24 +// This invokes the command and logs a debug message that the command
25 +// is being executed, and then returns the exec.Cmd object for the command.
26 +func CommandUnprivileged(ctx context.Context, logger *logger.Logger, arg ...string) *exec.Cmd {
27 + ndrunPath := filepath.Join(buildinfo.NetdataBinDir, "nd-run")
28 +
29 + cmd := exec.CommandContext(ctx, ndrunPath, arg...)
30 + if logger != nil {
31 + logger.Debugf("executing '%s'", cmd)
32 + }
33 +
34 + return cmd
35 +}
36 +
37 +// RunUnprivileged runs a command without any inherited privileges via
38 +// the nd-run helper.
39 +//
40 +// logger is a Logger instance to use to log the command to be executed.
41 +// timeout indicates the timeout for the command. arg is a list of the
42 +// command arguments, with the first string in the slice being the command
43 +// to run.
44 +//
45 +// This handles constructing the context for execution, logs a debug
46 +// message that the command is being executed, and checks for errors in
47 +// the command invocation, then returns the command output.
48 +func RunUnprivileged(logger *logger.Logger, timeout time.Duration, arg ...string) ([]byte, error) {
49 + ctx, cancel := context.WithTimeout(context.Background(), timeout)
50 + defer cancel()
51 +
52 + cmd := CommandUnprivileged(ctx, logger, arg...)
53 +
54 + bs, err := cmd.Output()
55 + if err != nil {
56 + return nil, fmt.Errorf("error on '%s': %v", cmd, err)
57 + }
58 +
59 + return bs, nil
60 +}
61 +
62 +// CommandNDSudo runs a command via the ndsudo helper and returns the exec.Cmd instance.
63 +//
64 +// ctx is a context.Context to use to run the command. logger is a Logger
65 +// instance to use to log the command to be executed. timeout indicates
66 +// the timeout for the command. arg is a list of the command arguments,
67 +// with the first string in the slice being the command to run.
68 +//
69 +// This invokes the command and logs a debug message that the command
70 +// is being executed, and then returns the exec.Cmd object for the command.
71 +func CommandNDSudo(ctx context.Context, logger *logger.Logger, arg ...string) *exec.Cmd {
72 + ndsudoPath := filepath.Join(buildinfo.PluginsDir, "ndsudo")
73 +
74 + cmd := exec.CommandContext(ctx, ndsudoPath, arg...)
75 + if logger != nil {
76 + logger.Debugf("executing '%s'", cmd)
77 + }
78 +
79 + return cmd
80 +}
81 +
82 +// RunNDSudo runs a command via the ndsudo helper.
83 +//
84 +// logger is a Logger instance to use to log the command to be executed.
85 +// timeout indicates the timeout for the command. arg is a list of the
86 +// command arguments, with the first string in the slice being the command
87 +// to run.
88 +//
89 +// This handles constructing the context for execution, logs a debug
90 +// message that the command is being executed, and checks for errors in
91 +// the command invocation, then returns the command output.
92 +//
93 +// The command to be run must also be properly handled by ndsudo.
94 +func RunNDSudo(logger *logger.Logger, timeout time.Duration, arg ...string) ([]byte, error) {
95 + ctx, cancel := context.WithTimeout(context.Background(), timeout)
96 + defer cancel()
97 +
98 + cmd := CommandNDSudo(ctx, logger, arg...)
99 +
100 + bs, err := cmd.Output()
101 + if err != nil {
102 + return nil, fmt.Errorf("error on '%s': %v", cmd, err)
103 + }
104 +
105 + return bs, nil
106 +}
src/libnetdata/common.h
+1 -1
@@ -254,7 +254,7 @@ typedef uint32_t uid_t;
254
255 #include <zlib.h>
256
257 -#ifdef HAVE_SYS_CAPABILITY_H
257 +#ifdef HAVE_CAPABILITY
258 #include <sys/capability.h>
259 #endif
260