@cryptotaxi247 / netdata-1 / commits / 0707fbaaa

Reimplemented mypopen() function family (#6339)

* Reimplementd mypopen() family based on posix_spawn() instead of fork() and execl(). The problem with fork() is that if the parent process has a large address space then the fork() may fail due to insufficient free memory in the system if memory overcommit is not enabled. posix_spawn() does not call fork() and does not suffer from this problem. It is also more portable than vfork() which is deprecated and clone() which is linux only. * Removed dead code

Markos Fountoulakis committed Jul 9, 2019 at 07:25 UTC 0707fbaaac974dc63f9d5a1b9298b0d09b1c95da
3 files changed +72 -104
collectors/tc.plugin/plugin_tc.c
-14
@@ -874,9 +874,6 @@ void *tc_main(void *ptr) {
874 uint32_t SETDEVICEGROUP_HASH = simple_hash("SETDEVICEGROUP");
875 uint32_t SETCLASSNAME_HASH = simple_hash("SETCLASSNAME");
876 uint32_t WORKTIME_HASH = simple_hash("WORKTIME");
877 -#ifdef DETACH_PLUGINS_FROM_NETDATA
878 - uint32_t MYPID_HASH = simple_hash("MYPID");
879 -#endif
877 uint32_t first_hash;
878
879 snprintfz(command, TC_LINE_MAX, "%s/tc-qos-helper.sh", netdata_configured_primary_plugins_dir);
@@ -1119,17 +1116,6 @@ void *tc_main(void *ptr) {
1116 rrdset_done(sttime);
1117
1118 }
1122 -#ifdef DETACH_PLUGINS_FROM_NETDATA
1123 - else if(unlikely(first_hash == MYPID_HASH && (strcmp(words[0], "MYPID") == 0))) {
1124 - // debug(D_TC_LOOP, "MYPID line '%s'", words[1]);
1125 - char *id = words[1];
1126 - pid_t pid = atol(id);
1127 -
1128 - if(likely(pid)) tc_child_pid = pid;
1129 -
1130 - debug(D_TC_LOOP, "TC: Child PID is %d.", tc_child_pid);
1131 - }
1132 -#endif
1119 //else {
1120 // debug(D_TC_LOOP, "IGNORED line");
1121 //}
libnetdata/libnetdata.h
+1
@@ -81,6 +81,7 @@
81 #include <time.h>
82 #include <unistd.h>
83 #include <uuid/uuid.h>
84 +#include <spawn.h>
85
86 #ifdef HAVE_NETINET_IN_H
87 #include <netinet/in.h>
libnetdata/popen/popen.c
+71 -90
@@ -45,110 +45,91 @@ static void mypopen_del(FILE *fp) {
45 #define PIPE_READ 0
46 #define PIPE_WRITE 1
47
48 -FILE *mypopen(const char *command, volatile pid_t *pidptr)
49 -{
50 - int pipefd[2];
51 -
52 - if(pipe(pipefd) == -1) return NULL;
48 +static inline FILE *custom_popene(const char *command, volatile pid_t *pidptr, char **env) {
49 + FILE *fp;
50 + int pipefd[2], error;
51 + pid_t pid;
52 + char *const spawn_argv[] = {
53 + "sh",
54 + "-c",
55 + (char *)command,
56 + NULL
57 + };
58 + posix_spawnattr_t attr;
59 + posix_spawn_file_actions_t fa;
60
54 - int pid = fork();
55 - if(pid == -1) {
56 - close(pipefd[PIPE_READ]);
57 - close(pipefd[PIPE_WRITE]);
61 + if(pipe(pipefd) == -1)
62 return NULL;
63 + if ((fp = fdopen(pipefd[PIPE_READ], "r")) == NULL) {
64 + goto error_after_pipe;
65 }
60 - if(pid != 0) {
61 - // the parent
62 - *pidptr = pid;
63 - close(pipefd[PIPE_WRITE]);
64 - FILE *fp = fdopen(pipefd[PIPE_READ], "r");
65 - /*mypopen_add(fp, pid);*/
66 - return(fp);
67 - }
68 - // the child
66
70 - // close all files
67 + // Mark all files to be closed by the exec() stage of posix_spawn()
68 int i;
69 for(i = (int) (sysconf(_SC_OPEN_MAX) - 1); i >= 0; i--)
73 - if(i != STDIN_FILENO && i != STDERR_FILENO && i != pipefd[PIPE_WRITE]) close(i);
74 -
75 - // move the pipe to stdout
76 - if(pipefd[PIPE_WRITE] != STDOUT_FILENO) {
77 - dup2(pipefd[PIPE_WRITE], STDOUT_FILENO);
78 - close(pipefd[PIPE_WRITE]);
70 + if(i != STDIN_FILENO && i != STDERR_FILENO)
71 + fcntl(i, F_SETFD, FD_CLOEXEC);
72 +
73 + if (!posix_spawn_file_actions_init(&fa)) {
74 + // move the pipe to stdout in the child
75 + if (posix_spawn_file_actions_adddup2(&fa, pipefd[PIPE_WRITE], STDOUT_FILENO)) {
76 + error("posix_spawn_file_actions_adddup2() failed");
77 + goto error_after_posix_spawn_file_actions_init;
78 + }
79 + } else {
80 + error("posix_spawn_file_actions_init() failed.");
81 + goto error_after_pipe;
82 }
80 -
81 -#ifdef DETACH_PLUGINS_FROM_NETDATA
82 - // this was an attempt to detach the child and use the suspend mode charts.d
83 - // unfortunatelly it does not work as expected.
84 -
85 - // fork again to become session leader
86 - pid = fork();
87 - if(pid == -1)
88 - error("pre-execution of command '%s' on pid %d: Cannot fork 2nd time.", command, getpid());
89 -
90 - if(pid != 0) {
91 - // the parent
92 - exit(0);
83 + if (!(error = posix_spawnattr_init(&attr))) {
84 + // reset all signals in the child
85 + sigset_t mask;
86 +
87 + if (posix_spawnattr_setflags(&attr, POSIX_SPAWN_SETSIGMASK | POSIX_SPAWN_SETSIGDEF))
88 + error("posix_spawnattr_setflags() failed.");
89 + sigemptyset(&mask);
90 + if (posix_spawnattr_setsigmask(&attr, &mask))
91 + error("posix_spawnattr_setsigmask() failed.");
92 + } else {
93 + error("posix_spawnattr_init() failed.");
94 }
94 -
95 - // set a new process group id for just this child
96 - if( setpgid(0, 0) != 0 )
97 - error("pre-execution of command '%s' on pid %d: Cannot set a new process group.", command, getpid());
98 -
99 - if( getpgid(0) != getpid() )
100 - error("pre-execution of command '%s' on pid %d: Cannot set a new process group. Process group set is incorrect. Expected %d, found %d", command, getpid(), getpid(), getpgid(0));
101 -
102 - if( setsid() != 0 )
103 - error("pre-execution of command '%s' on pid %d: Cannot set session id.", command, getpid());
104 -
105 - fprintf(stdout, "MYPID %d\n", getpid());
106 - fflush(NULL);
107 -#endif
108 -
109 - // reset all signals
110 - signals_unblock();
111 - signals_reset();
112 -
113 - debug(D_CHILDS, "executing command: '%s' on pid %d.", command, getpid());
114 - execl("/bin/sh", "sh", "-c", command, NULL);
115 - exit(1);
116 -}
117 -
118 -FILE *mypopene(const char *command, volatile pid_t *pidptr, char **env) {
119 - int pipefd[2];
120 -
121 - if(pipe(pipefd) == -1)
122 - return NULL;
123 -
124 - int pid = fork();
125 - if(pid == -1) {
95 + if (!posix_spawn(&pid, "/bin/sh", &fa, &attr, spawn_argv, env)) {
96 + *pidptr = pid;
97 + debug(D_CHILDS, "Spawned command: '%s' on pid %d from parent pid %d.", command, pid, getpid());
98 + } else {
99 + error("Failed to spawn command: '%s' from parent pid %d.", command, getpid());
100 close(pipefd[PIPE_READ]);
127 - close(pipefd[PIPE_WRITE]);
128 - return NULL;
101 + fp = NULL;
102 }
130 - if(pid != 0) {
131 - // the parent
132 - *pidptr = pid;
133 - close(pipefd[PIPE_WRITE]);
134 - FILE *fp = fdopen(pipefd[PIPE_READ], "r");
135 - return(fp);
103 + close(pipefd[PIPE_WRITE]);
104 +
105 + if (!error) {
106 + // posix_spawnattr_init() succeeded
107 + if (posix_spawnattr_destroy(&attr))
108 + error("posix_spawnattr_destroy");
109 }
137 - // the child
110 + if (posix_spawn_file_actions_destroy(&fa))
111 + error("posix_spawn_file_actions_destroy");
112 +
113 + return fp;
114 +
115 +error_after_posix_spawn_file_actions_init:
116 + if (posix_spawn_file_actions_destroy(&fa))
117 + error("posix_spawn_file_actions_destroy");
118 +error_after_pipe:
119 + close(pipefd[PIPE_READ]);
120 + close(pipefd[PIPE_WRITE]);
121 + return NULL;
122 +}
123
139 - // close all files
140 - int i;
141 - for(i = (int) (sysconf(_SC_OPEN_MAX) - 1); i >= 0; i--)
142 - if(i != STDIN_FILENO && i != STDERR_FILENO && i != pipefd[PIPE_WRITE]) close(i);
124 +// See man environ
125 +extern char **environ;
126
144 - // move the pipe to stdout
145 - if(pipefd[PIPE_WRITE] != STDOUT_FILENO) {
146 - dup2(pipefd[PIPE_WRITE], STDOUT_FILENO);
147 - close(pipefd[PIPE_WRITE]);
148 - }
127 +FILE *mypopen(const char *command, volatile pid_t *pidptr) {
128 + return custom_popene(command, pidptr, environ);
129 +}
130
150 - execle("/bin/sh", "sh", "-c", command, NULL, env);
151 - exit(1);
131 +FILE *mypopene(const char *command, volatile pid_t *pidptr, char **env) {
132 + return custom_popene(command, pidptr, env);
133 }
134
135 int mypclose(FILE *fp, pid_t pid) {