lintian doesn't like orphan packages with uploaders...
[debian/amanda] / xfer-src / filter-process.c
1 /*
2  * Amanda, The Advanced Maryland Automatic Network Disk Archiver
3  * Copyright (c) 2008-2012 Zmanda, Inc.  All Rights Reserved.
4  *
5  * This program is free software; you can redistribute it and/or
6  * modify it under the terms of the GNU General Public License
7  * as published by the Free Software Foundation; either version 2
8  * of the License, or (at your option) any later version.
9  *
10  * This program is distributed in the hope that it will be useful, but
11  * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
12  * or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
13  * for more details.
14  *
15  * You should have received a copy of the GNU General Public License along
16  * with this program; if not, write to the Free Software Foundation, Inc.,
17  * 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
18  *
19  * Contact information: Zmanda Inc., 465 S. Mathilda Ave., Suite 300
20  * Sunnyvale, CA 94085, USA, or: http://www.zmanda.com
21  */
22
23 #include "amanda.h"
24 #include "amxfer.h"
25 #include "event.h"
26 #include "util.h"
27
28 /*
29  * Class declaration
30  *
31  * This declaration is entirely private; nothing but xfer_filter_process() references
32  * it directly.
33  */
34
35 GType xfer_filter_process_get_type(void);
36 #define XFER_FILTER_PROCESS_TYPE (xfer_filter_process_get_type())
37 #define XFER_FILTER_PROCESS(obj) G_TYPE_CHECK_INSTANCE_CAST((obj), xfer_filter_process_get_type(), XferFilterProcess)
38 #define XFER_FILTER_PROCESS_CONST(obj) G_TYPE_CHECK_INSTANCE_CAST((obj), xfer_filter_process_get_type(), XferFilterProcess const)
39 #define XFER_FILTER_PROCESS_CLASS(klass) G_TYPE_CHECK_CLASS_CAST((klass), xfer_filter_process_get_type(), XferFilterProcessClass)
40 #define IS_XFER_FILTER_PROCESS(obj) G_TYPE_CHECK_INSTANCE_TYPE((obj), xfer_filter_process_get_type ())
41 #define XFER_FILTER_PROCESS_GET_CLASS(obj) G_TYPE_INSTANCE_GET_CLASS((obj), xfer_filter_process_get_type(), XferFilterProcessClass)
42
43 static GObjectClass *parent_class = NULL;
44
45 /*
46  * Main object structure
47  */
48
49 typedef struct XferFilterProcess {
50     XferElement __parent__;
51
52     gchar **argv;
53     gboolean need_root;
54     int pipe_err[2];
55
56     pid_t child_pid;
57     GSource *child_watch;
58     gboolean child_killed;
59 } XferFilterProcess;
60
61 /*
62  * Class definition
63  */
64
65 typedef struct {
66     XferElementClass __parent__;
67     int (*get_err_fd)(XferFilterProcess *elt);
68
69 } XferFilterProcessClass;
70
71 /*
72  * Implementation
73  */
74
75 static void
76 child_watch_callback(
77     pid_t pid,
78     gint status,
79     gpointer data)
80 {
81     XferFilterProcess *self = XFER_FILTER_PROCESS(data);
82     XferElement *elt = (XferElement *)self;
83     XMsg *msg;
84     char *errmsg = NULL;
85
86     g_assert(pid == self->child_pid);
87     self->child_pid = -1; /* it's gone now.. */
88
89     if (WIFEXITED(status)) {
90         int exitcode = WEXITSTATUS(status);
91         g_debug("%s: process exited with status %d", xfer_element_repr(elt), exitcode);
92         if (exitcode != 0) {
93             errmsg = g_strdup_printf("%s exited with status %d",
94                 self->argv[0], exitcode);
95         }
96     } else if (WIFSIGNALED(status)) {
97         int signal = WTERMSIG(status);
98         if (signal != SIGKILL || !self->child_killed) {
99             errmsg = g_strdup_printf("%s died on signal %d", self->argv[0], signal);
100             g_debug("%s: %s", xfer_element_repr(elt), errmsg);
101         }
102     }
103
104     /* if this is an error exit, send an XMSG_ERROR and cancel */
105     if (errmsg) {
106         msg = xmsg_new(XFER_ELEMENT(self), XMSG_ERROR, 0);
107         msg->message = errmsg;
108         xfer_queue_message(XFER_ELEMENT(self)->xfer, msg);
109
110         xfer_cancel(elt->xfer);
111
112         /* this element is as good as cancelled already, so fall through to XMSG_DONE */
113     }
114
115     xfer_queue_message(XFER_ELEMENT(self)->xfer, xmsg_new(XFER_ELEMENT(self), XMSG_DONE, 0));
116 }
117
118 static int
119 get_err_fd_impl(
120     XferFilterProcess *xfp)
121 {
122     return xfp->pipe_err[0];
123 }
124
125 static gboolean
126 start_impl(
127     XferElement *elt)
128 {
129     XferFilterProcess *self = (XferFilterProcess *)elt;
130     char *cmd_str;
131     char **argv;
132     char *errmsg;
133     char **env;
134     int rfd, wfd;
135
136     /* first build up a log message of what we're going to do, properly shell quoted */
137     argv = self->argv;
138     cmd_str = g_shell_quote(*(argv++));
139     while (*argv) {
140         char *qarg = g_shell_quote(*(argv++));
141         cmd_str = newvstralloc(cmd_str, cmd_str, " ", qarg, NULL);
142         g_free(qarg);
143     }
144     g_debug("%s spawning: %s", xfer_element_repr(elt), cmd_str);
145
146     rfd = xfer_element_swap_output_fd(elt->upstream, -1);
147     wfd = xfer_element_swap_input_fd(elt->downstream, -1);
148
149     /* now fork off the child and connect the pipes */
150     switch (self->child_pid = fork()) {
151         case -1:
152             error("cannot fork: %s", strerror(errno));
153             /* NOTREACHED */
154
155         case 0: /* child */
156             /* first, copy our fd's out of the stdio range */
157             while (rfd <= STDERR_FILENO)
158                 rfd = dup(rfd);
159             while (wfd <= STDERR_FILENO)
160                 wfd = dup(wfd);
161
162             /* set up stdin, stdout, and stderr, overwriting anything already open
163              * on those fd's */
164             dup2(rfd, STDIN_FILENO);
165             dup2(wfd, STDOUT_FILENO);
166             dup2(self->pipe_err[1], STDERR_FILENO);
167
168             /* and close everything else */
169             safe_fd(-1, 0);
170             env = safe_env();
171
172             if (self->need_root && !become_root()) {
173                 errmsg = g_strdup_printf("could not become root: %s\n", strerror(errno));
174                 full_write(STDERR_FILENO, errmsg, strlen(errmsg));
175                 exit(1);
176             }
177
178             execve(self->argv[0], self->argv, env);
179             errmsg = g_strdup_printf("exec failed: %s\n", strerror(errno));
180             full_write(STDERR_FILENO, errmsg, strlen(errmsg));
181             exit(1);
182
183         default: /* parent */
184             break;
185     }
186     g_free(cmd_str);
187
188     /* close the pipe fd's */
189     close(rfd);
190     close(wfd);
191     close(self->pipe_err[1]);
192
193     /* watch for child death */
194     self->child_watch = new_child_watch_source(self->child_pid);
195     g_source_set_callback(self->child_watch,
196             (GSourceFunc)child_watch_callback, self, NULL);
197     g_source_attach(self->child_watch, NULL);
198     g_source_unref(self->child_watch);
199
200     return TRUE;
201 }
202
203 static gboolean
204 cancel_impl(
205     XferElement *elt,
206     gboolean expect_eof)
207 {
208     XferFilterProcess *self = (XferFilterProcess *)elt;
209
210     /* chain up first */
211     XFER_ELEMENT_CLASS(parent_class)->cancel(elt, expect_eof);
212
213     /* if the process is running as root, we can't do anything but wait until
214      * we get an upstream EOF, or downstream does something to trigger a
215      * SIGPIPE */
216     if (self->need_root)
217         return expect_eof;
218
219     /* avoid the risk of SIGPIPEs by not killing the process if it is already
220      * expecting an EOF */
221     if (expect_eof) {
222         return expect_eof;
223     }
224
225     /* and kill the process, if it's not already dead; this will likely send
226      * SIGPIPE to anything upstream. */
227     if (self->child_pid != -1) {
228         g_debug("%s: killing child process", xfer_element_repr(elt));
229         if (kill(self->child_pid, SIGKILL) < 0) {
230             /* log but ignore */
231             g_debug("while killing child process: %s", strerror(errno));
232             return FALSE; /* downstream should not expect EOF */
233         }
234
235         /* make sure we don't send an XMSG_ERROR about this */
236         self->child_killed = 1;
237     }
238
239     return TRUE; /* downstream should expect an EOF */
240 }
241
242 static void
243 instance_init(
244     XferElement *elt)
245 {
246     XferFilterProcess *self = (XferFilterProcess *)elt;
247
248     /* we can generate an EOF *unless* the process is running as root */
249     elt->can_generate_eof = !self->need_root;
250
251     self->argv = NULL;
252     self->child_pid = -1;
253     self->child_killed = FALSE;
254 }
255
256 static void
257 finalize_impl(
258     GObject * obj_self)
259 {
260     XferFilterProcess *self = XFER_FILTER_PROCESS(obj_self);
261
262     if (self->argv)
263         g_strfreev(self->argv);
264
265     /* chain up */
266     G_OBJECT_CLASS(parent_class)->finalize(obj_self);
267 }
268
269 static void
270 class_init(
271     XferFilterProcessClass * selfc)
272 {
273     XferElementClass *klass = XFER_ELEMENT_CLASS(selfc);
274     GObjectClass *goc = (GObjectClass*) klass;
275     static xfer_element_mech_pair_t mech_pairs[] = {
276         { XFER_MECH_READFD, XFER_MECH_WRITEFD, XFER_NROPS(1), XFER_NTHREADS(0) },
277         { XFER_MECH_NONE, XFER_MECH_NONE, XFER_NROPS(0), XFER_NTHREADS(0) },
278     };
279
280     klass->start = start_impl;
281     klass->cancel = cancel_impl;
282
283     klass->perl_class = "Amanda::Xfer::Filter::Process";
284     klass->mech_pairs = mech_pairs;
285     selfc->get_err_fd = get_err_fd_impl;
286
287     goc->finalize = finalize_impl;
288
289     parent_class = g_type_class_peek_parent(selfc);
290 }
291
292 GType
293 xfer_filter_process_get_type (void)
294 {
295     static GType type = 0;
296
297     if G_UNLIKELY(type == 0) {
298         static const GTypeInfo info = {
299             sizeof (XferFilterProcessClass),
300             (GBaseInitFunc) NULL,
301             (GBaseFinalizeFunc) NULL,
302             (GClassInitFunc) class_init,
303             (GClassFinalizeFunc) NULL,
304             NULL /* class_data */,
305             sizeof (XferFilterProcess),
306             0 /* n_preallocs */,
307             (GInstanceInitFunc) instance_init,
308             NULL
309         };
310
311         type = g_type_register_static (XFER_ELEMENT_TYPE, "XferFilterProcess", &info, 0);
312     }
313
314     return type;
315 }
316
317 /* create an element of this class; prototype is in xfer-element.h */
318 XferElement *
319 xfer_filter_process(
320     gchar **argv,
321     gboolean need_root)
322 {
323     XferFilterProcess *xfp = (XferFilterProcess *)g_object_new(XFER_FILTER_PROCESS_TYPE, NULL);
324     XferElement *elt = XFER_ELEMENT(xfp);
325
326     if (!argv || !*argv)
327         error("xfer_filter_process got a NULL or empty argv");
328
329     xfp->argv = argv;
330     xfp->need_root = need_root;
331     if (pipe(xfp->pipe_err) < 0) {
332         g_critical(_("Can't create pipe: %s"), strerror(errno));
333     }
334     return elt;
335 }
336
337 int get_err_fd(XferElement *elt);
338 int get_err_fd(
339     XferElement *elt)
340 {
341     XferFilterProcessClass *klass;
342     g_assert(IS_XFER_FILTER_PROCESS(elt));
343
344     klass = XFER_FILTER_PROCESS_GET_CLASS(elt);
345     if (klass->get_err_fd)
346         return klass->get_err_fd(XFER_FILTER_PROCESS(elt));
347     else
348         return 0;
349 }