[evolution-patches] Camel-stream-process.



Camel stream implementation for running commands. I'll update the IMAP
and preauth stuff shortly but this is simple...



-- 
dwmw2
Index: camel/ChangeLog
===================================================================
RCS file: /cvs/gnome/evolution/camel/ChangeLog,v
retrieving revision 1.1876
diff -u -p -r1.1876 ChangeLog
--- camel/ChangeLog	30 Aug 2003 01:04:38 -0000	1.1876
+++ camel/ChangeLog	3 Sep 2003 15:21:46 -0000
@@ -1,3 +1,9 @@
+2003-09-03  David Woodhouse  <dwmw2 infradead org>
+
+	* camel-stream-process.[ch]: New stream implementation for running
+	commands.
+	* Makefile.am: Compile the above
+
 2003-08-29  Not Zed  <NotZed Ximian com>
 
 	* camel-object.c (camel_object_state_write): 
Index: camel/Makefile.am
===================================================================
RCS file: /cvs/gnome/evolution/camel/Makefile.am,v
retrieving revision 1.189
diff -u -p -r1.189 Makefile.am
--- camel/Makefile.am	11 Aug 2003 17:57:31 -0000	1.189
+++ camel/Makefile.am	3 Sep 2003 15:21:46 -0000
@@ -100,6 +100,7 @@ libcamel_la_SOURCES = 				\
 	camel-stream-fs.c			\
 	camel-stream-mem.c			\
 	camel-stream-null.c			\
+	camel-stream-process.c			\
 	camel-stream.c				\
 	camel-string-utils.c			\
 	camel-text-index.c			\
@@ -198,6 +199,7 @@ libcamelinclude_HEADERS =			\
 	camel-stream-fs.h			\
 	camel-stream-mem.h			\
 	camel-stream-null.h			\
+	camel-stream-process.h			\
 	camel-stream.h				\
 	camel-string-utils.h			\
 	camel-text-index.h			\
