/*-*- mode: C; tab-width:4 -*-*/ /* execve,select */ #ifndef __MINGW32__ #ifdef HAVE_SYS_TIME_H #include #endif #ifdef HAVE_SYS_SELECT_H #include #endif #endif /* opendir */ #ifndef __MINGW32__ #include #endif /* stat, mkdir */ #include #include #include /* fcntl */ #include /* strings */ #include /* setenv */ #include /* signal */ #include /* poll */ #include /* wait */ #include /* miscellaneous */ #include #include #include /* path separator */ #include "php_wrapper.h" #include "zend.h" #include "php_java.h" #include "java_bridge.h" #ifndef EXTENSION_DIR #error EXTENSION_DIR must point to the PHP extension directory #endif const static char inet_socket_prefix[]="INET_LOCAL:"; const static char local_socket_prefix[]="LOCAL:"; const static char ext_dir[] = "extension_dir"; EXT_EXTERN_MODULE_GLOBALS(EXT) static short use_wrapper(char*wrapper) { struct stat buf; short use_wrapper=(EXT_GLOBAL(option_set_by_user) (U_WRAPPER, EXT_GLOBAL(ini_user))); if(use_wrapper) return use_wrapper; #ifndef __MINGW32__ if(!stat(wrapper, &buf) && (S_IFREG&buf.st_mode)) { if(getuid()==buf.st_uid) use_wrapper=(S_IXUSR&buf.st_mode); else if(getgid()==buf.st_gid) use_wrapper=(S_IXGRP&buf.st_mode); else use_wrapper=(S_IXOTH&buf.st_mode); } #endif return use_wrapper; } /** * Check if the file ext_dir/javabridge.policy exists * and return an allocated string containing s+ext_dir/javabridge.policy * or NULL. * @param s The prefix, for example "java.security.policy=" */ static char *check_policy(char *s) { struct stat buf; short use_policy = 0; char *p = 0; #ifndef __MINGW32__ const static char bridge[]="/javabridge.policy"; char *slash, *ext= php_ini_string((char*)ext_dir, sizeof ext_dir, 0); size_t slen = strlen(s); p=malloc(slen+strlen(ext)+sizeof(bridge)); if(!p) return 0; strcpy(p, s); strcat(p, ext); slash=p+strlen(p)-1; if(*p&&(*slash=='/'||*slash=='\\')) *slash=0; strcat(p, bridge); /* check if it exists */ if(!stat(p+slen, &buf) && (S_IFREG&buf.st_mode)) { if(getuid()==buf.st_uid) use_policy=(S_IRUSR&buf.st_mode); else if(getgid()==buf.st_gid) use_policy=(S_IRGRP&buf.st_mode); else use_policy=(S_IROTH&buf.st_mode); } if(!use_policy) { free(p); p=0; } #endif return p; } /* Windows can handle slashes as well as backslashes so use / everywhere */ static const char path_separator[2] = {ZEND_PATHS_SEPARATOR, 0}; static const char bridge_base[] = "-Dphp.java.bridge.base="; #if EXTENSION == JAVA static void EXT_GLOBAL(get_server_args)(char*env[N_SENV], char*args[N_SARGS], short for_display TSRMLS_DC) { char *s, *p; char*program=EXT_GLOBAL(cfg)->vm; char*cp=EXT_GLOBAL(cfg)->classpath; char*lib_path=EXT_GLOBAL(cfg)->ld_library_path; char*sys_libpath=getenv("LD_LIBRARY_PATH"); char*home=EXT_GLOBAL(cfg)->vm_home; char *ext = php_ini_string((char*)ext_dir, sizeof ext_dir, 0); #ifdef CFG_JAVA_SOCKET_INET const char* s_prefix = inet_socket_prefix; short any_port = 1; /* let back-end select the port# */ #else const char *s_prefix = local_socket_prefix; short any_port = for_display; #endif char *sockname, *cfg_sockname=EXT_GLOBAL(get_sockname)(TSRMLS_C), *cfg_logFile=EXT_GLOBAL(cfg)->logFile; /* if socketname is off, show the user how to start a TCP backend */ if(any_port && !(EXT_GLOBAL(option_set_by_user) (U_SOCKNAME, EXT_GLOBAL(ini_user)))) { cfg_sockname="0"; s_prefix=inet_socket_prefix; //cfg_logFile=""; } /* send a prefix so that the server does not select a different protocol */ sockname = malloc(strlen(s_prefix)+strlen(cfg_sockname)+1); strcpy(sockname, s_prefix); strcat(sockname, cfg_sockname); /* library path usually points to the extension dir */ if(!(EXT_GLOBAL(option_set_by_user) (U_LIBRARY_PATH, EXT_GLOBAL(ini_user)))) { /* look into extension_dir then */ if(ext) lib_path = ext; } if(!*program) { /* look into extension_dir then */ static const char java[] = "/java"; if(ext) { program = malloc(strlen(ext)+sizeof(java)); assert(program); if(!program) exit(6); strcpy(program, ext); strcat(program, java); } else { program = strdup(program); } } else { program = strdup(program); } if(!sys_libpath) sys_libpath=""; args[0]=program; s="-Djava.library.path="; p=malloc(strlen(s)+strlen(lib_path)+1); strcpy(p, s); strcat(p, lib_path); args[1] = p; /* library path */ s="-Djava.class.path="; /* library path usually points to the extension dir */ if(ext && !(EXT_GLOBAL(option_set_by_user) (U_CLASSPATH, EXT_GLOBAL(ini_user)))) { /* look into extension_dir then */ static char bridge[]="/JavaBridge.jar"; char *slash; p=malloc(strlen(s)+strlen(ext)+sizeof(bridge)); strcpy(p, s); strcat(p, ext); slash=p+strlen(p)-1; if(*p&&(*slash=='/'||*slash=='\\')) *slash=0; strcat(p, bridge); } else { p=malloc(strlen(s)+strlen(cp)+1); strcpy(p, s); strcat(p, cp); } args[2] = p; /* user classes */ /* policy */ s="-Djava.security.policy="; p=0; if(!p && EXT_GLOBAL(option_set_by_user) (U_POLICY, EXT_GLOBAL(ini_user))) { char *cp = EXT_GLOBAL(cfg)->policy; if(*cp==0||cp[1]==0) { /* policy=On (stored as '\0' or '1\0') */ p = check_policy(s); } else { p=malloc(strlen(s)+strlen(cp)+1); strcpy(p, s); strcat(p, cp); } } if(!p) { /* no policy at all */ p = strdup("-Djava.awt.headless=true"); } args[3] = p; /* base */ p = malloc(strlen(ext)+sizeof(bridge_base)); strcpy(p, bridge_base); strcat(p, ext); args[4] = p; args[5] = strdup("php.java.bridge.Standalone"); args[6] = sockname; args[7] = strdup(EXT_GLOBAL(cfg)->logLevel); args[8] = strdup(cfg_logFile); args[9] = NULL; if(*home) { /* set VM home */ s="JAVA_HOME="; p=malloc(strlen(s)+strlen(home)+1); strcpy(p, s); strcat(p, home); env[0] = p; } else { /* VM in PATH; don't set java home */ env[0] = strdup(""); } s="LD_LIBRARY_PATH="; p=malloc(strlen(s)+strlen(lib_path)+1+strlen(sys_libpath)+1); strcpy(p, s); strcat(p, lib_path); strcat(p, path_separator); strcat(p, sys_libpath); env[1] = p; /* library path */ env[2] = NULL; } #elif EXTENSION == MONO static void EXT_GLOBAL(get_server_args)(char*env[N_SENV], char*args[N_SARGS], short for_display TSRMLS_DC) { static const char executable[] = "/MonoBridge.exe"; char *p, *slash; char*program=EXT_GLOBAL(cfg)->vm; #ifdef CFG_JAVA_SOCKET_INET const char* s_prefix = inet_socket_prefix; short any_port = 1; /* let back-end select the port# */ #else const char *s_prefix = local_socket_prefix; short any_port = for_display; #endif char *sockname, *cfg_sockname=EXT_GLOBAL(get_sockname)(TSRMLS_C), *cfg_logFile=EXT_GLOBAL(cfg)->logFile; char*home = EXT_GLOBAL(cfg)->vm_home; if(!(EXT_GLOBAL(option_set_by_user) (U_JAVA_HOME, EXT_GLOBAL(ini_user)))) { /* look into extension_dir then */ char *ext = php_ini_string((char*)ext_dir, sizeof ext_dir, 0); if(ext) home = ext; } args[0]=strdup(program); /* mono */ p=malloc(strlen(home)+sizeof executable); strcpy(p, home); slash=p+strlen(p)-1; if(*p&&(*slash=='/'||*slash=='\\')) *slash=0; strcat(p, executable); args[1] = p; /* if socketname is off, show the user how to start a TCP backend */ if(any_port && !(EXT_GLOBAL(option_set_by_user) (U_SOCKNAME, EXT_GLOBAL(ini_user)))) { cfg_sockname="0"; s_prefix=inet_socket_prefix; //cfg_logFile=""; } /* send a prefix so that the server does not select a different */ /* channel */ sockname = malloc(strlen(s_prefix)+strlen(cfg_sockname)+1); strcpy(sockname, s_prefix); strcat(sockname, cfg_sockname); args[2] = sockname; args[3] = strdup(EXT_GLOBAL(cfg)->logLevel); args[4] = strdup(cfg_logFile); args[5] = NULL; env[0] = NULL; } #endif /* * Get a string of the server arguments. Useful for display only. */ static char*get_server_string(short for_display TSRMLS_DC) { short must_use_wrapper = use_wrapper(EXT_GLOBAL(cfg)->wrapper); int i; char*s; char*env[N_SENV]; char*args[N_SARGS]; unsigned int length = 0; EXT_GLOBAL(get_server_args)(env, args, for_display TSRMLS_CC); if(must_use_wrapper) length+=strlen(EXT_GLOBAL(cfg)->wrapper)+1; #ifndef __MINGW32__ for(i=0; i< (sizeof env)/(sizeof*env); i++) { if(!env[i]) break; length+=strlen(env[i])+1; } #endif for(i=0; i< (sizeof args)/(sizeof*args); i++) { size_t l; if(!args[i]) break; l=strlen(args[i]); length+=(l?l:2)+1; } s=malloc(length+1); assert(s); if(!s) exit(9); *s=0; #ifndef __MINGW32__ for(i=0; i< (sizeof env)/(sizeof*env); i++) { if(!env[i]) break; strcat(s, env[i]); strcat(s, " "); free(env[i]); } #endif if(must_use_wrapper) { strcat(s, EXT_GLOBAL(cfg)->wrapper); strcat(s, " "); } for(i=0; i< (sizeof args)/(sizeof*args); i++) { if(!args[i]) break; strcat(s, args[i]); strcat(s, " "); free(args[i]); } s[length]=0; return s; } char*EXT_GLOBAL(get_server_string)(TSRMLS_D) { return get_server_string(1 TSRMLS_CC); } static void exec_vm(TSRMLS_D) { static char*env[N_SENV]; static char*_args[N_SARGS+1]; char **args=_args+1, *cmd; EXT_GLOBAL(get_server_args)(env, args, 0 TSRMLS_CC); if(N_SENV>2) { if(*env[0]) putenv(env[0]); if(*env[1]) putenv(env[1]); } if(use_wrapper(EXT_GLOBAL(cfg)->wrapper)) { *--args = strdup(EXT_GLOBAL(cfg)->wrapper); execv(args[0], args); } if(*args[0]=='/') execv(args[0], args); else execvp(args[0], args); /* exec failed */ cmd = get_server_string(0 TSRMLS_CC); php_error(E_WARNING, "php_mod_"/**/EXT_NAME()/**/"(%d) system error: Could not execute backend: %s: %s", 105, cmd, strerror(errno)); free(cmd); } static const int is_true = 1; static int test_local_server(void) { int sock, n; #ifndef CFG_JAVA_SOCKET_INET sock = socket (PF_LOCAL, SOCK_STREAM, 0); #else sock = socket (PF_INET, SOCK_STREAM, 0); if(sock!=-1) setsockopt(sock, 0x6, TCP_NODELAY, (void*)&is_true, sizeof is_true); #endif if(sock==-1) return -1; n = connect(sock,(struct sockaddr*)&EXT_GLOBAL(cfg)->saddr, sizeof EXT_GLOBAL(cfg)->saddr); if(n==-1) { close(sock); return -1; } return sock; } /* return 0 if user has hard-coded the socketname */ static short can_fork(void) { return EXT_GLOBAL(cfg)->can_fork; } /* * Test for a running server. Return the server name and the socket * if _socket!=NULL. If all ckecks fail a local backend is started. */ char* EXT_GLOBAL(test_server)(int *_socket, short *local, struct sockaddr*_saddr TSRMLS_DC) { int sock; short called_from_init = !(local || _socket); /* java.servlet=On forces java.socketname Off */ short socketname_set = EXT_GLOBAL(cfg)->socketname_set && EXT_GLOBAL(option_set_by_user) (U_SOCKNAME, JG(ini_user)) && !(EXT_GLOBAL(option_set_by_user) (U_SERVLET, JG(ini_user))); if(local) *local=0; /* check for local server if socketname set or (socketname not set and hosts not set), in which case we may have started a local backend ourselfs. Do not check if socketname not set and we are called from init, in which case we know that a local backend is not running. */ if (((socketname_set || can_fork()) && (socketname_set || !called_from_init)) && -1!=(sock=test_local_server()) ) { if(_socket) { *_socket=sock; } else { close(sock); } if(local) *local=1; return strdup(EXT_GLOBAL(get_sockname)(TSRMLS_C)); } /* host list */ if(JG(hosts) && *(JG(hosts))) { char *host, *hosts = strdup(JG(hosts)); assert(hosts); if(!hosts) return 0; for(host=strtok(hosts, "; "); host; host=strtok(0, "; ")) { struct sockaddr_in saddr; char *_port = strrchr(host, ':'), *ret; int port = 0; if(_port) { *_port++=0; if(strlen(_port)) port=atoi(_port); } if(!port) port=atoi(DEFAULT_PORT); memset(&saddr, 0, sizeof saddr); saddr.sin_family = AF_INET; saddr.sin_port=htons(port); #ifndef __MINGW32__ if(!isdigit(*host)) { struct hostent *hostent = gethostbyname(host); if(hostent) { memcpy(&saddr.sin_addr,hostent->h_addr,sizeof(struct in_addr)); } else { inet_aton(host, &saddr.sin_addr); } } else { inet_aton(host, &saddr.sin_addr); } #else saddr.sin_addr.s_addr = inet_addr(host); #endif sock = socket (PF_INET, SOCK_STREAM, 0); if(-1==sock) continue; if (-1==connect(sock,(struct sockaddr*)&saddr, sizeof (struct sockaddr))) { close(sock); continue; } if(_socket) { *_socket=sock; setsockopt(sock, 0x6, TCP_NODELAY, (void*)&is_true, sizeof is_true); } else close(sock); if(_port) _port[-1]=':'; ret = strdup(host); free(hosts); if(_saddr) memcpy(_saddr, &saddr, sizeof (struct sockaddr)); if(EXT_GLOBAL(cfg)->socketname_set) EXT_GLOBAL(cfg)->socketname_set = 0; return ret; } free(hosts); } socketname_set = EXT_GLOBAL(option_set_by_user) (U_SOCKNAME, JG(ini_user)) ; if (((socketname_set || can_fork()) && (socketname_set || !called_from_init)) && -1!=(sock=test_local_server()) ) { if(_socket) { *_socket=sock; } else { close(sock); } if(local) *local=1; if(!EXT_GLOBAL(cfg)->socketname_set) EXT_GLOBAL(cfg)->socketname_set = 1; return strdup(EXT_GLOBAL(get_sockname)(TSRMLS_C)); } return 0; } static const long timeout = 50000l; /* ys */ static void sleep_ms() { struct timeval timeval = {0l, timeout}; select(0, 0, 0, 0, &timeval); } static int wait_server(void) { #ifndef __MINGW32__ static const int wait_count = 30; int count=wait_count, sock; struct pollfd pollfd[1] = {{EXT_GLOBAL(cfg)->err, POLLIN, 0}}; /* wait for the server that has just started */ while(EXT_GLOBAL(cfg)->cid && -1==(sock=test_local_server()) && --count) { if(EXT_GLOBAL(cfg)->err && poll(pollfd, 1, 0)) return FAILURE; /* server terminated with error code */ sleep_ms(); } count=30; while(EXT_GLOBAL(cfg)->cid && -1==sock && -1==(sock=test_local_server()) && --count) { if(EXT_GLOBAL(cfg)->err && poll(pollfd, 1, 0)) return FAILURE; /* server terminated with error code */ php_error(E_NOTICE, "php_mod_"/**/EXT_NAME()/**/"(%d): waiting for server another %d seconds",57, count); sleep(1); } #else static const int wait_count = 5; int count=wait_count, sock; while(EXT_GLOBAL(cfg)->cid && -1==(sock=test_local_server()) && --count) { Sleep(500); } count=15; while(EXT_GLOBAL(cfg)->cid && -1==sock && -1==(sock=test_local_server()) && --count) { php_error(E_NOTICE, "php_mod_"/**/EXT_NAME()/**/"(%d): waiting for server another %d interval",57, count); Sleep(500); } #endif if(EXT_GLOBAL(cfg)->cid && count) { close(sock); return SUCCESS; } else { return FAILURE; } } /* handle keyboard interrupt */ #ifndef __MINGW32__ static int s_pid=0; static void s_kill(int sig) { if(s_pid) kill(s_pid, SIGTERM); } #else #ifndef _WIN32_WINNT # define _WIN32_WINNT 0x500 #endif #include #include #include #include static struct s_pid { short use_wrapper; PROCESS_INFORMATION p; } s_pid; /** * Unix kill emulation for windows. * From http://www.rsdn.ru/?qna/?baseserv/killproc.xml */ static BOOL WINAPI KillProcess(IN DWORD dwProcessId) { HANDLE hProcess; DWORD dwError; // first try to obtain handle to the process without the use of any // additional privileges hProcess = OpenProcess(PROCESS_TERMINATE, FALSE, dwProcessId); if (hProcess == NULL) { if (GetLastError() != ERROR_ACCESS_DENIED) return FALSE; OSVERSIONINFO osvi; // determine operating system version osvi.dwOSVersionInfoSize = sizeof(osvi); GetVersionEx(&osvi); // we cannot do anything else if this is not Windows NT if (osvi.dwPlatformId != VER_PLATFORM_WIN32_NT) return FALSE; // enable SE_DEBUG_NAME privilege and try again TOKEN_PRIVILEGES Priv, PrivOld; DWORD cbPriv = sizeof(PrivOld); HANDLE hToken; // obtain the token of the current thread if (!OpenThreadToken(GetCurrentThread(), TOKEN_QUERY|TOKEN_ADJUST_PRIVILEGES, FALSE, &hToken)) { if (GetLastError() != ERROR_NO_TOKEN) return FALSE; // revert to the process token if (!OpenProcessToken(GetCurrentProcess(), TOKEN_QUERY|TOKEN_ADJUST_PRIVILEGES, &hToken)) return FALSE; } assert(ANYSIZE_ARRAY > 0); Priv.PrivilegeCount = 1; Priv.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED; LookupPrivilegeValue(NULL, SE_DEBUG_NAME, &Priv.Privileges[0].Luid); // try to enable the privilege if (!AdjustTokenPrivileges(hToken, FALSE, &Priv, sizeof(Priv), &PrivOld, &cbPriv)) { dwError = GetLastError(); CloseHandle(hToken); return FALSE; } if (GetLastError() == ERROR_NOT_ALL_ASSIGNED) { // the SE_DEBUG_NAME privilege is not present in the caller's // token CloseHandle(hToken); return FALSE; } // try to open process handle again hProcess = OpenProcess(PROCESS_TERMINATE, FALSE, dwProcessId); dwError = GetLastError(); // restore the original state of the privilege AdjustTokenPrivileges(hToken, FALSE, &PrivOld, sizeof(PrivOld), NULL, NULL); CloseHandle(hToken); if (hProcess == NULL) return FALSE; } // terminate the process if (!TerminateProcess(hProcess, (UINT)-1)) { dwError = GetLastError(); CloseHandle(hProcess); return FALSE; } CloseHandle(hProcess); // completed successfully return TRUE; } typedef LONG NTSTATUS; typedef LONG KPRIORITY; #define NT_SUCCESS(Status) ((NTSTATUS)(Status) >= 0) #define STATUS_INFO_LENGTH_MISMATCH ((NTSTATUS)0xC0000004L) #define SystemProcessesAndThreadsInformation 5 typedef struct _CLIENT_ID { DWORD UniqueProcess; DWORD UniqueThread; } CLIENT_ID; typedef struct _UNICODE_STRING { USHORT Length; USHORT MaximumLength; PWSTR Buffer; } UNICODE_STRING; typedef struct _VM_COUNTERS { SIZE_T PeakVirtualSize; SIZE_T VirtualSize; ULONG PageFaultCount; SIZE_T PeakWorkingSetSize; SIZE_T WorkingSetSize; SIZE_T QuotaPeakPagedPoolUsage; SIZE_T QuotaPagedPoolUsage; SIZE_T QuotaPeakNonPagedPoolUsage; SIZE_T QuotaNonPagedPoolUsage; SIZE_T PagefileUsage; SIZE_T PeakPagefileUsage; } VM_COUNTERS; typedef struct _SYSTEM_THREADS { LARGE_INTEGER KernelTime; LARGE_INTEGER UserTime; LARGE_INTEGER CreateTime; ULONG WaitTime; PVOID StartAddress; CLIENT_ID ClientId; KPRIORITY Priority; KPRIORITY BasePriority; ULONG ContextSwitchCount; LONG State; LONG WaitReason; } SYSTEM_THREADS, * PSYSTEM_THREADS; // Note that the size of the SYSTEM_PROCESSES structure is different on // NT 4 and Win2K, but we don't care about it, since we don't access neither // IoCounters member nor Threads array typedef struct _SYSTEM_PROCESSES { ULONG NextEntryDelta; ULONG ThreadCount; ULONG Reserved1[6]; LARGE_INTEGER CreateTime; LARGE_INTEGER UserTime; LARGE_INTEGER KernelTime; UNICODE_STRING ProcessName; KPRIORITY BasePriority; ULONG ProcessId; ULONG InheritedFromProcessId; ULONG HandleCount; ULONG Reserved2[2]; VM_COUNTERS VmCounters; #if _WIN32_WINNT >= 0x500 IO_COUNTERS IoCounters; #endif SYSTEM_THREADS Threads[1]; } SYSTEM_PROCESSES, * PSYSTEM_PROCESSES; static BOOL WINAPI KillProcessTreeNtHelper(IN PSYSTEM_PROCESSES pInfo, IN DWORD dwProcessId) { assert(pInfo != NULL); PSYSTEM_PROCESSES p = pInfo; // kill all children first for (;;) { if (p->InheritedFromProcessId == dwProcessId) KillProcessTreeNtHelper(pInfo, p->ProcessId); if (p->NextEntryDelta == 0) break; // find the address of the next process structure p = (PSYSTEM_PROCESSES)(((LPBYTE)p) + p->NextEntryDelta); } // kill the process itself if (!KillProcess(dwProcessId)) return GetLastError(); return ERROR_SUCCESS; } static BOOL WINAPI KillProcessTreeWinHelper(IN DWORD dwProcessId) { HINSTANCE hKernel; HANDLE (WINAPI * _CreateToolhelp32Snapshot)(DWORD, DWORD); BOOL (WINAPI * _Process32First)(HANDLE, PROCESSENTRY32 *); BOOL (WINAPI * _Process32Next)(HANDLE, PROCESSENTRY32 *); // get handle to KERNEL32.DLL hKernel = GetModuleHandle(_T("kernel32.dll")); assert(hKernel != NULL); // locate necessary functions in KERNEL32.DLL *(FARPROC *)&_CreateToolhelp32Snapshot = GetProcAddress(hKernel, "CreateToolhelp32Snapshot"); *(FARPROC *)&_Process32First = GetProcAddress(hKernel, "Process32First"); *(FARPROC *)&_Process32Next = GetProcAddress(hKernel, "Process32Next"); if (_CreateToolhelp32Snapshot == NULL || _Process32First == NULL || _Process32Next == NULL) return ERROR_PROC_NOT_FOUND; HANDLE hSnapshot; PROCESSENTRY32 Entry; // create a snapshot hSnapshot = _CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0); if (hSnapshot == INVALID_HANDLE_VALUE) return GetLastError(); Entry.dwSize = sizeof(Entry); if (!_Process32First(hSnapshot, &Entry)) { DWORD dwError = GetLastError(); CloseHandle(hSnapshot); return dwError; } // kill all children first do { if (Entry.th32ParentProcessID == dwProcessId) KillProcessTreeWinHelper(Entry.th32ProcessID); Entry.dwSize = sizeof(Entry); } while (_Process32Next(hSnapshot, &Entry)); CloseHandle(hSnapshot); // kill the process itself if (!KillProcess(dwProcessId)) return GetLastError(); return ERROR_SUCCESS; } static BOOL WINAPI KillProcessEx(IN DWORD dwProcessId, IN BOOL bTree) { if (!bTree) return KillProcess(dwProcessId); OSVERSIONINFO osvi; DWORD dwError; // determine operating system version osvi.dwOSVersionInfoSize = sizeof(osvi); GetVersionEx(&osvi); if (osvi.dwPlatformId == VER_PLATFORM_WIN32_NT && osvi.dwMajorVersion < 5) { HINSTANCE hNtDll; NTSTATUS (WINAPI * _ZwQuerySystemInformation)(UINT, PVOID, ULONG, PULONG); // get handle to NTDLL.DLL hNtDll = GetModuleHandle(_T("ntdll.dll")); assert(hNtDll != NULL); // find the address of ZwQuerySystemInformation *(FARPROC *)&_ZwQuerySystemInformation = GetProcAddress(hNtDll, "ZwQuerySystemInformation"); if (_ZwQuerySystemInformation == NULL) return FALSE; // obtain a handle to the default process heap HANDLE hHeap = GetProcessHeap(); NTSTATUS Status; ULONG cbBuffer = 0x8000; PVOID pBuffer = NULL; // it is difficult to say a priory which size of the buffer // will be enough to retrieve all information, so we start // with 32K buffer and increase its size until we get the // information successfully do { pBuffer = HeapAlloc(hHeap, 0, cbBuffer); if (pBuffer == NULL) return FALSE; Status = _ZwQuerySystemInformation( SystemProcessesAndThreadsInformation, pBuffer, cbBuffer, NULL); if (Status == STATUS_INFO_LENGTH_MISMATCH) { HeapFree(hHeap, 0, pBuffer); cbBuffer *= 2; } else if (!NT_SUCCESS(Status)) { HeapFree(hHeap, 0, pBuffer); return FALSE; } } while (Status == STATUS_INFO_LENGTH_MISMATCH); // call the helper function dwError = KillProcessTreeNtHelper((PSYSTEM_PROCESSES)pBuffer, dwProcessId); HeapFree(hHeap, 0, pBuffer); } else { // call the helper function dwError = KillProcessTreeWinHelper(dwProcessId); } return dwError == ERROR_SUCCESS; } static void s_kill(int sig) { if(!s_pid.use_wrapper) { /* we can kill our child directly */ if(s_pid.p.hProcess) TerminateProcess(s_pid.p.hProcess, 1); } else { /* emulate unix kill behaviour */ if(s_pid.p.dwProcessId) KillProcessEx(s_pid.p.dwProcessId, 1); } } #endif static void make_local_socket_info(TSRMLS_D) { memset(&EXT_GLOBAL(cfg)->saddr, 0, sizeof EXT_GLOBAL(cfg)->saddr); #ifndef CFG_JAVA_SOCKET_INET EXT_GLOBAL(cfg)->saddr.sun_family = AF_LOCAL; memset(EXT_GLOBAL(cfg)->saddr.sun_path, 0, sizeof EXT_GLOBAL(cfg)->saddr.sun_path); strcpy(EXT_GLOBAL(cfg)->saddr.sun_path, EXT_GLOBAL(get_sockname)(TSRMLS_C)); # ifdef HAVE_ABSTRACT_NAMESPACE *EXT_GLOBAL(cfg)->saddr.sun_path=0; # endif #else EXT_GLOBAL(cfg)->saddr.sin_family = AF_INET; EXT_GLOBAL(cfg)->saddr.sin_port=htons(atoi(EXT_GLOBAL(get_sockname)(TSRMLS_C))); EXT_GLOBAL(cfg)->saddr.sin_addr.s_addr = inet_addr( "127.0.0.1" ); #endif } void EXT_GLOBAL(start_server)(TSRMLS_D) { int pid=0, err=-1, p[2], st[2], stx; char buf[127], count, *test_server = 0, *name; #ifndef __MINGW32__ if(can_fork() && !(test_server=EXT_GLOBAL(test_server)(0, 0, 0 TSRMLS_CC)) && pipe(p)!=-1) { if(!(pid=fork())) { /* daemon */ close(p[0]); stx = pipe(st); if(!fork()) { /* guard */ setsid(); if(!(pid=fork())) { /* java */ if(close(p[1])!=-1&& stx!=-1&& close(st[0])!=-1&& dup2(st[1], 1)!=-1) exec_vm(TSRMLS_C); exit(105); } /* protect guard */ signal(SIGHUP, SIG_IGN); s_pid=pid; signal(SIGINT, s_kill); signal(SIGTERM, SIG_IGN); write(p[1], &pid, sizeof pid); if(stx!=-1 && close(st[1])!=-1) err = read(st[0], buf, sizeof buf); count = (err==-1) ? 0 : (0xff & err); write(p[1], &count, 1); if(count) write(p[1], buf, count); waitpid(pid, &err, 0); write(p[1], &err, sizeof err); exit(0); } exit(0); } close(p[1]); wait(&err); if((read(p[0], &pid, sizeof pid))!=(sizeof pid)) pid=0; if((read(p[0], &count, 1))!=1) count=0; EXT_GLOBAL(cfg)->cid=pid; EXT_GLOBAL(cfg)->err=p[0]; if(count&&((read(p[0], buf, sizeof buf))==count)) { /* received channel # */ size_t n = count; name = malloc(n+1); if(!name) exit(9); memcpy(name, buf, n); name[n]=0; //php_printf("got server channel: %ld, %s", n, name); free(EXT_GLOBAL(cfg)->default_sockname); EXT_GLOBAL(cfg)->default_sockname=name; make_local_socket_info(TSRMLS_C); } else { make_local_socket_info(TSRMLS_C); wait_server(); } } else #else if(can_fork() && !(test_server=EXT_GLOBAL(test_server)(0, 0, 0 TSRMLS_CC))) { char *cmd = get_server_string(0 TSRMLS_CC); DWORD properties = CREATE_NEW_CONSOLE | CREATE_NEW_PROCESS_GROUP; STARTUPINFO su_info; HANDLE read_pipe, write_pipe, read_pipe_dup; SECURITY_ATTRIBUTES pipe_sattr = {sizeof(SECURITY_ATTRIBUTES),NULL,TRUE}; HANDLE pid = GetCurrentProcess(); DWORD bread; if(!CreatePipe(&read_pipe, &write_pipe, &pipe_sattr, 0)) { goto cannot_fork; } if(!DuplicateHandle(pid,read_pipe,pid,&read_pipe_dup,0,FALSE,DUPLICATE_SAME_ACCESS)) { CloseHandle(read_pipe); goto cannot_fork; } CloseHandle(read_pipe); s_pid.use_wrapper = use_wrapper(EXT_GLOBAL(cfg)->wrapper); ZeroMemory(&su_info, sizeof(STARTUPINFO)); su_info.cb = sizeof(STARTUPINFO); su_info.dwFlags = STARTF_USESTDHANDLES; su_info.hStdError = GetStdHandle(STD_ERROR_HANDLE); su_info.hStdInput = GetStdHandle(STD_INPUT_HANDLE); su_info.hStdOutput = write_pipe; EXT_GLOBAL(cfg)->cid=0; if(CreateProcess(NULL, cmd, NULL, NULL, 1, properties, NULL, NULL, &su_info, &s_pid.p)) { EXT_GLOBAL(cfg)->cid=s_pid.p.dwProcessId; if(ReadFile(read_pipe_dup, buf, sizeof(buf), &bread, NULL) && bread) { name = malloc(bread+1); if(!name) exit(9); memcpy(name, buf, bread); name[bread]=0; //php_printf("got server channel: %ld, %s", bread, name); free(EXT_GLOBAL(cfg)->default_sockname); EXT_GLOBAL(cfg)->default_sockname=name; make_local_socket_info(TSRMLS_C); } else { make_local_socket_info(TSRMLS_C); wait_server(); } CloseHandle(s_pid.p.hThread); CloseHandle(read_pipe_dup); } else { cannot_fork: php_error(E_WARNING, "php_mod_"/**/EXT_NAME()/**/"(%d) system error: Could not start back-end: %s; Code: %ld.", 105, cmd, (long)GetLastError()); make_local_socket_info(TSRMLS_C); } free(cmd); } else #endif /* MINGW32 */ { make_local_socket_info(TSRMLS_C); EXT_GLOBAL(cfg)->cid=EXT_GLOBAL(cfg)->err=0; } if(test_server) free(test_server); } static void wait_for_daemon(void) { #ifndef __MINGW32__ static const int sig[] = {SIGTERM, SIGKILL}; fd_set rfds; int err, i; assert(EXT_GLOBAL(cfg)->err); if(!(EXT_GLOBAL(cfg)->err)) return; assert(EXT_GLOBAL(cfg)->cid); /* first kill is trapped, second kill is received with default handler. If the server still exists, we send it a -9 */ kill(EXT_GLOBAL(cfg)->cid, SIGTERM); FD_ZERO(&rfds); FD_SET(EXT_GLOBAL(cfg)->err, &rfds); for(i=0; i<2; i++) { struct timeval timeval = {2l, 0}; if(select(1+EXT_GLOBAL(cfg)->err, &rfds, 0, 0, &timeval) > 0) break; kill(EXT_GLOBAL(cfg)->cid, sig[i]); } if((read(EXT_GLOBAL(cfg)->err, &err, sizeof err))!=sizeof err) err=0; //printf("VM terminated with code: %ld\n", err); close(EXT_GLOBAL(cfg)->err); EXT_GLOBAL(cfg)->err=0; #else s_kill(0); /* always -9 on windows */ #endif } void EXT_GLOBAL(shutdown_library)() { if(EXT_GLOBAL(cfg)->cid) wait_for_daemon(); } void EXT_GLOBAL(sys_error)(const char *str, int code) { #ifndef __MINGW32__ php_error(E_ERROR, "php_mod_"/**/EXT_NAME()/**/"(%d) system error: %s. %s.", code, strerror(errno), str); #else php_error(E_ERROR, "php_mod_"/**/EXT_NAME()/**/"(%d) system error code: %ld. %s.", code, (long)GetLastError(), str); #endif } static void fcgi_do_rmtmpdir() { #ifndef __MINGW32__ extern EXT_GLOBAL(is_parent); static const char lck[] = ".lck"; DIR * dir; struct dirent * file; size_t len; char *name, *lock_name; int lock_file, err; /* If this is not an fcgi child, do nothing. */ if(!EXT_GLOBAL(cfg) || !EXT_GLOBAL(cfg)->tmpdir) return; len = strlen(EXT_GLOBAL(cfg)->tmpdir); lock_name = malloc(len+sizeof lck); strcpy(lock_name, (EXT_GLOBAL(cfg)->tmpdir)); strcat(lock_name, lck); lock_file = open(lock_name, O_CREAT | O_EXCL, 0700); unlink(lock_name); free(lock_name); if(lock_file==-1) return; dir = opendir(EXT_GLOBAL(cfg)->tmpdir); if(!dir) return; while(file = readdir(dir)) { if(file->d_name[0]!='p') continue; name = malloc(len + strlen(file->d_name)+2); if(!name) exit(6); strcpy(name, (EXT_GLOBAL(cfg)->tmpdir)); strcat(name, "/"); strcat(name, file->d_name); unlink(name); free(name); } closedir(dir); rmdir(EXT_GLOBAL(cfg)->tmpdir); #endif } static void fcgi_rmtmpdir(int sig) { fcgi_do_rmtmpdir(); exit(0); } /* Delete the temp directory which contains the comm. pipes */ static void rmtmpdir () { #ifndef __MINGW32__ extern EXT_GLOBAL(is_parent); DIR * dir; struct dirent * file; size_t len; char *name; int err; int lock; /* If this is a child, do nothing. */ if(!EXT_GLOBAL(cfg) || !EXT_GLOBAL(cfg)->tmpdir || EXT_GLOBAL(cfg)->pid!=getpid()) return; len = strlen(EXT_GLOBAL(cfg)->tmpdir); dir = opendir(EXT_GLOBAL(cfg)->tmpdir); if(!dir) { EXT_GLOBAL(sys_error)("Could not open tmpdir", 65); return; } while(file = readdir(dir)) { if(file->d_name[0]!='p') continue; name = malloc(len + strlen(file->d_name)+2); if(!name) exit(6); strcpy(name, (EXT_GLOBAL(cfg)->tmpdir)); strcat(name, "/"); strcat(name, file->d_name); php_error(E_NOTICE, "php_mod_"/**/EXT_NAME()/**/"(%d): Removing %s which is not (yet?) connected. ", 66, name); unlink(name); free(name); } err = closedir(dir); if(err==-1) { EXT_GLOBAL(sys_error)("Could not close tmpdir", 67); return; } err = rmdir(EXT_GLOBAL(cfg)->tmpdir); if(err==-1) { EXT_GLOBAL(sys_error)("Could not unlink tmpdir", 68); return; } free(EXT_GLOBAL(cfg)->tmpdir); EXT_GLOBAL(cfg)->tmpdir=0; #endif } void EXT_GLOBAL(rmtmpdir) () { /* see FastCGI comment below */ if(!EXT_GLOBAL(cfg)->is_fcgi_servlet) rmtmpdir(); } #ifndef __MINGW32__ static char *makedtemp(char tmpl[]) { #ifdef HAVE_MKDTEMP char *str = 0, *s = mkdtemp (tmpl); if(s) if(!(str=strdup(s))) { rmdir(s); exit(6); } return str; #else char *s, *p = strrchr(tmpl, '/'); char c = *p; *p=0; p[6]=0; s = tempnam(tmpl,p+1); *p=c; if(!s) return 0; if(-1==mkdir(s, 0700)) { free(s); return 0; } return s; #endif } #endif void EXT_GLOBAL(mktmpdir) () { #ifndef __MINGW32__ char sockname[] = SOCKNAME; char sockname_shm[] = SOCKNAME_SHM; char *tmpdir; /* The FastCGI SAPI is completely odd, it calls the parent(!) mshutdown for each killed child. Ignore this nonsense and call rmtmpdir ourselfs when the parent exits. */ if(EXT_GLOBAL(cfg)->is_fcgi_servlet) signal(SIGTERM, fcgi_rmtmpdir); tmpdir = makedtemp(sockname_shm); if(!tmpdir) tmpdir = makedtemp(sockname); if(!tmpdir) {EXT_GLOBAL(cfg)->tmpdir=0; return;} EXT_GLOBAL(cfg)->tmpdir=tmpdir; chmod(tmpdir, 01777); #else /* There's no standard tmpdir on windows */ EXT_GLOBAL(cfg)->tmpdir=0; #endif } #ifndef PHP_WRAPPER_H #error must include php_wrapper.h #endif