1 /* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8; fill-column: 160 -*- */
2 /* camel-stream-process.c : stream over piped process */
5 * Copyright (C) 1999-2008 Novell, Inc. (www.novell.com)
7 * Authors: David Woodhouse <dwmw2@infradead.org>,
8 * Jeffrey Stedfast <fejj@ximian.com>
11 * This program is free software; you can redistribute it and/or
12 * modify it under the terms of version 2 of the GNU Lesser General Public
13 * License as published by the Free Software Foundation.
15 * This program is distributed in the hope that it will be useful,
16 * but WITHOUT ANY WARRANTY; without even the implied warranty of
17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 * GNU Lesser General Public License for more details.
20 * You should have received a copy of the GNU Lesser General Public License
21 * along with this program; if not, write to the Free Software
22 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301
37 #include <sys/ioctl.h>
38 #include <sys/socket.h>
39 #include <sys/types.h>
42 #include <glib/gi18n-lib.h>
44 #include "camel-file-utils.h"
45 #include "camel-stream-process.h"
47 extern gint camel_verbose_debug;
49 G_DEFINE_TYPE (CamelStreamProcess, camel_stream_process, CAMEL_TYPE_STREAM)
52 stream_process_finalize (GObject *object)
54 /* Ensure we clean up after ourselves -- kill
55 * the child process and reap it. */
56 camel_stream_close (CAMEL_STREAM (object), NULL, NULL);
58 /* Chain up to parent's finalize() method. */
59 G_OBJECT_CLASS (camel_stream_process_parent_class)->finalize (object);
63 stream_process_read (CamelStream *stream,
66 GCancellable *cancellable,
69 CamelStreamProcess *stream_process = CAMEL_STREAM_PROCESS (stream);
70 gint fd = stream_process->sockfd;
72 return camel_read (fd, buffer, n, cancellable, error);
76 stream_process_write (CamelStream *stream,
79 GCancellable *cancellable,
82 CamelStreamProcess *stream_process = CAMEL_STREAM_PROCESS (stream);
83 gint fd = stream_process->sockfd;
85 return camel_write (fd, buffer, n, cancellable, error);
89 stream_process_close (CamelStream *object,
90 GCancellable *cancellable,
93 CamelStreamProcess *stream = CAMEL_STREAM_PROCESS (object);
95 if (camel_verbose_debug)
96 fprintf (stderr, "Process stream close. sockfd %d, childpid %d\n",
97 stream->sockfd, stream->childpid);
99 if (stream->sockfd != -1) {
100 close (stream->sockfd);
104 if (stream->childpid) {
106 for (i = 0; i < 4; i++) {
107 ret = waitpid (stream->childpid, NULL, WNOHANG);
108 if (camel_verbose_debug)
109 fprintf (stderr, "waitpid() for pid %d returned %d (errno %d)\n",
110 stream->childpid, ret, ret == -1 ? errno : 0);
111 if (ret == stream->childpid || errno == ECHILD)
115 if (camel_verbose_debug)
116 fprintf (stderr, "Sending SIGTERM to pid %d\n",
118 kill (stream->childpid, SIGTERM);
121 if (camel_verbose_debug)
122 fprintf (stderr, "Sending SIGKILL to pid %d\n",
124 kill (stream->childpid, SIGKILL);
133 stream->childpid = 0;
140 stream_process_flush (CamelStream *stream,
141 GCancellable *cancellable,
148 camel_stream_process_class_init (CamelStreamProcessClass *class)
150 GObjectClass *object_class;
151 CamelStreamClass *stream_class;
153 object_class = G_OBJECT_CLASS (class);
154 object_class->finalize = stream_process_finalize;
156 stream_class = CAMEL_STREAM_CLASS (class);
157 stream_class->read = stream_process_read;
158 stream_class->write = stream_process_write;
159 stream_class->close = stream_process_close;
160 stream_class->flush = stream_process_flush;
164 camel_stream_process_init (CamelStreamProcess *stream)
167 stream->childpid = 0;
171 * camel_stream_process_new:
173 * Returns a PROCESS stream.
175 * Returns: the stream
178 camel_stream_process_new (void)
180 return g_object_new (CAMEL_TYPE_STREAM_PROCESS, NULL);
183 G_GNUC_NORETURN static void
184 do_exec_command (gint fd,
185 const gchar *command,
190 /* Not a lot we can do if there's an error other than bail. */
191 if (dup2 (fd, 0) == -1)
193 if (dup2 (fd, 1) == -1)
196 /* What to do with stderr? Possibly put it through a separate pipe
197 * and bring up a dialog box with its output if anything does get
198 * spewed to it? It'd help the user understand what was going wrong
199 * with their command, but it's hard to do cleanly. For now we just
200 * leave it as it is. Perhaps we should close it and reopen /dev/null? */
202 maxopen = sysconf (_SC_OPEN_MAX);
203 for (i = 3; i < maxopen; i++)
204 fcntl (i, F_SETFD, FD_CLOEXEC);
208 /* Detach from the controlling tty if we have one. Otherwise,
209 * SSH might do something stupid like trying to use it instead
210 * of running $SSH_ASKPASS. Doh. */
211 if ((fd = open ("/dev/tty", O_RDONLY)) != -1) {
212 ioctl (fd, TIOCNOTTY, NULL);
215 #endif /* TIOCNOTTY */
217 /* Set up child's environment. We _add_ to it, don't use execle,
218 * because otherwise we'd destroy stuff like SSH_AUTH_SOCK etc. */
219 for (; env && *env; env++)
222 execl ("/bin/sh", "/bin/sh", "-c", command, NULL);
224 if (camel_verbose_debug)
225 fprintf (stderr, "exec failed %d\n", errno);
231 camel_stream_process_connect (CamelStreamProcess *stream,
232 const gchar *command,
238 g_return_val_if_fail (CAMEL_IS_STREAM_PROCESS (stream), -1);
239 g_return_val_if_fail (command != NULL, -1);
241 if (stream->sockfd != -1 || stream->childpid)
242 camel_stream_close (CAMEL_STREAM (stream), NULL, NULL);
244 if (socketpair (AF_UNIX, SOCK_STREAM, 0, sockfds))
247 stream->childpid = fork ();
248 if (!stream->childpid) {
249 do_exec_command (sockfds[1], command, (gchar **) env);
250 } else if (stream->childpid == -1) {
258 stream->sockfd = sockfds[0];
266 G_IO_ERROR_CANCELLED,
267 _("Connection cancelled"));
271 g_io_error_from_errno (errno),
272 _("Could not connect with command \"%s\": %s"),
273 command, g_strerror (errno));