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>
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
, size_t &vmsize
, size_t &vmrss
,
86 size_t &vmtext
, size_t &vmdata
, size_t &vmlib
,
90 fhandler_process::exists ()
92 const char *path
= get_name ();
93 debug_printf ("exists (%s)", path
);
95 while (*path
!= 0 && !isdirsep (*path
))
100 virt_tab_t
*entry
= virt_tab_search (path
+ 1, true, process_tab
,
104 if (!path
[entry
->name_len
+ 1])
106 fileid
= entry
- process_tab
;
109 if (entry
->type
== virt_directory
) /* fd subdir only */
111 fileid
= entry
- process_tab
;
114 /* Check for nameless device entries. */
115 path
= strrchr (path
, '/');
118 if (!strncmp (path
, "pipe:[", 6))
120 else if (!strncmp (path
, "socket:[", 8))
128 fhandler_process::fhandler_process ():
134 fhandler_process::fstat (struct stat
*buf
)
136 const char *path
= get_name ();
137 int file_type
= exists ();
138 fhandler_base::fstat (buf
);
139 path
+= proc_len
+ 1;
143 /* If p->pid != pid, then pid is actually the Windows PID for an execed
144 Cygwin process, and the pinfo entry is the additional entry created
145 at exec time. We don't want to enable the user to access a process
146 entry by using the Win32 PID, though. */
147 if (!p
|| p
->pid
!= pid
)
153 buf
->st_mode
&= ~_IFMT
& NO_W
;
162 buf
->st_ctime
= buf
->st_mtime
= buf
->st_birthtime
= p
->start_time
;
163 buf
->st_ctim
.tv_nsec
= buf
->st_mtim
.tv_nsec
164 = buf
->st_birthtim
.tv_nsec
= 0;
165 time_as_timestruc_t (&buf
->st_atim
);
166 buf
->st_uid
= p
->uid
;
167 buf
->st_gid
= p
->gid
;
168 buf
->st_mode
|= S_IFDIR
| S_IXUSR
| S_IXGRP
| S_IXOTH
;
169 if (file_type
== virt_directory
)
176 buf
->st_uid
= p
->uid
;
177 buf
->st_gid
= p
->gid
;
178 buf
->st_mode
= S_IFLNK
| S_IRWXU
| S_IRWXG
| S_IRWXO
;
181 buf
->st_uid
= p
->uid
;
182 buf
->st_gid
= p
->gid
;
183 buf
->st_mode
= S_IFIFO
| S_IRUSR
| S_IWUSR
;
186 buf
->st_uid
= p
->uid
;
187 buf
->st_gid
= p
->gid
;
188 buf
->st_mode
= S_IFSOCK
| S_IRUSR
| S_IWUSR
;
192 buf
->st_uid
= p
->uid
;
193 buf
->st_gid
= p
->gid
;
194 buf
->st_mode
|= S_IFREG
| S_IRUSR
| S_IRGRP
| S_IROTH
;
200 fhandler_process::opendir (int fd
)
202 DIR *dir
= fhandler_virtual::opendir (fd
);
203 if (dir
&& process_tab
[fileid
].fhandler
== FH_PROCESSFD
)
209 fhandler_process::closedir (DIR *dir
)
211 return fhandler_virtual::closedir (dir
);
215 fhandler_process::readdir (DIR *dir
, dirent
*de
)
218 if (process_tab
[fileid
].fhandler
== FH_PROCESSFD
)
220 if ((size_t) dir
->__d_position
>= 2 + filesize
/ sizeof (int))
223 else if (dir
->__d_position
>= PROCESS_LINK_COUNT
)
225 if (process_tab
[fileid
].fhandler
== FH_PROCESSFD
&& dir
->__d_position
> 1)
227 int *p
= (int *) filebuf
;
228 __small_sprintf (de
->d_name
, "%d", p
[dir
->__d_position
++ - 2]);
233 strcpy (de
->d_name
, process_tab
[dir
->__d_position
].name
);
234 de
->d_type
= virt_ftype_to_dtype (process_tab
[dir
->__d_position
].type
);
237 dir
->__flags
|= dirent_saw_dot
| dirent_saw_dot_dot
;
240 syscall_printf ("%d = readdir(%p, %p) (%s)", res
, dir
, de
, de
->d_name
);
245 fhandler_process::open (int flags
, mode_t mode
)
247 int res
= fhandler_virtual::open (flags
, mode
);
254 path
= get_name () + proc_len
+ 1;
256 while (*path
!= 0 && !isdirsep (*path
))
261 if ((flags
& (O_CREAT
| O_EXCL
)) == (O_CREAT
| O_EXCL
))
267 else if (flags
& O_WRONLY
)
281 entry
= virt_tab_search (path
+ 1, true, process_tab
, PROCESS_LINK_COUNT
);
284 set_errno ((flags
& O_CREAT
) ? EROFS
: ENOENT
);
288 if (entry
->fhandler
== FH_PROCESSFD
)
293 if (flags
& O_WRONLY
)
300 fileid
= entry
- process_tab
;
301 if (!fill_filebuf ())
307 if (flags
& O_APPEND
)
314 set_flags ((flags
& ~O_TEXT
) | O_BINARY
);
317 syscall_printf ("%d = fhandler_proc::open(%y, 0%o)", res
, flags
, mode
);
321 struct process_fd_t
{
324 virtual_ftype_t
*fd_type
;
328 fhandler_process::fill_filebuf ()
331 path
= get_name () + proc_len
+ 1;
336 /* If p->pid != pid, then pid is actually the Windows PID for an execed
337 Cygwin process, and the pinfo entry is the additional entry created
338 at exec time. We don't want to enable the user to access a process
339 entry by using the Win32 PID, though. */
340 if (!p
|| p
->pid
!= pid
)
346 if (process_tab
[fileid
].format_func
)
348 if (process_tab
[fileid
].fhandler
== FH_PROCESSFD
)
350 process_fd_t fd
= { path
, p
, &fd_type
};
351 filesize
= process_tab
[fileid
].format_func (&fd
, filebuf
);
354 filesize
= process_tab
[fileid
].format_func (p
, filebuf
);
355 return filesize
< 0 ? false : true;
361 format_process_fd (void *data
, char *&destbuf
)
363 _pinfo
*p
= ((process_fd_t
*) data
)->p
;
364 const char *path
= ((process_fd_t
*) data
)->path
;
366 /* path looks like "$PID/fd", "$PID/fd/", "$PID/fd/[0-9]*". In the latter
367 case a trailing slash and more followup chars are allowed, provided the
368 descriptor symlink points to a directory. */
369 char *fdp
= strchr (path
, '/') + 3;
370 /* The "fd" directory itself? */
371 if (fdp
[0] =='\0' || (fdp
[0] == '/' && fdp
[1] == '\0'))
375 destbuf
= p
? p
->fds (fs
) : NULL
;
376 *((process_fd_t
*) data
)->fd_type
= virt_symlink
;
385 fd
= strtol (++fdp
, &e
, 10);
386 if (fd
< 0 || e
== fdp
|| (*e
!= '/' && *e
!= '\0'))
391 destbuf
= p
? p
->fd (fd
, fs
) : NULL
;
392 if (!destbuf
|| !*destbuf
)
398 *((process_fd_t
*) data
)->fd_type
= virt_fdsymlink
;
399 else /* trailing path */
401 /* Does the descriptor link point to a directory? */
403 if (*destbuf
!= '/') /* e.g., "pipe:[" or "socket:[" */
407 path_conv
pc (destbuf
);
416 char *newbuf
= (char *) cmalloc_abort (HEAP_STR
, strlen (destbuf
)
418 stpcpy (stpcpy (newbuf
, destbuf
), e
);
421 *((process_fd_t
*) data
)->fd_type
= virt_fsdir
;
428 format_process_ppid (void *data
, char *&destbuf
)
430 _pinfo
*p
= (_pinfo
*) data
;
431 destbuf
= (char *) crealloc_abort (destbuf
, 40);
432 return __small_sprintf (destbuf
, "%d\n", p
->ppid
);
436 format_process_uid (void *data
, char *&destbuf
)
438 _pinfo
*p
= (_pinfo
*) data
;
439 destbuf
= (char *) crealloc_abort (destbuf
, 40);
440 return __small_sprintf (destbuf
, "%d\n", p
->uid
);
444 format_process_pgid (void *data
, char *&destbuf
)
446 _pinfo
*p
= (_pinfo
*) data
;
447 destbuf
= (char *) crealloc_abort (destbuf
, 40);
448 return __small_sprintf (destbuf
, "%d\n", p
->pgid
);
452 format_process_sid (void *data
, char *&destbuf
)
454 _pinfo
*p
= (_pinfo
*) data
;
455 destbuf
= (char *) crealloc_abort (destbuf
, 40);
456 return __small_sprintf (destbuf
, "%d\n", p
->sid
);
460 format_process_gid (void *data
, char *&destbuf
)
462 _pinfo
*p
= (_pinfo
*) data
;
463 destbuf
= (char *) crealloc_abort (destbuf
, 40);
464 return __small_sprintf (destbuf
, "%d\n", p
->gid
);
468 format_process_ctty (void *data
, char *&destbuf
)
470 _pinfo
*p
= (_pinfo
*) data
;
471 if (!CTTY_IS_VALID (p
->ctty
))
473 destbuf
= (char *) crealloc_abort (destbuf
, 2);
474 return __small_sprintf (destbuf
, "\n");
478 destbuf
= (char *) crealloc_abort (destbuf
, strlen (d
.name ()) + 2);
479 return __small_sprintf (destbuf
, "%s\n", d
.name ());
483 format_process_root (void *data
, char *&destbuf
)
485 _pinfo
*p
= (_pinfo
*) data
;
493 destbuf
= p
? p
->root (fs
) : NULL
;
494 if (!destbuf
|| !*destbuf
)
496 destbuf
= cstrdup ("<defunct>");
497 fs
= strlen (destbuf
) + 1;
503 format_process_cwd (void *data
, char *&destbuf
)
505 _pinfo
*p
= (_pinfo
*) data
;
513 destbuf
= p
? p
->cwd (fs
) : NULL
;
514 if (!destbuf
|| !*destbuf
)
516 destbuf
= cstrdup ("<defunct>");
517 fs
= strlen (destbuf
) + 1;
523 format_process_cmdline (void *data
, char *&destbuf
)
525 _pinfo
*p
= (_pinfo
*) data
;
533 destbuf
= p
? p
->cmdline (fs
) : NULL
;
534 if (destbuf
&& *destbuf
)
536 return format_process_exename (data
, destbuf
);
540 format_process_exename (void *data
, char *&destbuf
)
542 _pinfo
*p
= (_pinfo
*) data
;
546 char *buf
= tp
.c_get ();
547 if (p
->process_state
& PID_EXITED
)
548 stpcpy (buf
, "<defunct>");
551 mount_table
->conv_to_posix_path (p
->progname
, buf
, CCP_RELATIVE
);
555 char *s
= buf
+ len
- 4;
556 if (ascii_strcasematch (s
, ".exe"))
560 destbuf
= (char *) crealloc_abort (destbuf
, (len
= strlen (buf
)) + 1);
561 stpcpy (destbuf
, buf
);
566 format_process_winpid (void *data
, char *&destbuf
)
568 _pinfo
*p
= (_pinfo
*) data
;
569 destbuf
= (char *) crealloc_abort (destbuf
, 20);
570 return __small_sprintf (destbuf
, "%d\n", p
->dwProcessId
);
574 format_process_winexename (void *data
, char *&destbuf
)
576 _pinfo
*p
= (_pinfo
*) data
;
577 size_t len
= sys_wcstombs (NULL
, 0, p
->progname
);
578 destbuf
= (char *) crealloc_abort (destbuf
, len
+ 1);
579 /* With trailing \0 for backward compat reasons. */
580 sys_wcstombs (destbuf
, len
+ 1, p
->progname
);
585 format_process_environ (void *data
, char *&destbuf
)
587 _pinfo
*p
= (_pinfo
*) data
;
595 destbuf
= p
? p
->environ (fs
) : NULL
;
596 if (!destbuf
|| !*destbuf
)
598 destbuf
= cstrdup ("<defunct>");
599 fs
= strlen (destbuf
) + 1;
614 heap
*heap_vm_chunks
;
616 heap_info (DWORD pid
)
617 : heap_vm_chunks (NULL
)
621 PDEBUG_HEAP_ARRAY harray
;
623 buf
= RtlCreateQueryDebugBuffer (16 * 65536, FALSE
);
626 status
= RtlQueryProcessDebugInformation (pid
, PDI_HEAPS
| PDI_HEAP_BLOCKS
,
628 if (NT_SUCCESS (status
)
629 && (harray
= (PDEBUG_HEAP_ARRAY
) buf
->HeapInformation
) != NULL
)
630 for (ULONG hcnt
= 0; hcnt
< harray
->Count
; ++hcnt
)
632 PDEBUG_HEAP_BLOCK barray
= (PDEBUG_HEAP_BLOCK
)
633 harray
->Heaps
[hcnt
].Blocks
;
636 for (ULONG bcnt
= 0; bcnt
< harray
->Heaps
[hcnt
].BlockCount
; ++bcnt
)
637 if (barray
[bcnt
].Flags
& 2)
639 heap
*h
= (heap
*) malloc (sizeof (heap
));
642 *h
= (heap
) { heap_vm_chunks
,
643 hcnt
, (char *) barray
[bcnt
].Address
,
644 (char *) barray
[bcnt
].Address
646 harray
->Heaps
[hcnt
].Flags
};
651 RtlDestroyQueryDebugBuffer (buf
);
654 char *fill_if_match (char *base
, ULONG type
, char *dest
)
656 for (heap
*h
= heap_vm_chunks
; h
; h
= h
->next
)
657 if (base
>= h
->base
&& base
< h
->end
)
659 char *p
= dest
+ __small_sprintf (dest
, "[win heap %ld", h
->heap_id
);
660 if (!(h
->flags
& HEAP_FLAG_NONDEFAULT
))
661 p
= stpcpy (p
, " default");
662 if ((h
->flags
& HEAP_FLAG_SHAREABLE
) && (type
& MEM_MAPPED
))
663 p
= stpcpy (p
, " shared");
664 if (h
->flags
& HEAP_FLAG_EXECUTABLE
)
665 p
= stpcpy (p
, " exec");
666 if (h
->flags
& HEAP_FLAG_GROWABLE
)
667 p
= stpcpy (p
, " grow");
668 if (h
->flags
& HEAP_FLAG_NOSERIALIZE
)
669 p
= stpcpy (p
, " noserial");
670 if (h
->flags
== HEAP_FLAG_DEBUGGED
)
671 p
= stpcpy (p
, " debug");
681 for (heap
*m
= heap_vm_chunks
; m
; m
= n
)
701 thread_info (DWORD pid
, HANDLE process
)
706 ULONG size
= 50 * (sizeof (SYSTEM_PROCESS_INFORMATION
)
707 + 16 * sizeof (SYSTEM_THREADS
));
708 PSYSTEM_PROCESS_INFORMATION proc
;
709 PSYSTEM_THREADS thread
;
713 buf
= realloc (buf
, size
);
714 status
= NtQuerySystemInformation (SystemProcessInformation
,
718 while (status
== STATUS_INFO_LENGTH_MISMATCH
);
719 if (!NT_SUCCESS (status
))
723 debug_printf ("NtQuerySystemInformation, %y", status
);
726 proc
= (PSYSTEM_PROCESS_INFORMATION
) buf
;
729 if ((DWORD
) (uintptr_t) proc
->UniqueProcessId
== pid
)
731 if (!proc
->NextEntryOffset
)
736 proc
= (PSYSTEM_PROCESS_INFORMATION
) ((PBYTE
) proc
737 + proc
->NextEntryOffset
);
739 thread
= proc
->Threads
;
740 for (ULONG i
= 0; i
< proc
->NumberOfThreads
; ++i
)
742 THREAD_BASIC_INFORMATION tbi
;
746 thread_h
= OpenThread (THREAD_QUERY_LIMITED_INFORMATION
, FALSE
,
747 (ULONG
) (ULONG_PTR
) thread
[i
].ClientId
.UniqueThread
);
750 status
= NtQueryInformationThread (thread_h
, ThreadBasicInformation
,
751 &tbi
, sizeof tbi
, NULL
);
752 CloseHandle (thread_h
);
753 if (!NT_SUCCESS (status
))
755 region
*r
= (region
*) malloc (sizeof (region
));
758 *r
= (region
) { regions
,
759 (ULONG
) (ULONG_PTR
) thread
[i
].ClientId
.UniqueThread
,
760 (char *) tbi
.TebBaseAddress
,
761 (char *) tbi
.TebBaseAddress
762 + 2 * wincap
.page_size (),
766 if (!ReadProcessMemory (process
, (PVOID
) tbi
.TebBaseAddress
,
767 &teb
, sizeof teb
, NULL
))
769 r
= (region
*) malloc (sizeof (region
));
772 *r
= (region
) { regions
,
773 (ULONG
) (ULONG_PTR
) thread
[i
].ClientId
.UniqueThread
,
774 (char *) (teb
.DeallocationStack
775 ?: teb
.Tib
.StackLimit
),
776 (char *) teb
.Tib
.StackBase
,
784 char *fill_if_match (char *base
, ULONG type
, char *dest
)
786 for (region
*r
= regions
; r
; r
= r
->next
)
787 if (base
>= r
->start
&& base
< r
->end
)
789 char *p
= dest
+ __small_sprintf (dest
, "[%s (tid %ld)",
790 r
->teb
? "teb" : "stack",
792 if (type
& MEM_MAPPED
)
793 p
= stpcpy (p
, " shared");
799 /* Helper to look for TEBs inside single allocated region since W10 1511. */
800 char *fill_if_match (char *start
, char *dest
)
802 for (region
*r
= regions
; r
; r
= r
->next
)
803 if (r
->teb
&& start
== r
->start
)
805 __small_sprintf (dest
, "[teb (tid %ld)]", r
->thread_id
);
814 for (region
*m
= regions
; m
; m
= n
)
823 format_process_maps (void *data
, char *&destbuf
)
825 _pinfo
*p
= (_pinfo
*) data
;
826 HANDLE proc
= OpenProcess (PROCESS_QUERY_INFORMATION
827 | PROCESS_VM_READ
, FALSE
, p
->dwProcessId
);
830 if (!(p
->process_state
& PID_EXITED
))
832 DWORD error
= GetLastError ();
833 __seterrno_from_win_error (error
);
834 debug_printf ("OpenProcess: ret %u; pid: %d", error
, p
->dwProcessId
);
839 /* Else it's a zombie process; just return an empty string */
840 destbuf
= (char *) crealloc_abort (destbuf
, 1);
847 PROCESS_BASIC_INFORMATION pbi
;
850 memset (&pbi
, 0, sizeof (pbi
));
851 status
= NtQueryInformationProcess (proc
, ProcessBasicInformation
,
852 &pbi
, sizeof pbi
, NULL
);
853 if (NT_SUCCESS (status
))
854 peb
= pbi
.PebBaseAddress
;
855 /* myself is in the same spot in every process, so is the pointer to the
856 procinfo. But make sure the destructor doesn't try to release procinfo! */
858 if (ReadProcessMemory (proc
, &myself
, &proc_pinfo
, sizeof proc_pinfo
, NULL
))
859 proc_pinfo
.preserve ();
860 /* The heap info on the cygheap is also in the same spot in each process
861 because the cygheap is located at the same address. */
862 user_heap_info user_heap
;
863 shared_region_info region_info
;
864 ReadProcessMemory (proc
, &cygheap
->user_heap
, &user_heap
,
865 sizeof user_heap
, NULL
);
866 ReadProcessMemory (proc
, &cygheap
->shared_regions
, ®ion_info
,
867 sizeof region_info
, NULL
);
883 } cur
= {{{'\0'}}, (char *)1, 0, 0};
885 MEMORY_BASIC_INFORMATION mb
;
886 dos_drive_mappings drive_maps
;
887 heap_info
heaps (p
->dwProcessId
);
888 thread_info
threads (p
->dwProcessId
, proc
);
893 PMEMORY_SECTION_NAME msi
= (PMEMORY_SECTION_NAME
) tp
.w_get ();
894 char *posix_modname
= tp
.c_get ();
896 char *peb_teb_abase
= NULL
;
904 /* Iterate over each VM region in the address space, coalescing
905 memory regions with the same permissions. Once we run out, do one
906 last_pass to trigger output of the last accumulated region. */
908 VirtualQueryEx (proc
, i
, &mb
, sizeof(mb
)) || (1 == ++last_pass
);
912 posix_modname
[0] = '\0';
913 if (mb
.State
== MEM_FREE
)
915 else if (mb
.State
== MEM_RESERVE
)
917 char *p
= stpcpy (a
.flags
, "===");
918 stpcpy (p
, (mb
.Type
& MEM_MAPPED
) ? "s" : "p");
922 static DWORD
const RO
= (PAGE_EXECUTE_READ
| PAGE_READONLY
);
923 static DWORD
const RW
= (PAGE_EXECUTE_READWRITE
| PAGE_READWRITE
924 | PAGE_EXECUTE_WRITECOPY
| PAGE_WRITECOPY
);
925 static DWORD
const X
= (PAGE_EXECUTE
| PAGE_EXECUTE_READ
926 | PAGE_EXECUTE_READWRITE
927 | PAGE_EXECUTE_WRITECOPY
);
928 static DWORD
const WC
= (PAGE_EXECUTE_WRITECOPY
| PAGE_WRITECOPY
);
929 DWORD p
= mb
.Protect
;
931 (p
& (RO
| RW
)) ? 'r' : '-',
932 (p
& (RW
)) ? 'w' : '-',
933 (p
& (X
)) ? 'x' : '-',
934 (mb
.Type
& MEM_MAPPED
) && !(p
& (WC
)) ? 's'
935 : (p
& PAGE_GUARD
) ? 'g' : 'p',
936 '\0', // zero-fill the remaining bytes
941 (char *) mb
.AllocationBase
,
942 (char *) mb
.BaseAddress
,
943 (char *) mb
.BaseAddress
+mb
.RegionSize
,
947 /* Windows permissions are more fine-grained than the unix rwxp,
948 so we reduce clutter by manually coalescing regions sharing
949 the same allocation base and effective permissions. */
950 bool newbase
= (next
.abase
!= cur
.abase
);
951 if (!last_pass
&& !newbase
&& next
.a
.word
== cur
.a
.word
)
952 cur
.rend
= next
.rend
; /* merge with previous */
955 char *peb_teb_end
= NULL
;
956 peb_teb_rinse_repeat
:
957 /* Starting with W10 1511, PEB and TEBs don't get allocated
958 separately. Rather they are created in a single region. Examine
959 the region starting at the PEB address page-wise. */
960 if (wincap
.has_new_pebteb_region ())
962 if (peb_teb_abase
&& !peb_teb_end
&& cur
.abase
== peb_teb_abase
)
964 posix_modname
[0] = '\0';
965 peb_teb_end
= cur
.rend
;
966 if (cur
.state
== MEM_COMMIT
)
967 cur
.rend
= cur
.rbase
+ wincap
.page_size ();
969 if (cur
.state
== MEM_COMMIT
)
971 if (!peb_teb_abase
&& cur
.rbase
== (char *) peb
)
973 peb_teb_abase
= cur
.abase
;
974 peb_teb_end
= cur
.rend
;
975 cur
.rend
= cur
.rbase
+ wincap
.page_size ();
976 strcpy (posix_modname
, "[peb]");
978 else if (peb_teb_end
)
981 posix_modname
[0] = '\0';
982 end
= threads
.fill_if_match (cur
.rbase
, posix_modname
);
988 char *base
= cur
.rbase
;
991 base
+= wincap
.page_size ();
993 while (!threads
.fill_if_match (base
, posix_modname
)
994 && base
< peb_teb_end
);
995 if (posix_modname
[0])
997 posix_modname
[0] = '\0';
1001 cur
.rend
= peb_teb_end
;
1006 /* output the current region if it's "interesting". */
1009 size_t newlen
= strlen (posix_modname
) + 62;
1010 if (len
+ newlen
>= maxsize
)
1012 crealloc_abort (destbuf
,
1013 maxsize
+= roundup2 (newlen
, 2048UL));
1014 int written
= __small_sprintf (destbuf
+ len
,
1015 "%08lx-%08lx %s %08lx %04x:%04x %U ",
1016 cur
.rbase
, cur
.rend
, cur
.a
.flags
,
1017 cur
.rbase
- cur
.abase
,
1021 while (written
< 62)
1022 destbuf
[len
+ written
++] = ' ';
1024 len
+= __small_sprintf (destbuf
+ len
, "%s\n", posix_modname
);
1027 if (peb_teb_end
&& cur
.state
== MEM_COMMIT
)
1029 cur
.rbase
= cur
.rend
;
1030 cur
.rend
+= wincap
.page_size ();
1031 if (cur
.rbase
< peb_teb_end
)
1032 goto peb_teb_rinse_repeat
;
1034 /* start of a new region (but possibly still the same allocation). */
1036 /* if a new allocation, figure out what kind it is. */
1037 if (newbase
&& !last_pass
&& cur
.state
!= MEM_FREE
)
1039 /* If the return length pointer is missing, NtQueryVirtualMemory
1040 returns with STATUS_ACCESS_VIOLATION on Windows 2000. */
1045 if ((mb
.Type
& (MEM_MAPPED
| MEM_IMAGE
))
1046 && NT_SUCCESS (status
= NtQueryVirtualMemory (proc
, cur
.abase
,
1048 msi
, 65536, &ret_len
)))
1051 drive_maps
.fixup_if_match (msi
->SectionFileName
.Buffer
);
1052 if (mount_table
->conv_to_posix_path (dosname
,
1054 sys_wcstombs (posix_modname
, NT_MAX_PATH
, dosname
);
1055 stat (posix_modname
, &st
);
1057 else if (!threads
.fill_if_match (cur
.abase
, mb
.Type
,
1059 && !heaps
.fill_if_match (cur
.abase
, mb
.Type
,
1062 if (cur
.abase
== (char *) peb
)
1063 strcpy (posix_modname
, "[peb]");
1064 else if (cur
.abase
== (char *) &SharedUserData
)
1065 strcpy (posix_modname
, "[shared-user-data]");
1066 else if (cur
.abase
== region_info
.cygwin_shared_addr
)
1067 strcpy (posix_modname
, "[cygwin-shared]");
1068 else if (cur
.abase
== region_info
.user_shared_addr
)
1069 strcpy (posix_modname
, "[cygwin-user-shared]");
1070 else if (cur
.abase
== region_info
.myself_shared_addr
)
1071 strcpy (posix_modname
, "[procinfo]");
1072 else if (cur
.abase
== region_info
.console_shared_addr
)
1073 strcpy (posix_modname
, "[cygwin-shared-console]");
1074 else if (cur
.abase
== (char *) cygheap
)
1075 strcpy (posix_modname
, "[cygheap]");
1076 else if (cur
.abase
== user_heap
.base
)
1077 strcpy (posix_modname
, "[heap]");
1079 posix_modname
[0] = 0;
1089 format_process_stat (void *data
, char *&destbuf
)
1091 _pinfo
*p
= (_pinfo
*) data
;
1092 char cmd
[NAME_MAX
+ 1];
1094 unsigned long fault_count
= 0UL,
1095 vmsize
= 0UL, vmrss
= 0UL, vmmaxrss
= 0UL;
1096 uint64_t utime
= 0ULL, stime
= 0ULL, start_time
= 0ULL;
1098 /* ctty maj is 31:16, min is 15:0; tty_nr s/b maj 15:8, min 31:20, 7:0;
1099 maj is 31:16 >> 16 & fff << 8; min is 15:0 >> 8 & ff << 20 | & ff */
1101 if (CTTY_IS_VALID (p
->ctty
))
1102 tty_nr
= (((p
->ctty
>> 8) & 0xff) << 20)
1103 | (((p
->ctty
>> 16) & 0xfff) << 8)
1106 if (p
->process_state
& PID_EXITED
)
1107 strcpy (cmd
, "<defunct>");
1110 PWCHAR last_slash
= wcsrchr (p
->progname
, L
'\\');
1111 sys_wcstombs (cmd
, NAME_MAX
+ 1,
1112 last_slash
? last_slash
+ 1 : p
->progname
);
1113 int len
= strlen (cmd
);
1116 char *s
= cmd
+ len
- 4;
1117 if (ascii_strcasematch (s
, ".exe"))
1121 /* Note: under Windows, a process is always running - it's only threads
1122 that get suspended. Therefore the default state is R (runnable). */
1123 if (p
->process_state
& PID_EXITED
)
1125 else if (p
->process_state
& PID_STOPPED
)
1128 state
= get_process_state (p
->dwProcessId
);
1132 VM_COUNTERS vmc
= { 0 };
1133 KERNEL_USER_TIMES put
= { 0 };
1134 QUOTA_LIMITS ql
= { 0 };
1135 SYSTEM_TIMEOFDAY_INFORMATION stodi
= { 0 };
1137 hProcess
= OpenProcess (PROCESS_QUERY_LIMITED_INFORMATION
,
1138 FALSE
, p
->dwProcessId
);
1139 if (hProcess
== NULL
)
1141 if (!(p
->process_state
& PID_EXITED
))
1143 DWORD error
= GetLastError ();
1144 __seterrno_from_win_error (error
);
1145 debug_printf ("OpenProcess: ret %u; pid: %d", error
, p
->dwProcessId
);
1148 /* Else it's a zombie process; just leave each structure zero'd */
1152 status
= NtQueryInformationProcess (hProcess
, ProcessVmCounters
,
1153 (PVOID
) &vmc
, sizeof vmc
, NULL
);
1154 if (!NT_SUCCESS (status
))
1155 debug_printf ("NtQueryInformationProcess(ProcessVmCounters): status %y",
1157 status
= NtQueryInformationProcess (hProcess
, ProcessTimes
,
1158 (PVOID
) &put
, sizeof put
, NULL
);
1159 if (!NT_SUCCESS (status
))
1160 debug_printf ("NtQueryInformationProcess(ProcessTimes): status %y",
1162 status
= NtQueryInformationProcess (hProcess
, ProcessQuotaLimits
,
1163 (PVOID
) &ql
, sizeof ql
, NULL
);
1164 if (!NT_SUCCESS (status
))
1165 debug_printf ("NtQueryInformationProcess(ProcessQuotaLimits): "
1166 "status %y", status
);
1167 nice
= winprio_to_nice (GetPriorityClass (hProcess
));
1168 CloseHandle (hProcess
);
1170 status
= NtQuerySystemInformation (SystemTimeOfDayInformation
,
1171 (PVOID
) &stodi
, sizeof stodi
, NULL
);
1172 if (!NT_SUCCESS (status
))
1173 debug_printf ("NtQuerySystemInformation(SystemTimeOfDayInformation): "
1174 "status %y", status
);
1175 fault_count
= vmc
.PageFaultCount
;
1176 utime
= put
.UserTime
.QuadPart
* CLOCKS_PER_SEC
/ NS100PERSEC
;
1177 stime
= put
.KernelTime
.QuadPart
* CLOCKS_PER_SEC
/ NS100PERSEC
;
1178 if (put
.CreateTime
.QuadPart
)
1179 start_time
= (put
.CreateTime
.QuadPart
- stodi
.BootTime
.QuadPart
)
1180 * CLOCKS_PER_SEC
/ NS100PERSEC
;
1182 start_time
= (p
->start_time
- to_time_t (&stodi
.BootTime
)) * CLOCKS_PER_SEC
;
1183 unsigned page_size
= wincap
.page_size ();
1184 vmsize
= vmc
.PagefileUsage
; /* bytes */
1185 vmrss
= vmc
.WorkingSetSize
/ page_size
; /* pages */
1186 vmmaxrss
= ql
.MaximumWorkingSetSize
; /* bytes */
1188 destbuf
= (char *) crealloc_abort (destbuf
, strlen (cmd
) + 320);
1189 return __small_sprintf (destbuf
, "%d (%s) %c "
1191 "%d %u %lu %lu %u %u "
1197 p
->ppid
, p
->pgid
, p
->sid
, tty_nr
,
1198 -1, 0, fault_count
, fault_count
, 0, 0,
1199 utime
, stime
, utime
, stime
,
1200 NZERO
+ nice
, nice
, 0, 0,
1202 vmsize
, vmrss
, vmmaxrss
1207 format_process_status (void *data
, char *&destbuf
)
1209 _pinfo
*p
= (_pinfo
*) data
;
1210 char cmd
[NAME_MAX
+ 1];
1212 const char *state_str
= "unknown";
1213 size_t vmsize
= 0, vmrss
= 0, vmdata
= 0, vmlib
= 0, vmtext
= 0, vmshare
= 0;
1214 sigset_t pnd
= 0, blk
= 0, ign
= 0;
1215 bool fetch_siginfo
= false;
1217 PWCHAR last_slash
= wcsrchr (p
->progname
, L
'\\');
1218 sys_wcstombs (cmd
, NAME_MAX
+ 1, last_slash
? last_slash
+ 1 : p
->progname
);
1219 int len
= strlen (cmd
);
1222 char *s
= cmd
+ len
- 4;
1223 if (ascii_strcasematch (s
, ".exe"))
1226 /* Note: under Windows, a process is always running - it's only threads
1227 that get suspended. Therefore the default state is R (runnable). */
1228 if (p
->process_state
& PID_EXITED
)
1230 else if (p
->process_state
& PID_STOPPED
)
1233 state
= get_process_state (p
->dwProcessId
);
1237 state_str
= "running";
1238 fetch_siginfo
= true;
1242 state_str
= "sleeping";
1243 fetch_siginfo
= true;
1246 state_str
= "runnable";
1247 fetch_siginfo
= true;
1250 state_str
= "zombie";
1253 state_str
= "stopped";
1256 get_mem_values (p
->dwProcessId
, vmsize
, vmrss
, vmtext
, vmdata
,
1259 p
->siginfo (pnd
, blk
, ign
);
1260 /* The real uid value for *this* process is stored at cygheap->user.real_uid
1261 but we can't get at the real uid value for any other process, so
1262 just fake it as p->uid. Similar for p->gid. */
1263 size_t kb_per_page
= wincap
.allocation_granularity() / 1024;
1264 destbuf
= (char *) crealloc_abort (destbuf
, strlen (cmd
) + 320);
1265 return __small_sprintf (destbuf
, "Name:\t%s\n"
1270 "Uid:\t%d %d %d %d\n"
1271 "Gid:\t%d %d %d %d\n"
1272 "VmSize:\t%8lu kB\n"
1275 "VmData:\t%8lu kB\n"
1281 "SigIgn:\t%016lx\n",
1287 p
->uid
, p
->uid
, p
->uid
, p
->uid
,
1288 p
->gid
, p
->gid
, p
->gid
, p
->gid
,
1289 vmsize
* kb_per_page
, 0UL, vmrss
* kb_per_page
,
1290 vmdata
* kb_per_page
, 0UL, vmtext
* kb_per_page
,
1291 vmlib
* kb_per_page
,
1297 format_process_statm (void *data
, char *&destbuf
)
1299 _pinfo
*p
= (_pinfo
*) data
;
1300 size_t vmsize
= 0, vmrss
= 0, vmtext
= 0, vmdata
= 0, vmlib
= 0, vmshare
= 0;
1302 if (!get_mem_values (p
->dwProcessId
, vmsize
, vmrss
, vmtext
, vmdata
,
1303 vmlib
, vmshare
) && !(p
->process_state
& PID_EXITED
))
1304 return -1; /* Error out unless it's a zombie process */
1306 destbuf
= (char *) crealloc_abort (destbuf
, 96);
1307 return __small_sprintf (destbuf
, "%lu %lu %lu %lu %lu %lu 0\n",
1308 vmsize
, vmrss
, vmshare
, vmtext
, vmlib
, vmdata
);
1312 FILE *setmntent (const char *, const char *);
1313 struct mntent
*getmntent (FILE *);
1317 format_process_mountstuff (void *data
, char *&destbuf
, bool mountinfo
)
1319 _pinfo
*p
= (_pinfo
*) data
;
1320 user_info
*u_shared
= NULL
;
1321 HANDLE u_hdl
= NULL
;
1325 if (p
->uid
!= myself
->uid
)
1327 WCHAR sid_string
[UNLEN
+ 1] = L
""; /* Large enough for SID */
1331 if (!p_sid
.getfrompw (internal_getpwuid (p
->uid
)))
1333 p_sid
.string (sid_string
);
1334 u_shared
= (user_info
*) open_shared (sid_string
, USER_VERSION
, u_hdl
,
1335 sizeof (user_info
), SH_JUSTOPEN
,
1341 u_shared
= user_shared
;
1342 mount_info
*mtab
= &u_shared
->mountinfo
;
1344 /* Store old value of _my_tls.locals here. */
1345 int iteration
= _my_tls
.locals
.iteration
;
1346 unsigned available_drives
= _my_tls
.locals
.available_drives
;
1347 /* This reinitializes the above values in _my_tls. */
1348 setmntent (NULL
, NULL
);
1349 /* Restore iteration immediately since it's not used below. We use the
1350 local iteration variable instead*/
1351 _my_tls
.locals
.iteration
= iteration
;
1353 for (iteration
= 0; (mnt
= mtab
->getmntent (iteration
)); ++iteration
)
1355 /* We have no access to the drives mapped into another user session and
1356 _my_tls.locals.available_drives contains the mappings of the current
1357 user. So, when printing the mount table of another user, we check
1358 each cygdrive entry if it's a remote drive. If so, ignore it. */
1359 if (iteration
>= mtab
->nmounts
&& u_hdl
)
1361 WCHAR drive
[3] = { (WCHAR
) mnt
->mnt_fsname
[0], L
':', L
'\0' };
1362 disk_type dt
= get_disk_type (drive
);
1364 if (dt
== DT_SHARE_SMB
|| dt
== DT_SHARE_NFS
)
1367 destbuf
= (char *) crealloc_abort (destbuf
, len
1368 + strlen (mnt
->mnt_fsname
)
1369 + strlen (mnt
->mnt_dir
)
1370 + strlen (mnt
->mnt_type
)
1371 + strlen (mnt
->mnt_opts
)
1375 path_conv
pc (mnt
->mnt_dir
, PC_SYM_NOFOLLOW
| PC_POSIX
);
1376 dev_t dev
= pc
.exists () ? pc
.fs_serial_number () : -1;
1378 len
+= __small_sprintf (destbuf
+ len
,
1379 "%d %d %d:%d / %s %s - %s %s %s\n",
1380 iteration
, iteration
,
1381 major (dev
), minor (dev
),
1382 mnt
->mnt_dir
, mnt
->mnt_opts
,
1383 mnt
->mnt_type
, mnt
->mnt_fsname
,
1384 (pc
.fs_flags () & FILE_READ_ONLY_VOLUME
)
1388 len
+= __small_sprintf (destbuf
+ len
, "%s %s %s %s %d %d\n",
1389 mnt
->mnt_fsname
, mnt
->mnt_dir
, mnt
->mnt_type
,
1390 mnt
->mnt_opts
, mnt
->mnt_freq
, mnt
->mnt_passno
);
1393 /* Restore available_drives */
1394 _my_tls
.locals
.available_drives
= available_drives
;
1396 if (u_hdl
) /* Only not-NULL if open_shared has been called. */
1398 UnmapViewOfFile (u_shared
);
1399 CloseHandle (u_hdl
);
1405 format_process_mounts (void *data
, char *&destbuf
)
1407 return format_process_mountstuff (data
, destbuf
, false);
1411 format_process_mountinfo (void *data
, char *&destbuf
)
1413 return format_process_mountstuff (data
, destbuf
, true);
1417 get_process_state (DWORD dwProcessId
)
1419 /* This isn't really heavy magic - just go through the processes' threads
1420 one by one and return a value accordingly. Errors are silently ignored. */
1422 PSYSTEM_PROCESS_INFORMATION p
, sp
;
1426 p
= (PSYSTEM_PROCESS_INFORMATION
) malloc (n
);
1431 status
= NtQuerySystemInformation (SystemProcessInformation
,
1432 (PVOID
) p
, n
, NULL
);
1433 if (status
!= STATUS_INFO_LENGTH_MISMATCH
)
1436 PSYSTEM_PROCESS_INFORMATION new_p
= (PSYSTEM_PROCESS_INFORMATION
) realloc (p
, n
);
1441 if (!NT_SUCCESS (status
))
1443 debug_printf ("NtQuerySystemInformation: status %y, %u",
1444 status
, RtlNtStatusToDosError (status
));
1451 if ((DWORD
) (uintptr_t) sp
->UniqueProcessId
== dwProcessId
)
1454 st
= &sp
->Threads
[0];
1456 for (unsigned i
= 0; i
< sp
->NumberOfThreads
; i
++)
1458 /* FIXME: at some point we should consider generating 'O' */
1459 if (st
->State
== StateRunning
||
1460 st
->State
== StateReady
)
1469 if (!sp
->NextEntryOffset
)
1471 sp
= (PSYSTEM_PROCESS_INFORMATION
) ((char *) sp
+ sp
->NextEntryOffset
);
1479 get_mem_values (DWORD dwProcessId
, size_t &vmsize
, size_t &vmrss
,
1480 size_t &vmtext
, size_t &vmdata
, size_t &vmlib
, size_t &vmshare
)
1486 PMEMORY_WORKING_SET_LIST p
;
1487 SIZE_T n
= 0x4000, length
;
1488 const size_t page_scale
= wincap
.allocation_granularity()
1489 / wincap
.page_size();
1491 /* This appears to work despite MSDN claiming that QueryWorkingSet requires
1492 PROCESS_QUERY_INFORMATION *and* PROCESS_VM_READ. Since we're trying to do
1493 everything with least perms, we stick to PROCESS_QUERY_INFORMATION only
1494 unless this changes in Windows for some reason. */
1495 hProcess
= OpenProcess (PROCESS_QUERY_INFORMATION
, FALSE
, dwProcessId
);
1496 if (hProcess
== NULL
)
1499 debug_printf ("OpenProcess, %E");
1502 p
= (PMEMORY_WORKING_SET_LIST
) malloc (n
);
1507 status
= NtQueryVirtualMemory (hProcess
, 0, MemoryWorkingSetList
,
1509 (length
= (SIZE_T
) -1, &length
));
1510 if (status
!= STATUS_INFO_LENGTH_MISMATCH
)
1513 PMEMORY_WORKING_SET_LIST new_p
= (PMEMORY_WORKING_SET_LIST
)
1519 if (!NT_SUCCESS (status
))
1521 debug_printf ("NtQueryVirtualMemory: status %y", status
);
1522 if (status
== STATUS_PROCESS_IS_TERMINATING
)
1524 vmsize
= vmrss
= vmtext
= vmdata
= vmlib
= vmshare
= 0;
1528 __seterrno_from_nt_status (status
);
1531 for (unsigned long i
= 0; i
< p
->NumberOfPages
; i
++)
1534 unsigned flags
= p
->WorkingSetList
[i
] & 0x0FFF;
1535 if ((flags
& (WSLE_PAGE_EXECUTE
| WSLE_PAGE_SHAREABLE
))
1536 == (WSLE_PAGE_EXECUTE
| WSLE_PAGE_SHAREABLE
))
1538 else if (flags
& WSLE_PAGE_SHAREABLE
)
1540 else if (flags
& WSLE_PAGE_EXECUTE
)
1545 status
= NtQueryInformationProcess (hProcess
, ProcessVmCounters
, (PVOID
) &vmc
,
1547 if (!NT_SUCCESS (status
))
1549 debug_printf ("NtQueryInformationProcess: status %y", status
);
1550 __seterrno_from_nt_status (status
);
1553 vmsize
= vmc
.PagefileUsage
/ wincap
.page_size ();
1554 /* Return number of Cygwin pages. Page size in Cygwin is equivalent
1555 to Windows allocation_granularity. */
1556 vmsize
= howmany (vmsize
, page_scale
);
1557 vmrss
= howmany (vmrss
, page_scale
);
1558 vmshare
= howmany (vmshare
, page_scale
);
1559 vmtext
= howmany (vmtext
, page_scale
);
1560 vmlib
= howmany (vmlib
, page_scale
);
1561 vmdata
= howmany (vmdata
, page_scale
);
1565 CloseHandle (hProcess
);