~james-page/ubuntu/raring/dovecot/autopkgtest

« back to all changes in this revision

Viewing changes to src/util/script.c

  • Committer: Package Import Robot
  • Author(s): James Page
  • Date: 2012-06-11 11:11:54 UTC
  • mfrom: (1.15.2) (4.1.27 sid)
  • Revision ID: package-import@ubuntu.com-20120611111154-678cwbdj6ktgsv1h
Tags: 1:2.1.7-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/{control,rules}: enable PIE hardening.
  + 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.
  + d/control: Added Pre-Depends: dpkg (>= 1.15.6) to dovecot-dbg to support
    xz compression in Ubuntu.
  + d/control: Demote dovecot-common Recommends: to Suggests: to prevent
    install of extra packages on upgrade.
  + d/patches/dovecot-drac.patch: Updated with version for dovecot >= 2.0.0.
  + d/control: Drop B-D on systemd.
* Dropped changes:
  + d/patches/fix-racey-restart.patch: part of 2.1.x, no longer required.

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
/* Copyright (c) 2010-2011 Dovecot authors, see the included COPYING file */
 
1
/* Copyright (c) 2010-2012 Dovecot authors, see the included COPYING file */
2
2
 
3
3
#include "lib.h"
4
4
#include "array.h"
5
5
#include "str.h"
6
6
#include "env-util.h"
7
7
#include "execv-const.h"
 
8
#include "write-full.h"
8
9
#include "restrict-access.h"
9
10
#include "master-interface.h"
10
11
#include "master-service.h"
11
12
 
12
13
#include <stdlib.h>
13
14
#include <unistd.h>
 
15
#include <sys/types.h>
 
16
#include <sys/wait.h>
 
17
#include <sys/socket.h>
14
18
 
15
 
#define SCRIPT_MAJOR_VERSION 1
 
19
#define SCRIPT_MAJOR_VERSION 3
16
20
#define SCRIPT_READ_TIMEOUT_SECS 10
17
21
 
18
22
static ARRAY_TYPE(const_string) exec_args;
26
30
        }
27
31
}
28
32
 
29
 
static void client_connected(struct master_service_connection *conn)
30
 
