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) 2003 Ximian Inc.
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 "camel-file-utils.h"
43 #include "camel-stream-process.h"
45 extern int camel_verbose_debug;
47 static CamelObjectClass *parent_class = NULL;
49 /* Returns the class for a CamelStream */
50 #define CS_CLASS(so) CAMEL_STREAM_PROCESS_CLASS(CAMEL_OBJECT_GET_CLASS(so))
52 /* dummy implementations, for a PROCESS stream */
53 static ssize_t stream_read (CamelStream *stream, char *buffer, size_t n);
54 static ssize_t stream_write (CamelStream *stream, const char *buffer, size_t n);
55 static int stream_close (CamelStream *stream);
56 static int stream_flush (CamelStream *stream);
59 camel_stream_process_finalise (CamelObject *object)
61 /* Ensure we clean up after ourselves -- kill
62 the child process and reap it. */
63 stream_close (CAMEL_STREAM (object));
67 camel_stream_process_class_init (CamelStreamProcessClass *camel_stream_process_class)
69 CamelStreamClass *camel_stream_class = (CamelStreamClass *) camel_stream_process_class;
71 parent_class = camel_type_get_global_classfuncs (CAMEL_OBJECT_TYPE);
73 /* virtual method definition */
74 camel_stream_class->read = stream_read;
75 camel_stream_class->write = stream_write;
76 camel_stream_class->close = stream_close;
77 camel_stream_class->flush = stream_flush;
81 camel_stream_process_init (gpointer object, gpointer klass)
83 CamelStreamProcess *stream = CAMEL_STREAM_PROCESS (object);
91 camel_stream_process_get_type (void)
93 static CamelType type = CAMEL_INVALID_TYPE;
95 if (type == CAMEL_INVALID_TYPE) {
96 type = camel_type_register (camel_stream_get_type (),
98 sizeof (CamelStreamProcess),
99 sizeof (CamelStreamProcessClass),
100 (CamelObjectClassInitFunc) camel_stream_process_class_init,
102 (CamelObjectInitFunc) camel_stream_process_init,
103 (CamelObjectFinalizeFunc) camel_stream_process_finalise);
110 * camel_stream_process_new:
112 * Returns a PROCESS stream.
114 * Return value: the stream
117 camel_stream_process_new (void)
119 return (CamelStream *) camel_object_new (camel_stream_process_get_type ());
124 stream_read (CamelStream *stream, char *buffer, size_t n)
126 CamelStreamProcess *stream_process = CAMEL_STREAM_PROCESS (stream);
128 return camel_read (stream_process->sockfd, buffer, n);
132 stream_write (CamelStream *stream, const char *buffer, size_t n)
134 CamelStreamProcess *stream_process = CAMEL_STREAM_PROCESS (stream);
136 return camel_write (stream_process->sockfd, buffer, n);
140 stream_flush (CamelStream *stream)
146 stream_close (CamelStream *object)
148 CamelStreamProcess *stream = CAMEL_STREAM_PROCESS (object);
150 if (camel_verbose_debug)
151 fprintf (stderr, "Process stream close. sockfd %d, childpid %d\n",
152 stream->sockfd, stream->childpid);
154 if (stream->sockfd != -1) {
155 close (stream->sockfd);
159 if (stream->childpid) {
161 for (i = 0; i < 4; i++) {
162 ret = waitpid (stream->childpid, NULL, WNOHANG);
163 if (camel_verbose_debug)
164 fprintf (stderr, "waitpid() for pid %d returned %d (errno %d)\n",
165 stream->childpid, ret, ret == -1 ? errno : 0);
166 if (ret == stream->childpid || errno == ECHILD)
170 if (camel_verbose_debug)
171 fprintf (stderr, "Sending SIGTERM to pid %d\n",
173 kill (stream->childpid, SIGTERM);
176 if (camel_verbose_debug)
177 fprintf (stderr, "Sending SIGKILL to pid %d\n",
179 kill (stream->childpid, SIGKILL);
188 stream->childpid = 0;
195 do_exec_command (int fd, const char *command, char **env)
199 /* Not a lot we can do if there's an error other than bail. */
200 if (dup2 (fd, 0) == -1)
202 if (dup2 (fd, 1) == -1)
205 /* What to do with stderr? Possibly put it through a separate pipe
206 and bring up a dialog box with its output if anything does get
207 spewed to it? It'd help the user understand what was going wrong
208 with their command, but it's hard to do cleanly. For now we just
209 leave it as it is. Perhaps we should close it and reopen /dev/null? */
211 maxopen = sysconf (_SC_OPEN_MAX);
212 for (i = 3; i < maxopen; i++)
213 fcntl (i, F_SETFD, FD_CLOEXEC);
217 /* Detach from the controlling tty if we have one. Otherwise,
218 SSH might do something stupid like trying to use it instead
219 of running $SSH_ASKPASS. Doh. */
220 if ((fd = open ("/dev/tty", O_RDONLY)) != -1) {
221 ioctl (fd, TIOCNOTTY, NULL);
224 #endif /* TIOCNOTTY */
226 /* Set up child's environment. We _add_ to it, don't use execle,
227 because otherwise we'd destroy stuff like SSH_AUTH_SOCK etc. */
228 for ( ; env && *env; env++)
231 execl ("/bin/sh", "/bin/sh", "-c", command, NULL);
233 if (camel_verbose_debug)
234 fprintf (stderr, "exec failed %d\n", errno);
240 camel_stream_process_connect (CamelStreamProcess *stream, const char *command, const char **env)
244 if (stream->sockfd != -1 || stream->childpid)
245 stream_close (CAMEL_STREAM (stream));
247 if (socketpair (AF_UNIX, SOCK_STREAM, 0, sockfds))
250 stream->childpid = fork ();
251 if (!stream->childpid) {
252 do_exec_command (sockfds[1], command, (char **)env);
253 } else if (stream->childpid == -1) {
261 stream->sockfd = sockfds[0];