2 * Amanda, The Advanced Maryland Automatic Network Disk Archiver
3 * Copyright (c) 2008-2012 Zmanda, Inc. All Rights Reserved.
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.
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
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
19 * Contact information: Zmanda Inc., 465 S. Mathilda Ave., Suite 300
20 * Sunnyvale, CA 94085, USA, or: http://www.zmanda.com
31 * This declaration is entirely private; nothing but xfer_filter_process() references
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)
43 static GObjectClass *parent_class = NULL;
46 * Main object structure
49 typedef struct XferFilterProcess {
50 XferElement __parent__;
58 gboolean child_killed;
66 XferElementClass __parent__;
67 int (*get_err_fd)(XferFilterProcess *elt);
69 } XferFilterProcessClass;
81 XferFilterProcess *self = XFER_FILTER_PROCESS(data);
82 XferElement *elt = (XferElement *)self;
86 g_assert(pid == self->child_pid);
87 self->child_pid = -1; /* it's gone now.. */
89 if (WIFEXITED(status)) {
90 int exitcode = WEXITSTATUS(status);
91 g_debug("%s: process exited with status %d", xfer_element_repr(elt), exitcode);
93 errmsg = g_strdup_printf("%s exited with status %d",
94 self->argv[0], exitcode);
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);
104 /* if this is an error exit, send an XMSG_ERROR and cancel */
106 msg = xmsg_new(XFER_ELEMENT(self), XMSG_ERROR, 0);
107 msg->message = errmsg;
108 xfer_queue_message(XFER_ELEMENT(self)->xfer, msg);
110 xfer_cancel(elt->xfer);
112 /* this element is as good as cancelled already, so fall through to XMSG_DONE */
115 xfer_queue_message(XFER_ELEMENT(self)->xfer, xmsg_new(XFER_ELEMENT(self), XMSG_DONE, 0));
120 XferFilterProcess *xfp)
122 return xfp->pipe_err[0];
129 XferFilterProcess *self = (XferFilterProcess *)elt;
136 /* first build up a log message of what we're going to do, properly shell quoted */
138 cmd_str = g_shell_quote(*(argv++));
140 char *qarg = g_shell_quote(*(argv++));
141 cmd_str = newvstralloc(cmd_str, cmd_str, " ", qarg, NULL);
144 g_debug("%s spawning: %s", xfer_element_repr(elt), cmd_str);
146 rfd = xfer_element_swap_output_fd(elt->upstream, -1);
147 wfd = xfer_element_swap_input_fd(elt->downstream, -1);
149 /* now fork off the child and connect the pipes */
150 switch (self->child_pid = fork()) {
152 error("cannot fork: %s", strerror(errno));
156 /* first, copy our fd's out of the stdio range */
157 while (rfd <= STDERR_FILENO)
159 while (wfd <= STDERR_FILENO)
162 /* set up stdin, stdout, and stderr, overwriting anything already open
164 dup2(rfd, STDIN_FILENO);
165 dup2(wfd, STDOUT_FILENO);
166 dup2(self->pipe_err[1], STDERR_FILENO);
168 /* and close everything else */
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));
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));
183 default: /* parent */
188 /* close the pipe fd's */
191 close(self->pipe_err[1]);
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);
208 XferFilterProcess *self = (XferFilterProcess *)elt;
211 XFER_ELEMENT_CLASS(parent_class)->cancel(elt, expect_eof);
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
219 /* avoid the risk of SIGPIPEs by not killing the process if it is already
220 * expecting an EOF */
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) {
231 g_debug("while killing child process: %s", strerror(errno));
232 return FALSE; /* downstream should not expect EOF */
235 /* make sure we don't send an XMSG_ERROR about this */
236 self->child_killed = 1;
239 return TRUE; /* downstream should expect an EOF */
246 XferFilterProcess *self = (XferFilterProcess *)elt;
248 /* we can generate an EOF *unless* the process is running as root */
249 elt->can_generate_eof = !self->need_root;
252 self->child_pid = -1;
253 self->child_killed = FALSE;
260 XferFilterProcess *self = XFER_FILTER_PROCESS(obj_self);
263 g_strfreev(self->argv);
266 G_OBJECT_CLASS(parent_class)->finalize(obj_self);
271 XferFilterProcessClass * selfc)
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) },
280 klass->start = start_impl;
281 klass->cancel = cancel_impl;
283 klass->perl_class = "Amanda::Xfer::Filter::Process";
284 klass->mech_pairs = mech_pairs;
285 selfc->get_err_fd = get_err_fd_impl;
287 goc->finalize = finalize_impl;
289 parent_class = g_type_class_peek_parent(selfc);
293 xfer_filter_process_get_type (void)
295 static GType type = 0;
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),
307 (GInstanceInitFunc) instance_init,
311 type = g_type_register_static (XFER_ELEMENT_TYPE, "XferFilterProcess", &info, 0);
317 /* create an element of this class; prototype is in xfer-element.h */
323 XferFilterProcess *xfp = (XferFilterProcess *)g_object_new(XFER_FILTER_PROCESS_TYPE, NULL);
324 XferElement *elt = XFER_ELEMENT(xfp);
327 error("xfer_filter_process got a NULL or empty argv");
330 xfp->need_root = need_root;
331 if (pipe(xfp->pipe_err) < 0) {
332 g_critical(_("Can't create pipe: %s"), strerror(errno));
337 int get_err_fd(XferElement *elt);
341 XferFilterProcessClass *klass;
342 g_assert(IS_XFER_FILTER_PROCESS(elt));
344 klass = XFER_FILTER_PROCESS_GET_CLASS(elt);
345 if (klass->get_err_fd)
346 return klass->get_err_fd(XFER_FILTER_PROCESS(elt));