{
31
 
        const unsigned char *end;
 
33
 
 
34
static void
 
35
exec_child(struct master_service_connection *conn, const char *const *args)
 
36
{
 
37
        unsigned int i, socket_count;
 
38
 
 
39
        if (dup2(conn->fd, STDIN_FILENO) < 0)
 
40
                i_fatal("dup2() failed: %m");
 
41
        if (dup2(conn->fd, STDOUT_FILENO) < 0)
 
42
                i_fatal("dup2() failed: %m");
 
43
 
 
44
        /* close all fds */
 
45
        socket_count = master_service_get_socket_count(master_service);
 
46
        for (i = 0; i < socket_count; i++) {
 
47
                if (close(MASTER_LISTEN_FD_FIRST + i) < 0)
 
48
                        i_error("close(listener) failed: %m");
 
49
        }
 
50
        if (close(MASTER_STATUS_FD) < 0)
 
51
                i_error("close(status) failed: %m");
 
52
        if (close(conn->fd) < 0)
 
53
                i_error("close(conn->fd) failed: %m");
 
54
 
 
55
        for (; *args != NULL; args++)
 
56
                array_append(&exec_args, args, 1);
 
57
        (void)array_append_space(&exec_args);
 
58
 
 
59
        env_clean();
 
60
        args = array_idx(&exec_args, 0);
 
61
        execvp_const(args[0], args);
 
62
}
 
63
 
 
64
static bool client_exec_script(struct master_service_connection *conn)
 
65
{
32
66
        const char *const *args;
33
 
        buffer_t *input;
 
67
        string_t *input;
34
68
        void *buf;
35
 
        unsigned int i, socket_count;
36
 
        size_t prev_size;
 
69
        size_t prev_size, scanpos;
 
70
        bool header_complete = FALSE;
37
71
        ssize_t ret;
 
72
        int status;
 
73
        pid_t pid;
38
74
 
39
75
        net_set_nonblock(conn->fd, FALSE);
40
76
        input = buffer_create_dynamic(pool_datastack_create(), IO_BLOCK_SIZE);
42
78
        /* Input contains:
43
79
 
44
80
           VERSION .. <lf>
 
81
           [timeout=<timeout>]
 
82
           <noreply> | "-" <lf>
 
83
 
45
84
           arg 1 <lf>
46
85
           arg 2 <lf>
47
86
           ...
48
 
           <lf> */
 
87
           <lf>
 
88
           DATA
 
89
        */              
49
90
        alarm(SCRIPT_READ_TIMEOUT_SECS);
50
 
        do {
 
91
        scanpos = 1;
 
92
        while (!header_complete) {
 
93
                const unsigned char *pos, *end;
 
94
 
51
95
                prev_size = input->used;
52
96
                buf = buffer_append_space_unsafe(input, IO_BLOCK_SIZE);
53
 
                ret = read(conn->fd, buf, IO_BLOCK_SIZE);
 
97
 
 
98
                /* peek in socket input buffer */
 
99
                ret = recv(conn->fd, buf, IO_BLOCK_SIZE, MSG_PEEK);
54
100
                if (ret <= 0) {
55
101
                        buffer_set_used_size(input, prev_size);
56
102
                        if (strchr(str_c(input), '\n') != NULL)
57
 
                                script_verify_version(t_strcut(str_c(input), '\t'));
58
 
 
59
 
                        if (ret < 0)
60
 
                                i_fatal("read() failed: %m");
61
 
                        else
62
 
                                i_fatal("read() failed: disconnected");
63
 
                }
64
 
                buffer_set_used_size(input, prev_size + ret);
65
 
                end = CONST_PTR_OFFSET(input->data, input->used);
66
 
        } while (!(end[-1] == '\n' && (input->used == 1 || end[-2] == '\n')));
 
103
                                script_verify_version(t_strcut(str_c(input), '\n'));
 
104
 
 
105
                        if (ret < 0)
 
106
                                i_fatal("recv(MSG_PEEK) failed: %m");
 
107
 
 
108
                        i_fatal("recv(MSG_PEEK) failed: disconnected");
 
109
                }
 
110
 
 
111
                /* scan for final \n\n */
 
112
                pos = CONST_PTR_OFFSET(input->data, scanpos);
 
113
                end = CONST_PTR_OFFSET(input->data, prev_size + ret);
 
114
                for (; pos < end; pos++) {
 
115
                        if (pos[-1] == '\n' && pos[0] == '\n') {
 
116
                                header_complete = TRUE;
 
117
                                pos++;
 
118
                                break;
 
119
                        }
 
120
                }
 
121
                scanpos = pos - (const unsigned char *)input->data;
 
122
 
 
123
                /* read data for real (up to and including \n\n) */
 
124
                ret = recv(conn->fd, buf, scanpos-prev_size, 0);
 
125
                if (prev_size+ret != scanpos) {
 
126
                        if (ret < 0)
 
127
                                i_fatal("recv() failed: %m");
 
128
                        if (ret == 0)
 
129
                                i_fatal("recv() failed: disconnected");
 
130
                        i_fatal("recv() failed: size of definitive recv() differs from peek");
 
131
                }
 
132
                buffer_set_used_size(input, scanpos);
 
133
        }
 
134
        alarm(0);
67
135
 
68
136
        /* drop the last LF */
69
 
        buffer_set_used_size(input, input->used - 1);
 
137
        buffer_set_used_size(input, scanpos-1);
70
138
 
71
139
        args = t_strsplit(str_c(input), "\n");
72
 
        script_verify_version(*args);
73
 
 
74
 
        for (args++; *args != NULL; args++)
75
 
                array_append(&exec_args, args, 1);
76
 
        (void)array_append_space(&exec_args);
77
 
 
78
 
        /* close all fds */
79
 
        socket_count = master_service_get_socket_count(master_service);
80
 
        for (i = 0; i < socket_count; i++) {
81
 
                if (close(MASTER_LISTEN_FD_FIRST + i) < 0)
82
 
                        i_error("close(listener) failed: %m");
83
 
        }
84
 
        if (close(MASTER_STATUS_FD) < 0)
85
 
                i_error("close(status) failed: %m");
86
 
        if (close(conn->fd) < 0)
87
 
                i_error("close() failed: %m");
88
 
 
89
 
        env_clean();
90
 
        args = array_idx(&exec_args, 0);
91
 
        execvp_const(args[0], args);
 
140
        script_verify_version(*args); args++;
 
141
        if (*args != NULL) {
 
142
                if (strncmp(*args, "alarm=", 6) == 0) {
 
143
                        alarm(atoi(*args + 6));
 
144
                        args++;
 
145
                }
 
146
                if (strcmp(*args, "noreply") == 0) {
 
147
                        /* no need to fork and check exit status */
 
148
                        exec_child(conn, args + 1);
 
149
                        i_unreached();
 
150
                }
 
151
                if (*args == '\0')
 
152
                        i_fatal("empty options");
 
153
                args++;
 
154
        }
 
155
 
 
156
        if ((pid = fork()) == (pid_t)-1) {
 
157
                i_error("fork() failed: %m");
 
158
                return FALSE;
 
159
        }
 
160
 
 
161
        if (pid == 0) {
 
162
                /* child */
 
163
                exec_child(conn, args);
 
164
                i_unreached();
 
165
        }
 
166
 
 
167
        /* parent */
 
168
 
 
169
        /* check script exit status */
 
170
        if (waitpid(pid, &status, 0) < 0) {
 
171
                i_error("waitpid() failed: %m");
 
172
                return FALSE;
 
173
        } else if (WIFEXITED(status)) {
 
174
                ret = WEXITSTATUS(status);
 
175
                if (ret != 0) {
 
176
                        i_error("Script terminated abnormally, exit status %d", (int)ret);
 
177
                        return FALSE;
 
178
                }
 
179
        } else if (WIFSIGNALED(status)) {
 
180
                i_error("Script terminated abnormally, signal %d", WTERMSIG(status));
 
181
                return FALSE;
 
182
        } else if (WIFSTOPPED(status)) {
 
183
                i_fatal("Script stopped, signal %d", WSTOPSIG(status));
 
184
                return FALSE;
 
185
        } else {
 
186
                i_fatal("Script terminated abnormally, return status %d", status);
 
187
                return FALSE;
 
188
        }
 
189
        return TRUE;
 
190
}
 
191
 
 
192
static void client_connected(struct master_service_connection *conn)
 
193
{
 
194
        char response[2];
 
195
 
 
196
        response[0] = client_exec_script(conn) ? '+' : '-';
 
197
        response[1] = '\n';
 
198
        if (write_full(conn->fd, &response, 2) < 0)
 
199
                i_error("write(response) failed: %m");
92
200
}
93
201
 
94
202
int main(int argc, char *argv[])
127
235
 
128
236
        master_service_run(master_service, client_connected);
129
237
        master_service_deinit(&master_service);
130
 
        return 0;
 
238
        return 0;
131
239
}