Fossil SCM

fossil-scm / src / popen.c
Source Blame History 230 lines
0e42cc1… drh 1 /*
c19f34c… drh 2 ** Copyright (c) 2010 D. Richard Hipp
0e42cc1… drh 3 **
0e42cc1… drh 4 ** This program is free software; you can redistribute it and/or
0e42cc1… drh 5 ** modify it under the terms of the Simplified BSD License (also
0e42cc1… drh 6 ** known as the "2-Clause License" or "FreeBSD License".)
0e42cc1… drh 7
0e42cc1… drh 8 ** This program is distributed in the hope that it will be useful,
0e42cc1… drh 9 ** but without any warranty; without even the implied warranty of
0e42cc1… drh 10 ** merchantability or fitness for a particular purpose.
0e42cc1… drh 11 **
0e42cc1… drh 12 ** Author contact information:
0e42cc1… drh 13 ** [email protected]
0e42cc1… drh 14 ** http://www.hwaci.com/drh/
0e42cc1… drh 15 **
0e42cc1… drh 16 *******************************************************************************
0e42cc1… drh 17 **
0e42cc1… drh 18 ** This file contains an implementation of a bi-directional popen().
0e42cc1… drh 19 */
0e42cc1… drh 20 #include "config.h"
0e42cc1… drh 21 #include "popen.h"
0e42cc1… drh 22
3564af0… drh 23 #ifdef _WIN32
3564af0… drh 24 #include <windows.h>
3564af0… drh 25 #include <fcntl.h>
0a51263… drh 26 /*
0a51263… drh 27 ** Print a fatal error and quit.
0a51263… drh 28 */
0a51263… drh 29 static void win32_fatal_error(const char *zMsg){
99fcc43… drh 30 fossil_fatal("%s", zMsg);
0a51263… drh 31 }
dbb5e2d… drh 32 #else
dbb5e2d… drh 33 #include <signal.h>
dbb5e2d… drh 34 #include <sys/wait.h>
3564af0… drh 35 #endif
3564af0… drh 36
c1f6df7… drh 37 /*
c1f6df7… drh 38 ** The following macros are used to cast pointers to integers and
c1f6df7… drh 39 ** integers to pointers. The way you do this varies from one compiler
c1f6df7… drh 40 ** to the next, so we have developed the following set of #if statements
c1f6df7… drh 41 ** to generate appropriate macros for a wide range of compilers.
c1f6df7… drh 42 **
fc3d9f5… jan.nijtmans 43 ** The correct "ANSI" way to do this is to use the intptr_t type.
c1f6df7… drh 44 ** Unfortunately, that typedef is not available on all compilers, or
c1f6df7… drh 45 ** if it is available, it requires an #include of specific headers
c1f6df7… drh 46 ** that vary from one machine to the next.
c1f6df7… drh 47 **
c1f6df7… drh 48 ** This code is copied out of SQLite.
c1f6df7… drh 49 */
c1f6df7… drh 50 #if defined(__PTRDIFF_TYPE__) /* This case should work for GCC */
c1f6df7… drh 51 # define INT_TO_PTR(X) ((void*)(__PTRDIFF_TYPE__)(X))
c1f6df7… drh 52 # define PTR_TO_INT(X) ((int)(__PTRDIFF_TYPE__)(X))
c1f6df7… drh 53 #elif !defined(__GNUC__) /* Works for compilers other than LLVM */
c1f6df7… drh 54 # define INT_TO_PTR(X) ((void*)&((char*)0)[X])
c1f6df7… drh 55 # define PTR_TO_INT(X) ((int)(((char*)X)-(char*)0))
c1f6df7… drh 56 #elif defined(HAVE_STDINT_H) /* Use this case if we have ANSI headers */
c1f6df7… drh 57 # define INT_TO_PTR(X) ((void*)(intptr_t)(X))
c1f6df7… drh 58 # define PTR_TO_INT(X) ((int)(intptr_t)(X))
c1f6df7… drh 59 #else /* Generates a warning - but it always works */
c1f6df7… drh 60 # define INT_TO_PTR(X) ((void*)(X))
c1f6df7… drh 61 # define PTR_TO_INT(X) ((int)(X))
c1f6df7… drh 62 #endif
0a51263… drh 63
0a51263… drh 64
3564af0… drh 65 #ifdef _WIN32
0a51263… drh 66 /*
0a51263… drh 67 ** On windows, create a child process and specify the stdin, stdout,
0a51263… drh 68 ** and stderr channels for that process to use.
0a51263… drh 69 **
0a51263… drh 70 ** Return the number of errors.
0a51263… drh 71 */
0a51263… drh 72 static int win32_create_child_process(
0c7ae64… drh 73 wchar_t *zCmd, /* The command that the child process will run */
0a51263… drh 74 HANDLE hIn, /* Standard input */
0a51263… drh 75 HANDLE hOut, /* Standard output */
0a51263… drh 76 HANDLE hErr, /* Standard error */
0a51263… drh 77 DWORD *pChildPid /* OUT: Child process handle */
0a51263… drh 78 ){
0c7ae64… drh 79 STARTUPINFOW si;
0a51263… drh 80 PROCESS_INFORMATION pi;
0a51263… drh 81 BOOL rc;
0a51263… drh 82
0a51263… drh 83 memset(&si, 0, sizeof(si));
0a51263… drh 84 si.cb = sizeof(si);
0a51263… drh 85 si.dwFlags = STARTF_USESTDHANDLES;
0a51263… drh 86 SetHandleInformation(hIn, HANDLE_FLAG_INHERIT, TRUE);
0a51263… drh 87 si.hStdInput = hIn;
0a51263… drh 88 SetHandleInformation(hOut, HANDLE_FLAG_INHERIT, TRUE);
0a51263… drh 89 si.hStdOutput = hOut;
0a51263… drh 90 SetHandleInformation(hErr, HANDLE_FLAG_INHERIT, TRUE);
0a51263… drh 91 si.hStdError = hErr;
0c7ae64… drh 92 rc = CreateProcessW(
0a51263… drh 93 NULL, /* Application Name */
0a51263… drh 94 zCmd, /* Command-line */
0a51263… drh 95 NULL, /* Process attributes */
0a51263… drh 96 NULL, /* Thread attributes */
0a51263… drh 97 TRUE, /* Inherit Handles */
0a51263… drh 98 0, /* Create flags */
0a51263… drh 99 NULL, /* Environment */
0a51263… drh 100 NULL, /* Current directory */
0a51263… drh 101 &si, /* Startup Info */
0a51263… drh 102 &pi /* Process Info */
0a51263… drh 103 );
0a51263… drh 104 if( rc ){
0a51263… drh 105 CloseHandle( pi.hProcess );
0a51263… drh 106 CloseHandle( pi.hThread );
0a51263… drh 107 *pChildPid = pi.dwProcessId;
0a51263… drh 108 }else{
0a51263… drh 109 win32_fatal_error("cannot create child process");
0a51263… drh 110 }
0a51263… drh 111 return rc!=0;
0a51263… drh 112 }
0a51263… drh 113 #endif
0a51263… drh 114
0e42cc1… drh 115 /*
0e42cc1… drh 116 ** Create a child process running shell command "zCmd". *ppOut is
fc3d9f5… jan.nijtmans 117 ** a FILE that becomes the standard input of the child process.
0e42cc1… drh 118 ** (The caller writes to *ppOut in order to send text to the child.)
0e42cc1… drh 119 ** *ppIn is stdout from the child process. (The caller
0e42cc1… drh 120 ** reads from *ppIn in order to receive input from the child.)
0a51263… drh 121 ** Note that *ppIn is an unbuffered file descriptor, not a FILE.
0e42cc1… drh 122 ** The process ID of the child is written into *pChildPid.
0e42cc1… drh 123 **
0e42cc1… drh 124 ** Return the number of errors.
0e42cc1… drh 125 */
ec56c69… drh 126 int popen2(
ec56c69… drh 127 const char *zCmd, /* Command to run in the child process */
ec56c69… drh 128 int *pfdIn, /* Read from child using this file descriptor */
ec56c69… drh 129 FILE **ppOut, /* Write to child using this file descriptor */
ec56c69… drh 130 int *pChildPid, /* PID of the child process */
ec56c69… drh 131 int bDirect /* 0: run zCmd as a shell cmd. 1: run directly */
ec56c69… drh 132 ){
3564af0… drh 133 #ifdef _WIN32
274079a… drh 134 HANDLE hStdinRd, hStdinWr, hStdoutRd, hStdoutWr, hStderr;
fc3d9f5… jan.nijtmans 135 SECURITY_ATTRIBUTES saAttr;
3c326ea… ron 136 DWORD childPid = 0;
0a51263… drh 137 int fd;
0a51263… drh 138
0a51263… drh 139 saAttr.nLength = sizeof(saAttr);
0a51263… drh 140 saAttr.bInheritHandle = TRUE;
fc3d9f5… jan.nijtmans 141 saAttr.lpSecurityDescriptor = NULL;
274079a… drh 142 hStderr = GetStdHandle(STD_ERROR_HANDLE);
0a51263… drh 143 if( !CreatePipe(&hStdoutRd, &hStdoutWr, &saAttr, 4096) ){
0a51263… drh 144 win32_fatal_error("cannot create pipe for stdout");
0a51263… drh 145 }
0a51263… drh 146 SetHandleInformation( hStdoutRd, HANDLE_FLAG_INHERIT, FALSE);
0a51263… drh 147
0a51263… drh 148 if( !CreatePipe(&hStdinRd, &hStdinWr, &saAttr, 4096) ){
0a51263… drh 149 win32_fatal_error("cannot create pipe for stdin");
0a51263… drh 150 }
0a51263… drh 151 SetHandleInformation( hStdinWr, HANDLE_FLAG_INHERIT, FALSE);
fc3d9f5… jan.nijtmans 152
0c7ae64… drh 153 win32_create_child_process(fossil_utf8_to_unicode(zCmd),
274079a… drh 154 hStdinRd, hStdoutWr, hStderr,&childPid);
0a51263… drh 155 *pChildPid = childPid;
c1f6df7… drh 156 *pfdIn = _open_osfhandle(PTR_TO_INT(hStdoutRd), 0);
c1f6df7… drh 157 fd = _open_osfhandle(PTR_TO_INT(hStdinWr), 0);
0a51263… drh 158 *ppOut = _fdopen(fd, "w");
fc3d9f5… jan.nijtmans 159 CloseHandle(hStdinRd);
0a51263… drh 160 CloseHandle(hStdoutWr);
0a51263… drh 161 return 0;
0e42cc1… drh 162 #else
0e42cc1… drh 163 int pin[2], pout[2];
0a51263… drh 164 *pfdIn = 0;
0e42cc1… drh 165 *ppOut = 0;
0e42cc1… drh 166 *pChildPid = 0;
0e42cc1… drh 167
0e42cc1… drh 168 if( pipe(pin)<0 ){
0e42cc1… drh 169 return 1;
0e42cc1… drh 170 }
0e42cc1… drh 171 if( pipe(pout)<0 ){
0e42cc1… drh 172 close(pin[0]);
0e42cc1… drh 173 close(pin[1]);
0e42cc1… drh 174 return 1;
0e42cc1… drh 175 }
0e42cc1… drh 176 *pChildPid = fork();
0e42cc1… drh 177 if( *pChildPid<0 ){
0e42cc1… drh 178 close(pin[0]);
0e42cc1… drh 179 close(pin[1]);
0e42cc1… drh 180 close(pout[0]);
0e42cc1… drh 181 close(pout[1]);
0e42cc1… drh 182 *pChildPid = 0;
0e42cc1… drh 183 return 1;
0e42cc1… drh 184 }
dbb5e2d… drh 185 signal(SIGPIPE,SIG_IGN);
0e42cc1… drh 186 if( *pChildPid==0 ){
553159a… drh 187 int fd;
0e42cc1… drh 188 /* This is the child process */
0e42cc1… drh 189 close(0);
553159a… drh 190 fd = dup(pout[0]);
e486a0a… drh 191 if( fd!=0 ) fossil_panic("popen() failed to open file descriptor 0");
0e42cc1… drh 192 close(pout[0]);
0e42cc1… drh 193 close(pout[1]);
0e42cc1… drh 194 close(1);
553159a… drh 195 fd = dup(pin[1]);
e486a0a… drh 196 if( fd!=1 ) fossil_panic("popen() failed to open file descriptor 1");
0e42cc1… drh 197 close(pin[0]);
0e42cc1… drh 198 close(pin[1]);
ec56c69… drh 199 if( bDirect ){
ec56c69… drh 200 execl(zCmd, zCmd, (char*)0);
ec56c69… drh 201 }else{
ec56c69… drh 202 execl("/bin/sh", "/bin/sh", "-c", zCmd, (char*)0);
ec56c69… drh 203 }
0e42cc1… drh 204 return 1;
0e42cc1… drh 205 }else{
0e42cc1… drh 206 /* This is the parent process */
0e42cc1… drh 207 close(pin[1]);
0a51263… drh 208 *pfdIn = pin[0];
0e42cc1… drh 209 close(pout[0]);
0e42cc1… drh 210 *ppOut = fdopen(pout[1], "w");
0e42cc1… drh 211 return 0;
0e42cc1… drh 212 }
0e42cc1… drh 213 #endif
0e42cc1… drh 214 }
0e42cc1… drh 215
0e42cc1… drh 216 /*
0e42cc1… drh 217 ** Close the connection to a child process previously created using
0e8f0bc… andybradford 218 ** popen2().
0e42cc1… drh 219 */
0a51263… drh 220 void pclose2(int fdIn, FILE *pOut, int childPid){
3564af0… drh 221 #ifdef _WIN32
0e42cc1… drh 222 /* Not implemented, yet */
0cdb640… drh 223 close(fdIn);
0cdb640… drh 224 fclose(pOut);
0e42cc1… drh 225 #else
0a51263… drh 226 close(fdIn);
0e42cc1… drh 227 fclose(pOut);
dbb5e2d… drh 228 while( waitpid(0, 0, WNOHANG)>0 ) {}
0e42cc1… drh 229 #endif
0e42cc1… drh 230 }

Keyboard Shortcuts

Open search /
Next entry (timeline) j
Previous entry (timeline) k
Open focused entry Enter
Show this help ?
Toggle theme Top nav button