X-Git-Url: http://git.hcoop.net/bpt/emacs.git/blobdiff_plain/2a85f0c2f09bdf356640820ed228897520d6204b..b7e1d896e0651d34fda0dca3ebe1874b3bfddc01:/src/w32.c diff --git a/src/w32.c b/src/w32.c index 5667f99de4..c6660c0d3d 100644 --- a/src/w32.c +++ b/src/w32.c @@ -1,13 +1,13 @@ /* 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. -GNU Emacs is free software; you can redistribute it and/or modify +GNU Emacs is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by -the Free Software Foundation; either version 3, or (at your option) -any later version. +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. GNU Emacs is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of @@ -15,10 +15,9 @@ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License -along with GNU Emacs; see the file COPYING. If not, write to -the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, -Boston, MA 02110-1301, USA. +along with GNU Emacs. If not, see . */ +/* Geoff Voelker (voelker@cs.washington.edu) 7-29-94 */ #include /* for offsetof */ @@ -33,6 +32,7 @@ Boston, MA 02110-1301, USA. #include #include #include /* for _mbspbrk */ +#include /* must include CRT headers *before* config.h */ @@ -73,9 +73,41 @@ Boston, MA 02110-1301, USA. #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,6 +133,15 @@ Boston, MA 02110-1301, USA. #include "ndir.h" #include "w32heap.h" #include "systime.h" +#include "dispextern.h" /* for xstrcasecmp */ +#include "coding.h" /* for Vlocale_coding_system */ + +/* For serial_configure and serial_open. */ +#include "process.h" +/* From process.c */ +extern Lisp_Object QCport, QCspeed, QCprocess; +extern Lisp_Object QCbytesize, QCstopbits, QCparity, Qodd, Qeven; +extern Lisp_Object QCflowcontrol, Qhw, Qsw, QCsummary; typedef HRESULT (WINAPI * ShGetFolderPath_fn) (IN HWND, IN int, IN HANDLE, IN DWORD, OUT char *); @@ -137,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 @@ -201,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 @@ -548,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 @@ -721,7 +890,7 @@ getpwnam (char *name) if (!pw) return pw; - if (stricmp (name, pw->pw_name)) + if (xstrcasecmp (name, pw->pw_name)) return NULL; return pw; @@ -738,24 +907,41 @@ 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. */ - if (stricmp ("administrator", uname) == 0) + if (xstrcasecmp ("administrator", uname) == 0) { dflt_passwd.pw_uid = 500; /* well-known Administrator uid */ dflt_passwd.pw_gid = 513; /* well-known None gid */ @@ -767,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)) @@ -783,11 +979,11 @@ 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); - if (stricmp ("administrator", uname) == 0) + if (xstrcasecmp ("administrator", uname) == 0) dflt_passwd.pw_uid = 0; else dflt_passwd.pw_uid = 123; @@ -811,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); } @@ -1128,7 +1325,7 @@ w32_get_resource (key, lpdwtype) return (lpvalue); } - if (lpvalue) xfree (lpvalue); + xfree (lpvalue); RegCloseKey (hrootkey); } @@ -1145,7 +1342,7 @@ w32_get_resource (key, lpdwtype) return (lpvalue); } - if (lpvalue) xfree (lpvalue); + xfree (lpvalue); RegCloseKey (hrootkey); } @@ -1246,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. */ @@ -1297,7 +1489,7 @@ init_environment (char ** argv) abort (); *p = 0; - if ((p = strrchr (modname, '\\')) && stricmp (p, "\\bin") == 0) + if ((p = strrchr (modname, '\\')) && xstrcasecmp (p, "\\bin") == 0) { char buf[SET_ENV_BUF_SIZE]; @@ -1313,7 +1505,7 @@ init_environment (char ** argv) /* FIXME: should use substring of get_emacs_configuration (). But I don't think the Windows build supports alpha, mips etc anymore, so have taken the easy option for now. */ - else if (p && stricmp (p, "\\i386") == 0) + else if (p && xstrcasecmp (p, "\\i386") == 0) { *p = 0; p = strrchr (modname, '\\'); @@ -1321,7 +1513,7 @@ init_environment (char ** argv) { *p = 0; p = strrchr (modname, '\\'); - if (p && stricmp (p, "\\src") == 0) + if (p && xstrcasecmp (p, "\\src") == 0) { char buf[SET_ENV_BUF_SIZE]; @@ -1346,7 +1538,7 @@ init_environment (char ** argv) /* Also ignore empty environment variables. */ || *lpval == 0) { - if (lpval) xfree (lpval); + xfree (lpval); lpval = env_vars[i].def_value; dwType = REG_EXPAND_SZ; dont_free = 1; @@ -1653,7 +1845,7 @@ lookup_volume_info (char * root_dir) volume_info_data * info; for (info = volume_cache; info; info = info->next) - if (stricmp (info->root_dir, root_dir) == 0) + if (xstrcasecmp (info->root_dir, root_dir) == 0) break; return info; } @@ -1931,10 +2123,10 @@ is_exec (const char * name) char * p = strrchr (name, '.'); return (p != NULL - && (stricmp (p, ".exe") == 0 || - stricmp (p, ".com") == 0 || - stricmp (p, ".bat") == 0 || - stricmp (p, ".cmd") == 0)); + && (xstrcasecmp (p, ".exe") == 0 || + xstrcasecmp (p, ".com") == 0 || + xstrcasecmp (p, ".bat") == 0 || + xstrcasecmp (p, ".cmd") == 0)); } /* Emulate the Unix directory procedures opendir, closedir, @@ -2507,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 @@ -2550,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) { @@ -2577,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) { @@ -2694,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 @@ -2722,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. */ @@ -2747,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; @@ -2793,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; @@ -2878,7 +3142,7 @@ stat (const char * path, struct stat * buf) if (dir_find_handle != INVALID_HANDLE_VALUE && strnicmp (name, dir_pathname, len) == 0 && IS_DIRECTORY_SEP (name[len]) - && stricmp (name + len + 1, dir_static.d_name) == 0) + && xstrcasecmp (name + len + 1, dir_static.d_name) == 0) { /* This was the last entry returned by readdir. */ wfd = dir_find_data; @@ -2897,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)) @@ -2962,8 +3236,7 @@ stat (const char * path, struct stat * buf) get_file_owner_and_group (NULL, name, buf); } - if (psd) - xfree (psd); + xfree (psd); #if 0 /* Not sure if there is any point in this. */ @@ -3104,10 +3377,10 @@ fstat (int desc, struct stat * buf) #if 0 /* no way of knowing the filename */ char * p = strrchr (name, '.'); if (p != NULL && - (stricmp (p, ".exe") == 0 || - stricmp (p, ".com") == 0 || - stricmp (p, ".bat") == 0 || - stricmp (p, ".cmd") == 0)) + (xstrcasecmp (p, ".exe") == 0 || + xstrcasecmp (p, ".com") == 0 || + xstrcasecmp (p, ".bat") == 0 || + xstrcasecmp (p, ".cmd") == 0)) permission |= S_IEXEC; #endif } @@ -3154,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 +w32_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 +w32_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 @@ -3313,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 @@ -4064,10 +5037,10 @@ _sys_read_ahead (int fd) if (cp == NULL || cp->fd != fd || cp->status != STATUS_READ_READY) return STATUS_READ_ERROR; - if ((fd_info[fd].flags & (FILE_PIPE | FILE_SOCKET)) == 0 + if ((fd_info[fd].flags & (FILE_PIPE | FILE_SERIAL | FILE_SOCKET)) == 0 || (fd_info[fd].flags & FILE_READ) == 0) { - DebPrint (("_sys_read_ahead: internal error: fd %d is not a pipe or socket!\n", fd)); + DebPrint (("_sys_read_ahead: internal error: fd %d is not a pipe, serial port, or socket!\n", fd)); abort (); } @@ -4081,7 +5054,7 @@ _sys_read_ahead (int fd) reporting that input is available; we need this because Windows 95 connects DOS programs to pipes by making the pipe appear to be the normal console stdout - as a result most DOS programs will - write to stdout without buffering, ie. one character at a + write to stdout without buffering, ie. one character at a time. Even some W32 programs do this - "dir" in a command shell on NT is very slow if we don't do this. */ if (rc > 0) @@ -4097,6 +5070,29 @@ _sys_read_ahead (int fd) Sleep (0); } } + else if (fd_info[fd].flags & FILE_SERIAL) + { + HANDLE hnd = fd_info[fd].hnd; + OVERLAPPED *ovl = &fd_info[fd].cp->ovl_read; + COMMTIMEOUTS ct; + + /* Configure timeouts for blocking read. */ + if (!GetCommTimeouts (hnd, &ct)) + return STATUS_READ_ERROR; + ct.ReadIntervalTimeout = 0; + ct.ReadTotalTimeoutMultiplier = 0; + ct.ReadTotalTimeoutConstant = 0; + if (!SetCommTimeouts (hnd, &ct)) + return STATUS_READ_ERROR; + + if (!ReadFile (hnd, &cp->chr, sizeof (char), (DWORD*) &rc, ovl)) + { + if (GetLastError () != ERROR_IO_PENDING) + return STATUS_READ_ERROR; + if (!GetOverlappedResult (hnd, ovl, (DWORD*) &rc, TRUE)) + return STATUS_READ_ERROR; + } + } #ifdef HAVE_SOCKETS else if (fd_info[fd].flags & FILE_SOCKET) { @@ -4168,7 +5164,7 @@ sys_read (int fd, char * buffer, unsigned int count) return -1; } - if (fd < MAXDESC && fd_info[fd].flags & (FILE_PIPE | FILE_SOCKET)) + if (fd < MAXDESC && fd_info[fd].flags & (FILE_PIPE | FILE_SOCKET | FILE_SERIAL)) { child_process *cp = fd_info[fd].cp; @@ -4239,6 +5235,52 @@ sys_read (int fd, char * buffer, unsigned int count) if (to_read > 0) nchars += _read (fd, buffer, to_read); } + else if (fd_info[fd].flags & FILE_SERIAL) + { + HANDLE hnd = fd_info[fd].hnd; + OVERLAPPED *ovl = &fd_info[fd].cp->ovl_read; + DWORD err = 0; + int rc = 0; + COMMTIMEOUTS ct; + + if (count > 0) + { + /* Configure timeouts for non-blocking read. */ + if (!GetCommTimeouts (hnd, &ct)) + { + errno = EIO; + return -1; + } + ct.ReadIntervalTimeout = MAXDWORD; + ct.ReadTotalTimeoutMultiplier = 0; + ct.ReadTotalTimeoutConstant = 0; + if (!SetCommTimeouts (hnd, &ct)) + { + errno = EIO; + return -1; + } + + if (!ResetEvent (ovl->hEvent)) + { + errno = EIO; + return -1; + } + if (!ReadFile (hnd, buffer, count, (DWORD*) &rc, ovl)) + { + if (GetLastError () != ERROR_IO_PENDING) + { + errno = EIO; + return -1; + } + if (!GetOverlappedResult (hnd, ovl, (DWORD*) &rc, TRUE)) + { + errno = EIO; + return -1; + } + } + nchars += rc; + } + } #ifdef HAVE_SOCKETS else /* FILE_SOCKET */ { @@ -4300,6 +5342,9 @@ sys_read (int fd, char * buffer, unsigned int count) return nchars; } +/* From w32xfns.c */ +extern HANDLE interrupt_handle; + /* For now, don't bother with a non-blocking mode */ int sys_write (int fd, const void * buffer, unsigned int count) @@ -4312,7 +5357,7 @@ sys_write (int fd, const void * buffer, unsigned int count) return -1; } - if (fd < MAXDESC && fd_info[fd].flags & (FILE_PIPE | FILE_SOCKET)) + if (fd < MAXDESC && fd_info[fd].flags & (FILE_PIPE | FILE_SOCKET | FILE_SERIAL)) { if ((fd_info[fd].flags & FILE_WRITE) == 0) { @@ -4353,6 +5398,42 @@ sys_write (int fd, const void * buffer, unsigned int count) } } + if (fd < MAXDESC && fd_info[fd].flags & FILE_SERIAL) + { + HANDLE hnd = (HANDLE) _get_osfhandle (fd); + OVERLAPPED *ovl = &fd_info[fd].cp->ovl_write; + HANDLE wait_hnd[2] = { interrupt_handle, ovl->hEvent }; + DWORD active = 0; + + if (!WriteFile (hnd, buffer, count, (DWORD*) &nchars, ovl)) + { + if (GetLastError () != ERROR_IO_PENDING) + { + errno = EIO; + return -1; + } + if (detect_input_pending ()) + active = MsgWaitForMultipleObjects (2, wait_hnd, FALSE, INFINITE, + QS_ALLINPUT); + else + active = WaitForMultipleObjects (2, wait_hnd, FALSE, INFINITE); + if (active == WAIT_OBJECT_0) + { /* User pressed C-g, cancel write, then leave. Don't bother + cleaning up as we may only get stuck in buggy drivers. */ + PurgeComm (hnd, PURGE_TXABORT | PURGE_TXCLEAR); + CancelIo (hnd); + errno = EIO; + return -1; + } + if (active == WAIT_OBJECT_0 + 1 + && !GetOverlappedResult (hnd, ovl, (DWORD*) &nchars, TRUE)) + { + errno = EIO; + return -1; + } + } + } + else #ifdef HAVE_SOCKETS if (fd < MAXDESC && fd_info[fd].flags & FILE_SOCKET) { @@ -4603,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 @@ -4613,6 +5707,196 @@ globals_of_w32 () strcpy (dflt_group_name, "None"); } +/* For make-serial-process */ +int serial_open (char *port) +{ + HANDLE hnd; + child_process *cp; + int fd = -1; + + hnd = CreateFile (port, GENERIC_READ | GENERIC_WRITE, 0, 0, + OPEN_EXISTING, FILE_FLAG_OVERLAPPED, 0); + if (hnd == INVALID_HANDLE_VALUE) + error ("Could not open %s", port); + fd = (int) _open_osfhandle ((int) hnd, 0); + if (fd == -1) + error ("Could not open %s", port); + + cp = new_child (); + if (!cp) + error ("Could not create child process"); + cp->fd = fd; + cp->status = STATUS_READ_ACKNOWLEDGED; + fd_info[ fd ].hnd = hnd; + fd_info[ fd ].flags |= + FILE_READ | FILE_WRITE | FILE_BINARY | FILE_SERIAL; + if (fd_info[ fd ].cp != NULL) + { + error ("fd_info[fd = %d] is already in use", fd); + } + fd_info[ fd ].cp = cp; + cp->ovl_read.hEvent = CreateEvent (NULL, TRUE, FALSE, NULL); + if (cp->ovl_read.hEvent == NULL) + error ("Could not create read event"); + cp->ovl_write.hEvent = CreateEvent (NULL, TRUE, FALSE, NULL); + if (cp->ovl_write.hEvent == NULL) + error ("Could not create write event"); + + return fd; +} + +/* For serial-process-configure */ +void +serial_configure (struct Lisp_Process *p, + Lisp_Object contact) +{ + Lisp_Object childp2 = Qnil; + Lisp_Object tem = Qnil; + HANDLE hnd; + DCB dcb; + COMMTIMEOUTS ct; + char summary[4] = "???"; /* This usually becomes "8N1". */ + + if ((fd_info[ p->outfd ].flags & FILE_SERIAL) == 0) + error ("Not a serial process"); + hnd = fd_info[ p->outfd ].hnd; + + childp2 = Fcopy_sequence (p->childp); + + /* Initialize timeouts for blocking read and blocking write. */ + if (!GetCommTimeouts (hnd, &ct)) + error ("GetCommTimeouts() failed"); + ct.ReadIntervalTimeout = 0; + ct.ReadTotalTimeoutMultiplier = 0; + ct.ReadTotalTimeoutConstant = 0; + ct.WriteTotalTimeoutMultiplier = 0; + ct.WriteTotalTimeoutConstant = 0; + if (!SetCommTimeouts (hnd, &ct)) + error ("SetCommTimeouts() failed"); + /* Read port attributes and prepare default configuration. */ + memset (&dcb, 0, sizeof (dcb)); + dcb.DCBlength = sizeof (DCB); + if (!GetCommState (hnd, &dcb)) + error ("GetCommState() failed"); + dcb.fBinary = TRUE; + dcb.fNull = FALSE; + dcb.fAbortOnError = FALSE; + /* dcb.XonLim and dcb.XoffLim are set by GetCommState() */ + dcb.ErrorChar = 0; + dcb.EofChar = 0; + dcb.EvtChar = 0; + + /* Configure speed. */ + if (!NILP (Fplist_member (contact, QCspeed))) + tem = Fplist_get (contact, QCspeed); + else + tem = Fplist_get (p->childp, QCspeed); + CHECK_NUMBER (tem); + dcb.BaudRate = XINT (tem); + childp2 = Fplist_put (childp2, QCspeed, tem); + + /* Configure bytesize. */ + if (!NILP (Fplist_member (contact, QCbytesize))) + tem = Fplist_get (contact, QCbytesize); + else + tem = Fplist_get (p->childp, QCbytesize); + if (NILP (tem)) + tem = make_number (8); + CHECK_NUMBER (tem); + if (XINT (tem) != 7 && XINT (tem) != 8) + error (":bytesize must be nil (8), 7, or 8"); + dcb.ByteSize = XINT (tem); + summary[0] = XINT (tem) + '0'; + childp2 = Fplist_put (childp2, QCbytesize, tem); + + /* Configure parity. */ + if (!NILP (Fplist_member (contact, QCparity))) + tem = Fplist_get (contact, QCparity); + else + tem = Fplist_get (p->childp, QCparity); + if (!NILP (tem) && !EQ (tem, Qeven) && !EQ (tem, Qodd)) + error (":parity must be nil (no parity), `even', or `odd'"); + dcb.fParity = FALSE; + dcb.Parity = NOPARITY; + dcb.fErrorChar = FALSE; + if (NILP (tem)) + { + summary[1] = 'N'; + } + else if (EQ (tem, Qeven)) + { + summary[1] = 'E'; + dcb.fParity = TRUE; + dcb.Parity = EVENPARITY; + dcb.fErrorChar = TRUE; + } + else if (EQ (tem, Qodd)) + { + summary[1] = 'O'; + dcb.fParity = TRUE; + dcb.Parity = ODDPARITY; + dcb.fErrorChar = TRUE; + } + childp2 = Fplist_put (childp2, QCparity, tem); + + /* Configure stopbits. */ + if (!NILP (Fplist_member (contact, QCstopbits))) + tem = Fplist_get (contact, QCstopbits); + else + tem = Fplist_get (p->childp, QCstopbits); + if (NILP (tem)) + tem = make_number (1); + CHECK_NUMBER (tem); + if (XINT (tem) != 1 && XINT (tem) != 2) + error (":stopbits must be nil (1 stopbit), 1, or 2"); + summary[2] = XINT (tem) + '0'; + if (XINT (tem) == 1) + dcb.StopBits = ONESTOPBIT; + else if (XINT (tem) == 2) + dcb.StopBits = TWOSTOPBITS; + childp2 = Fplist_put (childp2, QCstopbits, tem); + + /* Configure flowcontrol. */ + if (!NILP (Fplist_member (contact, QCflowcontrol))) + tem = Fplist_get (contact, QCflowcontrol); + else + tem = Fplist_get (p->childp, QCflowcontrol); + if (!NILP (tem) && !EQ (tem, Qhw) && !EQ (tem, Qsw)) + error (":flowcontrol must be nil (no flowcontrol), `hw', or `sw'"); + dcb.fOutxCtsFlow = FALSE; + dcb.fOutxDsrFlow = FALSE; + dcb.fDtrControl = DTR_CONTROL_DISABLE; + dcb.fDsrSensitivity = FALSE; + dcb.fTXContinueOnXoff = FALSE; + dcb.fOutX = FALSE; + dcb.fInX = FALSE; + dcb.fRtsControl = RTS_CONTROL_DISABLE; + dcb.XonChar = 17; /* Control-Q */ + dcb.XoffChar = 19; /* Control-S */ + if (NILP (tem)) + { + /* Already configured. */ + } + else if (EQ (tem, Qhw)) + { + dcb.fRtsControl = RTS_CONTROL_HANDSHAKE; + dcb.fOutxCtsFlow = TRUE; + } + else if (EQ (tem, Qsw)) + { + dcb.fOutX = TRUE; + dcb.fInX = TRUE; + } + childp2 = Fplist_put (childp2, QCflowcontrol, tem); + + /* Activate configuration. */ + if (!SetCommState (hnd, &dcb)) + error ("SetCommState() failed"); + + childp2 = Fplist_put (childp2, QCsummary, build_string (summary)); + p->childp = childp2; +} + /* end of w32.c */ /* arch-tag: 90442dd3-37be-482b-b272-ac752e3049f1