~ubuntu-branches/ubuntu/utopic/dovecot/utopic-proposed

« back to all changes in this revision

Viewing changes to pigeonhole/src/plugins/sieve-extprograms/script-client-local.c

  • Committer: Package Import Robot
  • Author(s): James Page
  • Date: 2014-01-08 09:35:49 UTC
  • mfrom: (4.1.35 sid)
  • Revision ID: package-import@ubuntu.com-20140108093549-i72o93pux8p0dlaf
Tags: 1:2.2.9-1ubuntu1
* Merge from Debian unstable, remaining changes:
  + Add mail-stack-delivery package:
    - Update d/rules
    - d/control: convert existing dovecot-postfix package to a dummy
      package and add new mail-stack-delivery package.
    - Update maintainer scripts.
    - Rename d/dovecot-postfix.* to debian/mail-stack-delivery.*
    - d/mail-stack-delivery.preinst: Move previously installed backups and
      config files to a new package namespace.
    - d/mail-stack-delivery.prerm: Added to handle downgrades.
  + Use Snakeoil SSL certificates by default:
    - d/control: Depend on ssl-cert.
    - d/dovecot-core.postinst: Relax grep for SSL_* a bit.
  + Add autopkgtest to debian/tests/*.
  + Add ufw integration:
    - d/dovecot-core.ufw.profile: new ufw profile.
    - d/rules: install profile in dovecot-core.
    - d/control: dovecot-core - suggest ufw.
  + d/dovecot-core.dirs: Added usr/share/doc/dovecot-core
  + Add apport hook:
    - d/rules, d/source_dovecot.py
  + Add upstart job:
    - d/rules, d/dovecot-core.dovecot.upstart, d/control,
      d/dovecot-core.dirs, dovecot-imapd.{postrm, postinst, prerm},
      d/dovecot-pop3d.{postinst, postrm, prerm}.
      d/mail-stack-deliver.postinst: Convert init script to upstart.
  + Use the autotools-dev dh addon to update config.guess/config.sub for
    arm64.
* Dropped changes, included in Debian:
  - Update Dovecot name to reflect distribution in login greeting.
  - Update Drac plugin for >= 2.0.0 support.
* d/control: Drop dovecot-postfix package as its no longer required.

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/* Copyright (c) 2002-2013 Pigeonhole authors, see the included COPYING file
 
2
 */
 
3
 
 
4
#include "lib.h"
 
5
#include "lib-signals.h"
 
6
#include "env-util.h"
 
7
#include "execv-const.h"
 
8
#include "array.h"
 
9
#include "net.h"
 
10
#include "istream.h"
 
11
#include "ostream.h"
 
12
 
 
13
#include "script-client-private.h"
 
14
 
 
15
#include <sys/types.h>
 
16
#include <sys/socket.h>
 
17
#include <sys/stat.h>
 
18
#include <sys/wait.h>
 
19
#include <unistd.h>
 
20
#include <fcntl.h>
 
21
 
 
22
 
 
23
struct script_client_local {
 
24
        struct script_client client;
 
25
 
 
26
        pid_t pid;
 
27
};
 
28
 
 
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)
 
32
{
 
33
        ARRAY_TYPE(const_string) exec_args;
 
34
 
 
35
        if ( in_fd < 0 ) {
 
36
                in_fd = open("/dev/null", O_RDONLY);
 
37
 
 
38
                if ( in_fd == -1 )
 
39
                        i_fatal("open(/dev/null) failed: %m");
 
40
        }
 
41
 
 
42
        if ( out_fd < 0 ) {
 
43
                out_fd = open("/dev/null", O_WRONLY);
 
44
 
 
45
                if ( out_fd == -1 )
 
46
                        i_fatal("open(/dev/null) failed: %m");
 
47
        }
 
48
 
 
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");
 
53
 
 
54
        /* Close all fds */
 
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");
 
59
 
 
60
        t_array_init(&exec_args, 16);
 
61
        array_append(&exec_args, &bin_path, 1);
 
62
        if ( args != NULL ) {
 
63
                for (; *args != NULL; args++)
 
64
                        array_append(&exec_args, args, 1);
 
65
        }
 
66
        (void)array_append_space(&exec_args);
 
67
 
 
68
        env_clean();
 
69
        if ( envs != NULL ) {
 
70
                for (; *envs != NULL; envs++)
 
71
                        env_put(*envs);
 
72
        }
 
73
 
 
74
        args = array_idx(&exec_args, 0);
 
75
        execvp_const(args[0], args);
 
76
}
 
77
 
 
78
static int script_client_local_connect
 
79
(struct script_client *sclient)
 
80
{
 
81
        struct script_client_local *slclient = 
 
82
                (struct script_client_local *) sclient;
 
83
        int fd[2] = { -1, -1 };
 
84
 
 
85
        if ( sclient->input != NULL || sclient->output != NULL ) {
 
86
                if ( socketpair(AF_UNIX, SOCK_STREAM, 0, fd) < 0 ) {
 
87
                        i_error("socketpair() failed: %m");
 
88
                        return -1;
 
89
                }
 
90
        }
 
91
 
 
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");
 
96
                }
 
97
                if ( fd[1] >= 0 && close(fd[1]) < 0 ) {
 
98
                        i_error("close(pipe_fd[1]) failed: %m");
 
99
                }
 
100
                return -1;
 
101
        }
 