--- /dev/null	2003-08-15 21:29:27.000000000 +0100
+++ camel/camel-stream-process.h	2003-06-27 10:36:47.000000000 +0100
@@ -0,0 +1,59 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ *  Copyright (C) 2003 Ximian Inc.
+ *
+ *  Authors: David Woodhouse <dwmw2 infradead org>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of version 2 of the GNU General Public
+ * License as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public
+ * License along with this program; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+
+#ifndef _CAMEL_STREAM_NULL_H
+#define _CAMEL_STREAM_NULL_H
+
+#ifdef __cplusplus
+extern "C" {
+#pragma }
+#endif /* __cplusplus */
+
+#include <camel/camel-stream.h>
+
+#define CAMEL_STREAM_PROCESS(obj)         CAMEL_CHECK_CAST (obj, camel_stream_process_get_type (), CamelStreamProcess)
+#define CAMEL_STREAM_PROCESS_CLASS(klass) CAMEL_CHECK_CLASS_CAST (klass, camel_stream_process_get_type (), CamelStreamProcessClass)
+#define CAMEL_IS_STREAM_PROCESS(obj)      CAMEL_CHECK_TYPE (obj, camel_stream_process_get_type ())
+
+typedef struct _CamelStreamProcessClass CamelStreamProcessClass;
+typedef struct _CamelStreamProcess CamelStreamProcess;
+
+struct _CamelStreamProcess {
+	CamelStream parent;
+	
+	int sockfd;
+	pid_t childpid;
+};
+
+struct _CamelStreamProcessClass {
+	CamelStreamClass parent_class;
+};
+
+CamelType		camel_stream_process_get_type	(void);
+CamelStream            *camel_stream_process_new		(void);
+int camel_stream_process_connect(CamelStreamProcess *, const char *, const char **);
+
+#ifdef __cplusplus
+}
+#endif /* __cplusplus */
+
+#endif /* ! _CAMEL_STREAM_PROCESS_H */
--- /dev/null	2003-08-15 21:29:27.000000000 +0100
+++ camel/camel-stream-process.c	2003-09-03 12:14:23.000000000 +0100
@@ -0,0 +1,268 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8; fill-column: 160 -*- */
+/* camel-stream-process.c : stream over piped process */
+
+/*
+ *  Copyright (C) 2003 Ximian Inc.
+ *
+ *  Authors: David Woodhouse <dwmw2 infradead org>,
+ *	     Jeffrey Stedfast <fejj ximian com>
+ *  
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of version 2 of the GNU General Public
+ * License as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+ * USA
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <sys/ioctl.h>
+#include <sys/wait.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <errno.h>
+#include <limits.h>
+#include <stdio.h>
+#include <string.h>
+
+#include "camel-stream-process.h"
+#include "camel-file-utils.h"
+
+extern int camel_verbose_debug;
+
+static CamelObjectClass *parent_class = NULL;
+
+/* Returns the class for a CamelStream */
+#define CS_CLASS(so) CAMEL_STREAM_PROCESS_CLASS(CAMEL_OBJECT_GET_CLASS(so))
+
+/* dummy implementations, for a PROCESS stream */
+static ssize_t   stream_read       (CamelStream *stream, char *buffer, size_t n);
+static ssize_t   stream_write      (CamelStream *stream, const char *buffer, size_t n);
+static int       stream_close      (CamelStream *stream);
+static int       stream_flush      (CamelStream *stream);
+
+static void
+camel_stream_process_finalise (CamelObject *object)
+{
+	/* Ensure we clean up after ourselves -- kill
+	   the child process and reap it. */
+	stream_close(CAMEL_STREAM (object));
+}
+
+static void
+camel_stream_process_class_init (CamelStreamProcessClass *camel_stream_process_class)
+{
+	CamelStreamClass *camel_stream_class = (CamelStreamClass *)camel_stream_process_class;
+
+	parent_class = camel_type_get_global_classfuncs( CAMEL_OBJECT_TYPE );
+
+	/* virtual method definition */
+	camel_stream_class->read = stream_read;
+	camel_stream_class->write = stream_write;
+	camel_stream_class->close = stream_close;
+	camel_stream_class->flush = stream_flush;
+}
+
+static void
+camel_stream_process_init (gpointer object, gpointer klass)
+{
+        CamelStreamProcess *stream = CAMEL_STREAM_PROCESS (object);
+         
+        stream->sockfd = -1;
+	stream->childpid = 0;
+}
+
+
+CamelType
+camel_stream_process_get_type (void)
+{
+	static CamelType camel_stream_process_type = CAMEL_INVALID_TYPE;
+
+	if (camel_stream_process_type == CAMEL_INVALID_TYPE) {
+		camel_stream_process_type = 
+			camel_type_register( camel_stream_get_type(),
+					     "CamelStreamProcess",
+					     sizeof( CamelStreamProcess ),
+					     sizeof( CamelStreamProcessClass ),
+					     (CamelObjectClassInitFunc) camel_stream_process_class_init,
+					     NULL,
+					     (CamelObjectInitFunc) camel_stream_process_init,
+					     (CamelObjectFinalizeFunc) camel_stream_process_finalise);
+	}
+
+	return camel_stream_process_type;
+}
+
+/**
+ * camel_stream_process_new:
+ *
+ * Returns a PROCESS stream.
+ *
+ * Return value: the stream
+ **/
+CamelStream *
+camel_stream_process_new(void)
+{
+	return (CamelStream *)camel_object_new(camel_stream_process_get_type ());
+}
+
+
+static ssize_t
+stream_read (CamelStream *stream, char *buffer, size_t n)
+{
+	CamelStreamProcess *stream_process = CAMEL_STREAM_PROCESS (stream);
+
+	return camel_read(stream_process->sockfd, buffer, n);
+}
+
+static ssize_t
+stream_write (CamelStream *stream, const char *buffer, size_t n)
+{
+	CamelStreamProcess *stream_process = CAMEL_STREAM_PROCESS (stream);
+
+	return camel_write(stream_process->sockfd, buffer, n);
+}
+
+static int
+stream_flush (CamelStream *stream)
+{
+	return 0;
+}
+
+static int
+stream_close (CamelStream *object)
+{
+	CamelStreamProcess *stream = CAMEL_STREAM_PROCESS (object);
+	if (camel_verbose_debug)
+		fprintf(stderr, "Process stream close. sockfd %d, childpid %d\n",
+			stream->sockfd, stream->childpid);
+
+	if (stream->sockfd != -1) {
+		close(stream->sockfd);
+		stream->sockfd = -1;
+	}
+	if (stream->childpid) {
+		int ret, i;
+		for (i=0; i<4; i++) {
+			ret = waitpid(stream->childpid, NULL, WNOHANG);
+			if (camel_verbose_debug)
+				fprintf(stderr, "waitpid() for pid %d returned %d (errno %d)\n",
+					stream->childpid, ret, ret==-1?errno:0);
+			if (ret == stream->childpid || errno == ECHILD)
+				break;
+			switch(i) {
+			case 0:
+				if (camel_verbose_debug)
+					fprintf(stderr, "Sending SIGTERM to pid %d\n",
+						stream->childpid);
+				kill(stream->childpid, SIGTERM);
+				break;
+			case 2:
+				if (camel_verbose_debug)
+					fprintf(stderr, "Sending SIGKILL to pid %d\n",
+						stream->childpid);
+				kill(stream->childpid, SIGKILL);
+				break;
+			case 1:
+			case 3:
+				sleep(1);
+				break;
+			}
+		}
+		stream->childpid = 0;
+	}
+	return 0;
+}
+
+static void do_exec_command(int fd, const char *command, char **env)
+{
+	int i, maxopen;
+
+	/* Not a lot we can do if there's an error other than bail. */
+	if (dup2(fd, 0) == -1)
+		exit(1);
+	if (dup2(fd, 1) == -1)
+		exit(1);
+	
+	/* What to do with stderr? Possibly put it through a separate pipe
+	   and bring up a dialog box with its output if anything does get
+	   spewed to it? It'd help the user understand what was going wrong
+	   with their command, but it's hard to do cleanly. For now we just
+	   leave it as it is. Perhaps we should close it and reopen /dev/null? */
+
+	maxopen = sysconf(_SC_OPEN_MAX);
+	for (i=3; i < maxopen; i++)
+		close(i);
+
+	setsid();
+#ifdef TIOCNOTTY
+	/* Detach from the controlling tty if we have one. Otherwise, 
+	   SSH might do something stupid like trying to use it instead 
+	   of running $SSH_ASKPASS. Doh. */
+	fd = open("/dev/tty", O_RDONLY);
+	if (fd != -1) {
+		ioctl(fd, TIOCNOTTY, NULL);
+		close(fd);
+	}
+#endif /* TIOCNOTTY */
+
+	/* Set up child's environment. We _add_ to it, don't use execle, 
+	   because otherwise we'd destroy stuff like SSH_AUTH_SOCK etc. */
+	for (; env && *env; env++) {
+		char *eq = strchr(*env, '=');
+		if (!eq) {
+			unsetenv(*env);
+			continue;
+		}
+		*eq = 0;
+		eq++;
+		setenv(*env, eq, 1);
+	}
+
+	execl("/bin/sh", "/bin/sh", "-c", command, NULL);
+
+	if (camel_verbose_debug)
+		fprintf(stderr, "exec failed %d\n", errno);
+	exit(1);
+}
+
+int
+camel_stream_process_connect(CamelStreamProcess *stream, const char *command, const char **env)
+{
+	int sockfds[2];
+
+	if (stream->sockfd != -1 || stream->childpid) {
+		stream_close(CAMEL_STREAM (stream));
+	}
+	
+	if (socketpair(AF_UNIX, SOCK_STREAM, 0, sockfds))
+		return -1;
+
+	stream->childpid = fork();
+	if (!stream->childpid) {
+		do_exec_command(sockfds[1], command, (char **)env);
+	} else if (stream->childpid == -1) {
+		close(sockfds[0]);
+		close(sockfds[1]);
+		stream->sockfd = -1;
+		return -1;
+	}
+
+	close(sockfds[1]);
+	stream->sockfd = sockfds[0];
+
+	return 0;
+}


[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]