X-Git-Url: http://git.hcoop.net/bpt/emacs.git/blobdiff_plain/d888760c951787ccc3a48d7f90bc798ee0996b9b..e3021fe7dbe7a4bbbe9b4c9433c0f01f64cdcef3:/src/w32.c diff --git a/src/w32.c b/src/w32.c index e0e19c3508..5d70cbc48c 100644 --- a/src/w32.c +++ b/src/w32.c @@ -1,6 +1,6 @@ /* Utility and Unix shadow routines for GNU Emacs on the Microsoft W32 API. - Copyright (C) 1994, 1995, 2000, 2001, 2002, 2003, 2004, - 2005, 2006, 2007, 2008 Free Software Foundation, Inc. + Copyright (C) 1994, 1995, 2000, 2001, 2002, 2003, 2004, 2005, 2006, + 2007, 2008 Free Software Foundation, Inc. This file is part of GNU Emacs. @@ -32,6 +32,7 @@ along with GNU Emacs. If not, see . */ #include #include #include /* for _mbspbrk */ +#include /* must include CRT headers *before* config.h */ @@ -72,9 +73,41 @@ along with GNU Emacs. If not, see . */ #define _ANONYMOUS_STRUCT #endif #include +/* Some versions of compiler define MEMORYSTATUSEX, some don't, so we + use a different name to avoid compilation problems. */ +typedef struct _MEMORY_STATUS_EX { + DWORD dwLength; + DWORD dwMemoryLoad; + DWORDLONG ullTotalPhys; + DWORDLONG ullAvailPhys; + DWORDLONG ullTotalPageFile; + DWORDLONG ullAvailPageFile; + DWORDLONG ullTotalVirtual; + DWORDLONG ullAvailVirtual; + DWORDLONG ullAvailExtendedVirtual; +} MEMORY_STATUS_EX,*LPMEMORY_STATUS_EX; + #include #include +#include +#include +/* This either is not in psapi.h or guarded by higher value of + _WIN32_WINNT than what we use. */ +typedef struct _PROCESS_MEMORY_COUNTERS_EX { + DWORD cb; + DWORD PageFaultCount; + DWORD PeakWorkingSetSize; + DWORD WorkingSetSize; + DWORD QuotaPeakPagedPoolUsage; + DWORD QuotaPagedPoolUsage; + DWORD QuotaPeakNonPagedPoolUsage; + DWORD QuotaNonPagedPoolUsage; + DWORD PagefileUsage; + DWORD PeakPagefileUsage; + DWORD PrivateUsage; +} PROCESS_MEMORY_COUNTERS_EX,*PPROCESS_MEMORY_COUNTERS_EX; + #ifdef HAVE_SOCKETS /* TCP connection support, if kernel can do it */ #include #undef socket @@ -101,8 +134,9 @@ along with GNU Emacs. If not, see . */ #include "w32heap.h" #include "systime.h" #include "dispextern.h" /* for xstrcasecmp */ +#include "coding.h" /* for Vlocale_coding_system */ -/* For serial_configure() and serial_open() */ +/* For serial_configure and serial_open. */ #include "process.h" /* From process.c */ extern Lisp_Object QCport, QCspeed, QCprocess; @@ -144,6 +178,19 @@ static BOOL g_b_init_get_file_security; static BOOL g_b_init_get_security_descriptor_owner; static BOOL g_b_init_get_security_descriptor_group; static BOOL g_b_init_is_valid_sid; +static BOOL g_b_init_create_toolhelp32_snapshot; +static BOOL g_b_init_process32_first; +static BOOL g_b_init_process32_next; +static BOOL g_b_init_open_thread_token; +static BOOL g_b_init_impersonate_self; +static BOOL g_b_init_revert_to_self; +static BOOL g_b_init_get_process_memory_info; +static BOOL g_b_init_get_process_working_set_size; +static BOOL g_b_init_global_memory_status; +static BOOL g_b_init_global_memory_status_ex; +static BOOL g_b_init_get_length_sid; +static BOOL g_b_init_equal_sid; +static BOOL g_b_init_copy_sid; /* BEGIN: Wrapper functions around OpenProcessToken @@ -208,6 +255,46 @@ typedef BOOL (WINAPI * GetSecurityDescriptorGroup_Proc) ( LPBOOL lpbGroupDefaulted); typedef BOOL (WINAPI * IsValidSid_Proc) ( PSID sid); +typedef HANDLE (WINAPI * CreateToolhelp32Snapshot_Proc) ( + DWORD dwFlags, + DWORD th32ProcessID); +typedef BOOL (WINAPI * Process32First_Proc) ( + HANDLE hSnapshot, + LPPROCESSENTRY32 lppe); +typedef BOOL (WINAPI * Process32Next_Proc) ( + HANDLE hSnapshot, + LPPROCESSENTRY32 lppe); +typedef BOOL (WINAPI * OpenThreadToken_Proc) ( + HANDLE ThreadHandle, + DWORD DesiredAccess, + BOOL OpenAsSelf, + PHANDLE TokenHandle); +typedef BOOL (WINAPI * ImpersonateSelf_Proc) ( + SECURITY_IMPERSONATION_LEVEL ImpersonationLevel); +typedef BOOL (WINAPI * RevertToSelf_Proc) (void); +typedef BOOL (WINAPI * GetProcessMemoryInfo_Proc) ( + HANDLE Process, + PPROCESS_MEMORY_COUNTERS ppsmemCounters, + DWORD cb); +typedef BOOL (WINAPI * GetProcessWorkingSetSize_Proc) ( + HANDLE hProcess, + DWORD * lpMinimumWorkingSetSize, + DWORD * lpMaximumWorkingSetSize); +typedef BOOL (WINAPI * GlobalMemoryStatus_Proc) ( + LPMEMORYSTATUS lpBuffer); +typedef BOOL (WINAPI * GlobalMemoryStatusEx_Proc) ( + LPMEMORY_STATUS_EX lpBuffer); +typedef BOOL (WINAPI * CopySid_Proc) ( + DWORD nDestinationSidLength, + PSID pDestinationSid, + PSID pSourceSid); +typedef BOOL (WINAPI * EqualSid_Proc) ( + PSID pSid1, + PSID pSid2); +typedef DWORD (WINAPI * GetLengthSid_Proc) ( + PSID pSid); + + /* ** A utility function ** */ static BOOL @@ -555,6 +642,81 @@ BOOL WINAPI is_valid_sid ( return (s_pfn_Is_Valid_Sid (sid)); } +BOOL WINAPI equal_sid ( + PSID sid1, + PSID sid2) +{ + static EqualSid_Proc s_pfn_Equal_Sid = NULL; + HMODULE hm_advapi32 = NULL; + if (is_windows_9x () == TRUE) + { + return FALSE; + } + if (g_b_init_equal_sid == 0) + { + g_b_init_equal_sid = 1; + hm_advapi32 = LoadLibrary ("Advapi32.dll"); + s_pfn_Equal_Sid = + (EqualSid_Proc) GetProcAddress ( + hm_advapi32, "EqualSid"); + } + if (s_pfn_Equal_Sid == NULL) + { + return FALSE; + } + return (s_pfn_Equal_Sid (sid1, sid2)); +} + +DWORD WINAPI get_length_sid ( + PSID sid) +{ + static GetLengthSid_Proc s_pfn_Get_Length_Sid = NULL; + HMODULE hm_advapi32 = NULL; + if (is_windows_9x () == TRUE) + { + return 0; + } + if (g_b_init_get_length_sid == 0) + { + g_b_init_get_length_sid = 1; + hm_advapi32 = LoadLibrary ("Advapi32.dll"); + s_pfn_Get_Length_Sid = + (GetLengthSid_Proc) GetProcAddress ( + hm_advapi32, "GetLengthSid"); + } + if (s_pfn_Get_Length_Sid == NULL) + { + return 0; + } + return (s_pfn_Get_Length_Sid (sid)); +} + +BOOL WINAPI copy_sid ( + DWORD destlen, + PSID dest, + PSID src) +{ + static CopySid_Proc s_pfn_Copy_Sid = NULL; + HMODULE hm_advapi32 = NULL; + if (is_windows_9x () == TRUE) + { + return FALSE; + } + if (g_b_init_copy_sid == 0) + { + g_b_init_copy_sid = 1; + hm_advapi32 = LoadLibrary ("Advapi32.dll"); + s_pfn_Copy_Sid = + (CopySid_Proc) GetProcAddress ( + hm_advapi32, "CopySid"); + } + if (s_pfn_Copy_Sid == NULL) + { + return FALSE; + } + return (s_pfn_Copy_Sid (destlen, dest, src)); +} + /* END: Wrapper functions around OpenProcessToken and other functions in advapi32.dll that are only @@ -745,20 +907,37 @@ init_user_info () primary group sid from the process token). */ char uname[UNLEN+1], gname[GNLEN+1], domain[1025]; - DWORD ulength = sizeof (uname), dlength = sizeof (domain), trash; + DWORD ulength = sizeof (uname), dlength = sizeof (domain), needed; DWORD glength = sizeof (gname); HANDLE token = NULL; SID_NAME_USE user_type; - unsigned char buf[1024]; + unsigned char *buf = NULL; + DWORD blen = 0; TOKEN_USER user_token; TOKEN_PRIMARY_GROUP group_token; + BOOL result; - if (open_process_token (GetCurrentProcess (), TOKEN_QUERY, &token) - && get_token_information (token, TokenUser, - (PVOID)buf, sizeof (buf), &trash) - && (memcpy (&user_token, buf, sizeof (user_token)), - lookup_account_sid (NULL, user_token.User.Sid, uname, &ulength, - domain, &dlength, &user_type))) + result = open_process_token (GetCurrentProcess (), TOKEN_QUERY, &token); + if (result) + { + result = get_token_information (token, TokenUser, NULL, 0, &blen); + if (!result && GetLastError () == ERROR_INSUFFICIENT_BUFFER) + { + buf = xmalloc (blen); + result = get_token_information (token, TokenUser, + (LPVOID)buf, blen, &needed); + if (result) + { + memcpy (&user_token, buf, sizeof (user_token)); + result = lookup_account_sid (NULL, user_token.User.Sid, + uname, &ulength, + domain, &dlength, &user_type); + } + } + else + result = FALSE; + } + if (result) { strcpy (dflt_passwd.pw_name, uname); /* Determine a reasonable uid value. */ @@ -774,12 +953,22 @@ init_user_info () dflt_passwd.pw_uid = get_rid (user_token.User.Sid); /* Get group id and name. */ - if (get_token_information (token, TokenPrimaryGroup, - (PVOID)buf, sizeof (buf), &trash)) + result = get_token_information (token, TokenPrimaryGroup, + (LPVOID)buf, blen, &needed); + if (!result && GetLastError () == ERROR_INSUFFICIENT_BUFFER) + { + buf = xrealloc (buf, blen = needed); + result = get_token_information (token, TokenPrimaryGroup, + (LPVOID)buf, blen, &needed); + } + if (result) { memcpy (&group_token, buf, sizeof (group_token)); dflt_passwd.pw_gid = get_rid (group_token.PrimaryGroup); dlength = sizeof (domain); + /* If we can get at the real Primary Group name, use that. + Otherwise, the default group name was already set to + "None" in globals_of_w32. */ if (lookup_account_sid (NULL, group_token.PrimaryGroup, gname, &glength, NULL, &dlength, &user_type)) @@ -790,7 +979,7 @@ init_user_info () } } /* If security calls are not supported (presumably because we - are running under Windows 95), fallback to this. */ + are running under Windows 9X), fallback to this: */ else if (GetUserName (uname, &ulength)) { strcpy (dflt_passwd.pw_name, uname); @@ -818,6 +1007,7 @@ init_user_info () strcpy (dflt_passwd.pw_dir, getenv ("HOME")); strcpy (dflt_passwd.pw_shell, getenv ("SHELL")); + xfree (buf); if (token) CloseHandle (token); } @@ -1253,25 +1443,20 @@ init_environment (char ** argv) HRESULT profile_result; /* Dynamically load ShGetFolderPath, as it won't exist on versions of Windows 95 and NT4 that have not been updated to include - MSIE 5. Also we don't link with shell32.dll by default. */ - HMODULE shell32_dll; + MSIE 5. */ ShGetFolderPath_fn get_folder_path; - shell32_dll = GetModuleHandle ("shell32.dll"); get_folder_path = (ShGetFolderPath_fn) - GetProcAddress (shell32_dll, "SHGetFolderPathA"); + GetProcAddress (GetModuleHandle ("shell32.dll"), "SHGetFolderPathA"); if (get_folder_path != NULL) { profile_result = get_folder_path (NULL, CSIDL_APPDATA, NULL, 0, default_home); - /* If we can't get the appdata dir, revert to old behaviour. */ + /* If we can't get the appdata dir, revert to old behavior. */ if (profile_result == S_OK) env_vars[0].def_value = default_home; } - - /* Unload shell32.dll, it is not needed anymore. */ - FreeLibrary (shell32_dll); } /* Get default locale info and use it for LANG. */ @@ -2514,7 +2699,7 @@ sys_rename (const char * oldname, const char * newname) return -1; } - /* Emulate Unix behaviour - newname is deleted if it already exists + /* Emulate Unix behavior - newname is deleted if it already exists (at least if it is a file; don't do this for directories). Since we mustn't do this if we are just changing the case of the @@ -2557,6 +2742,14 @@ static FILETIME utc_base_ft; static long double utc_base; static int init = 0; +static long double +convert_time_raw (FILETIME ft) +{ + return + (long double) ft.dwHighDateTime + * 4096.0L * 1024.0L * 1024.0L + ft.dwLowDateTime; +} + static time_t convert_time (FILETIME ft) { @@ -2584,12 +2777,10 @@ convert_time (FILETIME ft) if (CompareFileTime (&ft, &utc_base_ft) < 0) return 0; - ret = (long double) ft.dwHighDateTime - * 4096.0L * 1024.0L * 1024.0L + ft.dwLowDateTime; - ret -= utc_base; - return (time_t) (ret * 1e-7L); + return (time_t) ((convert_time_raw (ft) - utc_base) * 1e-7L); } + void convert_from_time_t (time_t time, FILETIME * pft) { @@ -2701,6 +2892,69 @@ get_rid (PSID sid) return *get_sid_sub_authority (sid, n_subauthorities - 1); } +/* Caching SID and account values for faster lokup. */ + +#ifdef __GNUC__ +# define FLEXIBLE_ARRAY_MEMBER +#else +# define FLEXIBLE_ARRAY_MEMBER 1 +#endif + +struct w32_id { + int rid; + struct w32_id *next; + char name[GNLEN+1]; + unsigned char sid[FLEXIBLE_ARRAY_MEMBER]; +}; + +static struct w32_id *w32_idlist; + +static int +w32_cached_id (PSID sid, int *id, char *name) +{ + struct w32_id *tail, *found; + + for (found = NULL, tail = w32_idlist; tail; tail = tail->next) + { + if (equal_sid ((PSID)tail->sid, sid)) + { + found = tail; + break; + } + } + if (found) + { + *id = found->rid; + strcpy (name, found->name); + return 1; + } + else + return 0; +} + +static void +w32_add_to_cache (PSID sid, int id, char *name) +{ + DWORD sid_len; + struct w32_id *new_entry; + + /* We don't want to leave behind stale cache from when Emacs was + dumped. */ + if (initialized) + { + sid_len = get_length_sid (sid); + new_entry = xmalloc (offsetof (struct w32_id, sid) + sid_len); + if (new_entry) + { + new_entry->rid = id; + strcpy (new_entry->name, name); + copy_sid (sid_len, (PSID)new_entry->sid, sid); + new_entry->next = w32_idlist; + w32_idlist = new_entry; + } + } +} + #define UID 1 #define GID 2 @@ -2729,7 +2983,7 @@ get_name_and_id (PSECURITY_DESCRIPTOR psd, const char *fname, if (!result || !is_valid_sid (sid)) use_dflt = 1; - else + else if (!w32_cached_id (sid, id, nm)) { /* If FNAME is a UNC, we need to lookup account on the specified machine. */ @@ -2754,6 +3008,7 @@ get_name_and_id (PSECURITY_DESCRIPTOR psd, const char *fname, { *id = get_rid (sid); strcpy (nm, name); + w32_add_to_cache (sid, *id, name); } } return use_dflt; @@ -2800,6 +3055,8 @@ int stat (const char * path, struct stat * buf) { char *name, *r; + char drive_root[4]; + UINT devtype; WIN32_FIND_DATA wfd; HANDLE fh; unsigned __int64 fake_inode; @@ -2904,9 +3161,19 @@ stat (const char * path, struct stat * buf) } } + /* GetDriveType needs the root directory of NAME's drive. */ + if (!(strlen (name) >= 2 && IS_DEVICE_SEP (name[1]))) + devtype = GetDriveType (NULL); /* use root of current diectory */ + else + { + strncpy (drive_root, name, 3); + drive_root[3] = '\0'; + devtype = GetDriveType (drive_root); + } + if (!(NILP (Vw32_get_true_file_attributes) - || (EQ (Vw32_get_true_file_attributes, Qlocal) && - GetDriveType (name) != DRIVE_FIXED)) + || (EQ (Vw32_get_true_file_attributes, Qlocal) + && devtype != DRIVE_FIXED && devtype != DRIVE_RAMDISK)) /* No access rights required to get info. */ && (fh = CreateFile (name, 0, 0, NULL, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, NULL)) @@ -3160,6 +3427,706 @@ utime (const char *name, struct utimbuf *times) return 0; } + +/* Support for browsing other processes and their attributes. See + process.c for the Lisp bindings. */ + +/* Helper wrapper functions. */ + +HANDLE WINAPI create_toolhelp32_snapshot( + DWORD Flags, + DWORD Ignored) +{ + static CreateToolhelp32Snapshot_Proc s_pfn_Create_Toolhelp32_Snapshot = NULL; + + if (g_b_init_create_toolhelp32_snapshot == 0) + { + g_b_init_create_toolhelp32_snapshot = 1; + s_pfn_Create_Toolhelp32_Snapshot = (CreateToolhelp32Snapshot_Proc) + GetProcAddress (GetModuleHandle ("kernel32.dll"), + "CreateToolhelp32Snapshot"); + } + if (s_pfn_Create_Toolhelp32_Snapshot == NULL) + { + return INVALID_HANDLE_VALUE; + } + return (s_pfn_Create_Toolhelp32_Snapshot (Flags, Ignored)); +} + +BOOL WINAPI process32_first( + HANDLE hSnapshot, + LPPROCESSENTRY32 lppe) +{ + static Process32First_Proc s_pfn_Process32_First = NULL; + + if (g_b_init_process32_first == 0) + { + g_b_init_process32_first = 1; + s_pfn_Process32_First = (Process32First_Proc) + GetProcAddress (GetModuleHandle ("kernel32.dll"), + "Process32First"); + } + if (s_pfn_Process32_First == NULL) + { + return FALSE; + } + return (s_pfn_Process32_First (hSnapshot, lppe)); +} + +BOOL WINAPI process32_next( + HANDLE hSnapshot, + LPPROCESSENTRY32 lppe) +{ + static Process32Next_Proc s_pfn_Process32_Next = NULL; + + if (g_b_init_process32_next == 0) + { + g_b_init_process32_next = 1; + s_pfn_Process32_Next = (Process32Next_Proc) + GetProcAddress (GetModuleHandle ("kernel32.dll"), + "Process32Next"); + } + if (s_pfn_Process32_Next == NULL) + { + return FALSE; + } + return (s_pfn_Process32_Next (hSnapshot, lppe)); +} + +BOOL WINAPI open_thread_token ( + HANDLE ThreadHandle, + DWORD DesiredAccess, + BOOL OpenAsSelf, + PHANDLE TokenHandle) +{ + static OpenThreadToken_Proc s_pfn_Open_Thread_Token = NULL; + HMODULE hm_advapi32 = NULL; + if (is_windows_9x () == TRUE) + { + SetLastError (ERROR_NOT_SUPPORTED); + return FALSE; + } + if (g_b_init_open_thread_token == 0) + { + g_b_init_open_thread_token = 1; + hm_advapi32 = LoadLibrary ("Advapi32.dll"); + s_pfn_Open_Thread_Token = + (OpenThreadToken_Proc) GetProcAddress (hm_advapi32, "OpenThreadToken"); + } + if (s_pfn_Open_Thread_Token == NULL) + { + SetLastError (ERROR_NOT_SUPPORTED); + return FALSE; + } + return ( + s_pfn_Open_Thread_Token ( + ThreadHandle, + DesiredAccess, + OpenAsSelf, + TokenHandle) + ); +} + +BOOL WINAPI impersonate_self ( + SECURITY_IMPERSONATION_LEVEL ImpersonationLevel) +{ + static ImpersonateSelf_Proc s_pfn_Impersonate_Self = NULL; + HMODULE hm_advapi32 = NULL; + if (is_windows_9x () == TRUE) + { + return FALSE; + } + if (g_b_init_impersonate_self == 0) + { + g_b_init_impersonate_self = 1; + hm_advapi32 = LoadLibrary ("Advapi32.dll"); + s_pfn_Impersonate_Self = + (ImpersonateSelf_Proc) GetProcAddress (hm_advapi32, "ImpersonateSelf"); + } + if (s_pfn_Impersonate_Self == NULL) + { + return FALSE; + } + return s_pfn_Impersonate_Self (ImpersonationLevel); +} + +BOOL WINAPI revert_to_self (void) +{ + static RevertToSelf_Proc s_pfn_Revert_To_Self = NULL; + HMODULE hm_advapi32 = NULL; + if (is_windows_9x () == TRUE) + { + return FALSE; + } + if (g_b_init_revert_to_self == 0) + { + g_b_init_revert_to_self = 1; + hm_advapi32 = LoadLibrary ("Advapi32.dll"); + s_pfn_Revert_To_Self = + (RevertToSelf_Proc) GetProcAddress (hm_advapi32, "RevertToSelf"); + } + if (s_pfn_Revert_To_Self == NULL) + { + return FALSE; + } + return s_pfn_Revert_To_Self (); +} + +BOOL WINAPI get_process_memory_info ( + HANDLE h_proc, + PPROCESS_MEMORY_COUNTERS mem_counters, + DWORD bufsize) +{ + static GetProcessMemoryInfo_Proc s_pfn_Get_Process_Memory_Info = NULL; + HMODULE hm_psapi = NULL; + if (is_windows_9x () == TRUE) + { + return FALSE; + } + if (g_b_init_get_process_memory_info == 0) + { + g_b_init_get_process_memory_info = 1; + hm_psapi = LoadLibrary ("Psapi.dll"); + if (hm_psapi) + s_pfn_Get_Process_Memory_Info = (GetProcessMemoryInfo_Proc) + GetProcAddress (hm_psapi, "GetProcessMemoryInfo"); + } + if (s_pfn_Get_Process_Memory_Info == NULL) + { + return FALSE; + } + return s_pfn_Get_Process_Memory_Info (h_proc, mem_counters, bufsize); +} + +BOOL WINAPI get_process_working_set_size ( + HANDLE h_proc, + DWORD *minrss, + DWORD *maxrss) +{ + static GetProcessWorkingSetSize_Proc + s_pfn_Get_Process_Working_Set_Size = NULL; + + if (is_windows_9x () == TRUE) + { + return FALSE; + } + if (g_b_init_get_process_working_set_size == 0) + { + g_b_init_get_process_working_set_size = 1; + s_pfn_Get_Process_Working_Set_Size = (GetProcessWorkingSetSize_Proc) + GetProcAddress (GetModuleHandle ("kernel32.dll"), + "GetProcessWorkingSetSize"); + } + if (s_pfn_Get_Process_Working_Set_Size == NULL) + { + return FALSE; + } + return s_pfn_Get_Process_Working_Set_Size (h_proc, minrss, maxrss); +} + +BOOL WINAPI global_memory_status ( + MEMORYSTATUS *buf) +{ + static GlobalMemoryStatus_Proc s_pfn_Global_Memory_Status = NULL; + + if (is_windows_9x () == TRUE) + { + return FALSE; + } + if (g_b_init_global_memory_status == 0) + { + g_b_init_global_memory_status = 1; + s_pfn_Global_Memory_Status = (GlobalMemoryStatus_Proc) + GetProcAddress (GetModuleHandle ("kernel32.dll"), + "GlobalMemoryStatus"); + } + if (s_pfn_Global_Memory_Status == NULL) + { + return FALSE; + } + return s_pfn_Global_Memory_Status (buf); +} + +BOOL WINAPI global_memory_status_ex ( + MEMORY_STATUS_EX *buf) +{ + static GlobalMemoryStatusEx_Proc s_pfn_Global_Memory_Status_Ex = NULL; + + if (is_windows_9x () == TRUE) + { + return FALSE; + } + if (g_b_init_global_memory_status_ex == 0) + { + g_b_init_global_memory_status_ex = 1; + s_pfn_Global_Memory_Status_Ex = (GlobalMemoryStatusEx_Proc) + GetProcAddress (GetModuleHandle ("kernel32.dll"), + "GlobalMemoryStatusEx"); + } + if (s_pfn_Global_Memory_Status_Ex == NULL) + { + return FALSE; + } + return s_pfn_Global_Memory_Status_Ex (buf); +} + +Lisp_Object +list_system_processes () +{ + struct gcpro gcpro1; + Lisp_Object proclist = Qnil; + HANDLE h_snapshot; + + h_snapshot = create_toolhelp32_snapshot (TH32CS_SNAPPROCESS, 0); + + if (h_snapshot != INVALID_HANDLE_VALUE) + { + PROCESSENTRY32 proc_entry; + DWORD proc_id; + BOOL res; + + GCPRO1 (proclist); + + proc_entry.dwSize = sizeof (PROCESSENTRY32); + for (res = process32_first (h_snapshot, &proc_entry); res; + res = process32_next (h_snapshot, &proc_entry)) + { + proc_id = proc_entry.th32ProcessID; + proclist = Fcons (make_fixnum_or_float (proc_id), proclist); + } + + CloseHandle (h_snapshot); + UNGCPRO; + proclist = Fnreverse (proclist); + } + + return proclist; +} + +static int +enable_privilege (LPCTSTR priv_name, BOOL enable_p, TOKEN_PRIVILEGES *old_priv) +{ + TOKEN_PRIVILEGES priv; + DWORD priv_size = sizeof (priv); + DWORD opriv_size = sizeof (*old_priv); + HANDLE h_token = NULL; + HANDLE h_thread = GetCurrentThread (); + int ret_val = 0; + BOOL res; + + res = open_thread_token (h_thread, + TOKEN_QUERY | TOKEN_ADJUST_PRIVILEGES, + FALSE, &h_token); + if (!res && GetLastError () == ERROR_NO_TOKEN) + { + if (impersonate_self (SecurityImpersonation)) + res = open_thread_token (h_thread, + TOKEN_QUERY | TOKEN_ADJUST_PRIVILEGES, + FALSE, &h_token); + } + if (res) + { + priv.PrivilegeCount = 1; + priv.Privileges[0].Attributes = enable_p ? SE_PRIVILEGE_ENABLED : 0; + LookupPrivilegeValue (NULL, priv_name, &priv.Privileges[0].Luid); + if (AdjustTokenPrivileges (h_token, FALSE, &priv, priv_size, + old_priv, &opriv_size) + && GetLastError () != ERROR_NOT_ALL_ASSIGNED) + ret_val = 1; + } + if (h_token) + CloseHandle (h_token); + + return ret_val; +} + +static int +restore_privilege (TOKEN_PRIVILEGES *priv) +{ + DWORD priv_size = sizeof (*priv); + HANDLE h_token = NULL; + int ret_val = 0; + + if (open_thread_token (GetCurrentThread (), + TOKEN_QUERY | TOKEN_ADJUST_PRIVILEGES, + FALSE, &h_token)) + { + if (AdjustTokenPrivileges (h_token, FALSE, priv, priv_size, NULL, NULL) + && GetLastError () != ERROR_NOT_ALL_ASSIGNED) + ret_val = 1; + } + if (h_token) + CloseHandle (h_token); + + return ret_val; +} + +static Lisp_Object +ltime (time_sec, time_usec) + long time_sec, time_usec; +{ + return list3 (make_number ((time_sec >> 16) & 0xffff), + make_number (time_sec & 0xffff), + make_number (time_usec)); +} + +static int +process_times (h_proc, ctime, etime, stime, utime, pcpu) + HANDLE h_proc; + Lisp_Object *ctime, *etime, *stime, *utime; + double *pcpu; +{ + FILETIME ft_creation, ft_exit, ft_kernel, ft_user, ft_current; + long ctime_sec, ctime_usec, stime_sec, stime_usec, utime_sec, utime_usec; + long etime_sec, etime_usec; + long double tem1, tem2, tem; + + if (!h_proc + || !get_process_times_fn + || !(*get_process_times_fn)(h_proc, &ft_creation, &ft_exit, + &ft_kernel, &ft_user)) + return 0; + + GetSystemTimeAsFileTime (&ft_current); + + tem1 = convert_time_raw (ft_kernel) * 0.1L; + stime_usec = fmodl (tem1, 1000000.0L); + stime_sec = tem1 * 0.000001L; + *stime = ltime (stime_sec, stime_usec); + tem2 = convert_time_raw (ft_user) * 0.1L; + utime_usec = fmodl (tem2, 1000000.0L); + utime_sec = tem2 * 0.000001L; + *utime = ltime (utime_sec, utime_usec); + tem = convert_time_raw (ft_creation); + /* Process no 4 (System) returns zero creation time. */ + if (tem) + tem = (tem - utc_base) * 0.1; + ctime_usec = fmodl (tem, 1000000.0L); + ctime_sec = tem * 0.000001L; + *ctime = ltime (ctime_sec, ctime_usec); + if (tem) + tem = (convert_time_raw (ft_current) - utc_base) * 0.1L - tem; + etime_usec = fmodl (tem, 1000000.0L); + etime_sec = tem * 0.000001L; + *etime = ltime (etime_sec, etime_usec); + + if (tem) + { + *pcpu = 100.0 * (tem1 + tem2) / tem; + if (*pcpu > 100) + *pcpu = 100.0; + } + else + *pcpu = 0; + + return 1; +} + +Lisp_Object +system_process_attributes (pid) + Lisp_Object pid; +{ + struct gcpro gcpro1, gcpro2, gcpro3; + Lisp_Object attrs = Qnil; + Lisp_Object cmd_str, decoded_cmd, tem; + HANDLE h_snapshot, h_proc; + DWORD proc_id; + int found_proc = 0; + char uname[UNLEN+1], gname[GNLEN+1], domain[1025]; + DWORD ulength = sizeof (uname), dlength = sizeof (domain), needed; + DWORD glength = sizeof (gname); + HANDLE token = NULL; + SID_NAME_USE user_type; + unsigned char *buf = NULL; + DWORD blen = 0; + TOKEN_USER user_token; + TOKEN_PRIMARY_GROUP group_token; + int euid; + int egid; + DWORD sess; + PROCESS_MEMORY_COUNTERS mem; + PROCESS_MEMORY_COUNTERS_EX mem_ex; + DWORD minrss, maxrss; + MEMORYSTATUS memst; + MEMORY_STATUS_EX memstex; + double totphys = 0.0; + Lisp_Object ctime, stime, utime, etime; + double pcpu; + BOOL result = FALSE; + + CHECK_NUMBER_OR_FLOAT (pid); + proc_id = FLOATP (pid) ? XFLOAT_DATA (pid) : XINT (pid); + + h_snapshot = create_toolhelp32_snapshot (TH32CS_SNAPPROCESS, 0); + + GCPRO3 (attrs, decoded_cmd, tem); + + if (h_snapshot != INVALID_HANDLE_VALUE) + { + PROCESSENTRY32 pe; + BOOL res; + + pe.dwSize = sizeof (PROCESSENTRY32); + for (res = process32_first (h_snapshot, &pe); res; + res = process32_next (h_snapshot, &pe)) + { + if (proc_id == pe.th32ProcessID) + { + if (proc_id == 0) + decoded_cmd = build_string ("Idle"); + else + { + /* Decode the command name from locale-specific + encoding. */ + cmd_str = make_unibyte_string (pe.szExeFile, + strlen (pe.szExeFile)); + decoded_cmd = + code_convert_string_norecord (cmd_str, + Vlocale_coding_system, 0); + } + attrs = Fcons (Fcons (Qcomm, decoded_cmd), attrs); + attrs = Fcons (Fcons (Qppid, + make_fixnum_or_float (pe.th32ParentProcessID)), + attrs); + attrs = Fcons (Fcons (Qpri, make_number (pe.pcPriClassBase)), + attrs); + attrs = Fcons (Fcons (Qthcount, + make_fixnum_or_float (pe.cntThreads)), + attrs); + found_proc = 1; + break; + } + } + + CloseHandle (h_snapshot); + } + + if (!found_proc) + { + UNGCPRO; + return Qnil; + } + + h_proc = OpenProcess (PROCESS_QUERY_INFORMATION | PROCESS_VM_READ, + FALSE, proc_id); + /* If we were denied a handle to the process, try again after + enabling the SeDebugPrivilege in our process. */ + if (!h_proc) + { + TOKEN_PRIVILEGES priv_current; + + if (enable_privilege (SE_DEBUG_NAME, TRUE, &priv_current)) + { + h_proc = OpenProcess (PROCESS_QUERY_INFORMATION | PROCESS_VM_READ, + FALSE, proc_id); + restore_privilege (&priv_current); + revert_to_self (); + } + } + if (h_proc) + { + result = open_process_token (h_proc, TOKEN_QUERY, &token); + if (result) + { + result = get_token_information (token, TokenUser, NULL, 0, &blen); + if (!result && GetLastError () == ERROR_INSUFFICIENT_BUFFER) + { + buf = xmalloc (blen); + result = get_token_information (token, TokenUser, + (LPVOID)buf, blen, &needed); + if (result) + { + memcpy (&user_token, buf, sizeof (user_token)); + if (!w32_cached_id (user_token.User.Sid, &euid, uname)) + { + euid = get_rid (user_token.User.Sid); + result = lookup_account_sid (NULL, user_token.User.Sid, + uname, &ulength, + domain, &dlength, + &user_type); + if (result) + w32_add_to_cache (user_token.User.Sid, euid, uname); + else + { + strcpy (uname, "unknown"); + result = TRUE; + } + } + ulength = strlen (uname); + } + } + } + if (result) + { + /* Determine a reasonable euid and gid values. */ + if (xstrcasecmp ("administrator", uname) == 0) + { + euid = 500; /* well-known Administrator uid */ + egid = 513; /* well-known None gid */ + } + else + { + /* Get group id and name. */ + result = get_token_information (token, TokenPrimaryGroup, + (LPVOID)buf, blen, &needed); + if (!result && GetLastError () == ERROR_INSUFFICIENT_BUFFER) + { + buf = xrealloc (buf, blen = needed); + result = get_token_information (token, TokenPrimaryGroup, + (LPVOID)buf, blen, &needed); + } + if (result) + { + memcpy (&group_token, buf, sizeof (group_token)); + if (!w32_cached_id (group_token.PrimaryGroup, &egid, gname)) + { + egid = get_rid (group_token.PrimaryGroup); + dlength = sizeof (domain); + result = + lookup_account_sid (NULL, group_token.PrimaryGroup, + gname, &glength, NULL, &dlength, + &user_type); + if (result) + w32_add_to_cache (group_token.PrimaryGroup, + egid, gname); + else + { + strcpy (gname, "None"); + result = TRUE; + } + } + glength = strlen (gname); + } + } + } + if (buf) + xfree (buf); + } + if (!result) + { + if (!is_windows_9x ()) + { + /* We couldn't open the process token, presumably because of + insufficient access rights. Assume this process is run + by the system. */ + strcpy (uname, "SYSTEM"); + strcpy (gname, "None"); + euid = 18; /* SYSTEM */ + egid = 513; /* None */ + glength = strlen (gname); + ulength = strlen (uname); + } + /* If we are running under Windows 9X, where security calls are + not supported, we assume all processes are run by the current + user. */ + else if (GetUserName (uname, &ulength)) + { + if (xstrcasecmp ("administrator", uname) == 0) + euid = 0; + else + euid = 123; + egid = euid; + strcpy (gname, "None"); + glength = strlen (gname); + ulength = strlen (uname); + } + else + { + euid = 123; + egid = 123; + strcpy (uname, "administrator"); + ulength = strlen (uname); + strcpy (gname, "None"); + glength = strlen (gname); + } + if (token) + CloseHandle (token); + } + + attrs = Fcons (Fcons (Qeuid, make_fixnum_or_float (euid)), attrs); + tem = make_unibyte_string (uname, ulength); + attrs = Fcons (Fcons (Quser, + code_convert_string_norecord (tem, Vlocale_coding_system, 0)), + attrs); + attrs = Fcons (Fcons (Qegid, make_fixnum_or_float (egid)), attrs); + tem = make_unibyte_string (gname, glength); + attrs = Fcons (Fcons (Qgroup, + code_convert_string_norecord (tem, Vlocale_coding_system, 0)), + attrs); + + if (global_memory_status_ex (&memstex)) +#if __GNUC__ || (defined (_MSC_VER) && _MSC_VER >= 1300) + totphys = memstex.ullTotalPhys / 1024.0; +#else + /* Visual Studio 6 cannot convert an unsigned __int64 type to + double, so we need to do this for it... */ + { + DWORD tot_hi = memstex.ullTotalPhys >> 32; + DWORD tot_md = (memstex.ullTotalPhys & 0x00000000ffffffff) >> 10; + DWORD tot_lo = memstex.ullTotalPhys % 1024; + + totphys = tot_hi * 4194304.0 + tot_md + tot_lo / 1024.0; + } +#endif /* __GNUC__ || _MSC_VER >= 1300 */ + else if (global_memory_status (&memst)) + totphys = memst.dwTotalPhys / 1024.0; + + if (h_proc + && get_process_memory_info (h_proc, (PROCESS_MEMORY_COUNTERS *)&mem_ex, + sizeof (mem_ex))) + { + DWORD rss = mem_ex.WorkingSetSize / 1024; + + attrs = Fcons (Fcons (Qmajflt, + make_fixnum_or_float (mem_ex.PageFaultCount)), + attrs); + attrs = Fcons (Fcons (Qvsize, + make_fixnum_or_float (mem_ex.PrivateUsage / 1024)), + attrs); + attrs = Fcons (Fcons (Qrss, make_fixnum_or_float (rss)), attrs); + if (totphys) + attrs = Fcons (Fcons (Qpmem, make_float (100. * rss / totphys)), attrs); + } + else if (h_proc + && get_process_memory_info (h_proc, &mem, sizeof (mem))) + { + DWORD rss = mem_ex.WorkingSetSize / 1024; + + attrs = Fcons (Fcons (Qmajflt, + make_fixnum_or_float (mem.PageFaultCount)), + attrs); + attrs = Fcons (Fcons (Qrss, make_fixnum_or_float (rss)), attrs); + if (totphys) + attrs = Fcons (Fcons (Qpmem, make_float (100. * rss / totphys)), attrs); + } + else if (h_proc + && get_process_working_set_size (h_proc, &minrss, &maxrss)) + { + DWORD rss = maxrss / 1024; + + attrs = Fcons (Fcons (Qrss, make_fixnum_or_float (maxrss / 1024)), attrs); + if (totphys) + attrs = Fcons (Fcons (Qpmem, make_float (100. * rss / totphys)), attrs); + } + + if (process_times (h_proc, &ctime, &etime, &stime, &utime, &pcpu)) + { + attrs = Fcons (Fcons (Qutime, utime), attrs); + attrs = Fcons (Fcons (Qstime, stime), attrs); + attrs = Fcons (Fcons (Qstart, ctime), attrs); + attrs = Fcons (Fcons (Qetime, etime), attrs); + attrs = Fcons (Fcons (Qpcpu, make_float (pcpu)), attrs); + } + + /* FIXME: Retrieve command line by walking the PEB of the process. */ + + if (h_proc) + CloseHandle (h_proc); + UNGCPRO; + return attrs; +} + + #ifdef HAVE_SOCKETS /* Wrappers for winsock functions to map between our file descriptors @@ -3319,7 +4286,7 @@ init_winsock (int load_now) int h_errno = 0; -/* function to set h_errno for compatability; map winsock error codes to +/* function to set h_errno for compatibility; map winsock error codes to normal system codes where they overlap (non-overlapping definitions are already in */ static void @@ -4717,6 +5684,19 @@ globals_of_w32 () g_b_init_get_security_descriptor_owner = 0; g_b_init_get_security_descriptor_group = 0; g_b_init_is_valid_sid = 0; + g_b_init_create_toolhelp32_snapshot = 0; + g_b_init_process32_first = 0; + g_b_init_process32_next = 0; + g_b_init_open_thread_token = 0; + g_b_init_impersonate_self = 0; + g_b_init_revert_to_self = 0; + g_b_init_get_process_memory_info = 0; + g_b_init_get_process_working_set_size = 0; + g_b_init_global_memory_status = 0; + g_b_init_global_memory_status_ex = 0; + g_b_init_equal_sid = 0; + g_b_init_copy_sid = 0; + g_b_init_get_length_sid = 0; /* The following sets a handler for shutdown notifications for console apps. This actually applies to Emacs in both console and GUI modes, since we had to fool windows into thinking emacs is a