1
/* Copyright (c) 2002-2013 Pigeonhole authors, see the included COPYING file
5
#include "lib-signals.h"
7
#include "execv-const.h"
13
#include "script-client-private.h"
15
#include <sys/types.h>
16
#include <sys/socket.h>
23
struct script_client_local {
24
struct script_client client;
29
static void exec_child
30
(const char *bin_path, const char *const *args, const char *const *envs,
31
int in_fd, int out_fd)
33
ARRAY_TYPE(const_string) exec_args;
36
in_fd = open("/dev/null", O_RDONLY);
39
i_fatal("open(/dev/null) failed: %m");
43
out_fd = open("/dev/null", O_WRONLY);
46
i_fatal("open(/dev/null) failed: %m");
49
if ( dup2(in_fd, STDIN_FILENO) < 0 )
50
i_fatal("dup2(stdin) failed: %m");
51
if ( dup2(out_fd, STDOUT_FILENO) < 0 )
52
i_fatal("dup2(stdout) failed: %m");
55
if ( close(in_fd) < 0 )
56
i_error("close(in_fd) failed: %m");
57
if ( (out_fd != in_fd) && close(out_fd) < 0 )
58
i_error("close(out_fd) failed: %m");
60
t_array_init(&exec_args, 16);
61
array_append(&exec_args, &bin_path, 1);
63
for (; *args != NULL; args++)
64
array_append(&exec_args, args, 1);
66
(void)array_append_space(&exec_args);
70
for (; *envs != NULL; envs++)
74
args = array_idx(&exec_args, 0);
75
execvp_const(args[0], args);
78
static int script_client_local_connect
79
(struct script_client *sclient)
81
struct script_client_local *slclient =
82
(struct script_client_local *) sclient;
83
int fd[2] = { -1, -1 };
85
if ( sclient->input != NULL || sclient->output != NULL ) {
86
if ( socketpair(AF_UNIX, SOCK_STREAM, 0, fd) < 0 ) {
87
i_error("socketpair() failed: %m");
92
if ( (slclient->pid = fork()) == (pid_t)-1 ) {
93
i_error("fork() failed: %m");
94
if ( fd[0] >= 0 && close(fd[0]) < 0 ) {
95
i_error("close(pipe_fd[0]) failed: %m");
97
if ( fd[1] >= 0 && close(fd[1]) < 0 ) {
98
i_error("close(pipe_fd[1]) failed: %m");
103
if ( slclient->pid == 0 ) {
105
const char *const *envs = NULL;
108
if ( fd[1] >= 0 && close(fd[1]) < 0 ) {
109
i_error("close(pipe_fd[1]) failed: %m");
112
if ( array_is_created(&sclient->envs) )
113
envs = array_get(&sclient->envs, &count);
115
exec_child(sclient->path, sclient->args, envs,
116
( sclient->input != NULL ? fd[0] : -1 ),
117
( sclient->output != NULL ? fd[0] : -1 ));
122
if ( fd[0] >= 0 && close(fd[0]) < 0 ) {
123
i_error("close(pipe_fd[0]) failed: %m");
127
net_set_nonblock(fd[1], TRUE);
128
sclient->fd_in = ( sclient->output != NULL ? fd[1] : -1 );
129
sclient->fd_out = ( sclient->input != NULL ? fd[1] : -1 );
131
script_client_init_streams(sclient);
132
return script_client_script_connected(sclient);
135
static int script_client_local_close_output(struct script_client *sclient)
137
/* Shutdown output; script stdin will get EOF */
138
if ( sclient->fd_out >= 0 && shutdown(sclient->fd_out, SHUT_WR) < 0 ) {
139
i_error("shutdown(%s, SHUT_WR) failed: %m", sclient->path);
142
sclient->fd_out = -1;
146
static int script_client_local_disconnect
147
(struct script_client *sclient, bool force)
149
struct script_client_local *slclient =
150
(struct script_client_local *) sclient;
151
pid_t pid = slclient->pid;
152
time_t runtime, timeout = 0;
155
i_assert( pid >= 0 );
158
/* Calculate timeout */
159
runtime = ioloop_time - sclient->start_time;
160
if ( !force && sclient->set->input_idle_timeout_secs > 0 &&
161
runtime < (time_t)sclient->set->input_idle_timeout_secs )
162
timeout = sclient->set->input_idle_timeout_secs - runtime;
164
if ( sclient->debug ) {
165
i_debug("waiting for program `%s' to finish after %llu seconds",
166
sclient->path, (unsigned long long int)runtime);
169
/* Wait for child to exit */
171
(timeout == 0 && sclient->set->input_idle_timeout_secs > 0);
174
if ( force || waitpid(pid, &status, 0) < 0 ) {
175
if ( !force && errno != EINTR ) {
176
i_error("waitpid(%s) failed: %m", sclient->path);
177
(void)kill(pid, SIGKILL);
183
if ( sclient->error == SCRIPT_CLIENT_ERROR_NONE )
184
sclient->error = SCRIPT_CLIENT_ERROR_RUN_TIMEOUT;
185
if ( sclient->debug ) {
186
i_debug("program `%s' execution timed out after %llu seconds: "
187
"sending TERM signal", sclient->path,
188
(unsigned long long int)sclient->set->input_idle_timeout_secs);
191
/* Kill child gently first */
192
if ( kill(pid, SIGTERM) < 0 ) {
193
i_error("failed to send SIGTERM signal to program `%s'", sclient->path);
194
(void)kill(pid, SIGKILL);
198
/* Wait for it to die (give it some more time) */
200
if ( waitpid(pid, &status, 0) < 0 ) {
201
if ( errno != EINTR ) {
202
i_error("waitpid(%s) failed: %m", sclient->path);
203
(void)kill(pid, SIGKILL);
207
/* Timed out again */
208
if ( sclient->debug ) {
209
i_debug("program `%s' execution timed out: sending KILL signal",
213
/* Kill it brutally now */
214
if ( kill(pid, SIGKILL) < 0 ) {
215
i_error("failed to send SIGKILL signal to program `%s'",
220
/* Now it will die immediately */
221
if ( waitpid(pid, &status, 0) < 0 ) {
222
i_error("waitpid(%s) failed: %m", sclient->path);
228
/* Evaluate child exit status */
229
sclient->exit_code = -1;
230
if ( WIFEXITED(status) ) {
232
int exit_code = WEXITSTATUS(status);
234
if ( exit_code != 0 ) {
235
i_info("program `%s' terminated with non-zero exit code %d",
236
sclient->path, exit_code);
237
sclient->exit_code = 0;
241
sclient->exit_code = 1;
244
} else if ( WIFSIGNALED(status) ) {
245
/* Killed with a signal */
248
i_error("program `%s' was forcibly terminated with signal %d",
249
sclient->path, WTERMSIG(status));
251
i_error("program `%s' terminated abnormally, signal %d",
252
sclient->path, WTERMSIG(status));
256
} else if ( WIFSTOPPED(status) ) {
258
i_error("program `%s' stopped, signal %d",
259
sclient->path, WSTOPSIG(status));
264
i_error("program `%s' terminated abnormally, return status %d",
265
sclient->path, status);
269
static void script_client_local_failure
270
(struct script_client *sclient, enum script_client_error error)
273
case SCRIPT_CLIENT_ERROR_RUN_TIMEOUT:
274
i_error("program `%s' execution timed out (> %d secs)",
275
sclient->path, sclient->set->input_idle_timeout_secs);
282
struct script_client *script_client_local_create
283
(const char *bin_path, const char *const *args,
284
const struct script_client_settings *set)
286
struct script_client_local *sclient;
289
pool = pool_alloconly_create("script client local", 1024);
290
sclient = i_new(struct script_client_local, 1);
291
script_client_init(&sclient->client, pool, bin_path, args, set);
292
sclient->client.connect = script_client_local_connect;
293
sclient->client.close_output = script_client_local_close_output;
294
sclient->client.disconnect = script_client_local_disconnect;
295
sclient->client.failure = script_client_local_failure;
298
return &sclient->client;