summaryrefslogtreecommitdiff
path: root/src/popen.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/popen.c')
-rw-r--r--src/popen.c230
1 files changed, 230 insertions, 0 deletions
diff --git a/src/popen.c b/src/popen.c
new file mode 100644
index 0000000..8bfd935
--- /dev/null
+++ b/src/popen.c
@@ -0,0 +1,230 @@
+/*
+** Copyright (c) 2010 D. Richard Hipp
+**
+** This program is free software; you can redistribute it and/or
+** modify it under the terms of the Simplified BSD License (also
+** known as the "2-Clause License" or "FreeBSD License".)
+
+** 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.
+**
+** Author contact information:
+** drh@hwaci.com
+** http://www.hwaci.com/drh/
+**
+*******************************************************************************
+**
+** This file contains an implementation of a bi-directional popen().
+*/
+#include "config.h"
+#include "popen.h"
+
+#ifdef _WIN32
+#include <windows.h>
+#include <fcntl.h>
+/*
+** Print a fatal error and quit.
+*/
+static void win32_fatal_error(const char *zMsg){
+ fossil_fatal("%s", zMsg);
+}
+#else
+#include <signal.h>
+#include <sys/wait.h>
+#endif
+
+/*
+** The following macros are used to cast pointers to integers and
+** integers to pointers. The way you do this varies from one compiler
+** to the next, so we have developed the following set of #if statements
+** to generate appropriate macros for a wide range of compilers.
+**
+** The correct "ANSI" way to do this is to use the intptr_t type.
+** Unfortunately, that typedef is not available on all compilers, or
+** if it is available, it requires an #include of specific headers
+** that vary from one machine to the next.
+**
+** This code is copied out of SQLite.
+*/
+#if defined(__PTRDIFF_TYPE__) /* This case should work for GCC */
+# define INT_TO_PTR(X) ((void*)(__PTRDIFF_TYPE__)(X))
+# define PTR_TO_INT(X) ((int)(__PTRDIFF_TYPE__)(X))
+#elif !defined(__GNUC__) /* Works for compilers other than LLVM */
+# define INT_TO_PTR(X) ((void*)&((char*)0)[X])
+# define PTR_TO_INT(X) ((int)(((char*)X)-(char*)0))
+#elif defined(HAVE_STDINT_H) /* Use this case if we have ANSI headers */
+# define INT_TO_PTR(X) ((void*)(intptr_t)(X))
+# define PTR_TO_INT(X) ((int)(intptr_t)(X))
+#else /* Generates a warning - but it always works */
+# define INT_TO_PTR(X) ((void*)(X))
+# define PTR_TO_INT(X) ((int)(X))
+#endif
+
+
+#ifdef _WIN32
+/*
+** On windows, create a child process and specify the stdin, stdout,
+** and stderr channels for that process to use.
+**
+** Return the number of errors.
+*/
+static int win32_create_child_process(
+ wchar_t *zCmd, /* The command that the child process will run */
+ HANDLE hIn, /* Standard input */
+ HANDLE hOut, /* Standard output */
+ HANDLE hErr, /* Standard error */
+ DWORD *pChildPid /* OUT: Child process handle */
+){
+ STARTUPINFOW si;
+ PROCESS_INFORMATION pi;
+ BOOL rc;
+
+ memset(&si, 0, sizeof(si));
+ si.cb = sizeof(si);
+ si.dwFlags = STARTF_USESTDHANDLES;
+ SetHandleInformation(hIn, HANDLE_FLAG_INHERIT, TRUE);
+ si.hStdInput = hIn;
+ SetHandleInformation(hOut, HANDLE_FLAG_INHERIT, TRUE);
+ si.hStdOutput = hOut;
+ SetHandleInformation(hErr, HANDLE_FLAG_INHERIT, TRUE);
+ si.hStdError = hErr;
+ rc = CreateProcessW(
+ NULL, /* Application Name */
+ zCmd, /* Command-line */
+ NULL, /* Process attributes */
+ NULL, /* Thread attributes */
+ TRUE, /* Inherit Handles */
+ 0, /* Create flags */
+ NULL, /* Environment */
+ NULL, /* Current directory */
+ &si, /* Startup Info */
+ &pi /* Process Info */
+ );
+ if( rc ){
+ CloseHandle( pi.hProcess );
+ CloseHandle( pi.hThread );
+ *pChildPid = pi.dwProcessId;
+ }else{
+ win32_fatal_error("cannot create child process");
+ }
+ return rc!=0;
+}
+#endif
+
+/*
+** Create a child process running shell command "zCmd". *ppOut is
+** a FILE that becomes the standard input of the child process.
+** (The caller writes to *ppOut in order to send text to the child.)
+** *ppIn is stdout from the child process. (The caller
+** reads from *ppIn in order to receive input from the child.)
+** Note that *ppIn is an unbuffered file descriptor, not a FILE.
+** The process ID of the child is written into *pChildPid.
+**
+** Return the number of errors.
+*/
+int popen2(
+ const char *zCmd, /* Command to run in the child process */
+ int *pfdIn, /* Read from child using this file descriptor */
+ FILE **ppOut, /* Write to child using this file descriptor */
+ int *pChildPid, /* PID of the child process */
+ int bDirect /* 0: run zCmd as a shell cmd. 1: run directly */
+){
+#ifdef _WIN32
+ HANDLE hStdinRd, hStdinWr, hStdoutRd, hStdoutWr, hStderr;
+ SECURITY_ATTRIBUTES saAttr;
+ DWORD childPid = 0;
+ int fd;
+
+ saAttr.nLength = sizeof(saAttr);
+ saAttr.bInheritHandle = TRUE;
+ saAttr.lpSecurityDescriptor = NULL;
+ hStderr = GetStdHandle(STD_ERROR_HANDLE);
+ if( !CreatePipe(&hStdoutRd, &hStdoutWr, &saAttr, 4096) ){
+ win32_fatal_error("cannot create pipe for stdout");
+ }
+ SetHandleInformation( hStdoutRd, HANDLE_FLAG_INHERIT, FALSE);
+
+ if( !CreatePipe(&hStdinRd, &hStdinWr, &saAttr, 4096) ){
+ win32_fatal_error("cannot create pipe for stdin");
+ }
+ SetHandleInformation( hStdinWr, HANDLE_FLAG_INHERIT, FALSE);
+
+ win32_create_child_process(fossil_utf8_to_unicode(zCmd),
+ hStdinRd, hStdoutWr, hStderr,&childPid);
+ *pChildPid = childPid;
+ *pfdIn = _open_osfhandle(PTR_TO_INT(hStdoutRd), 0);
+ fd = _open_osfhandle(PTR_TO_INT(hStdinWr), 0);
+ *ppOut = _fdopen(fd, "w");
+ CloseHandle(hStdinRd);
+ CloseHandle(hStdoutWr);
+ return 0;
+#else
+ int pin[2], pout[2];
+ *pfdIn = 0;
+ *ppOut = 0;
+ *pChildPid = 0;
+
+ if( pipe(pin)<0 ){
+ return 1;
+ }
+ if( pipe(pout)<0 ){
+ close(pin[0]);
+ close(pin[1]);
+ return 1;
+ }
+ *pChildPid = fork();
+ if( *pChildPid<0 ){
+ close(pin[0]);
+ close(pin[1]);
+ close(pout[0]);
+ close(pout[1]);
+ *pChildPid = 0;
+ return 1;
+ }
+ signal(SIGPIPE,SIG_IGN);
+ if( *pChildPid==0 ){
+ int fd;
+ /* This is the child process */
+ close(0);
+ fd = dup(pout[0]);
+ if( fd!=0 ) fossil_panic("popen() failed to open file descriptor 0");
+ close(pout[0]);
+ close(pout[1]);
+ close(1);
+ fd = dup(pin[1]);
+ if( fd!=1 ) fossil_panic("popen() failed to open file descriptor 1");
+ close(pin[0]);
+ close(pin[1]);
+ if( bDirect ){
+ execl(zCmd, zCmd, (char*)0);
+ }else{
+ execl("/bin/sh", "/bin/sh", "-c", zCmd, (char*)0);
+ }
+ return 1;
+ }else{
+ /* This is the parent process */
+ close(pin[1]);
+ *pfdIn = pin[0];
+ close(pout[0]);
+ *ppOut = fdopen(pout[1], "w");
+ return 0;
+ }
+#endif
+}
+
+/*
+** Close the connection to a child process previously created using
+** popen2().
+*/
+void pclose2(int fdIn, FILE *pOut, int childPid){
+#ifdef _WIN32
+ /* Not implemented, yet */
+ close(fdIn);
+ fclose(pOut);
+#else
+ close(fdIn);
+ fclose(pOut);
+ while( waitpid(0, 0, WNOHANG)>0 ) {}
+#endif
+}