diff options
| author | cashmere <cashmere@autistici.org> | 2026-09-04 09:05:24 +0200 |
|---|---|---|
| committer | cashmere <cashmere@autistici.org> | 2026-09-04 09:05:24 +0200 |
| commit | 4f49b16079b27d7ce633619e4cddfdf2bffbc952 (patch) | |
| tree | c296c1e7f3c3e2f6859c6d3a1b1b208779b89679 /src/popen.c | |
| download | relic-4f49b16079b27d7ce633619e4cddfdf2bffbc952.tar.gz | |
Diffstat (limited to 'src/popen.c')
| -rw-r--r-- | src/popen.c | 230 |
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 +} |
