The Perl Toolchain Summit needs more sponsors. If your company depends on Perl, please support this very important event.
#include "EXTERN.h"
#include "perl.h"
#include "XSUB.h"

#ifdef WIN32
#include <windows.h>
#include <tlhelp32.h>
#include <malloc.h> //not there by default, needed for malloc (!)
#else
#include <sys/types.h>
#include <signal.h>
#include <errno.h>
#endif

////can't find a good ifdef for macs pre-macos x
//#if defined(macintosh) && !defined(MACOS_TRADITIONAL)
//#include "../Carbon.h"
//#include <Types.h>
//#include <Memory.h>
//#include <Processes.h>
//#endif
////with a good ifdef, compare GetProcessInformation from Mac::Processes

#include "ppport.h"

#define RETVAL_IS_UNSET -999

MODULE = Proc::Exists		PACKAGE = Proc::Exists		

PROTOTYPES: DISABLE

#returns 1 if the process exists, 0 if it doesn't
#on win32, it can also return <0 ( errors only happen on win32 ;-) )
int
_pexists(pid)
		int pid
	CODE:
#ifdef WIN32
		HANDLE hProcessSnap;
		PROCESSENTRY32 pe32;
		DWORD err;

		SetLastError(0);
		RETVAL=0;
		hProcessSnap = CreateToolhelp32Snapshot( TH32CS_SNAPPROCESS, 0 );
		if( hProcessSnap == INVALID_HANDLE_VALUE ) {
			RETVAL = -1;
		} else {
			pe32.dwSize = sizeof( PROCESSENTRY32 );
			if( !Process32First( hProcessSnap, &pe32 ) ) {
				CloseHandle( hProcessSnap );
				RETVAL = -2;
			} else {
				do {
					if(pid ==  pe32.th32ProcessID) { RETVAL=1; break; };
				} while( Process32Next( hProcessSnap, &pe32 ) );
				CloseHandle( hProcessSnap );
			}
		}

		////this is much faster, but it has a bug - if these exists a pid 4,
		////pid's 5, 6, and 7 will also return true. something in the windows
		////guts is chopping off the bottom two bits?
		//HANDLE hProcess;
		//PROCESSENTRY32 pe32;
		//DWORD err;

		//hProcess = OpenProcess( PROCESS_QUERY_INFORMATION, FALSE, pid );
		//if(hProcess == NULL) { 
		//	RETVAL = 0;
		//} else {
		//	RETVAL = 1;
		//	CloseHandle( hProcess );
		//}
#else
		int ret = kill(pid, 0);
		//existent process w/ perms:  ret: 0
		//existent process w/o perms: ret: -1, err: EPERM
		//nonexistent process:        ret: -1, err: ESRCH
		RETVAL = (ret==0) ? 1 : (errno!=ESRCH);
#endif
	OUTPUT:
		RETVAL



#a faster implementation for non-win32, non-wantarray case
int
_scalar_pexists(pids_ref, any, all)
		SV *pids_ref
		int any
		int all
	INIT:
		AV *pids;
		int npids;
		int i;
		int exists;
		int total=0;
#ifdef WIN32
		int *iv_pids;
		HANDLE hProcessSnap;
		PROCESSENTRY32 pe32;
		DWORD err;
#else
		int pid;
		int ret;
#endif
		//make sure pids_ref is a ref pointing at an array with some elements
//GRR no error when I typo avlen for av_len? wtf??
		if ((!SvROK(pids_ref)) || (SvTYPE(SvRV(pids_ref)) != SVt_PVAV) || 
			 ((npids = av_len((AV *)SvRV(pids_ref))) < 0)) {
			XSRETURN_UNDEF;
		}
		pids = (AV *)SvRV(pids_ref);
	CODE:
#ifdef WIN32

		npids++;
		//munge pids -> an int[] iv_pids
		iv_pids = malloc(npids * sizeof(int));
		if(iv_pids==NULL) { XSRETURN_UNDEF; };

		for(i=0; i<npids; i++) {
			iv_pids[i] = SvIV(*av_fetch(pids, i, 0));
		}

		//iterate over the proc list
		SetLastError(0);
		RETVAL=RETVAL_IS_UNSET;
		hProcessSnap = CreateToolhelp32Snapshot( TH32CS_SNAPPROCESS, 0 );
		if( hProcessSnap == INVALID_HANDLE_VALUE ) {
			RETVAL = -1;
		} else {
			pe32.dwSize = sizeof( PROCESSENTRY32 );
			if( !Process32First( hProcessSnap, &pe32 ) ) {
				CloseHandle( hProcessSnap );
				RETVAL = -2;
			} else {
				do {
					//mark off the current process from the handle (set -1)
					exists=0;
					for(i=0; i<npids; i++) {
						if(iv_pids[i] == pe32.th32ProcessID) {
							exists=1;
							break;
						}
					}

					//return immediately if any/all and appropriate value
					//note all && !exists makes no sense here since we are
					//iterating over the existing processes in the outer loop
					//TODO: THINK: probably a more efficient/elegant way here
					if(any && exists) {
						RETVAL = exists; break;
					} else {
						total+=exists;
					}
				} while( Process32Next( hProcessSnap, &pe32 ) );
				CloseHandle( hProcessSnap );
			}
		}
		free(iv_pids);
		if(RETVAL==RETVAL_IS_UNSET) {
			//handle the all case here, since we can't short-circuit
			//due to the nature of the win32 code
			if(all) {
				RETVAL = (npids == total) ? total : 0;
			} else {
				RETVAL = total;
			}
		};
#else
		RETVAL=RETVAL_IS_UNSET;
		for(i=0; i<=npids; i++) {
			pid = SvIV(*av_fetch(pids, i, 0));
			ret = kill(pid, 0);
			//existent process w/ perms:  ret: 0
			//existent process w/o perms: ret: -1, err: EPERM
			//nonexistent process:        ret: -1, err: ESRCH
			exists = (ret==0) ? 1 : (errno!=ESRCH);
			if((any && exists) || (all && !exists)) {
				RETVAL = exists; break;
			} else {
				total+=exists;
			}
		}
		if(RETVAL==RETVAL_IS_UNSET) { RETVAL = total; };
#endif
	OUTPUT:
		RETVAL