0
0
Fork 0
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:
Costa Tsaousis (ktsaou) 2017-07-30 19:29:46 +03:00
parent d2e9b448af
commit 012d456f38
No known key found for this signature in database
GPG key ID: 29CA335889B9A863
9 changed files with 364 additions and 4 deletions

2
.gitignore vendored
View file

@ -109,3 +109,5 @@ diagrams/plantuml.jar
netdata.cppcheck
profile/statsd-stress
src/cgroup-network
vgcore.*

View file

@ -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})

View file

@ -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

View file

@ -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}"

View file

@ -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"

View file

@ -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)

View file

@ -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
View 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;
}

View file

@ -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);
}