mirror of
https://github.com/netdata/netdata.git
synced 2025-04-28 14:42:31 +00:00
added cgroup-network that can find the network interface of a cgroup, given a pid in it
This commit is contained in:
parent
d2e9b448af
commit
012d456f38
9 changed files with 364 additions and 4 deletions
2
.gitignore
vendored
2
.gitignore
vendored
|
@ -109,3 +109,5 @@ diagrams/plantuml.jar
|
|||
netdata.cppcheck
|
||||
|
||||
profile/statsd-stress
|
||||
src/cgroup-network
|
||||
vgcore.*
|
||||
|
|
|
@ -188,6 +188,22 @@ set(FREEIPMI_PLUGIN_SOURCE_FILES
|
|||
config.h
|
||||
)
|
||||
|
||||
set(CGROUP_NETWORK_SOURCE_FILES
|
||||
src/cgroup-network.c
|
||||
src/common.c
|
||||
src/common.h
|
||||
src/clocks.c
|
||||
src/clocks.h
|
||||
src/inlined.h
|
||||
src/log.c
|
||||
src/log.h
|
||||
src/procfile.c
|
||||
src/procfile.h
|
||||
src/web_buffer.c
|
||||
src/web_buffer.h
|
||||
config.h
|
||||
)
|
||||
|
||||
include_directories(AFTER .)
|
||||
|
||||
add_definitions(-DHAVE_CONFIG_H -DCACHE_DIR="/var/cache/netdata" -DCONFIG_DIR="/etc/netdata" -DLOG_DIR="/var/log/netdata" -DPLUGINS_DIR="/usr/libexec/netdata" -DWEB_DIR="/usr/share/netdata" -DVARLIB_DIR="/var/lib/netdata")
|
||||
|
@ -200,3 +216,6 @@ target_link_libraries (apps.plugin m ${CMAKE_THREAD_LIBS_INIT})
|
|||
|
||||
add_executable(freeipmi.plugin ${FREEIPMI_PLUGIN_SOURCE_FILES})
|
||||
target_link_libraries (freeipmi.plugin ipmimonitoring)
|
||||
|
||||
add_executable(cgroup-network ${CGROUP_NETWORK_SOURCE_FILES})
|
||||
target_link_libraries (cgroup-network m ${CMAKE_THREAD_LIBS_INIT})
|
||||
|
|
16
configure.ac
16
configure.ac
|
@ -148,7 +148,6 @@ AC_HEADER_RESOLV
|
|||
|
||||
AC_CHECK_HEADERS_ONCE([sys/prctl.h])
|
||||
|
||||
|
||||
# -----------------------------------------------------------------------------
|
||||
# operating system detection
|
||||
|
||||
|
@ -425,6 +424,21 @@ AC_MSG_RESULT([${enable_plugin_nfacct}])
|
|||
AM_CONDITIONAL([ENABLE_PLUGIN_NFACCT], [test "${enable_plugin_nfacct}" = "yes"])
|
||||
|
||||
|
||||
# -----------------------------------------------------------------------------
|
||||
# check for setns() - cgroup-network
|
||||
|
||||
AC_CHECK_FUNC([setns])
|
||||
AC_MSG_CHECKING([if cgroup-network can be enabled])
|
||||
if test "$ac_cv_func_setns" = "yes" ; then
|
||||
have_setns="yes"
|
||||
AC_DEFINE([HAVE_SETNS], [1], [Define 1 if you have setns() function])
|
||||
else
|
||||
have_setns="no"
|
||||
fi
|
||||
AC_MSG_RESULT([${have_setns}])
|
||||
AM_CONDITIONAL([ENABLE_PLUGIN_CGROUP_NETWORK], [test "${have_setns}" = "yes"])
|
||||
|
||||
|
||||
# -----------------------------------------------------------------------------
|
||||
# Link-Time-Optimization
|
||||
|
||||
|
|
|
@ -138,7 +138,7 @@ run chown -R ${NETDATA_USER}:${NETDATA_GROUP} /opt/netdata
|
|||
# -----------------------------------------------------------------------------
|
||||
progress "fix plugin permissions"
|
||||
|
||||
for x in apps.plugin freeipmi.plugin
|
||||
for x in apps.plugin freeipmi.plugin cgroup-network
|
||||
do
|
||||
f="usr/libexec/netdata/plugins.d/${x}"
|
||||
|
||||
|
|
|
@ -782,6 +782,12 @@ if [ ${UID} -eq 0 ]
|
|||
run chmod 4755 "${NETDATA_PREFIX}/usr/libexec/netdata/plugins.d/freeipmi.plugin"
|
||||
fi
|
||||
|
||||
if [ -f "${NETDATA_PREFIX}/usr/libexec/netdata/plugins.d/cgroup-network" ]
|
||||
then
|
||||
run chown root "${NETDATA_PREFIX}/usr/libexec/netdata/plugins.d/cgroup-network"
|
||||
run chmod 4755 "${NETDATA_PREFIX}/usr/libexec/netdata/plugins.d/cgroup-network"
|
||||
fi
|
||||
|
||||
else
|
||||
run chown "${NETDATA_USER}:${NETDATA_USER}" "${NETDATA_LOG_DIR}"
|
||||
run chown -R "${NETDATA_USER}:${NETDATA_USER}" "${NETDATA_PREFIX}/usr/libexec/netdata"
|
||||
|
|
|
@ -37,6 +37,10 @@ if ENABLE_PLUGIN_FREEIPMI
|
|||
plugins_PROGRAMS += freeipmi.plugin
|
||||
endif
|
||||
|
||||
if ENABLE_PLUGIN_CGROUP_NETWORK
|
||||
plugins_PROGRAMS += cgroup-network
|
||||
endif
|
||||
|
||||
netdata_SOURCES = \
|
||||
adaptive_resortable_list.c \
|
||||
adaptive_resortable_list.h \
|
||||
|
@ -244,3 +248,18 @@ freeipmi_plugin_SOURCES = \
|
|||
freeipmi_plugin_LDADD = \
|
||||
$(OPTIONAL_IPMIMONITORING_LIBS) \
|
||||
$(NULL)
|
||||
|
||||
cgroup_network_SOURCES = \
|
||||
cgroup-network.c \
|
||||
clocks.c clocks.h \
|
||||
common.c common.h \
|
||||
inlined.h \
|
||||
log.c log.h \
|
||||
procfile.c procfile.h \
|
||||
web_buffer.c web_buffer.h \
|
||||
$(NULL)
|
||||
|
||||
cgroup_network_LDADD = \
|
||||
$(OPTIONAL_MATH_LIBS) \
|
||||
$(OPTIONAL_LIBCAP_LIBS) \
|
||||
$(NULL)
|
||||
|
|
|
@ -3197,7 +3197,7 @@ static void parse_args(int argc, char **argv)
|
|||
}
|
||||
}
|
||||
|
||||
if(strcmp("version", argv[i]) == 0 || strcmp("-v", argv[i]) == 0 || strcmp("-V", argv[i]) == 0) {
|
||||
if(strcmp("version", argv[i]) == 0 || strcmp("-version", argv[i]) == 0 || strcmp("--version", argv[i]) == 0 || strcmp("-v", argv[i]) == 0 || strcmp("-V", argv[i]) == 0) {
|
||||
printf("apps.plugin %s\n", VERSION);
|
||||
exit(0);
|
||||
}
|
||||
|
|
300
src/cgroup-network.c
Normal file
300
src/cgroup-network.c
Normal file
|
@ -0,0 +1,300 @@
|
|||
#include "common.h"
|
||||
|
||||
#ifdef HAVE_SETNS
|
||||
#ifndef _GNU_SOURCE
|
||||
#define _GNU_SOURCE /* See feature_test_macros(7) */
|
||||
#endif
|
||||
#include <sched.h>
|
||||
#endif
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
// callback required by fatal()
|
||||
|
||||
void netdata_cleanup_and_exit(int ret) {
|
||||
exit(ret);
|
||||
}
|
||||
|
||||
struct iface {
|
||||
const char *device;
|
||||
uint32_t hash;
|
||||
|
||||
unsigned int ifindex;
|
||||
unsigned int iflink;
|
||||
|
||||
struct iface *next;
|
||||
};
|
||||
|
||||
unsigned int read_iface_iflink(const char *prefix, const char *iface) {
|
||||
char filename[FILENAME_MAX + 1];
|
||||
snprintfz(filename, FILENAME_MAX, "%s/sys/class/net/%s/iflink", prefix?prefix:"", iface);
|
||||
|
||||
unsigned long long iflink = 0;
|
||||
int ret = read_single_number_file(filename, &iflink);
|
||||
if(ret) error("Cannot read '%s'.", filename);
|
||||
|
||||
return (unsigned int)iflink;
|
||||
}
|
||||
|
||||
unsigned int read_iface_ifindex(const char *prefix, const char *iface) {
|
||||
char filename[FILENAME_MAX + 1];
|
||||
snprintfz(filename, FILENAME_MAX, "%s/sys/class/net/%s/ifindex", prefix?prefix:"", iface);
|
||||
|
||||
unsigned long long ifindex = 0;
|
||||
int ret = read_single_number_file(filename, &ifindex);
|
||||
if(ret) error("Cannot read '%s'.", filename);
|
||||
|
||||
return (unsigned int)ifindex;
|
||||
}
|
||||
|
||||
struct iface *read_proc_net_dev(const char *prefix) {
|
||||
procfile *ff = NULL;
|
||||
char filename[FILENAME_MAX + 1];
|
||||
|
||||
snprintfz(filename, FILENAME_MAX, "%s%s", prefix?prefix:"", "/proc/net/dev");
|
||||
ff = procfile_open(filename, " \t,:|", PROCFILE_FLAG_DEFAULT);
|
||||
if(unlikely(!ff)) {
|
||||
error("Cannot open file '%s'", filename);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
ff = procfile_readall(ff);
|
||||
if(unlikely(!ff)) {
|
||||
error("Cannot read file '%s'", filename);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
size_t lines = procfile_lines(ff), l;
|
||||
struct iface *root = NULL;
|
||||
for(l = 2; l < lines ;l++) {
|
||||
if (unlikely(procfile_linewords(ff, l) < 1)) continue;
|
||||
|
||||
struct iface *t = callocz(1, sizeof(struct iface));
|
||||
t->device = strdupz(procfile_lineword(ff, l, 0));
|
||||
t->hash = simple_hash(t->device);
|
||||
t->ifindex = read_iface_ifindex(prefix, t->device);
|
||||
t->iflink = read_iface_iflink(prefix, t->device);
|
||||
t->next = root;
|
||||
root = t;
|
||||
}
|
||||
|
||||
return root;
|
||||
}
|
||||
|
||||
inline int iface_is_eligible(struct iface *iface) {
|
||||
if(iface->iflink != iface->ifindex)
|
||||
return 1;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int eligible_ifaces(struct iface *root) {
|
||||
int eligible = 0;
|
||||
|
||||
struct iface *t;
|
||||
for(t = root; t ; t = t->next)
|
||||
if(iface_is_eligible(t))
|
||||
eligible++;
|
||||
|
||||
return eligible;
|
||||
}
|
||||
|
||||
static void continue_as_child(void) {
|
||||
pid_t child = fork();
|
||||
int status;
|
||||
pid_t ret;
|
||||
|
||||
if (child < 0)
|
||||
error("fork() failed");
|
||||
|
||||
/* Only the child returns */
|
||||
if (child == 0)
|
||||
return;
|
||||
|
||||
for (;;) {
|
||||
ret = waitpid(child, &status, WUNTRACED);
|
||||
if ((ret == child) && (WIFSTOPPED(status))) {
|
||||
/* The child suspended so suspend us as well */
|
||||
kill(getpid(), SIGSTOP);
|
||||
kill(child, SIGCONT);
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/* Return the child's exit code if possible */
|
||||
if (WIFEXITED(status)) {
|
||||
exit(WEXITSTATUS(status));
|
||||
} else if (WIFSIGNALED(status)) {
|
||||
kill(getpid(), WTERMSIG(status));
|
||||
}
|
||||
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
int proc_pid_fd(const char *prefix, const char *ns, pid_t pid) {
|
||||
char filename[FILENAME_MAX + 1];
|
||||
snprintfz(filename, FILENAME_MAX, "%s/proc/%d/%s", prefix?prefix:"", (int)pid, ns);
|
||||
int fd = open(filename, O_RDONLY);
|
||||
|
||||
if(fd == -1)
|
||||
error("Cannot open file '%s'", filename);
|
||||
|
||||
return fd;
|
||||
}
|
||||
|
||||
static struct ns {
|
||||
int nstype;
|
||||
int fd;
|
||||
int status;
|
||||
const char *name;
|
||||
const char *path;
|
||||
} all_ns[] = {
|
||||
// { .nstype = CLONE_NEWUSER, .fd = -1, .status = -1, .name = "user", .path = "ns/user" },
|
||||
// { .nstype = CLONE_NEWCGROUP, .fd = -1, .status = -1, .name = "cgroup", .path = "ns/cgroup" },
|
||||
// { .nstype = CLONE_NEWIPC, .fd = -1, .status = -1, .name = "ipc", .path = "ns/ipc" },
|
||||
// { .nstype = CLONE_NEWUTS, .fd = -1, .status = -1, .name = "uts", .path = "ns/uts" },
|
||||
{ .nstype = CLONE_NEWNET, .fd = -1, .status = -1, .name = "network", .path = "ns/net" },
|
||||
{ .nstype = CLONE_NEWPID, .fd = -1, .status = -1, .name = "pid", .path = "ns/pid" },
|
||||
{ .nstype = CLONE_NEWNS, .fd = -1, .status = -1, .name = "mount", .path = "ns/mnt" },
|
||||
|
||||
// terminator
|
||||
{ .nstype = 0, .fd = -1, .status = -1, .name = NULL, .path = NULL }
|
||||
};
|
||||
|
||||
int switch_namespace(const char *prefix, pid_t pid) {
|
||||
#ifdef HAVE_SETNS
|
||||
|
||||
int i;
|
||||
for(i = 0; all_ns[i].name ; i++)
|
||||
all_ns[i].fd = proc_pid_fd(prefix, all_ns[i].path, pid);
|
||||
|
||||
int root_fd = proc_pid_fd(prefix, "root", pid);
|
||||
int cwd_fd = proc_pid_fd(prefix, "cwd", pid);
|
||||
|
||||
setgroups(0, NULL);
|
||||
|
||||
// 2 passes - found it at nsenter source code
|
||||
// this is related CLONE_NEWUSER functionality
|
||||
|
||||
int pass, errors = 0;
|
||||
for(pass = 0; pass < 2 ;pass++) {
|
||||
for(i = 0; all_ns[i].name ; i++) {
|
||||
if (all_ns[i].fd != -1 && all_ns[i].status == -1) {
|
||||
if(setns(all_ns[i].fd, all_ns[i].nstype) == -1) {
|
||||
if(pass == 1) {
|
||||
all_ns[i].status = 0;
|
||||
error("Cannot switch to %s namespace of pid %d", all_ns[i].name, (int) pid);
|
||||
errors++;
|
||||
}
|
||||
}
|
||||
else
|
||||
all_ns[i].status = 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
setgroups(0, NULL);
|
||||
|
||||
if(root_fd != -1) {
|
||||
if(fchdir(root_fd) < 0)
|
||||
error("Cannot fchdir() to pid %d root directory", (int)pid);
|
||||
|
||||
if(chroot(".") < 0)
|
||||
error("Cannot chroot() to pid %d root directory", (int)pid);
|
||||
|
||||
close(root_fd);
|
||||
}
|
||||
|
||||
if(cwd_fd != -1) {
|
||||
if(fchdir(cwd_fd) < 0)
|
||||
error("Cannot fchdir() to pid %d current working directory", (int)pid);
|
||||
|
||||
close(cwd_fd);
|
||||
}
|
||||
|
||||
int do_fork = 0;
|
||||
for(i = 0; all_ns[i].name ; i++)
|
||||
if(all_ns[i].fd != -1) {
|
||||
|
||||
// CLONE_NEWPID requires a fork() to become effective
|
||||
if(all_ns[i].nstype == CLONE_NEWPID && all_ns[i].status)
|
||||
do_fork = 1;
|
||||
|
||||
close(all_ns[i].fd);
|
||||
}
|
||||
|
||||
if(do_fork)
|
||||
continue_as_child();
|
||||
|
||||
return 0;
|
||||
|
||||
#else
|
||||
|
||||
errno = ENOSYS;
|
||||
error("setns() is missing on this system.");
|
||||
return 1;
|
||||
|
||||
#endif
|
||||
}
|
||||
|
||||
void usage(void) {
|
||||
fprintf(stderr, "%s -p PID\n", program_name);
|
||||
exit(1);
|
||||
}
|
||||
|
||||
int main(int argc, char **argv) {
|
||||
pid_t pid = 0;
|
||||
|
||||
program_name = argv[0];
|
||||
program_version = VERSION;
|
||||
error_log_syslog = 0;
|
||||
|
||||
if(argc == 2 && (!strcmp(argv[1], "version") || !strcmp(argv[1], "-version") || !strcmp(argv[1], "--version") || !strcmp(argv[1], "-v") || !strcmp(argv[1], "-V"))) {
|
||||
fprintf(stderr, "cgroup-network %s\n", VERSION);
|
||||
exit(0);
|
||||
}
|
||||
|
||||
if(argc != 3 || strcmp(argv[1], "-p") != 0)
|
||||
usage();
|
||||
|
||||
pid = atoi(argv[2]);
|
||||
if(pid <= 0)
|
||||
fatal("Invalid pid %d", (int)pid);
|
||||
|
||||
struct iface *host, *cgroup, *h, *c;
|
||||
const char *prefix = getenv("NETDATA_HOST_PREFIX");
|
||||
|
||||
host = read_proc_net_dev(prefix);
|
||||
if(!host)
|
||||
fatal("cannot read host interface list.");
|
||||
|
||||
if(!eligible_ifaces(host))
|
||||
fatal("there are no double-linked host interfaces available.");
|
||||
|
||||
if(switch_namespace(prefix, pid))
|
||||
fatal("cannot switch to the namespace of pid %u", (unsigned int)pid);
|
||||
|
||||
cgroup = read_proc_net_dev(NULL);
|
||||
if(!cgroup)
|
||||
fatal("cannot read cgroup interface list.");
|
||||
|
||||
if(!eligible_ifaces(cgroup))
|
||||
fatal("there are not double-linked cgroup interfaces available.");
|
||||
|
||||
int found = 0;
|
||||
for(h = host; h ; h = h->next) {
|
||||
if(iface_is_eligible(h)) {
|
||||
for (c = cgroup; c; c = c->next) {
|
||||
if(iface_is_eligible(c) && h->ifindex == c->iflink && h->iflink == c->ifindex) {
|
||||
printf("%s\n", h->device);
|
||||
found++;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if(!found)
|
||||
return 1;
|
||||
|
||||
return 0;
|
||||
}
|
|
@ -1433,7 +1433,7 @@ int main (int argc, char **argv) {
|
|||
continue;
|
||||
}
|
||||
}
|
||||
else if(strcmp("version", argv[i]) == 0 || strcmp("-v", argv[i]) == 0 || strcmp("-V", argv[i]) == 0) {
|
||||
else if(strcmp("version", argv[i]) == 0 || strcmp("-version", argv[i]) == 0 || strcmp("--version", argv[i]) == 0 || strcmp("-v", argv[i]) == 0 || strcmp("-V", argv[i]) == 0) {
|
||||
printf("freeipmi.plugin %s\n", VERSION);
|
||||
exit(0);
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue