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