2 * Amanda, The Advanced Maryland Automatic Network Disk Archiver
3 * Copyright (c) 2008, 2009, 2010 Zmanda, Inc. All Rights Reserved.
5 * This program is free software; you can redistribute it and/or modify it
6 * under the terms of the GNU General Public License version 2 as published
7 * by the Free Software Foundation.
9 * This program is distributed in the hope that it will be useful, but
10 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
11 * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
14 * You should have received a copy of the GNU General Public License along
15 * with this program; if not, write to the Free Software Foundation, Inc.,
16 * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
18 * Contact information: Zmanda Inc., 465 S. Mathilda Ave., Suite 300
19 * Sunnyvale, CA 94085, USA, or: http://www.zmanda.com
30 * This declaration is entirely private; nothing but xfer_filter_process() references
34 GType xfer_filter_process_get_type(void);
35 #define XFER_FILTER_PROCESS_TYPE (xfer_filter_process_get_type())
36 #define XFER_FILTER_PROCESS(obj) G_TYPE_CHECK_INSTANCE_CAST((obj), xfer_filter_process_get_type(), XferFilterProcess)
37 #define XFER_FILTER_PROCESS_CONST(obj) G_TYPE_CHECK_INSTANCE_CAST((obj), xfer_filter_process_get_type(), XferFilterProcess const)
38 #define XFER_FILTER_PROCESS_CLASS(klass) G_TYPE_CHECK_CLASS_CAST((klass), xfer_filter_process_get_type(), XferFilterProcessClass)
39 #define IS_XFER_FILTER_PROCESS(obj) G_TYPE_CHECK_INSTANCE_TYPE((obj), xfer_filter_process_get_type ())
40 #define XFER_FILTER_PROCESS_GET_CLASS(obj) G_TYPE_INSTANCE_GET_CLASS((obj), xfer_filter_process_get_type(), XferFilterProcessClass)
42 static GObjectClass *parent_class = NULL;
45 * Main object structure
48 typedef struct XferFilterProcess {
49 XferElement __parent__;
57 gboolean child_killed;
65 XferElementClass __parent__;
66 } XferFilterProcessClass;
79 XferFilterProcess *self = XFER_FILTER_PROCESS(data);
80 XferElement *elt = (XferElement *)self;
84 g_assert(pid == self->child_pid);
85 self->child_pid = -1; /* it's gone now.. */
87 if (WIFEXITED(status)) {
88 int exitcode = WEXITSTATUS(status);
89 g_debug("%s: process exited with status %d", xfer_element_repr(elt), exitcode);
91 errmsg = g_strdup_printf("%s exited with status %d",
92 self->argv[0], exitcode);
94 } else if (WIFSIGNALED(status)) {
95 int signal = WTERMSIG(status);
96 if (signal != SIGKILL || !self->child_killed) {
97 errmsg = g_strdup_printf("%s died on signal %d", self->argv[0], signal);
98 g_debug("%s: %s", xfer_element_repr(elt), errmsg);
102 /* if this is an error exit, send an XMSG_ERROR and cancel */
104 msg = xmsg_new(XFER_ELEMENT(self), XMSG_ERROR, 0);
105 msg->message = errmsg;
106 xfer_queue_message(XFER_ELEMENT(self)->xfer, msg);
108 xfer_cancel(elt->xfer);
110 /* this element is as good as cancelled already, so fall through to XMSG_DONE */
113 xfer_queue_message(XFER_ELEMENT(self)->xfer, xmsg_new(XFER_ELEMENT(self), XMSG_DONE, 0));
120 XferFilterProcess *self = (XferFilterProcess *)elt;
127 /* first build up a log message of what we're going to do, properly shell quoted */
129 cmd_str = g_shell_quote(*(argv++));
131 char *qarg = g_shell_quote(*(argv++));
132 cmd_str = newvstralloc(cmd_str, cmd_str, " ", qarg, NULL);
135 g_debug("%s spawning: %s", xfer_element_repr(elt), cmd_str);
137 rfd = xfer_element_swap_output_fd(elt->upstream, -1);
138 wfd = xfer_element_swap_input_fd(elt->downstream, -1);
140 /* now fork off the child and connect the pipes */
141 switch (self->child_pid = fork()) {
143 error("cannot fork: %s", strerror(errno));
147 /* first, copy our fd's out of the stdio range */
148 while (rfd <= STDERR_FILENO)
150 while (wfd <= STDERR_FILENO)
153 /* set up stdin, stdout, and stderr, overwriting anything already open
155 dup2(rfd, STDIN_FILENO);
156 dup2(wfd, STDOUT_FILENO);
157 if (!self->log_stderr)
158 debug_dup_stderr_to_debug();
160 /* and close everything else */
164 if (self->need_root && !become_root()) {
165 errmsg = g_strdup_printf("could not become root: %s\n", strerror(errno));
166 full_write(STDERR_FILENO, errmsg, strlen(errmsg));
170 execve(self->argv[0], self->argv, env);
171 errmsg = g_strdup_printf("exec failed: %s\n", strerror(errno));
172 full_write(STDERR_FILENO, errmsg, strlen(errmsg));
175 default: /* parent */
180 /* close the pipe fd's */
184 /* watch for child death */
185 self->child_watch = new_child_watch_source(self->child_pid);
186 g_source_set_callback(self->child_watch,
187 (GSourceFunc)child_watch_callback, self, NULL);
188 g_source_attach(self->child_watch, NULL);
189 g_source_unref(self->child_watch);
199 XferFilterProcess *self = (XferFilterProcess *)elt;
202 XFER_ELEMENT_CLASS(parent_class)->cancel(elt, expect_eof);
204 /* if the process is running as root, we can't do anything but wait until
205 * we get an upstream EOF, or downstream does something to trigger a
210 /* avoid the risk of SIGPIPEs by not killing the process if it is already
211 * expecting an EOF */
216 /* and kill the process, if it's not already dead; this will likely send
217 * SIGPIPE to anything upstream. */
218 if (self->child_pid != -1) {
219 g_debug("%s: killing child process", xfer_element_repr(elt));
220 if (kill(self->child_pid, SIGKILL) < 0) {
222 g_debug("while killing child process: %s", strerror(errno));
223 return FALSE; /* downstream should not expect EOF */
226 /* make sure we don't send an XMSG_ERROR about this */
227 self->child_killed = 1;
230 return TRUE; /* downstream should expect an EOF */
237 XferFilterProcess *self = (XferFilterProcess *)elt;
239 /* we can generate an EOF *unless* the process is running as root */
240 elt->can_generate_eof = !self->need_root;
243 self->child_pid = -1;
244 self->child_killed = FALSE;
251 XferFilterProcess *self = XFER_FILTER_PROCESS(obj_self);
254 g_strfreev(self->argv);
257 G_OBJECT_CLASS(parent_class)->finalize(obj_self);
262 XferFilterProcessClass * selfc)
264 XferElementClass *klass = XFER_ELEMENT_CLASS(selfc);
265 GObjectClass *goc = (GObjectClass*) klass;
266 static xfer_element_mech_pair_t mech_pairs[] = {
267 { XFER_MECH_READFD, XFER_MECH_WRITEFD, 1, 0},
268 { XFER_MECH_NONE, XFER_MECH_NONE, 0, 0},
271 klass->start = start_impl;
272 klass->cancel = cancel_impl;
274 klass->perl_class = "Amanda::Xfer::Filter::Process";
275 klass->mech_pairs = mech_pairs;
277 goc->finalize = finalize_impl;
279 parent_class = g_type_class_peek_parent(selfc);
283 xfer_filter_process_get_type (void)
285 static GType type = 0;
287 if G_UNLIKELY(type == 0) {
288 static const GTypeInfo info = {
289 sizeof (XferFilterProcessClass),
290 (GBaseInitFunc) NULL,
291 (GBaseFinalizeFunc) NULL,
292 (GClassInitFunc) class_init,
293 (GClassFinalizeFunc) NULL,
294 NULL /* class_data */,
295 sizeof (XferFilterProcess),
297 (GInstanceInitFunc) instance_init,
301 type = g_type_register_static (XFER_ELEMENT_TYPE, "XferFilterProcess", &info, 0);
307 /* create an element of this class; prototype is in xfer-element.h */
314 XferFilterProcess *xfp = (XferFilterProcess *)g_object_new(XFER_FILTER_PROCESS_TYPE, NULL);
315 XferElement *elt = XFER_ELEMENT(xfp);
318 error("xfer_filter_process got a NULL or empty argv");
321 xfp->need_root = need_root;
322 xfp->log_stderr = log_stderr;