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 int (*get_err_fd)(XferFilterProcess *elt);
68 } XferFilterProcessClass;
80 XferFilterProcess *self = XFER_FILTER_PROCESS(data);
81 XferElement *elt = (XferElement *)self;
85 g_assert(pid == self->child_pid);
86 self->child_pid = -1; /* it's gone now.. */
88 if (WIFEXITED(status)) {
89 int exitcode = WEXITSTATUS(status);
90 g_debug("%s: process exited with status %d", xfer_element_repr(elt), exitcode);
92 errmsg = g_strdup_printf("%s exited with status %d",
93 self->argv[0], exitcode);
95 } else if (WIFSIGNALED(status)) {
96 int signal = WTERMSIG(status);
97 if (signal != SIGKILL || !self->child_killed) {
98 errmsg = g_strdup_printf("%s died on signal %d", self->argv[0], signal);
99 g_debug("%s: %s", xfer_element_repr(elt), errmsg);
103 /* if this is an error exit, send an XMSG_ERROR and cancel */
105 msg = xmsg_new(XFER_ELEMENT(self), XMSG_ERROR, 0);
106 msg->message = errmsg;
107 xfer_queue_message(XFER_ELEMENT(self)->xfer, msg);
109 xfer_cancel(elt->xfer);
111 /* this element is as good as cancelled already, so fall through to XMSG_DONE */
114 xfer_queue_message(XFER_ELEMENT(self)->xfer, xmsg_new(XFER_ELEMENT(self), XMSG_DONE, 0));
119 XferFilterProcess *xfp)
121 return xfp->pipe_err[0];
128 XferFilterProcess *self = (XferFilterProcess *)elt;
135 /* first build up a log message of what we're going to do, properly shell quoted */
137 cmd_str = g_shell_quote(*(argv++));
139 char *qarg = g_shell_quote(*(argv++));
140 cmd_str = newvstralloc(cmd_str, cmd_str, " ", qarg, NULL);
143 g_debug("%s spawning: %s", xfer_element_repr(elt), cmd_str);
145 rfd = xfer_element_swap_output_fd(elt->upstream, -1);
146 wfd = xfer_element_swap_input_fd(elt->downstream, -1);
148 /* now fork off the child and connect the pipes */
149 switch (self->child_pid = fork()) {
151 error("cannot fork: %s", strerror(errno));
155 /* first, copy our fd's out of the stdio range */
156 while (rfd <= STDERR_FILENO)
158 while (wfd <= STDERR_FILENO)
161 /* set up stdin, stdout, and stderr, overwriting anything already open
163 dup2(rfd, STDIN_FILENO);
164 dup2(wfd, STDOUT_FILENO);
165 dup2(self->pipe_err[1], STDERR_FILENO);
167 /* and close everything else */
171 if (self->need_root && !become_root()) {
172 errmsg = g_strdup_printf("could not become root: %s\n", strerror(errno));
173 full_write(STDERR_FILENO, errmsg, strlen(errmsg));
177 execve(self->argv[0], self->argv, env);
178 errmsg = g_strdup_printf("exec failed: %s\n", strerror(errno));
179 full_write(STDERR_FILENO, errmsg, strlen(errmsg));
182 default: /* parent */
187 /* close the pipe fd's */
190 close(self->pipe_err[1]);
192 /* watch for child death */
193 self->child_watch = new_child_watch_source(self->child_pid);
194 g_source_set_callback(self->child_watch,
195 (GSourceFunc)child_watch_callback, self, NULL);
196 g_source_attach(self->child_watch, NULL);
197 g_source_unref(self->child_watch);
207 XferFilterProcess *self = (XferFilterProcess *)elt;
210 XFER_ELEMENT_CLASS(parent_class)->cancel(elt, expect_eof);
212 /* if the process is running as root, we can't do anything but wait until
213 * we get an upstream EOF, or downstream does something to trigger a
218 /* avoid the risk of SIGPIPEs by not killing the process if it is already
219 * expecting an EOF */
224 /* and kill the process, if it's not already dead; this will likely send
225 * SIGPIPE to anything upstream. */
226 if (self->child_pid != -1) {
227 g_debug("%s: killing child process", xfer_element_repr(elt));
228 if (kill(self->child_pid, SIGKILL) < 0) {
230 g_debug("while killing child process: %s", strerror(errno));
231 return FALSE; /* downstream should not expect EOF */
234 /* make sure we don't send an XMSG_ERROR about this */
235 self->child_killed = 1;
238 return TRUE; /* downstream should expect an EOF */
245 XferFilterProcess *self = (XferFilterProcess *)elt;
247 /* we can generate an EOF *unless* the process is running as root */
248 elt->can_generate_eof = !self->need_root;
251 self->child_pid = -1;
252 self->child_killed = FALSE;
259 XferFilterProcess *self = XFER_FILTER_PROCESS(obj_self);
262 g_strfreev(self->argv);
265 G_OBJECT_CLASS(parent_class)->finalize(obj_self);
270 XferFilterProcessClass * selfc)
272 XferElementClass *klass = XFER_ELEMENT_CLASS(selfc);
273 GObjectClass *goc = (GObjectClass*) klass;
274 static xfer_element_mech_pair_t mech_pairs[] = {
275 { XFER_MECH_READFD, XFER_MECH_WRITEFD, XFER_NROPS(1), XFER_NTHREADS(0) },
276 { XFER_MECH_NONE, XFER_MECH_NONE, XFER_NROPS(0), XFER_NTHREADS(0) },
279 klass->start = start_impl;
280 klass->cancel = cancel_impl;
282 klass->perl_class = "Amanda::Xfer::Filter::Process";
283 klass->mech_pairs = mech_pairs;
284 selfc->get_err_fd = get_err_fd_impl;
286 goc->finalize = finalize_impl;
288 parent_class = g_type_class_peek_parent(selfc);
292 xfer_filter_process_get_type (void)
294 static GType type = 0;
296 if G_UNLIKELY(type == 0) {
297 static const GTypeInfo info = {
298 sizeof (XferFilterProcessClass),
299 (GBaseInitFunc) NULL,
300 (GBaseFinalizeFunc) NULL,
301 (GClassInitFunc) class_init,
302 (GClassFinalizeFunc) NULL,
303 NULL /* class_data */,
304 sizeof (XferFilterProcess),
306 (GInstanceInitFunc) instance_init,
310 type = g_type_register_static (XFER_ELEMENT_TYPE, "XferFilterProcess", &info, 0);
316 /* create an element of this class; prototype is in xfer-element.h */
322 XferFilterProcess *xfp = (XferFilterProcess *)g_object_new(XFER_FILTER_PROCESS_TYPE, NULL);
323 XferElement *elt = XFER_ELEMENT(xfp);
326 error("xfer_filter_process got a NULL or empty argv");
329 xfp->need_root = need_root;
330 if (pipe(xfp->pipe_err) < 0) {
331 g_critical(_("Can't create pipe: %s"), strerror(errno));
336 int get_err_fd(XferElement *elt);
340 XferFilterProcessClass *klass;
341 g_assert(IS_XFER_FILTER_PROCESS(elt));
343 klass = XFER_FILTER_PROCESS_GET_CLASS(elt);
344 if (klass->get_err_fd)
345 return klass->get_err_fd(XFER_FILTER_PROCESS(elt));