def ErrCheckBool(result, func, args): """errcheck function for Windows functions that return a BOOL True on success""" if not result: raise WinError() return args
def set_handle_inheritable(handle, inheritable): flags = HANDLE_FLAG_INHERIT if inheritable else 0 ok = SetHandleInformation(handle, HANDLE_FLAG_INHERIT, flags) if not ok: raise WinError()
def err_on_zero_or_null_check(result, func, args): if not result: raise WinError() return args
def _wait(self): # First, check to see if the process is still running if self._handle: self.returncode = winprocess.GetExitCodeProcess( self._handle) else: # Dude, the process is like totally dead! return self.returncode threadalive = False if hasattr(self, "_procmgrthread"): threadalive = self._procmgrthread.is_alive() if self._job and threadalive and threading.current_thread( ) != self._procmgrthread: self.debug("waiting with IO completion port") # Then we are managing with IO Completion Ports # wait on a signal so we know when we have seen the last # process come through. # We use queues to synchronize between the thread and this # function because events just didn't have robust enough error # handling on pre-2.7 versions try: # timeout is the max amount of time the procmgr thread will wait for # child processes to shutdown before killing them with extreme prejudice. item = self._process_events.get( timeout=self. MAX_IOCOMPLETION_PORT_NOTIFICATION_DELAY + self.MAX_PROCESS_KILL_DELAY) if item[self.pid] == 'FINISHED': self.debug( "received 'FINISHED' from _procmgrthread") self._process_events.task_done() except: traceback.print_exc() raise OSError( "IO Completion Port failed to signal process shutdown" ) finally: if self._handle: self.returncode = winprocess.GetExitCodeProcess( self._handle) self._cleanup() else: # Not managing with job objects, so all we can reasonably do # is call waitforsingleobject and hope for the best self.debug("waiting without IO completion port") if not self._ignore_children: self.debug("NOT USING JOB OBJECTS!!!") # First, make sure we have not already ended if self.returncode != winprocess.STILL_ACTIVE: self._cleanup() return self.returncode rc = None if self._handle: rc = winprocess.WaitForSingleObject(self._handle, -1) if rc == winprocess.WAIT_TIMEOUT: # The process isn't dead, so kill it print("Timed out waiting for process to close, " "attempting TerminateProcess") self.kill() elif rc == winprocess.WAIT_OBJECT_0: # We caught WAIT_OBJECT_0, which indicates all is well print("Single process terminated successfully") self.returncode = winprocess.GetExitCodeProcess( self._handle) else: # An error occured we should probably throw rc = winprocess.GetLastError() if rc: raise WinError(rc) self._cleanup() return self.returncode
def setNoInherit(sock): """Mark the given socket fd as non-inheritable to child processes""" if not _SetHandleInformation(sock.fileno(), 1, 0): raise WinError()
def _errCheck(result, func, args): if result == 0: raise WinError() return args
# and kill the other children too). SINK = object() # get default args from subprocess.Popen to use in subproc.Popen a = inspect.getargspec(subprocess.Popen.__init__) _Popen_defaults = zip(a.args[-len(a.defaults):], a.defaults) del a if mswindows: # required for os.kill() to work _Popen_creationflags = subprocess.CREATE_NEW_PROCESS_GROUP if _kill_children_on_death: _chJob = win32job.CreateJobObject(None, "") if not _chJob: raise WinError() chJeli = win32job.QueryInformationJobObject( _chJob, win32job.JobObjectExtendedLimitInformation) # JOB_OBJECT_LIMIT_BREAKAWAY_OK allows children to assign grandchildren # to their own jobs chJeli['BasicLimitInformation']['LimitFlags'] |= ( win32job.JOB_OBJECT_LIMIT_KILL_ON_JOB_CLOSE | win32job.JOB_OBJECT_LIMIT_BREAKAWAY_OK) if win32job.SetInformationJobObject( _chJob, win32job.JobObjectExtendedLimitInformation, chJeli) == 0: raise WinError() del chJeli
def read_memory(self, addr, size): buffer = create_string_buffer(size) if not self.kernel32.ReadProcessMemory(self.handle, c_long(addr), buffer, size, None): raise WinError() return buffer
def _check(result, func, args): if result == error_value: raise WinError() return result
def GetDiskFreeSpaceExErrCheck(result, unused_func, args): if not result: raise WinError() return args[1].value
def pdh_error_check(result, func, arguments): if result: raise WinError(result) return result
def write(self, text): try: if self._hConsole is None: if isinstance(text, unicode): text = text.encode('utf-8') self._stream.write(text) else: if not isinstance(text, unicode): text = str(text).decode('utf-8') remaining = len(text) while remaining > 0: n = DWORD(0) # There is a shorter-than-documented limitation on the length of the string # passed to WriteConsoleW (see #1232). retval = WriteConsoleW(self._hConsole, text, min(remaining, 10000), byref(n), None) if retval == 0: raise IOError("WriteConsoleW failed with WinError: %s" % (WinError(get_last_error()),)) if n.value == 0: raise IOError("WriteConsoleW returned %r, n.value = 0" % (retval,)) remaining -= n.value if remaining == 0: break text = text[n.value:] except Exception, e: _complain("%s.write: %r" % (self.name, e)) raise
# <https://msdn.microsoft.com/en-us/library/windows/desktop/ms683156%28v=vs.85%29.aspx> GetCommandLineW = WINFUNCTYPE( LPWSTR, use_last_error=True )(("GetCommandLineW", windll.kernel32)) # <https://msdn.microsoft.com/en-us/library/windows/desktop/bb776391%28v=vs.85%29.aspx> CommandLineToArgvW = WINFUNCTYPE( POINTER(LPWSTR), LPCWSTR, POINTER(c_int), use_last_error=True )(("CommandLineToArgvW", windll.shell32)) argc = c_int(0) argv_unicode = CommandLineToArgvW(GetCommandLineW(), byref(argc)) if argv_unicode is None: raise WinError(get_last_error()) # Because of <http://bugs.python.org/issue8775> (and similar limitations in # twisted), the 'bin/tahoe' script cannot invoke us with the actual Unicode arguments. # Instead it "mangles" or escapes them using \x7F as an escape character, which we # unescape here. def unmangle(s): return re.sub(ur'\x7F[0-9a-fA-F]*\;', lambda m: unichr(int(m.group(0)[1:-1], 16)), s) try: argv = [unmangle(argv_unicode[i]).encode('utf-8') for i in xrange(0, argc.value)] except Exception, e: _complain("%s: could not unmangle Unicode arguments.\n%r" % (sys.argv[0], [argv_unicode[i] for i in xrange(0, argc.value)])) raise
def terminate(self): """Terminates the timer. This should be called from the thread that initiated the timer. """ if not user32.KillTimer(self.hwnd, self.idEvent): raise WinError()
def _raise_winerror(code, error_desc): win_error_desc = FormatError(code).strip() error_desc = "%s: %s".format(error_desc, win_error_desc) raise WinError(code, error_desc)
def free_remote(self, addr, size): if not self.kernel32.VirtualFreeEx(self.handle, addr, c_int(0), self.MEM_RELEASE): raise WinError()
def get_disk_stats(whichdir, reserved_space=0): """Return disk statistics for the storage disk, in the form of a dict with the following fields. total: total bytes on disk free_for_root: bytes actually free on disk free_for_nonroot: bytes free for "a non-privileged user" [Unix] or the current user [Windows]; might take into account quotas depending on platform used: bytes used on disk avail: bytes available excluding reserved space An AttributeError can occur if the OS has no API to get disk information. An EnvironmentError can occur if the OS call fails. whichdir is a directory on the filesystem in question -- the answer is about the filesystem, not about the directory, so the directory is used only to specify which filesystem. reserved_space is how many bytes to subtract from the answer, so you can pass how many bytes you would like to leave unused on this filesystem as reserved_space. """ if have_GetDiskFreeSpaceExW: # If this is a Windows system and GetDiskFreeSpaceExW is available, use it. # (This might put up an error dialog unless # SetErrorMode(SEM_FAILCRITICALERRORS | SEM_NOOPENFILEERRORBOX) has been called, # which we do in allmydata.windows.fixups.initialize().) n_free_for_nonroot = c_ulonglong(0) n_total = c_ulonglong(0) n_free_for_root = c_ulonglong(0) retval = GetDiskFreeSpaceExW(whichdir, byref(n_free_for_nonroot), byref(n_total), byref(n_free_for_root)) if retval == 0: raise OSError( "WinError: %s\n attempting to get disk statistics for %r" % (WinError(get_last_error()), whichdir)) free_for_nonroot = n_free_for_nonroot.value total = n_total.value free_for_root = n_free_for_root.value else: # For Unix-like systems. # <http://docs.python.org/library/os.html#os.statvfs> # <http://opengroup.org/onlinepubs/7990989799/xsh/fstatvfs.html> # <http://opengroup.org/onlinepubs/7990989799/xsh/sysstatvfs.h.html> s = os.statvfs(whichdir) # on my mac laptop: # statvfs(2) is a wrapper around statfs(2). # statvfs.f_frsize = statfs.f_bsize : # "minimum unit of allocation" (statvfs) # "fundamental file system block size" (statfs) # statvfs.f_bsize = statfs.f_iosize = stat.st_blocks : preferred IO size # on an encrypted home directory ("FileVault"), it gets f_blocks # wrong, and s.f_blocks*s.f_frsize is twice the size of my disk, # but s.f_bavail*s.f_frsize is correct total = s.f_frsize * s.f_blocks free_for_root = s.f_frsize * s.f_bfree free_for_nonroot = s.f_frsize * s.f_bavail # valid for all platforms: used = total - free_for_root avail = max(free_for_nonroot - reserved_space, 0) return { 'total': total, 'free_for_root': free_for_root, 'free_for_nonroot': free_for_nonroot, 'used': used, 'avail': avail, }
def write_memory(self, addr, string): size = len(string) if not self.kernel32.WriteProcessMemory(self.handle, addr, string, size, None): raise WinError()
def __enter__(self): self.data_ptr = memory.GlobalLock(self.handle) if not self.data_ptr: del self.data_ptr raise WinError() return self
def ErrorIfZero(handle): if handle == 0: raise WinError() else: return handle
def prevent_socket_inheritance(sock): """Mark the given socket fd as non-inheritable (Windows).""" if not windll.kernel32.SetHandleInformation(sock.fileno(), 1, 0): raise WinError()
def _shutdown_with_windows_restart_manager(self, pid): """ Shut down a process using the Windows Restart Manager. When Windows shuts down, it uses a protocol including the WM_QUERYENDSESSION and WM_ENDSESSION messages to give applications a chance to shut down safely. The best way to simulate this is via the Restart Manager, which allows a process (such as an installer) to use the same mechanism to shut down any other processes which are using registered resources. This function starts a Restart Manager session, registers the process as a resource, and shuts down the process. :param pid: The process id (int) of the process to shutdown :raises: WindowsError: if a Windows API call fails """ import ctypes from ctypes import Structure, POINTER, WINFUNCTYPE, windll, pointer, WinError from ctypes.wintypes import HANDLE, DWORD, BOOL, WCHAR, UINT, ULONG, LPCWSTR # set up Windows SDK types OpenProcess = windll.kernel32.OpenProcess OpenProcess.restype = HANDLE OpenProcess.argtypes = [ DWORD, # dwDesiredAccess BOOL, # bInheritHandle DWORD ] # dwProcessId PROCESS_QUERY_INFORMATION = 0x0400 class FILETIME(Structure): _fields_ = [('dwLowDateTime', DWORD), ('dwHighDateTime', DWORD)] LPFILETIME = POINTER(FILETIME) GetProcessTimes = windll.kernel32.GetProcessTimes GetProcessTimes.restype = BOOL GetProcessTimes.argtypes = [ HANDLE, # hProcess LPFILETIME, # lpCreationTime LPFILETIME, # lpExitTime LPFILETIME, # lpKernelTime LPFILETIME ] # lpUserTime ERROR_SUCCESS = 0 class RM_UNIQUE_PROCESS(Structure): _fields_ = [('dwProcessId', DWORD), ('ProcessStartTime', FILETIME)] RmStartSession = windll.rstrtmgr.RmStartSession RmStartSession.restype = DWORD RmStartSession.argtypes = [ POINTER(DWORD), # pSessionHandle DWORD, # dwSessionFlags POINTER(WCHAR) ] # strSessionKey class GUID(ctypes.Structure): _fields_ = [('Data1', ctypes.c_ulong), ('Data2', ctypes.c_ushort), ('Data3', ctypes.c_ushort), ('Data4', ctypes.c_ubyte * 8)] CCH_RM_SESSION_KEY = ctypes.sizeof(GUID) * 2 RmRegisterResources = windll.rstrtmgr.RmRegisterResources RmRegisterResources.restype = DWORD RmRegisterResources.argtypes = [ DWORD, # dwSessionHandle UINT, # nFiles POINTER(LPCWSTR), # rgsFilenames UINT, # nApplications POINTER(RM_UNIQUE_PROCESS), # rgApplications UINT, # nServices POINTER(LPCWSTR) ] # rgsServiceNames RM_WRITE_STATUS_CALLBACK = WINFUNCTYPE(None, UINT) RmShutdown = windll.rstrtmgr.RmShutdown RmShutdown.restype = DWORD RmShutdown.argtypes = [ DWORD, # dwSessionHandle ULONG, # lActionFlags RM_WRITE_STATUS_CALLBACK ] # fnStatus RmEndSession = windll.rstrtmgr.RmEndSession RmEndSession.restype = DWORD RmEndSession.argtypes = [DWORD] # dwSessionHandle # Get the info needed to uniquely identify the process hProc = OpenProcess(PROCESS_QUERY_INFORMATION, False, pid) if not hProc: raise WinError() creationTime = FILETIME() exitTime = FILETIME() kernelTime = FILETIME() userTime = FILETIME() if not GetProcessTimes(hProc, pointer(creationTime), pointer(exitTime), pointer(kernelTime), pointer(userTime)): raise WinError() # Start the Restart Manager Session dwSessionHandle = DWORD() sessionKeyType = WCHAR * (CCH_RM_SESSION_KEY + 1) sessionKey = sessionKeyType() if RmStartSession(pointer(dwSessionHandle), 0, sessionKey) != ERROR_SUCCESS: raise WinError() try: UProcs_count = 1 UProcsArrayType = RM_UNIQUE_PROCESS * UProcs_count UProcs = UProcsArrayType(RM_UNIQUE_PROCESS(pid, creationTime)) # Register the process as a resource if RmRegisterResources(dwSessionHandle, 0, None, UProcs_count, UProcs, 0, None) != ERROR_SUCCESS: raise WinError() # Shut down all processes using registered resources if RmShutdown( dwSessionHandle, 0, ctypes.cast(None, RM_WRITE_STATUS_CALLBACK)) != ERROR_SUCCESS: raise WinError() finally: RmEndSession(dwSessionHandle)
def handle_err_check(result, func, args): if result == INVALID_HANDLE_VALUE: raise WinError(get_last_error()) return result
def _poll_iocompletion_port(self): # Watch the IO Completion port for status self._spawned_procs = {} countdowntokill = 0 self.debug("start polling IO completion port") while True: msgid = c_ulong(0) compkey = c_ulong(0) pid = c_ulong(0) portstatus = winprocess.GetQueuedCompletionStatus( self._io_port, byref(msgid), byref(compkey), byref(pid), 5000) # If the countdowntokill has been activated, we need to check # if we should start killing the children or not. if countdowntokill != 0: diff = datetime.now() - countdowntokill # Arbitrarily wait 3 minutes for windows to get its act together # Windows sometimes takes a small nap between notifying the # IO Completion port and actually killing the children, and we # don't want to mistake that situation for the situation of an unexpected # parent abort (which is what we're looking for here). if diff.seconds > self.MAX_IOCOMPLETION_PORT_NOTIFICATION_DELAY: print( "WARNING | IO Completion Port failed to signal " "process shutdown", file=sys.stderr) print( "Parent process %s exited with children alive:" % self.pid, file=sys.stderr) print("PIDS: %s" % ', '.join( [str(i) for i in self._spawned_procs]), file=sys.stderr) print( "Attempting to kill them, but no guarantee of success", file=sys.stderr) self.kill() self._process_events.put({self.pid: 'FINISHED'}) break if not portstatus: # Check to see what happened errcode = winprocess.GetLastError() if errcode == winprocess.ERROR_ABANDONED_WAIT_0: # Then something has killed the port, break the loop print("IO Completion Port unexpectedly closed", file=sys.stderr) self._process_events.put({self.pid: 'FINISHED'}) break elif errcode == winprocess.WAIT_TIMEOUT: # Timeouts are expected, just keep on polling continue else: print( "Error Code %s trying to query IO Completion Port, " "exiting" % errcode, file=sys.stderr) raise WinError(errcode) break if compkey.value == winprocess.COMPKEY_TERMINATE.value: self.debug("compkeyterminate detected") # Then we're done break # Check the status of the IO Port and do things based on it if compkey.value == winprocess.COMPKEY_JOBOBJECT.value: if msgid.value == winprocess.JOB_OBJECT_MSG_ACTIVE_PROCESS_ZERO: # No processes left, time to shut down # Signal anyone waiting on us that it is safe to shut down self.debug("job object msg active processes zero") self._process_events.put({self.pid: 'FINISHED'}) break elif msgid.value == winprocess.JOB_OBJECT_MSG_NEW_PROCESS: # New Process started # Add the child proc to our list in case our parent flakes out on us # without killing everything. if pid.value != self.pid: self._spawned_procs[pid.value] = 1 self.debug( "new process detected with pid value: %s" % pid.value) elif msgid.value == winprocess.JOB_OBJECT_MSG_EXIT_PROCESS: self.debug("process id %s exited normally" % pid.value) # One process exited normally if pid.value == self.pid and len( self._spawned_procs) > 0: # Parent process dying, start countdown timer countdowntokill = datetime.now() elif pid.value in self._spawned_procs: # Child Process died remove from list del (self._spawned_procs[pid.value]) elif msgid.value == winprocess.JOB_OBJECT_MSG_ABNORMAL_EXIT_PROCESS: # One process existed abnormally self.debug("process id %s exited abnormally" % pid.value) if pid.value == self.pid and len( self._spawned_procs) > 0: # Parent process dying, start countdown timer countdowntokill = datetime.now() elif pid.value in self._spawned_procs: # Child Process died remove from list del self._spawned_procs[pid.value] else: # We don't care about anything else self.debug("We got a message %s" % msgid.value) pass
def bool_err_check(result, func, args): if not result: raise WinError(get_last_error()) return result
def ErrCheckResumeThread(result, func, args): if result == -1: raise WinError() return args
def load_from_pid(self, pid): self.unload() self.pid = c_ulong(pid) self.handle = self.kernel32.OpenProcess(self.PROC_ALL_ACCESS, 0, pid) if not self.handle: raise WinError()
def ErrCheckHandle(result, func, args): """errcheck function for Windows functions that return a HANDLE.""" if not result: raise WinError() return AutoHANDLE(result)
def unload(self): if self.handle: self.kernel32.CloseHandle(self.handle) if not self.handle: raise WinError() self.handle = None
def ZERO(i): if i != 0 and GetLastError() != 0: exc = WinError() exc.result = i raise exc return i
def PostMessage(hwnd, msg, wParam, lParam): if not user32.PostMessageW(hwnd, msg, wParam, lParam): raise WinError()