#!/usr/bin/env python # # Copyright (c) 2008, Thomas Hurst # # Use of this file is unrestricted provided this notice is retained. # If you use it, it'd be nice if you dropped me a note. Also beer. """ freebsd.get_process_cwd(pid): Use sysctl() to retrieve the cwd of an arbitrary process on FreeBSD using kern.proc.filedesc, as used by procstat(1). Tested on FreeBSD 7-STABLE/amd64 from April 11 2008. """ import platform from ctypes import * from ctypes.util import find_library class sockaddr_storage(Structure): """struct sockaddr_storage, defined in /usr/include/sys/socket.h""" _fields_ = [ ('ss_len', c_char), ('ss_family', c_char), # /usr/include/sys/_types.h; _uint8_t ('__ss_pad1', c_char * 6), # (sizeof(int64) - sizeof(char) - sizeof(ss_family_t)) ('__ss_align', c_longlong), ('__ss_pad2', c_char * 112), # (128(maxsize) - sizeof(char) - sizeof(ss_family_t) - # sizeof(ss_pad1) - sizeof(int64)) ] class kinfo_file(Structure): """struct kinfo_file, defined in /usr/include/sys/user.h """ _fields_ = [ ('kf_structsize', c_int), ('kf_type', c_int), ('kf_fd', c_int), ('kf_ref_count', c_int), ('kf_flags', c_int), ('kf_offset', c_size_t), # this is a off_t, a pointer ('kf_vnode_type', c_int), ('kf_sock_domain', c_int), ('kf_sock_type', c_int), ('kf_sock_protocol', c_int), ('kf_path', c_char * 1024), # PATH_MAX ('kf_sa_local', sockaddr_storage), ('kf_sa_peer', sockaddr_storage), ] def get_process_cwd(pid): """Return string containing the current working directory of the given pid, or None on failure.""" # /usr/include/sys/sysctl.h # CTL_KERN, KERN_PROC, KERN_PROC_FILEDESC oid = (c_uint * 4)(1, 14, 14, pid) if libc.sysctl(oid, 4, None, byref(uintlen), None, 0) < 0: return None buf = c_char_p(" " * uintlen.value) if libc.sysctl(oid, 4, buf, byref(uintlen), None, 0) < 0: return None kifs = cast(buf, POINTER(kinfo_file)) for i in range(0, uintlen.value / sizeof(kinfo_file)): kif = kifs[i] if kif.kf_fd == -1: # KF_FD_TYPE_CWD return kif.kf_path if platform.system() in ['FreeBSD', 'OpenBSD']: libc = CDLL(find_library('c')) uintlen = c_size_t(sizeof(c_uint)) ver = c_uint(0) if (libc.sysctlbyname('kern.osreldate', byref(ver), byref(uintlen), None, 0) < 0): raise OSError("sysctlbyname returned < 0") # kern.proc.filedesc added for procstat(1) after these __FreeBSD_versions if ver.value < 700104 and ver.value < 800019: raise NotImplementedError("cwd detection requires a recent 7.0-STABLE or 8-CURRENT") if __name__ == '__main__': import os, sys print(" => %d cwd = %s" % (os.getpid(), get_process_cwd(os.getpid()))) for pid in sys.argv: try: pid = int(pid) except: pass else: print(" => %d cwd = %s" % (pid, get_process_cwd(pid)))