/* ** 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 #include /* ** Print a fatal error and quit. */ static void win32_fatal_error(const char *zMsg){ fossil_fatal("%s"); } #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( char *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 */ ){ STARTUPINFO 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 = CreateProcess( 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, int *pfdIn, FILE **ppOut, int *pChildPid){ #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((char*)zCmd, hStdinRd, hStdoutWr, hStderr,&childPid); *pChildPid = childPid; *pfdIn = _open_osfhandle((long)hStdoutRd, 0); fd = _open_osfhandle((long)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; } if( *pChildPid==0 ){ /* This is the child process */ close(0); dup(pout[0]); close(pout[0]); close(pout[1]); close(1); dup(pin[1]); close(pin[0]); close(pin[1]); 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(). Kill off the child process, then close the pipes. */ void pclose2(int fdIn, FILE *pOut, int childPid){ #ifdef _WIN32 /* Not implemented, yet */ close(fdIn); fclose(pOut); #else close(fdIn); fclose(pOut); kill(childPid, SIGINT); #endif }