1 /* fhandler_process.cc: fhandler for /proc/<pid> virtual filesystem
3 This file is part of Cygwin.
5 This software is a copyrighted work licensed under the terms of the
6 Cygwin license. Please consult the file "CYGWIN_LICENSE" for
12 #include <sys/cygwin.h>
17 #include "fhandler_virtual.h"
19 #include "shared_info.h"
26 #include <sys/sysmacros.h>
27 #include <sys/param.h>
30 #define _COMPILING_NEWLIB
33 static off_t
format_process_maps (void *, char *&);
34 static off_t
format_process_stat (void *, char *&);
35 static off_t
format_process_status (void *, char *&);
36 static off_t
format_process_statm (void *, char *&);
37 static off_t
format_process_winexename (void *, char *&);
38 static off_t
format_process_winpid (void *, char *&);
39 static off_t
format_process_exename (void *, char *&);
40 static off_t
format_process_root (void *, char *&);
41 static off_t
format_process_cwd (void *, char *&);
42 static off_t
format_process_cmdline (void *, char *&);
43 static off_t
format_process_ppid (void *, char *&);
44 static off_t
format_process_uid (void *, char *&);
45 static off_t
format_process_pgid (void *, char *&);
46 static off_t
format_process_sid (void *, char *&);
47 static off_t
format_process_gid (void *, char *&);
48 static off_t
format_process_ctty (void *, char *&);
49 static off_t
format_process_fd (void *, char *&);
50 static off_t
format_process_mounts (void *, char *&);
51 static off_t
format_process_mountinfo (void *, char *&);
52 static off_t
format_process_environ (void *, char *&);
54 static const virt_tab_t process_tab
[] =
56 { _VN ("."), FH_PROCESS
, virt_directory
, NULL
},
57 { _VN (".."), FH_PROCESS
, virt_directory
, NULL
},
58 { _VN ("cmdline"), FH_PROCESS
, virt_file
, format_process_cmdline
},
59 { _VN ("ctty"), FH_PROCESS
, virt_file
, format_process_ctty
},
60 { _VN ("cwd"), FH_PROCESS
, virt_symlink
, format_process_cwd
},
61 { _VN ("environ"), FH_PROCESS
, virt_file
, format_process_environ
},
62 { _VN ("exe"), FH_PROCESS
, virt_symlink
, format_process_exename
},
63 { _VN ("exename"), FH_PROCESS
, virt_file
, format_process_exename
},
64 { _VN ("fd"), FH_PROCESSFD
, virt_directory
, format_process_fd
},
65 { _VN ("gid"), FH_PROCESS
, virt_file
, format_process_gid
},
66 { _VN ("maps"), FH_PROCESS
, virt_file
, format_process_maps
},
67 { _VN ("mountinfo"), FH_PROCESS
, virt_file
, format_process_mountinfo
},
68 { _VN ("mounts"), FH_PROCESS
, virt_file
, format_process_mounts
},
69 { _VN ("pgid"), FH_PROCESS
, virt_file
, format_process_pgid
},
70 { _VN ("ppid"), FH_PROCESS
, virt_file
, format_process_ppid
},
71 { _VN ("root"), FH_PROCESS
, virt_symlink
, format_process_root
},
72 { _VN ("sid"), FH_PROCESS
, virt_file
, format_process_sid
},
73 { _VN ("stat"), FH_PROCESS
, virt_file
, format_process_stat
},
74 { _VN ("statm"), FH_PROCESS
, virt_file
, format_process_statm
},
75 { _VN ("status"), FH_PROCESS
, virt_file
, format_process_status
},
76 { _VN ("uid"), FH_PROCESS
, virt_file
, format_process_uid
},
77 { _VN ("winexename"), FH_PROCESS
, virt_file
, format_process_winexename
},
78 { _VN ("winpid"), FH_PROCESS
, virt_file
, format_process_winpid
},
79 { NULL
, 0, FH_NADA
, virt_none
, NULL
}
82 static const int PROCESS_LINK_COUNT
=
83 (sizeof (process_tab
) / sizeof (virt_tab_t
)) - 1;
84 int get_process_state (DWORD dwProcessId
);
85 static bool get_mem_values (DWORD dwProcessId
, unsigned long *vmsize
,
86 unsigned long *vmrss
, unsigned long *vmtext
,
87 unsigned long *vmdata
, unsigned long *vmlib
,
88 unsigned long *vmshare
);
90 /* Returns 0 if path doesn't exist, >0 if path is a directory,
91 -1 if path is a file, -2 if path is a symlink, -3 if path is a pipe,
92 -4 if path is a socket. */
94 fhandler_process::exists ()
96 const char *path
= get_name ();
97 debug_printf ("exists (%s)", path
);
99 while (*path
!= 0 && !isdirsep (*path
))
104 virt_tab_t
*entry
= virt_tab_search (path
+ 1, true, process_tab
,
108 if (!path
[entry
->name_len
+ 1])
110 fileid
= entry
- process_tab
;
113 if (entry
->type
== virt_directory
) /* fd subdir only */
115 fileid
= entry
- process_tab
;
118 /* Check for nameless device entries. */
119 path
= strrchr (path
, '/');
122 if (!strncmp (path
, "pipe:[", 6))
124 else if (!strncmp (path
, "socket:[", 8))
132 fhandler_process::fhandler_process ():
138 fhandler_process::fstat (struct stat
*buf
)
140 const char *path
= get_name ();
141 int file_type
= exists ();
142 fhandler_base::fstat (buf
);
143 path
+= proc_len
+ 1;
147 /* If p->pid != pid, then pid is actually the Windows PID for an execed
148 Cygwin process, and the pinfo entry is the additional entry created
149 at exec time. We don't want to enable the user to access a process
150 entry by using the Win32 PID, though. */
151 if (!p
|| p
->pid
!= pid
)
157 buf
->st_mode
&= ~_IFMT
& NO_W
;
166 buf
->st_ctime
= buf
->st_mtime
= buf
->st_birthtime
= p
->start_time
;
167 buf
->st_ctim
.tv_nsec
= buf
->st_mtim
.tv_nsec
168 = buf
->st_birthtim
.tv_nsec
= 0;
169 time_as_timestruc_t (&buf
->st_atim
);
170 buf
->st_uid
= p
->uid
;
171 buf
->st_gid
= p
->gid
;
172 buf
->st_mode
|= S_IFDIR
| S_IXUSR
| S_IXGRP
| S_IXOTH
;
179 buf
->st_uid
= p
->uid
;
180 buf
->st_gid
= p
->gid
;
181 buf
->st_mode
= S_IFLNK
| S_IRWXU
| S_IRWXG
| S_IRWXO
;
184 buf
->st_uid
= p
->uid
;
185 buf
->st_gid
= p
->gid
;
186 buf
->st_mode
= S_IFIFO
| S_IRUSR
| S_IWUSR
;
189 buf
->st_uid
= p
->uid
;
190 buf
->st_gid
= p
->gid
;
191 buf
->st_mode
= S_IFSOCK
| S_IRUSR
| S_IWUSR
;
195 buf
->st_uid
= p
->uid
;
196 buf
->st_gid
= p
->gid
;
197 buf
->st_mode
|= S_IFREG
| S_IRUSR
| S_IRGRP
| S_IROTH
;
203 fhandler_process::opendir (int fd
)
205 DIR *dir
= fhandler_virtual::opendir (fd
);
206 if (dir
&& process_tab
[fileid
].fhandler
== FH_PROCESSFD
)
212 fhandler_process::closedir (DIR *dir
)
214 return fhandler_virtual::closedir (dir
);
218 fhandler_process::readdir (DIR *dir
, dirent
*de
)
221 if (process_tab
[fileid
].fhandler
== FH_PROCESSFD
)
223 if ((size_t) dir
->__d_position
>= 2 + filesize
/ sizeof (int))
226 else if (dir
->__d_position
>= PROCESS_LINK_COUNT
)
228 if (process_tab
[fileid
].fhandler
== FH_PROCESSFD
&& dir
->__d_position
> 1)
230 int *p
= (int *) filebuf
;
231 __small_sprintf (de
->d_name
, "%d", p
[dir
->__d_position
++ - 2]);
236 strcpy (de
->d_name
, process_tab
[dir
->__d_position
].name
);
237 de
->d_type
= virt_ftype_to_dtype (process_tab
[dir
->__d_position
].type
);
240 dir
->__flags
|= dirent_saw_dot
| dirent_saw_dot_dot
;
243 syscall_printf ("%d = readdir(%p, %p) (%s)", res
, dir
, de
, de
->d_name
);
248 fhandler_process::open (int flags
, mode_t mode
)
250 int res
= fhandler_virtual::open (flags
, mode
);
257 path
= get_name () + proc_len
+ 1;
259 while (*path
!= 0 && !isdirsep (*path
))
264 if ((flags
& (O_CREAT
| O_EXCL
)) == (O_CREAT
| O_EXCL
))
270 else if (flags
& O_WRONLY
)
284 entry
= virt_tab_search (path
+ 1, true, process_tab
, PROCESS_LINK_COUNT
);
287 set_errno ((flags
& O_CREAT
) ? EROFS
: ENOENT
);
291 if (entry
->fhandler
== FH_PROCESSFD
)
296 if (flags
& O_WRONLY
)
303 fileid
= entry
- process_tab
;
304 if (!fill_filebuf ())
310 if (flags
& O_APPEND
)
317 set_flags ((flags
& ~O_TEXT
) | O_BINARY
);
320 syscall_printf ("%d = fhandler_proc::open(%y, 0%o)", res
, flags
, mode
);
324 struct process_fd_t
{
327 virtual_ftype_t
*fd_type
;
331 fhandler_process::fill_filebuf ()
334 path
= get_name () + proc_len
+ 1;
339 /* If p->pid != pid, then pid is actually the Windows PID for an execed
340 Cygwin process, and the pinfo entry is the additional entry created
341 at exec time. We don't want to enable the user to access a process
342 entry by using the Win32 PID, though. */
343 if (!p
|| p
->pid
!= pid
)
349 if (process_tab
[fileid
].format_func
)
351 if (process_tab
[fileid
].fhandler
== FH_PROCESSFD
)
353 process_fd_t fd
= { path
, p
, &fd_type
};
354 filesize
= process_tab
[fileid
].format_func (&fd
, filebuf
);
357 filesize
= process_tab
[fileid
].format_func (p
, filebuf
);
358 return !filesize
? false : true;
364 format_process_fd (void *data
, char *&destbuf
)
366 _pinfo
*p
= ((process_fd_t
*) data
)->p
;
367 const char *path
= ((process_fd_t
*) data
)->path
;
369 /* path looks like "$PID/fd", "$PID/fd/", "$PID/fd/[0-9]*". In the latter
370 case a trailing slash and more followup chars are allowed, provided the
371 descriptor symlink points to a directory. */
372 char *fdp
= strchr (path
, '/') + 3;
373 /* The "fd" directory itself? */
374 if (fdp
[0] =='\0' || (fdp
[0] == '/' && fdp
[1] == '\0'))
378 destbuf
= p
? p
->fds (fs
) : NULL
;
379 *((process_fd_t
*) data
)->fd_type
= virt_symlink
;
388 fd
= strtol (++fdp
, &e
, 10);
389 if (fd
< 0 || e
== fdp
|| (*e
!= '/' && *e
!= '\0'))
394 destbuf
= p
? p
->fd (fd
, fs
) : NULL
;
395 if (!destbuf
|| !*destbuf
)
401 *((process_fd_t
*) data
)->fd_type
= virt_symlink
;
402 else /* trailing path */
404 char *newbuf
= (char *) cmalloc_abort (HEAP_STR
, strlen (destbuf
)
406 stpcpy (stpcpy (newbuf
, destbuf
), e
);
409 *((process_fd_t
*) data
)->fd_type
= virt_fsdir
;
416 format_process_ppid (void *data
, char *&destbuf
)
418 _pinfo
*p
= (_pinfo
*) data
;
419 destbuf
= (char *) crealloc_abort (destbuf
, 40);
420 return __small_sprintf (destbuf
, "%d\n", p
->ppid
);
424 format_process_uid (void *data
, char *&destbuf
)
426 _pinfo
*p
= (_pinfo
*) data
;
427 destbuf
= (char *) crealloc_abort (destbuf
, 40);
428 return __small_sprintf (destbuf
, "%d\n", p
->uid
);
432 format_process_pgid (void *data
, char *&destbuf
)
434 _pinfo
*p
= (_pinfo
*) data
;
435 destbuf
= (char *) crealloc_abort (destbuf
, 40);
436 return __small_sprintf (destbuf
, "%d\n", p
->pgid
);
440 format_process_sid (void *data
, char *&destbuf
)
442 _pinfo
*p
= (_pinfo
*) data
;
443 destbuf
= (char *) crealloc_abort (destbuf
, 40);
444 return __small_sprintf (destbuf
, "%d\n", p
->sid
);
448 format_process_gid (void *data
, char *&destbuf
)
450 _pinfo
*p
= (_pinfo
*) data
;
451 destbuf
= (char *) crealloc_abort (destbuf
, 40);
452 return __small_sprintf (destbuf
, "%d\n", p
->gid
);
456 format_process_ctty (void *data
, char *&destbuf
)
458 _pinfo
*p
= (_pinfo
*) data
;
461 destbuf
= (char *) crealloc_abort (destbuf
, 2);
462 return __small_sprintf (destbuf
, "\n");
466 destbuf
= (char *) crealloc_abort (destbuf
, strlen (d
.name ()) + 2);
467 return __small_sprintf (destbuf
, "%s\n", d
.name ());
471 format_process_root (void *data
, char *&destbuf
)
473 _pinfo
*p
= (_pinfo
*) data
;
481 destbuf
= p
? p
->root (fs
) : NULL
;
482 if (!destbuf
|| !*destbuf
)
484 destbuf
= cstrdup ("<defunct>");
485 fs
= strlen (destbuf
) + 1;
491 format_process_cwd (void *data
, char *&destbuf
)
493 _pinfo
*p
= (_pinfo
*) data
;
501 destbuf
= p
? p
->cwd (fs
) : NULL
;
502 if (!destbuf
|| !*destbuf
)
504 destbuf
= cstrdup ("<defunct>");
505 fs
= strlen (destbuf
) + 1;
511 format_process_cmdline (void *data
, char *&destbuf
)
513 _pinfo
*p
= (_pinfo
*) data
;
521 destbuf
= p
? p
->cmdline (fs
) : NULL
;
522 if (!destbuf
|| !*destbuf
)
524 destbuf
= cstrdup ("<defunct>");
525 fs
= strlen (destbuf
) + 1;
531 format_process_exename (void *data
, char *&destbuf
)
533 _pinfo
*p
= (_pinfo
*) data
;
537 char *buf
= tp
.c_get ();
538 if (p
->process_state
& PID_EXITED
)
539 stpcpy (buf
, "<defunct>");
542 mount_table
->conv_to_posix_path (p
->progname
, buf
, CCP_RELATIVE
);
546 char *s
= buf
+ len
- 4;
547 if (ascii_strcasematch (s
, ".exe"))
551 destbuf
= (char *) crealloc_abort (destbuf
, (len
= strlen (buf
)) + 1);
552 stpcpy (destbuf
, buf
);
557 format_process_winpid (void *data
, char *&destbuf
)
559 _pinfo
*p
= (_pinfo
*) data
;
560 destbuf
= (char *) crealloc_abort (destbuf
, 20);
561 return __small_sprintf (destbuf
, "%d\n", p
->dwProcessId
);
565 format_process_winexename (void *data
, char *&destbuf
)
567 _pinfo
*p
= (_pinfo
*) data
;
568 size_t len
= sys_wcstombs (NULL
, 0, p
->progname
);
569 destbuf
= (char *) crealloc_abort (destbuf
, len
+ 1);
570 /* With trailing \0 for backward compat reasons. */
571 sys_wcstombs (destbuf
, len
+ 1, p
->progname
);
576 format_process_environ (void *data
, char *&destbuf
)
578 _pinfo
*p
= (_pinfo
*) data
;
586 destbuf
= p
? p
->environ (fs
) : NULL
;
587 if (!destbuf
|| !*destbuf
)
589 destbuf
= cstrdup ("<defunct>");
590 fs
= strlen (destbuf
) + 1;
605 heap
*heap_vm_chunks
;
607 heap_info (DWORD pid
)
608 : heap_vm_chunks (NULL
)
612 PDEBUG_HEAP_ARRAY harray
;
614 buf
= RtlCreateQueryDebugBuffer (16 * 65536, FALSE
);
617 status
= RtlQueryProcessDebugInformation (pid
, PDI_HEAPS
| PDI_HEAP_BLOCKS
,
619 if (NT_SUCCESS (status
)
620 && (harray
= (PDEBUG_HEAP_ARRAY
) buf
->HeapInformation
) != NULL
)
621 for (ULONG hcnt
= 0; hcnt
< harray
->Count
; ++hcnt
)
623 PDEBUG_HEAP_BLOCK barray
= (PDEBUG_HEAP_BLOCK
)
624 harray
->Heaps
[hcnt
].Blocks
;
627 for (ULONG bcnt
= 0; bcnt
< harray
->Heaps
[hcnt
].BlockCount
; ++bcnt
)
628 if (barray
[bcnt
].Flags
& 2)
630 heap
*h
= (heap
*) malloc (sizeof (heap
));
633 *h
= (heap
) { heap_vm_chunks
,
634 hcnt
, (char *) barray
[bcnt
].Address
,
635 (char *) barray
[bcnt
].Address
637 harray
->Heaps
[hcnt
].Flags
};
642 RtlDestroyQueryDebugBuffer (buf
);
645 char *fill_if_match (char *base
, ULONG type
, char *dest
)
647 for (heap
*h
= heap_vm_chunks
; h
; h
= h
->next
)
648 if (base
>= h
->base
&& base
< h
->end
)
650 char *p
= dest
+ __small_sprintf (dest
, "[win heap %ld", h
->heap_id
);
651 if (!(h
->flags
& HEAP_FLAG_NONDEFAULT
))
652 p
= stpcpy (p
, " default");
653 if ((h
->flags
& HEAP_FLAG_SHAREABLE
) && (type
& MEM_MAPPED
))
654 p
= stpcpy (p
, " shared");
655 if (h
->flags
& HEAP_FLAG_EXECUTABLE
)
656 p
= stpcpy (p
, " exec");
657 if (h
->flags
& HEAP_FLAG_GROWABLE
)
658 p
= stpcpy (p
, " grow");
659 if (h
->flags
& HEAP_FLAG_NOSERIALIZE
)
660 p
= stpcpy (p
, " noserial");
661 if (h
->flags
== HEAP_FLAG_DEBUGGED
)
662 p
= stpcpy (p
, " debug");
672 for (heap
*m
= heap_vm_chunks
; m
; m
= n
)
692 thread_info (DWORD pid
, HANDLE process
)
697 ULONG size
= 50 * (sizeof (SYSTEM_PROCESS_INFORMATION
)
698 + 16 * sizeof (SYSTEM_THREADS
));
699 PSYSTEM_PROCESS_INFORMATION proc
;
700 PSYSTEM_THREADS thread
;
704 buf
= realloc (buf
, size
);
705 status
= NtQuerySystemInformation (SystemProcessInformation
,
709 while (status
== STATUS_INFO_LENGTH_MISMATCH
);
710 if (!NT_SUCCESS (status
))
714 debug_printf ("NtQuerySystemInformation, %y", status
);
717 proc
= (PSYSTEM_PROCESS_INFORMATION
) buf
;
720 if ((DWORD
) (uintptr_t) proc
->UniqueProcessId
== pid
)
722 if (!proc
->NextEntryOffset
)
727 proc
= (PSYSTEM_PROCESS_INFORMATION
) ((PBYTE
) proc
728 + proc
->NextEntryOffset
);
730 thread
= proc
->Threads
;
731 for (ULONG i
= 0; i
< proc
->NumberOfThreads
; ++i
)
733 THREAD_BASIC_INFORMATION tbi
;
737 thread_h
= OpenThread (THREAD_QUERY_LIMITED_INFORMATION
, FALSE
,
738 (ULONG
) (ULONG_PTR
) thread
[i
].ClientId
.UniqueThread
);
741 status
= NtQueryInformationThread (thread_h
, ThreadBasicInformation
,
742 &tbi
, sizeof tbi
, NULL
);
743 CloseHandle (thread_h
);
744 if (!NT_SUCCESS (status
))
746 region
*r
= (region
*) malloc (sizeof (region
));
749 *r
= (region
) { regions
,
750 (ULONG
) (ULONG_PTR
) thread
[i
].ClientId
.UniqueThread
,
751 (char *) tbi
.TebBaseAddress
,
752 (char *) tbi
.TebBaseAddress
753 + 2 * wincap
.page_size (),
757 if (!ReadProcessMemory (process
, (PVOID
) tbi
.TebBaseAddress
,
758 &teb
, sizeof teb
, NULL
))
760 r
= (region
*) malloc (sizeof (region
));
763 *r
= (region
) { regions
,
764 (ULONG
) (ULONG_PTR
) thread
[i
].ClientId
.UniqueThread
,
765 (char *) (teb
.DeallocationStack
766 ?: teb
.Tib
.StackLimit
),
767 (char *) teb
.Tib
.StackBase
,
775 char *fill_if_match (char *base
, ULONG type
, char *dest
)
777 for (region
*r
= regions
; r
; r
= r
->next
)
778 if ((base
>= r
->start
&& base
< r
->end
)
779 /* Special case WOW64. The TEB is 8K within the region reserved
780 for it. No idea what the lower 8K are used for. */
781 || (r
->teb
&& wincap
.is_wow64 ()
782 && r
->start
== base
+ 2 * wincap
.page_size ()))
784 char *p
= dest
+ __small_sprintf (dest
, "[%s (tid %ld)",
785 r
->teb
? "teb" : "stack",
787 if (type
& MEM_MAPPED
)
788 p
= stpcpy (p
, " shared");
794 /* Helper to look for TEBs inside single allocated region since W10 1511. */
795 char *fill_if_match (char *start
, char *dest
)
797 for (region
*r
= regions
; r
; r
= r
->next
)
798 if (r
->teb
&& start
== r
->start
)
800 __small_sprintf (dest
, "[teb (tid %ld)]", r
->thread_id
);
809 for (region
*m
= regions
; m
; m
= n
)
818 format_process_maps (void *data
, char *&destbuf
)
820 _pinfo
*p
= (_pinfo
*) data
;
821 HANDLE proc
= OpenProcess (PROCESS_QUERY_INFORMATION
822 | PROCESS_VM_READ
, FALSE
, p
->dwProcessId
);
827 PROCESS_BASIC_INFORMATION pbi
;
830 memset (&pbi
, 0, sizeof (pbi
));
831 status
= NtQueryInformationProcess (proc
, ProcessBasicInformation
,
832 &pbi
, sizeof pbi
, NULL
);
833 if (NT_SUCCESS (status
))
834 peb
= pbi
.PebBaseAddress
;
835 /* myself is in the same spot in every process, so is the pointer to the
836 procinfo. But make sure the destructor doesn't try to release procinfo! */
838 if (ReadProcessMemory (proc
, &myself
, &proc_pinfo
, sizeof proc_pinfo
, NULL
))
839 proc_pinfo
.preserve ();
840 /* The heap info on the cygheap is also in the same spot in each process
841 because the cygheap is located at the same address. */
842 user_heap_info user_heap
;
843 ReadProcessMemory (proc
, &cygheap
->user_heap
, &user_heap
,
844 sizeof user_heap
, NULL
);
860 } cur
= {{{'\0'}}, (char *)1, 0, 0};
862 MEMORY_BASIC_INFORMATION mb
;
863 dos_drive_mappings drive_maps
;
864 heap_info
heaps (p
->dwProcessId
);
865 thread_info
threads (p
->dwProcessId
, proc
);
870 PMEMORY_SECTION_NAME msi
= (PMEMORY_SECTION_NAME
) tp
.w_get ();
871 char *posix_modname
= tp
.c_get ();
873 char *peb_teb_abase
= NULL
;
881 /* Iterate over each VM region in the address space, coalescing
882 memory regions with the same permissions. Once we run out, do one
883 last_pass to trigger output of the last accumulated region. */
885 VirtualQueryEx (proc
, i
, &mb
, sizeof(mb
)) || (1 == ++last_pass
);
889 posix_modname
[0] = '\0';
890 if (mb
.State
== MEM_FREE
)
892 else if (mb
.State
== MEM_RESERVE
)
894 char *p
= stpcpy (a
.flags
, "===");
895 stpcpy (p
, (mb
.Type
& MEM_MAPPED
) ? "s" : "p");
899 static DWORD
const RO
= (PAGE_EXECUTE_READ
| PAGE_READONLY
);
900 static DWORD
const RW
= (PAGE_EXECUTE_READWRITE
| PAGE_READWRITE
901 | PAGE_EXECUTE_WRITECOPY
| PAGE_WRITECOPY
);
902 static DWORD
const X
= (PAGE_EXECUTE
| PAGE_EXECUTE_READ
903 | PAGE_EXECUTE_READWRITE
904 | PAGE_EXECUTE_WRITECOPY
);
905 static DWORD
const WC
= (PAGE_EXECUTE_WRITECOPY
| PAGE_WRITECOPY
);
906 DWORD p
= mb
.Protect
;
908 (p
& (RO
| RW
)) ? 'r' : '-',
909 (p
& (RW
)) ? 'w' : '-',
910 (p
& (X
)) ? 'x' : '-',
911 (mb
.Type
& MEM_MAPPED
) && !(p
& (WC
)) ? 's'
912 : (p
& PAGE_GUARD
) ? 'g' : 'p',
913 '\0', // zero-fill the remaining bytes
918 (char *) mb
.AllocationBase
,
919 (char *) mb
.BaseAddress
,
920 (char *) mb
.BaseAddress
+mb
.RegionSize
,
924 /* Windows permissions are more fine-grained than the unix rwxp,
925 so we reduce clutter by manually coalescing regions sharing
926 the same allocation base and effective permissions. */
927 bool newbase
= (next
.abase
!= cur
.abase
);
928 if (!last_pass
&& !newbase
&& next
.a
.word
== cur
.a
.word
)
929 cur
.rend
= next
.rend
; /* merge with previous */
932 char *peb_teb_end
= NULL
;
933 peb_teb_rinse_repeat
:
934 /* Starting with W10 1511, PEB and TEBs don't get allocated
935 separately. Rather they are created in a single region. Examine
936 the region starting at the PEB address page-wise. */
937 if (wincap
.has_new_pebteb_region ())
939 if (peb_teb_abase
&& !peb_teb_end
&& cur
.abase
== peb_teb_abase
)
941 posix_modname
[0] = '\0';
942 peb_teb_end
= cur
.rend
;
943 if (cur
.state
== MEM_COMMIT
)
944 cur
.rend
= cur
.rbase
+ wincap
.page_size ();
946 if (cur
.state
== MEM_COMMIT
)
948 if (!peb_teb_abase
&& cur
.rbase
== (char *) peb
)
950 peb_teb_abase
= cur
.abase
;
951 peb_teb_end
= cur
.rend
;
952 cur
.rend
= cur
.rbase
+ wincap
.page_size ();
953 strcpy (posix_modname
, "[peb]");
955 else if (peb_teb_end
)
958 posix_modname
[0] = '\0';
959 end
= threads
.fill_if_match (cur
.rbase
, posix_modname
);
965 char *base
= cur
.rbase
;
968 base
+= wincap
.page_size ();
970 while (!threads
.fill_if_match (base
, posix_modname
)
971 && base
< peb_teb_end
);
972 if (posix_modname
[0])
974 posix_modname
[0] = '\0';
978 cur
.rend
= peb_teb_end
;
983 /* output the current region if it's "interesting". */
986 size_t newlen
= strlen (posix_modname
) + 62;
987 if (len
+ newlen
>= maxsize
)
989 crealloc_abort (destbuf
,
990 maxsize
+= roundup2 (newlen
, 2048UL));
991 int written
= __small_sprintf (destbuf
+ len
,
992 "%08lx-%08lx %s %08lx %04x:%04x %U ",
993 cur
.rbase
, cur
.rend
, cur
.a
.flags
,
994 cur
.rbase
- cur
.abase
,
999 destbuf
[len
+ written
++] = ' ';
1001 len
+= __small_sprintf (destbuf
+ len
, "%s\n", posix_modname
);
1004 if (peb_teb_end
&& cur
.state
== MEM_COMMIT
)
1006 cur
.rbase
= cur
.rend
;
1007 cur
.rend
+= wincap
.page_size ();
1008 if (cur
.rbase
< peb_teb_end
)
1009 goto peb_teb_rinse_repeat
;
1011 /* start of a new region (but possibly still the same allocation). */
1013 /* if a new allocation, figure out what kind it is. */
1014 if (newbase
&& !last_pass
&& cur
.state
!= MEM_FREE
)
1016 /* If the return length pointer is missing, NtQueryVirtualMemory
1017 returns with STATUS_ACCESS_VIOLATION on Windows 2000. */
1022 if ((mb
.Type
& (MEM_MAPPED
| MEM_IMAGE
))
1023 && NT_SUCCESS (status
= NtQueryVirtualMemory (proc
, cur
.abase
,
1025 msi
, 65536, &ret_len
)))
1028 drive_maps
.fixup_if_match (msi
->SectionFileName
.Buffer
);
1029 if (mount_table
->conv_to_posix_path (dosname
,
1031 sys_wcstombs (posix_modname
, NT_MAX_PATH
, dosname
);
1032 stat64 (posix_modname
, &st
);
1034 else if (!threads
.fill_if_match (cur
.abase
, mb
.Type
,
1036 && !heaps
.fill_if_match (cur
.abase
, mb
.Type
,
1039 if (cur
.abase
== (char *) peb
)
1040 strcpy (posix_modname
, "[peb]");
1041 else if (cur
.abase
== (char *) &SharedUserData
)
1042 strcpy (posix_modname
, "[shared-user-data]");
1043 else if (cur
.abase
== (char *) cygwin_shared
)
1044 strcpy (posix_modname
, "[cygwin-shared]");
1045 else if (cur
.abase
== (char *) user_shared
)
1046 strcpy (posix_modname
, "[cygwin-user-shared]");
1047 else if (cur
.abase
== (char *) *proc_pinfo
)
1048 strcpy (posix_modname
, "[procinfo]");
1049 else if (cur
.abase
== user_heap
.base
)
1050 strcpy (posix_modname
, "[heap]");
1052 posix_modname
[0] = 0;
1062 format_process_stat (void *data
, char *&destbuf
)
1064 _pinfo
*p
= (_pinfo
*) data
;
1065 char cmd
[NAME_MAX
+ 1];
1067 unsigned long fault_count
= 0UL,
1068 utime
= 0UL, stime
= 0UL,
1070 vmsize
= 0UL, vmrss
= 0UL, vmmaxrss
= 0UL;
1072 if (p
->process_state
& PID_EXITED
)
1073 strcpy (cmd
, "<defunct>");
1076 PWCHAR last_slash
= wcsrchr (p
->progname
, L
'\\');
1077 sys_wcstombs (cmd
, NAME_MAX
+ 1,
1078 last_slash
? last_slash
+ 1 : p
->progname
);
1079 int len
= strlen (cmd
);
1082 char *s
= cmd
+ len
- 4;
1083 if (ascii_strcasematch (s
, ".exe"))
1087 /* Note: under Windows, a process is always running - it's only threads
1088 that get suspended. Therefore the default state is R (runnable). */
1089 if (p
->process_state
& PID_EXITED
)
1091 else if (p
->process_state
& PID_STOPPED
)
1094 state
= get_process_state (p
->dwProcessId
);
1095 start_time
= (GetTickCount () / 1000 - time (NULL
) + p
->start_time
) * HZ
;
1100 KERNEL_USER_TIMES put
;
1101 PROCESS_BASIC_INFORMATION pbi
;
1103 SYSTEM_TIMEOFDAY_INFORMATION stodi
;
1104 SYSTEM_PROCESSOR_PERFORMANCE_INFORMATION spt
;
1105 hProcess
= OpenProcess (PROCESS_QUERY_LIMITED_INFORMATION
| PROCESS_VM_READ
,
1106 FALSE
, p
->dwProcessId
);
1107 if (hProcess
!= NULL
)
1109 status
= NtQueryInformationProcess (hProcess
, ProcessVmCounters
,
1110 (PVOID
) &vmc
, sizeof vmc
, NULL
);
1111 if (NT_SUCCESS (status
))
1112 status
= NtQueryInformationProcess (hProcess
, ProcessTimes
,
1113 (PVOID
) &put
, sizeof put
, NULL
);
1114 if (NT_SUCCESS (status
))
1115 status
= NtQueryInformationProcess (hProcess
, ProcessBasicInformation
,
1116 (PVOID
) &pbi
, sizeof pbi
, NULL
);
1117 if (NT_SUCCESS (status
))
1118 status
= NtQueryInformationProcess (hProcess
, ProcessQuotaLimits
,
1119 (PVOID
) &ql
, sizeof ql
, NULL
);
1120 CloseHandle (hProcess
);
1124 DWORD error
= GetLastError ();
1125 __seterrno_from_win_error (error
);
1126 debug_printf ("OpenProcess: ret %u", error
);
1129 if (NT_SUCCESS (status
))
1130 status
= NtQuerySystemInformation (SystemTimeOfDayInformation
,
1131 (PVOID
) &stodi
, sizeof stodi
, NULL
);
1132 if (NT_SUCCESS (status
))
1133 status
= NtQuerySystemInformation (SystemProcessorPerformanceInformation
,
1134 (PVOID
) &spt
, sizeof spt
, NULL
);
1135 if (!NT_SUCCESS (status
))
1137 __seterrno_from_nt_status (status
);
1138 debug_printf ("NtQueryInformationProcess: status %y, %E", status
);
1141 fault_count
= vmc
.PageFaultCount
;
1142 utime
= put
.UserTime
.QuadPart
* HZ
/ 10000000ULL;
1143 stime
= put
.KernelTime
.QuadPart
* HZ
/ 10000000ULL;
1145 if (stodi
.CurrentTime
.QuadPart
> put
.CreateTime
.QuadPart
)
1146 start_time
= (spt
.KernelTime
.QuadPart
+ spt
.UserTime
.QuadPart
-
1147 stodi
.CurrentTime
.QuadPart
+ put
.CreateTime
.QuadPart
) * HZ
/ 10000000ULL;
1150 * sometimes stodi.CurrentTime is a bit behind
1151 * Note: some older versions of procps are broken and can't cope
1152 * with process start times > time(NULL).
1154 start_time
= (spt
.KernelTme
.QuadPart
+ spt
.UserTime
.QuadPart
) * HZ
/ 10000000ULL;
1156 /* The BasePriority returned to a 32 bit process under WOW64 is
1157 apparently broken, for 32 and 64 bit target processes. 64 bit
1158 processes get the correct base priority, even for 32 bit processes. */
1159 if (wincap
.is_wow64 ())
1160 priority
= 8; /* Default value. */
1162 priority
= pbi
.BasePriority
;
1163 unsigned page_size
= wincap
.page_size ();
1164 vmsize
= vmc
.PagefileUsage
;
1165 vmrss
= vmc
.WorkingSetSize
/ page_size
;
1166 vmmaxrss
= ql
.MaximumWorkingSetSize
/ page_size
;
1168 destbuf
= (char *) crealloc_abort (destbuf
, strlen (cmd
) + 320);
1169 return __small_sprintf (destbuf
, "%d (%s) %c "
1171 "%u %lu %lu %u %u %lu %lu "
1172 "%ld %ld %d %d %d %d "
1176 p
->ppid
, p
->pgid
, p
->sid
, p
->ctty
, -1,
1177 0, fault_count
, fault_count
, 0, 0, utime
, stime
,
1178 utime
, stime
, priority
, 0, 0, 0,
1185 format_process_status (void *data
, char *&destbuf
)
1187 _pinfo
*p
= (_pinfo
*) data
;
1188 char cmd
[NAME_MAX
+ 1];
1190 const char *state_str
= "unknown";
1191 unsigned long vmsize
= 0UL, vmrss
= 0UL, vmdata
= 0UL, vmlib
= 0UL,
1192 vmtext
= 0UL, vmshare
= 0UL;
1194 PWCHAR last_slash
= wcsrchr (p
->progname
, L
'\\');
1195 sys_wcstombs (cmd
, NAME_MAX
+ 1, last_slash
? last_slash
+ 1 : p
->progname
);
1196 int len
= strlen (cmd
);
1199 char *s
= cmd
+ len
- 4;
1200 if (ascii_strcasematch (s
, ".exe"))
1203 /* Note: under Windows, a process is always running - it's only threads
1204 that get suspended. Therefore the default state is R (runnable). */
1205 if (p
->process_state
& PID_EXITED
)
1207 else if (p
->process_state
& PID_STOPPED
)
1210 state
= get_process_state (p
->dwProcessId
);
1214 state_str
= "running";
1218 state_str
= "sleeping";
1221 state_str
= "runnable";
1224 state_str
= "zombie";
1227 state_str
= "stopped";
1230 if (!get_mem_values (p
->dwProcessId
, &vmsize
, &vmrss
, &vmtext
, &vmdata
,
1233 unsigned page_size
= wincap
.allocation_granularity ();
1234 vmsize
*= page_size
; vmrss
*= page_size
; vmdata
*= page_size
;
1235 vmtext
*= page_size
; vmlib
*= page_size
;
1236 /* The real uid value for *this* process is stored at cygheap->user.real_uid
1237 but we can't get at the real uid value for any other process, so
1238 just fake it as p->uid. Similar for p->gid. */
1239 destbuf
= (char *) crealloc_abort (destbuf
, strlen (cmd
) + 320);
1240 return __small_sprintf (destbuf
, "Name:\t%s\n"
1245 "Uid:\t%d %d %d %d\n"
1246 "Gid:\t%d %d %d %d\n"
1262 p
->uid
, p
->uid
, p
->uid
, p
->uid
,
1263 p
->gid
, p
->gid
, p
->gid
, p
->gid
,
1264 vmsize
>> 10, 0, vmrss
>> 10, vmdata
>> 10, 0,
1265 vmtext
>> 10, vmlib
>> 10,
1266 0, 0, _my_tls
.sigmask
1271 format_process_statm (void *data
, char *&destbuf
)
1273 _pinfo
*p
= (_pinfo
*) data
;
1274 unsigned long vmsize
= 0UL, vmrss
= 0UL, vmtext
= 0UL, vmdata
= 0UL,
1275 vmlib
= 0UL, vmshare
= 0UL;
1277 if (!get_mem_values (p
->dwProcessId
, &vmsize
, &vmrss
, &vmtext
, &vmdata
,
1281 page_scale
= wincap
.allocation_granularity() / wincap
.page_size();
1282 destbuf
= (char *) crealloc_abort (destbuf
, 96);
1283 return __small_sprintf (destbuf
, "%ld %ld %ld %ld %ld %ld 0\n",
1284 vmsize
/ page_scale
, vmrss
/ page_scale
, vmshare
/ page_scale
,
1285 vmtext
/ page_scale
, vmlib
/ page_scale
, vmdata
/ page_scale
);
1289 FILE *setmntent (const char *, const char *);
1290 struct mntent
*getmntent (FILE *);
1294 format_process_mountstuff (void *data
, char *&destbuf
, bool mountinfo
)
1296 _pinfo
*p
= (_pinfo
*) data
;
1297 user_info
*u_shared
= NULL
;
1298 HANDLE u_hdl
= NULL
;
1302 if (p
->uid
!= myself
->uid
)
1304 WCHAR sid_string
[UNLEN
+ 1] = L
""; /* Large enough for SID */
1308 if (!p_sid
.getfrompw (internal_getpwuid (p
->uid
)))
1310 p_sid
.string (sid_string
);
1311 u_shared
= (user_info
*) open_shared (sid_string
, USER_VERSION
, u_hdl
,
1312 sizeof (user_info
), SH_JUSTOPEN
,
1318 u_shared
= user_shared
;
1319 mount_info
*mtab
= &u_shared
->mountinfo
;
1321 /* Store old value of _my_tls.locals here. */
1322 int iteration
= _my_tls
.locals
.iteration
;
1323 unsigned available_drives
= _my_tls
.locals
.available_drives
;
1324 /* This reinitializes the above values in _my_tls. */
1325 setmntent (NULL
, NULL
);
1326 /* Restore iteration immediately since it's not used below. We use the
1327 local iteration variable instead*/
1328 _my_tls
.locals
.iteration
= iteration
;
1330 for (iteration
= 0; (mnt
= mtab
->getmntent (iteration
)); ++iteration
)
1332 /* We have no access to the drives mapped into another user session and
1333 _my_tls.locals.available_drives contains the mappings of the current
1334 user. So, when printing the mount table of another user, we check
1335 each cygdrive entry if it's a remote drive. If so, ignore it. */
1336 if (iteration
>= mtab
->nmounts
&& u_hdl
)
1338 WCHAR drive
[3] = { (WCHAR
) mnt
->mnt_fsname
[0], L
':', L
'\0' };
1339 disk_type dt
= get_disk_type (drive
);
1341 if (dt
== DT_SHARE_SMB
|| dt
== DT_SHARE_NFS
)
1344 destbuf
= (char *) crealloc_abort (destbuf
, len
1345 + strlen (mnt
->mnt_fsname
)
1346 + strlen (mnt
->mnt_dir
)
1347 + strlen (mnt
->mnt_type
)
1348 + strlen (mnt
->mnt_opts
)
1352 path_conv
pc (mnt
->mnt_dir
, PC_SYM_NOFOLLOW
| PC_POSIX
);
1353 dev_t dev
= pc
.exists () ? pc
.fs_serial_number () : -1;
1355 len
+= __small_sprintf (destbuf
+ len
,
1356 "%d %d %d:%d / %s %s - %s %s %s\n",
1357 iteration
, iteration
,
1358 major (dev
), minor (dev
),
1359 mnt
->mnt_dir
, mnt
->mnt_opts
,
1360 mnt
->mnt_type
, mnt
->mnt_fsname
,
1361 (pc
.fs_flags () & FILE_READ_ONLY_VOLUME
)
1365 len
+= __small_sprintf (destbuf
+ len
, "%s %s %s %s %d %d\n",
1366 mnt
->mnt_fsname
, mnt
->mnt_dir
, mnt
->mnt_type
,
1367 mnt
->mnt_opts
, mnt
->mnt_freq
, mnt
->mnt_passno
);
1370 /* Restore available_drives */
1371 _my_tls
.locals
.available_drives
= available_drives
;
1373 if (u_hdl
) /* Only not-NULL if open_shared has been called. */
1375 UnmapViewOfFile (u_shared
);
1376 CloseHandle (u_hdl
);
1382 format_process_mounts (void *data
, char *&destbuf
)
1384 return format_process_mountstuff (data
, destbuf
, false);
1388 format_process_mountinfo (void *data
, char *&destbuf
)
1390 return format_process_mountstuff (data
, destbuf
, true);
1394 get_process_state (DWORD dwProcessId
)
1396 /* This isn't really heavy magic - just go through the processes' threads
1397 one by one and return a value accordingly. Errors are silently ignored. */
1399 PSYSTEM_PROCESS_INFORMATION p
, sp
;
1403 p
= (PSYSTEM_PROCESS_INFORMATION
) malloc (n
);
1408 status
= NtQuerySystemInformation (SystemProcessInformation
,
1409 (PVOID
) p
, n
, NULL
);
1410 if (status
!= STATUS_INFO_LENGTH_MISMATCH
)
1413 PSYSTEM_PROCESS_INFORMATION new_p
= (PSYSTEM_PROCESS_INFORMATION
) realloc (p
, n
);
1418 if (!NT_SUCCESS (status
))
1420 debug_printf ("NtQuerySystemInformation: status %y, %u",
1421 status
, RtlNtStatusToDosError (status
));
1428 if ((DWORD
) (uintptr_t) sp
->UniqueProcessId
== dwProcessId
)
1431 st
= &sp
->Threads
[0];
1433 for (unsigned i
= 0; i
< sp
->NumberOfThreads
; i
++)
1435 /* FIXME: at some point we should consider generating 'O' */
1436 if (st
->State
== StateRunning
||
1437 st
->State
== StateReady
)
1446 if (!sp
->NextEntryOffset
)
1448 sp
= (PSYSTEM_PROCESS_INFORMATION
) ((char *) sp
+ sp
->NextEntryOffset
);
1456 get_mem_values (DWORD dwProcessId
, unsigned long *vmsize
, unsigned long *vmrss
,
1457 unsigned long *vmtext
, unsigned long *vmdata
,
1458 unsigned long *vmlib
, unsigned long *vmshare
)
1464 PMEMORY_WORKING_SET_LIST p
;
1465 SIZE_T n
= 0x4000, length
;
1467 /* This appears to work despite MSDN claiming that QueryWorkingSet requires
1468 PROCESS_QUERY_INFORMATION *and* PROCESS_VM_READ. Since we're trying to do
1469 everything with least perms, we stick to PROCESS_QUERY_INFORMATION only
1470 unless this changes in Windows for some reason. */
1471 hProcess
= OpenProcess (PROCESS_QUERY_INFORMATION
, FALSE
, dwProcessId
);
1472 if (hProcess
== NULL
)
1475 debug_printf ("OpenProcess, %E");
1478 p
= (PMEMORY_WORKING_SET_LIST
) malloc (n
);
1483 status
= NtQueryVirtualMemory (hProcess
, 0, MemoryWorkingSetList
,
1485 (length
= (SIZE_T
) -1, &length
));
1486 if (status
!= STATUS_INFO_LENGTH_MISMATCH
)
1489 PMEMORY_WORKING_SET_LIST new_p
= (PMEMORY_WORKING_SET_LIST
)
1495 if (!NT_SUCCESS (status
))
1497 debug_printf ("NtQueryVirtualMemory: status %y", status
);
1498 if (status
== STATUS_PROCESS_IS_TERMINATING
)
1500 *vmsize
= *vmrss
= *vmtext
= *vmdata
= *vmlib
= *vmshare
= 0;
1504 __seterrno_from_nt_status (status
);
1507 for (unsigned long i
= 0; i
< p
->NumberOfPages
; i
++)
1510 unsigned flags
= p
->WorkingSetList
[i
] & 0x0FFF;
1511 if ((flags
& (WSLE_PAGE_EXECUTE
| WSLE_PAGE_SHAREABLE
))
1512 == (WSLE_PAGE_EXECUTE
| WSLE_PAGE_SHAREABLE
))
1514 else if (flags
& WSLE_PAGE_SHAREABLE
)
1516 else if (flags
& WSLE_PAGE_EXECUTE
)
1521 status
= NtQueryInformationProcess (hProcess
, ProcessVmCounters
, (PVOID
) &vmc
,
1523 if (!NT_SUCCESS (status
))
1525 debug_printf ("NtQueryInformationProcess: status %y", status
);
1526 __seterrno_from_nt_status (status
);
1529 *vmsize
= vmc
.PagefileUsage
/ wincap
.page_size ();
1533 CloseHandle (hProcess
);