102
 
 
103
        if ( slclient->pid == 0 ) {
 
104
                unsigned int count;
 
105
                const char *const *envs = NULL;
 
106
 
 
107
                /* child */
 
108
                if ( fd[1] >= 0 && close(fd[1]) < 0 ) {
 
109
                        i_error("close(pipe_fd[1]) failed: %m");
 
110
                }
 
111
 
 
112
                if ( array_is_created(&sclient->envs) )
 
113
                        envs = array_get(&sclient->envs, &count);
 
114
 
 
115
                exec_child(sclient->path, sclient->args, envs,
 
116
                        ( sclient->input != NULL ? fd[0] : -1 ),
 
117
                        ( sclient->output != NULL ? fd[0] : -1 ));
 
118
                i_unreached();
 
119
        }
 
120
 
 
121
        /* parent */
 
122
        if ( fd[0] >= 0 && close(fd[0]) < 0 ) {
 
123
                i_error("close(pipe_fd[0]) failed: %m");
 
124
        }
 
125
 
 
126
        if ( fd[1] >= 0 ) {
 
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 );
 
130
        }
 
131
        script_client_init_streams(sclient);
 
132
        return script_client_script_connected(sclient);
 
133
}
 
134
 
 
135
static int script_client_local_close_output(struct script_client *sclient)
 
136
{
 
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);
 
140
                return -1;
 
141
        }
 
142
        sclient->fd_out = -1;
 
143
        return 1;
 
144
}
 
145
 
 
146
static int script_client_local_disconnect
 
147
(struct script_client *sclient, bool force)
 
148
{
 
149
        struct script_client_local *slclient = 
 
150
                (struct script_client_local *) sclient;
 
151
        pid_t pid = slclient->pid;
 
152
        time_t runtime, timeout = 0;
 
153
        int status;
 
154
        
 
155
        i_assert( pid >= 0 );
 
156
        slclient->pid = -1;
 
157
 
 
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;
 
163
 
 
164
        if ( sclient->debug ) {
 
165
                i_debug("waiting for program `%s' to finish after %llu seconds",
 
166
                        sclient->path, (unsigned long long int)runtime);
 
167
        }
 
168
 
 
169
        /* Wait for child to exit */
 
170
        force = force ||
 
171
                (timeout == 0 && sclient->set->input_idle_timeout_secs > 0);
 
172
        if ( !force )
 
173
                alarm(timeout);
 
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);
 
178
                        return -1;
 
179
                }
 
180
 
 
181
                /* Timed out */
 
182
                force = TRUE;
 
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);
 
189
                }
 
190
 
 
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);
 
195
                        return -1;
 
196
                } 
 
197
                        
 
198
                /* Wait for it to die (give it some more time) */
 
199
                alarm(5);
 
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); 
 
204
                                return -1;
 
205
                        }
 
206
 
 
207
                        /* Timed out again */
 
208
                        if ( sclient->debug ) {
 
209
                                i_debug("program `%s' execution timed out: sending KILL signal",
 
210
                                        sclient->path);
 
211
                        }
 
212
 
 
213
                        /* Kill it brutally now */
 
214
                        if ( kill(pid, SIGKILL) < 0 ) {
 
215
                                i_error("failed to send SIGKILL signal to program `%s'",
 
216
                                        sclient->path);
 
217
                                return -1;
 
218
                        }
 
219
 
 
220
                        /* Now it will die immediately */
 
221
                        if ( waitpid(pid, &status, 0) < 0 ) {
 
222
                                i_error("waitpid(%s) failed: %m", sclient->path);
 
223
                                return -1;
 
224
                        }
 
225
                }
 
226
        }       
 
227
        
 
228
        /* Evaluate child exit status */
 
229
        sclient->exit_code = -1;
 
230
        if ( WIFEXITED(status) ) {
 
231
                /* Exited */
 
232
                int exit_code = WEXITSTATUS(status);
 
233
                                
 
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;
 
238
                        return 0;
 
239
                }
 
240
 
 
241
                sclient->exit_code = 1;
 
242
                return 1;       
 
243
 
 
244
        } else if ( WIFSIGNALED(status) ) {
 
245
                /* Killed with a signal */
 
246
                
 
247
                if ( force ) {
 
248
                        i_error("program `%s' was forcibly terminated with signal %d",
 
249
                                sclient->path, WTERMSIG(status));
 
250
                } else {
 
251
                        i_error("program `%s' terminated abnormally, signal %d",
 
252
                                sclient->path, WTERMSIG(status));
 
253
                }
 
254
                return -1;
 
255
 
 
256
        } else if ( WIFSTOPPED(status) ) {
 
257
                /* Stopped */
 
258
                i_error("program `%s' stopped, signal %d",
 
259
                        sclient->path, WSTOPSIG(status));
 
260
                return -1;
 
261
        } 
 
262
 
 
263
        /* Something else */
 
264
        i_error("program `%s' terminated abnormally, return status %d",
 
265
                sclient->path, status);
 
266
        return -1;
 
267
}
 
268
 
 
269
static void script_client_local_failure
 
270
(struct script_client *sclient, enum script_client_error error)
 
271
{
 
272
        switch ( 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);
 
276
                break;
 
277
        default:
 
278
                break;
 
279
        }
 
280
}
 
281
 
 
282
struct script_client *script_client_local_create
 
283
(const char *bin_path, const char *const *args,
 
284
        const struct script_client_settings *set)
 
285
{
 
286
        struct script_client_local *sclient;
 
287
        pool_t pool;
 
288
 
 
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;
 
296
        sclient->pid = -1;
 
297
 
 
298
        return &sclient->client;
 
299
}
 
300