2 * Amanda, The Advanced Maryland Automatic Network Disk Archiver
3 * Copyright (c) 2008,2009 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__;
56 gboolean child_killed;
64 XferElementClass __parent__;
65 } XferFilterProcessClass;
78 XferFilterProcess *self = XFER_FILTER_PROCESS(data);
79 XferElement *elt = (XferElement *)self;
83 g_assert(pid == self->child_pid);
84 self->child_pid = -1; /* it's gone now.. */
86 if (WIFEXITED(status)) {
87 int exitcode = WEXITSTATUS(status);
88 g_debug("%s: process exited with status %d", xfer_element_repr(elt), exitcode);
90 errmsg = g_strdup_printf("%s exited with status %d",
91 self->argv[0], exitcode);
93 } else if (WIFSIGNALED(status)) {
94 int signal = WTERMSIG(status);
95 if (signal != SIGKILL || !self->child_killed) {
96 errmsg = g_strdup_printf("%s died on signal %d", self->argv[0], signal);
97 g_debug("%s: %s", xfer_element_repr(elt), errmsg);
101 /* if this is an error exit, send an XMSG_ERROR and cancel */
103 msg = xmsg_new(XFER_ELEMENT(self), XMSG_ERROR, 0);
104 msg->message = errmsg;
105 xfer_queue_message(XFER_ELEMENT(self)->xfer, msg);
107 xfer_cancel(elt->xfer);
109 /* this element is as good as cancelled already, so fall through to XMSG_DONE */
112 xfer_queue_message(XFER_ELEMENT(self)->xfer, xmsg_new(XFER_ELEMENT(self), XMSG_DONE, 0));
119 XferFilterProcess *self = (XferFilterProcess *)elt;
125 /* first build up a log message of what we're going to do, properly shell quoted */
127 cmd_str = g_shell_quote(*(argv++));
129 char *qarg = g_shell_quote(*(argv++));
130 cmd_str = newvstralloc(cmd_str, cmd_str, " ", qarg, NULL);
133 g_debug("%s spawning: %s", xfer_element_repr(elt), cmd_str);
135 /* now fork off the child and connect the pipes */
136 switch (self->child_pid = fork()) {
138 error("cannot fork: %s", strerror(errno));
142 /* set up stdin, stdout, and stderr */
143 dup2(elt->upstream->output_fd, STDIN_FILENO);
144 dup2(elt->downstream->input_fd, STDOUT_FILENO);
145 debug_dup_stderr_to_debug();
147 /* and close everything else */
151 if (self->need_root && !become_root()) {
152 errmsg = g_strdup_printf("could not become root: %s\n", strerror(errno));
153 full_write(STDERR_FILENO, errmsg, strlen(errmsg));
157 execve(self->argv[0], self->argv, env);
158 errmsg = g_strdup_printf("exec failed: %s\n", strerror(errno));
159 full_write(STDERR_FILENO, errmsg, strlen(errmsg));
162 default: /* parent */
167 /* close the pipe fd's */
168 close(elt->upstream->output_fd);
169 close(elt->downstream->input_fd);
171 /* watch for child death */
172 self->child_watch = new_child_watch_source(self->child_pid);
173 g_source_set_callback(self->child_watch,
174 (GSourceFunc)child_watch_callback, self, NULL);
175 g_source_attach(self->child_watch, NULL);
176 g_source_unref(self->child_watch);
186 XferFilterProcess *self = (XferFilterProcess *)elt;
189 XFER_ELEMENT_CLASS(parent_class)->cancel(elt, expect_eof);
191 /* if the process is running as root, we can't do anything but wait until
192 * we get an upstream EOF, or downstream does something to trigger a
197 /* avoid the risk of SIGPIPEs by not killing the process if it is already
198 * expecting an EOF */
203 /* and kill the process, if it's not already dead; this will likely send
204 * SIGPIPE to anything upstream. */
205 if (self->child_pid != -1) {
206 g_debug("%s: killing child process", xfer_element_repr(elt));
207 if (kill(self->child_pid, SIGKILL) < 0) {
209 g_debug("while killing child process: %s", strerror(errno));
210 return FALSE; /* downstream should not expect EOF */
213 /* make sure we don't send an XMSG_ERROR about this */
214 self->child_killed = 1;
217 return TRUE; /* downstream should expect an EOF */
224 XferFilterProcess *self = (XferFilterProcess *)elt;
226 /* we can generate an EOF *unless* the process is running as root */
227 elt->can_generate_eof = !self->need_root;
230 self->child_pid = -1;
231 self->child_killed = FALSE;
238 XferFilterProcess *self = XFER_FILTER_PROCESS(obj_self);
241 g_strfreev(self->argv);
244 G_OBJECT_CLASS(parent_class)->finalize(obj_self);
249 XferFilterProcessClass * selfc)
251 XferElementClass *klass = XFER_ELEMENT_CLASS(selfc);
252 GObjectClass *goc = (GObjectClass*) klass;
253 static xfer_element_mech_pair_t mech_pairs[] = {
254 { XFER_MECH_READFD, XFER_MECH_WRITEFD, 1, 0},
255 { XFER_MECH_NONE, XFER_MECH_NONE, 0, 0},
258 klass->start = start_impl;
259 klass->cancel = cancel_impl;
261 klass->perl_class = "Amanda::Xfer::Filter::Process";
262 klass->mech_pairs = mech_pairs;
264 goc->finalize = finalize_impl;
266 parent_class = g_type_class_peek_parent(selfc);
270 xfer_filter_process_get_type (void)
272 static GType type = 0;
274 if G_UNLIKELY(type == 0) {
275 static const GTypeInfo info = {
276 sizeof (XferFilterProcessClass),
277 (GBaseInitFunc) NULL,
278 (GBaseFinalizeFunc) NULL,
279 (GClassInitFunc) class_init,
280 (GClassFinalizeFunc) NULL,
281 NULL /* class_data */,
282 sizeof (XferFilterProcess),
284 (GInstanceInitFunc) instance_init,
288 type = g_type_register_static (XFER_ELEMENT_TYPE, "XferFilterProcess", &info, 0);
294 /* create an element of this class; prototype is in xfer-element.h */
300 XferFilterProcess *xfp = (XferFilterProcess *)g_object_new(XFER_FILTER_PROCESS_TYPE, NULL);
301 XferElement *elt = XFER_ELEMENT(xfp);
304 error("xfer_filter_process got a NULL or empty argv");
307 xfp->need_root = need_root;