cgroup-network.c 21 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743
  1. // SPDX-License-Identifier: GPL-3.0-or-later
  2. #include "libnetdata/libnetdata.h"
  3. #include "libnetdata/required_dummies.h"
  4. #ifdef HAVE_SETNS
  5. #ifndef _GNU_SOURCE
  6. #define _GNU_SOURCE /* See feature_test_macros(7) */
  7. #endif
  8. #include <sched.h>
  9. #endif
  10. char env_netdata_host_prefix[FILENAME_MAX + 50] = "";
  11. char env_netdata_log_method[FILENAME_MAX + 50] = "";
  12. char env_netdata_log_format[FILENAME_MAX + 50] = "";
  13. char env_netdata_log_level[FILENAME_MAX + 50] = "";
  14. char *environment[] = {
  15. "PATH=/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin",
  16. env_netdata_host_prefix,
  17. env_netdata_log_method,
  18. env_netdata_log_format,
  19. env_netdata_log_level,
  20. NULL
  21. };
  22. struct iface {
  23. const char *device;
  24. uint32_t hash;
  25. unsigned int ifindex;
  26. unsigned int iflink;
  27. struct iface *next;
  28. };
  29. unsigned int calc_num_ifaces(struct iface *root) {
  30. unsigned int num = 0;
  31. for (struct iface *h = root; h; h = h->next) {
  32. num++;
  33. }
  34. return num;
  35. }
  36. unsigned int read_iface_iflink(const char *prefix, const char *iface) {
  37. if(!prefix) prefix = "";
  38. char filename[FILENAME_MAX + 1];
  39. snprintfz(filename, FILENAME_MAX, "%s/sys/class/net/%s/iflink", prefix, iface);
  40. unsigned long long iflink = 0;
  41. int ret = read_single_number_file(filename, &iflink);
  42. if(ret) collector_error("Cannot read '%s'.", filename);
  43. return (unsigned int)iflink;
  44. }
  45. unsigned int read_iface_ifindex(const char *prefix, const char *iface) {
  46. if(!prefix) prefix = "";
  47. char filename[FILENAME_MAX + 1];
  48. snprintfz(filename, FILENAME_MAX, "%s/sys/class/net/%s/ifindex", prefix, iface);
  49. unsigned long long ifindex = 0;
  50. int ret = read_single_number_file(filename, &ifindex);
  51. if(ret) collector_error("Cannot read '%s'.", filename);
  52. return (unsigned int)ifindex;
  53. }
  54. struct iface *read_proc_net_dev(const char *scope __maybe_unused, const char *prefix) {
  55. if(!prefix) prefix = "";
  56. procfile *ff = NULL;
  57. char filename[FILENAME_MAX + 1];
  58. snprintfz(filename, FILENAME_MAX, "%s%s", prefix, (*prefix)?"/proc/1/net/dev":"/proc/net/dev");
  59. #ifdef NETDATA_INTERNAL_CHECKS
  60. collector_info("parsing '%s'", filename);
  61. #endif
  62. ff = procfile_open(filename, " \t,:|", PROCFILE_FLAG_DEFAULT);
  63. if(unlikely(!ff)) {
  64. collector_error("Cannot open file '%s'", filename);
  65. return NULL;
  66. }
  67. ff = procfile_readall(ff);
  68. if(unlikely(!ff)) {
  69. collector_error("Cannot read file '%s'", filename);
  70. return NULL;
  71. }
  72. size_t lines = procfile_lines(ff), l;
  73. struct iface *root = NULL;
  74. for(l = 2; l < lines ;l++) {
  75. if (unlikely(procfile_linewords(ff, l) < 1)) continue;
  76. struct iface *t = callocz(1, sizeof(struct iface));
  77. t->device = strdupz(procfile_lineword(ff, l, 0));
  78. t->hash = simple_hash(t->device);
  79. t->ifindex = read_iface_ifindex(prefix, t->device);
  80. t->iflink = read_iface_iflink(prefix, t->device);
  81. t->next = root;
  82. root = t;
  83. #ifdef NETDATA_INTERNAL_CHECKS
  84. collector_info("added %s interface '%s', ifindex %u, iflink %u", scope, t->device, t->ifindex, t->iflink);
  85. #endif
  86. }
  87. procfile_close(ff);
  88. return root;
  89. }
  90. void free_iface(struct iface *iface) {
  91. freez((void *)iface->device);
  92. freez(iface);
  93. }
  94. void free_host_ifaces(struct iface *iface) {
  95. while(iface) {
  96. struct iface *t = iface->next;
  97. free_iface(iface);
  98. iface = t;
  99. }
  100. }
  101. int iface_is_eligible(struct iface *iface) {
  102. if(iface->iflink != iface->ifindex)
  103. return 1;
  104. return 0;
  105. }
  106. int eligible_ifaces(struct iface *root) {
  107. int eligible = 0;
  108. struct iface *t;
  109. for(t = root; t ; t = t->next)
  110. if(iface_is_eligible(t))
  111. eligible++;
  112. return eligible;
  113. }
  114. static void continue_as_child(void) {
  115. pid_t child = fork();
  116. int status;
  117. pid_t ret;
  118. if (child < 0)
  119. collector_error("fork() failed");
  120. /* Only the child returns */
  121. if (child == 0)
  122. return;
  123. for (;;) {
  124. ret = waitpid(child, &status, WUNTRACED);
  125. if ((ret == child) && (WIFSTOPPED(status))) {
  126. /* The child suspended so suspend us as well */
  127. kill(getpid(), SIGSTOP);
  128. kill(child, SIGCONT);
  129. } else {
  130. break;
  131. }
  132. }
  133. /* Return the child's exit code if possible */
  134. if (WIFEXITED(status)) {
  135. exit(WEXITSTATUS(status));
  136. } else if (WIFSIGNALED(status)) {
  137. kill(getpid(), WTERMSIG(status));
  138. }
  139. exit(EXIT_FAILURE);
  140. }
  141. int proc_pid_fd(const char *prefix, const char *ns, pid_t pid) {
  142. if(!prefix) prefix = "";
  143. char filename[FILENAME_MAX + 1];
  144. snprintfz(filename, FILENAME_MAX, "%s/proc/%d/%s", prefix, (int)pid, ns);
  145. int fd = open(filename, O_RDONLY);
  146. if(fd == -1)
  147. collector_error("Cannot open proc_pid_fd() file '%s'", filename);
  148. return fd;
  149. }
  150. static struct ns {
  151. int nstype;
  152. int fd;
  153. int status;
  154. const char *name;
  155. const char *path;
  156. } all_ns[] = {
  157. // { .nstype = CLONE_NEWUSER, .fd = -1, .status = -1, .name = "user", .path = "ns/user" },
  158. // { .nstype = CLONE_NEWCGROUP, .fd = -1, .status = -1, .name = "cgroup", .path = "ns/cgroup" },
  159. // { .nstype = CLONE_NEWIPC, .fd = -1, .status = -1, .name = "ipc", .path = "ns/ipc" },
  160. // { .nstype = CLONE_NEWUTS, .fd = -1, .status = -1, .name = "uts", .path = "ns/uts" },
  161. { .nstype = CLONE_NEWNET, .fd = -1, .status = -1, .name = "network", .path = "ns/net" },
  162. { .nstype = CLONE_NEWPID, .fd = -1, .status = -1, .name = "pid", .path = "ns/pid" },
  163. { .nstype = CLONE_NEWNS, .fd = -1, .status = -1, .name = "mount", .path = "ns/mnt" },
  164. // terminator
  165. { .nstype = 0, .fd = -1, .status = -1, .name = NULL, .path = NULL }
  166. };
  167. int switch_namespace(const char *prefix, pid_t pid) {
  168. #ifdef HAVE_SETNS
  169. int i;
  170. for(i = 0; all_ns[i].name ; i++)
  171. all_ns[i].fd = proc_pid_fd(prefix, all_ns[i].path, pid);
  172. int root_fd = proc_pid_fd(prefix, "root", pid);
  173. int cwd_fd = proc_pid_fd(prefix, "cwd", pid);
  174. setgroups(0, NULL);
  175. // 2 passes - found it at nsenter source code
  176. // this is related CLONE_NEWUSER functionality
  177. // This code cannot switch user namespace (it can all the other namespaces)
  178. // Fortunately, we don't need to switch user namespaces.
  179. int pass;
  180. for(pass = 0; pass < 2 ;pass++) {
  181. for(i = 0; all_ns[i].name ; i++) {
  182. if (all_ns[i].fd != -1 && all_ns[i].status == -1) {
  183. if(setns(all_ns[i].fd, all_ns[i].nstype) == -1) {
  184. if(pass == 1) {
  185. all_ns[i].status = 0;
  186. collector_error("Cannot switch to %s namespace of pid %d", all_ns[i].name, (int) pid);
  187. }
  188. }
  189. else
  190. all_ns[i].status = 1;
  191. }
  192. }
  193. }
  194. setgroups(0, NULL);
  195. if(root_fd != -1) {
  196. if(fchdir(root_fd) < 0)
  197. collector_error("Cannot fchdir() to pid %d root directory", (int)pid);
  198. if(chroot(".") < 0)
  199. collector_error("Cannot chroot() to pid %d root directory", (int)pid);
  200. close(root_fd);
  201. }
  202. if(cwd_fd != -1) {
  203. if(fchdir(cwd_fd) < 0)
  204. collector_error("Cannot fchdir() to pid %d current working directory", (int)pid);
  205. close(cwd_fd);
  206. }
  207. int do_fork = 0;
  208. for(i = 0; all_ns[i].name ; i++)
  209. if(all_ns[i].fd != -1) {
  210. // CLONE_NEWPID requires a fork() to become effective
  211. if(all_ns[i].nstype == CLONE_NEWPID && all_ns[i].status)
  212. do_fork = 1;
  213. close(all_ns[i].fd);
  214. }
  215. if(do_fork)
  216. continue_as_child();
  217. return 0;
  218. #else
  219. errno = ENOSYS;
  220. collector_error("setns() is missing on this system.");
  221. return 1;
  222. #endif
  223. }
  224. pid_t read_pid_from_cgroup_file(const char *filename) {
  225. int fd = open(filename, procfile_open_flags);
  226. if(fd == -1) {
  227. if (errno != ENOENT)
  228. collector_error("Cannot open pid_from_cgroup() file '%s'.", filename);
  229. return 0;
  230. }
  231. FILE *fp = fdopen(fd, "r");
  232. if(!fp) {
  233. collector_error("Cannot upgrade fd to fp for file '%s'.", filename);
  234. return 0;
  235. }
  236. char buffer[100 + 1];
  237. pid_t pid = 0;
  238. char *s;
  239. while((s = fgets(buffer, 100, fp))) {
  240. buffer[100] = '\0';
  241. pid = atoi(s);
  242. if(pid > 0) break;
  243. }
  244. fclose(fp);
  245. #ifdef NETDATA_INTERNAL_CHECKS
  246. if(pid > 0) collector_info("found pid %d on file '%s'", pid, filename);
  247. #endif
  248. return pid;
  249. }
  250. pid_t read_pid_from_cgroup_files(const char *path) {
  251. char filename[FILENAME_MAX + 1];
  252. snprintfz(filename, FILENAME_MAX, "%s/cgroup.procs", path);
  253. pid_t pid = read_pid_from_cgroup_file(filename);
  254. if(pid > 0) return pid;
  255. snprintfz(filename, FILENAME_MAX, "%s/tasks", path);
  256. return read_pid_from_cgroup_file(filename);
  257. }
  258. pid_t read_pid_from_cgroup(const char *path) {
  259. pid_t pid = read_pid_from_cgroup_files(path);
  260. if (pid > 0) return pid;
  261. DIR *dir = opendir(path);
  262. if (!dir) {
  263. collector_error("cannot read directory '%s'", path);
  264. return 0;
  265. }
  266. struct dirent *de = NULL;
  267. while ((de = readdir(dir))) {
  268. if (de->d_type == DT_DIR
  269. && (
  270. (de->d_name[0] == '.' && de->d_name[1] == '\0')
  271. || (de->d_name[0] == '.' && de->d_name[1] == '.' && de->d_name[2] == '\0')
  272. ))
  273. continue;
  274. if (de->d_type == DT_DIR) {
  275. char filename[FILENAME_MAX + 1];
  276. snprintfz(filename, FILENAME_MAX, "%s/%s", path, de->d_name);
  277. pid = read_pid_from_cgroup(filename);
  278. if(pid > 0) break;
  279. }
  280. }
  281. closedir(dir);
  282. return pid;
  283. }
  284. // ----------------------------------------------------------------------------
  285. // send the result to netdata
  286. struct found_device {
  287. const char *host_device;
  288. const char *guest_device;
  289. uint32_t host_device_hash;
  290. struct found_device *next;
  291. } *detected_devices = NULL;
  292. void add_device(const char *host, const char *guest) {
  293. #ifdef NETDATA_INTERNAL_CHECKS
  294. collector_info("adding device with host '%s', guest '%s'", host, guest);
  295. #endif
  296. uint32_t hash = simple_hash(host);
  297. if(guest && (!*guest || strcmp(host, guest) == 0))
  298. guest = NULL;
  299. struct found_device *f;
  300. for(f = detected_devices; f ; f = f->next) {
  301. if(f->host_device_hash == hash && !strcmp(host, f->host_device)) {
  302. if(guest && (!f->guest_device || !strcmp(f->host_device, f->guest_device))) {
  303. if(f->guest_device) freez((void *)f->guest_device);
  304. f->guest_device = strdupz(guest);
  305. }
  306. return;
  307. }
  308. }
  309. f = mallocz(sizeof(struct found_device));
  310. f->host_device = strdupz(host);
  311. f->host_device_hash = hash;
  312. f->guest_device = (guest)?strdupz(guest):NULL;
  313. f->next = detected_devices;
  314. detected_devices = f;
  315. }
  316. int send_devices(void) {
  317. int found = 0;
  318. struct found_device *f;
  319. for(f = detected_devices; f ; f = f->next) {
  320. found++;
  321. printf("%s %s\n", f->host_device, (f->guest_device)?f->guest_device:f->host_device);
  322. }
  323. return found;
  324. }
  325. // ----------------------------------------------------------------------------
  326. // this function should be called only **ONCE**
  327. // also it has to be the **LAST** to be called
  328. // since it switches namespaces, so after this call, everything is different!
  329. void detect_veth_interfaces(pid_t pid) {
  330. struct iface *cgroup = NULL;
  331. struct iface *host, *h, *c;
  332. host = read_proc_net_dev("host", netdata_configured_host_prefix);
  333. if(!host) {
  334. errno = 0;
  335. collector_error("cannot read host interface list.");
  336. goto cleanup;
  337. }
  338. if(!eligible_ifaces(host)) {
  339. errno = 0;
  340. collector_info("there are no double-linked host interfaces available.");
  341. goto cleanup;
  342. }
  343. if(switch_namespace(netdata_configured_host_prefix, pid)) {
  344. errno = 0;
  345. collector_error("cannot switch to the namespace of pid %u", (unsigned int) pid);
  346. goto cleanup;
  347. }
  348. #ifdef NETDATA_INTERNAL_CHECKS
  349. collector_info("switched to namespaces of pid %d", pid);
  350. #endif
  351. cgroup = read_proc_net_dev("cgroup", NULL);
  352. if(!cgroup) {
  353. errno = 0;
  354. collector_error("cannot read cgroup interface list.");
  355. goto cleanup;
  356. }
  357. if(!eligible_ifaces(cgroup)) {
  358. errno = 0;
  359. collector_error("there are not double-linked cgroup interfaces available.");
  360. goto cleanup;
  361. }
  362. unsigned int host_dev_num = calc_num_ifaces(host);
  363. unsigned int cgroup_dev_num = calc_num_ifaces(cgroup);
  364. // host ifaces == guest ifaces => we are still in the host namespace
  365. // and we can't really identify which ifaces belong to the cgroup (e.g. Proxmox VM).
  366. if (host_dev_num == cgroup_dev_num) {
  367. unsigned int m = 0;
  368. for (h = host; h; h = h->next) {
  369. for (c = cgroup; c; c = c->next) {
  370. if (h->ifindex == c->ifindex && h->iflink == c->iflink) {
  371. m++;
  372. break;
  373. }
  374. }
  375. }
  376. if (host_dev_num == m) {
  377. goto cleanup;
  378. }
  379. }
  380. for(h = host; h ; h = h->next) {
  381. if(iface_is_eligible(h)) {
  382. for (c = cgroup; c; c = c->next) {
  383. if(iface_is_eligible(c) && h->ifindex == c->iflink && h->iflink == c->ifindex) {
  384. add_device(h->device, c->device);
  385. }
  386. }
  387. }
  388. }
  389. cleanup:
  390. free_host_ifaces(cgroup);
  391. free_host_ifaces(host);
  392. }
  393. // ----------------------------------------------------------------------------
  394. // call the external helper
  395. #define CGROUP_NETWORK_INTERFACE_MAX_LINE 2048
  396. void call_the_helper(pid_t pid, const char *cgroup) {
  397. if(setresuid(0, 0, 0) == -1)
  398. collector_error("setresuid(0, 0, 0) failed.");
  399. char command[CGROUP_NETWORK_INTERFACE_MAX_LINE + 1];
  400. if(cgroup)
  401. snprintfz(command, CGROUP_NETWORK_INTERFACE_MAX_LINE, "exec " PLUGINS_DIR "/cgroup-network-helper.sh --cgroup '%s'", cgroup);
  402. else
  403. snprintfz(command, CGROUP_NETWORK_INTERFACE_MAX_LINE, "exec " PLUGINS_DIR "/cgroup-network-helper.sh --pid %d", pid);
  404. collector_info("running: %s", command);
  405. pid_t cgroup_pid;
  406. FILE *fp_child_input, *fp_child_output;
  407. if(cgroup) {
  408. (void)netdata_popen_raw_default_flags(&cgroup_pid, environment, &fp_child_input, &fp_child_output, PLUGINS_DIR "/cgroup-network-helper.sh", "--cgroup", cgroup);
  409. }
  410. else {
  411. char buffer[100];
  412. snprintfz(buffer, sizeof(buffer) - 1, "%d", pid);
  413. (void)netdata_popen_raw_default_flags(&cgroup_pid, environment, &fp_child_input, &fp_child_output, PLUGINS_DIR "/cgroup-network-helper.sh", "--pid", buffer);
  414. }
  415. if(fp_child_output) {
  416. char buffer[CGROUP_NETWORK_INTERFACE_MAX_LINE + 1];
  417. char *s;
  418. while((s = fgets(buffer, CGROUP_NETWORK_INTERFACE_MAX_LINE, fp_child_output))) {
  419. trim(s);
  420. if(*s && *s != '\n') {
  421. char *t = s;
  422. while(*t && *t != ' ') t++;
  423. if(*t == ' ') {
  424. *t = '\0';
  425. t++;
  426. }
  427. if(!*s || !*t) continue;
  428. add_device(s, t);
  429. }
  430. }
  431. netdata_pclose(fp_child_input, fp_child_output, cgroup_pid);
  432. }
  433. else
  434. collector_error("cannot execute cgroup-network helper script: %s", command);
  435. }
  436. int is_valid_path_symbol(char c) {
  437. switch(c) {
  438. case '/': // path separators
  439. case '\\': // needed for virsh domains \x2d1\x2dname
  440. case ' ': // space
  441. case '-': // hyphen
  442. case '_': // underscore
  443. case '.': // dot
  444. case ',': // comma
  445. return 1;
  446. default:
  447. return 0;
  448. }
  449. }
  450. // we will pass this path a shell script running as root
  451. // so, we need to make sure the path will be valid
  452. // and will not include anything that could allow
  453. // the caller use shell expansion for gaining escalated
  454. // privileges.
  455. int verify_path(const char *path) {
  456. struct stat sb;
  457. char c;
  458. const char *s = path;
  459. while((c = *s++)) {
  460. if(!( isalnum(c) || is_valid_path_symbol(c) )) {
  461. collector_error("invalid character in path '%s'", path);
  462. return -1;
  463. }
  464. }
  465. if(strstr(path, "\\") && !strstr(path, "\\x")) {
  466. collector_error("invalid escape sequence in path '%s'", path);
  467. return 1;
  468. }
  469. if(strstr(path, "/../")) {
  470. collector_error("invalid parent path sequence detected in '%s'", path);
  471. return 1;
  472. }
  473. if(path[0] != '/') {
  474. collector_error("only absolute path names are supported - invalid path '%s'", path);
  475. return -1;
  476. }
  477. if (stat(path, &sb) == -1) {
  478. collector_error("cannot stat() path '%s'", path);
  479. return -1;
  480. }
  481. if((sb.st_mode & S_IFMT) != S_IFDIR) {
  482. collector_error("path '%s' is not a directory", path);
  483. return -1;
  484. }
  485. return 0;
  486. }
  487. /*
  488. char *fix_path_variable(void) {
  489. const char *path = getenv("PATH");
  490. if(!path || !*path) return 0;
  491. char *p = strdupz(path);
  492. char *safe_path = callocz(1, strlen(p) + strlen("PATH=") + 1);
  493. strcpy(safe_path, "PATH=");
  494. int added = 0;
  495. char *ptr = p;
  496. while(ptr && *ptr) {
  497. char *s = strsep(&ptr, ":");
  498. if(s && *s) {
  499. if(verify_path(s) == -1) {
  500. collector_error("the PATH variable includes an invalid path '%s' - removed it.", s);
  501. }
  502. else {
  503. collector_info("the PATH variable includes a valid path '%s'.", s);
  504. if(added) strcat(safe_path, ":");
  505. strcat(safe_path, s);
  506. added++;
  507. }
  508. }
  509. }
  510. collector_info("unsafe PATH: '%s'.", path);
  511. collector_info(" safe PATH: '%s'.", safe_path);
  512. freez(p);
  513. return safe_path;
  514. }
  515. */
  516. // ----------------------------------------------------------------------------
  517. // main
  518. void usage(void) {
  519. fprintf(stderr, "%s [ -p PID | --pid PID | --cgroup /path/to/cgroup ]\n", program_name);
  520. exit(1);
  521. }
  522. int main(int argc, char **argv) {
  523. pid_t pid = 0;
  524. program_version = VERSION;
  525. clocks_init();
  526. nd_log_initialize_for_external_plugins("cgroup-network");
  527. // since cgroup-network runs as root, prevent it from opening symbolic links
  528. procfile_open_flags = O_RDONLY|O_NOFOLLOW;
  529. // ------------------------------------------------------------------------
  530. // make sure NETDATA_HOST_PREFIX is safe
  531. netdata_configured_host_prefix = getenv("NETDATA_HOST_PREFIX");
  532. if(verify_netdata_host_prefix() == -1) exit(1);
  533. if(netdata_configured_host_prefix[0] != '\0' && verify_path(netdata_configured_host_prefix) == -1)
  534. fatal("invalid NETDATA_HOST_PREFIX '%s'", netdata_configured_host_prefix);
  535. // ------------------------------------------------------------------------
  536. // build a safe environment for our script
  537. // the first environment variable is a fixed PATH=
  538. snprintfz(env_netdata_host_prefix, sizeof(env_netdata_host_prefix) - 1, "NETDATA_HOST_PREFIX=%s", netdata_configured_host_prefix);
  539. char *s;
  540. s = getenv("NETDATA_LOG_METHOD");
  541. snprintfz(env_netdata_log_method, sizeof(env_netdata_log_method) - 1, "NETDATA_LOG_METHOD=%s", nd_log_method_for_external_plugins(s));
  542. s = getenv("NETDATA_LOG_FORMAT");
  543. if (s)
  544. snprintfz(env_netdata_log_format, sizeof(env_netdata_log_format) - 1, "NETDATA_LOG_FORMAT=%s", s);
  545. s = getenv("NETDATA_LOG_LEVEL");
  546. if (s)
  547. snprintfz(env_netdata_log_level, sizeof(env_netdata_log_level) - 1, "NETDATA_LOG_LEVEL=%s", s);
  548. // ------------------------------------------------------------------------
  549. if(argc == 2 && (!strcmp(argv[1], "version") || !strcmp(argv[1], "-version") || !strcmp(argv[1], "--version") || !strcmp(argv[1], "-v") || !strcmp(argv[1], "-V"))) {
  550. fprintf(stderr, "cgroup-network %s\n", VERSION);
  551. exit(0);
  552. }
  553. if(argc != 3)
  554. usage();
  555. int arg = 1;
  556. int helper = 1;
  557. if (getenv("KUBERNETES_SERVICE_HOST") != NULL && getenv("KUBERNETES_SERVICE_PORT") != NULL)
  558. helper = 0;
  559. if(!strcmp(argv[arg], "-p") || !strcmp(argv[arg], "--pid")) {
  560. pid = atoi(argv[arg+1]);
  561. if(pid <= 0) {
  562. errno = 0;
  563. collector_error("Invalid pid %d given", (int) pid);
  564. return 2;
  565. }
  566. if(helper) call_the_helper(pid, NULL);
  567. }
  568. else if(!strcmp(argv[arg], "--cgroup")) {
  569. char *cgroup = argv[arg+1];
  570. if(verify_path(cgroup) == -1) {
  571. collector_error("cgroup '%s' does not exist or is not valid.", cgroup);
  572. return 1;
  573. }
  574. pid = read_pid_from_cgroup(cgroup);
  575. if(helper) call_the_helper(pid, cgroup);
  576. if(pid <= 0 && !detected_devices) {
  577. errno = 0;
  578. collector_error("Cannot find a cgroup PID from cgroup '%s'", cgroup);
  579. }
  580. }
  581. else
  582. usage();
  583. if(pid > 0)
  584. detect_veth_interfaces(pid);
  585. int found = send_devices();
  586. if(found <= 0) return 1;
  587. return 0;
  588. }