1 /* fhandler_process.cc: fhandler for /proc/<pid> virtual filesystem
3 Copyright 2002, 2003, 2004, 2005, 2006, 2007, 2008 Red Hat, Inc.
5 This file is part of Cygwin.
7 This software is a copyrighted work licensed under the terms of the
8 Cygwin license. Please consult the file "CYGWIN_LICENSE" for
13 #include <sys/cygwin.h>
19 #include "shared_info.h"
25 #include <sys/param.h>
29 #define _COMPILING_NEWLIB
32 static const int PROCESS_PPID
= 2;
33 static const int PROCESS_WINPID
= 3;
34 static const int PROCESS_WINEXENAME
= 4;
35 static const int PROCESS_STATUS
= 5;
36 static const int PROCESS_UID
= 6;
37 static const int PROCESS_GID
= 7;
38 static const int PROCESS_PGID
= 8;
39 static const int PROCESS_SID
= 9;
40 static const int PROCESS_CTTY
= 10;
41 static const int PROCESS_STAT
= 11;
42 static const int PROCESS_STATM
= 12;
43 static const int PROCESS_CMDLINE
= 13;
44 static const int PROCESS_MAPS
= 14;
45 static const int PROCESS_FD
= 15;
46 static const int PROCESS_EXENAME
= 16;
47 /* Keep symlinks always the last entries. */
48 static const int PROCESS_ROOT
= 17;
49 static const int PROCESS_EXE
= 18;
50 static const int PROCESS_CWD
= 19;
52 /* The position of "root" defines the beginning of symlik entries. */
53 #define is_symlink(nr) ((nr) >= PROCESS_ROOT)
55 static const char * const process_listing
[] =
74 /* Keep symlinks always the last entries. */
81 static const int PROCESS_LINK_COUNT
=
82 (sizeof (process_listing
) / sizeof (const char *)) - 1;
84 static _off64_t
format_process_maps (_pinfo
*p
, char *&destbuf
, size_t maxsize
);
85 static _off64_t
format_process_stat (_pinfo
*p
, char *destbuf
, size_t maxsize
);
86 static _off64_t
format_process_status (_pinfo
*p
, char *destbuf
, size_t maxsize
);
87 static _off64_t
format_process_statm (_pinfo
*p
, char *destbuf
, size_t maxsize
);
88 static int get_process_state (DWORD dwProcessId
);
89 static bool get_mem_values (DWORD dwProcessId
, unsigned long *vmsize
,
90 unsigned long *vmrss
, unsigned long *vmtext
,
91 unsigned long *vmdata
, unsigned long *vmlib
,
92 unsigned long *vmshare
);
94 /* Returns 0 if path doesn't exist, >0 if path is a directory,
95 * -1 if path is a file, -2 if path is a symlink, -3 if path is a pipe,
96 * -4 if path is a socket.
99 fhandler_process::exists ()
101 const char *path
= get_name ();
102 debug_printf ("exists (%s)", path
);
103 path
+= proc_len
+ 1;
104 while (*path
!= 0 && !isdirsep (*path
))
109 for (int i
= 0; process_listing
[i
]; i
++)
110 if (pathmatch (path
+ 1, process_listing
[i
]))
113 return is_symlink (i
) ? -2 : (i
== PROCESS_FD
) ? 1 : -1;
115 if (pathnmatch (strchr (path
, '/') + 1, "fd/", 3))
120 /* Check for nameless device entries. */
121 path
= strrchr (path
, '/');
124 if (!strncmp (path
, "pipe:[", 6))
126 else if (!strncmp (path
, "socket:[", 8))
133 fhandler_process::fhandler_process ():
139 fhandler_process::fstat (struct __stat64
*buf
)
141 const char *path
= get_name ();
142 int file_type
= exists ();
143 fhandler_base::fstat (buf
);
144 path
+= proc_len
+ 1;
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
;
175 buf
->st_uid
= p
->uid
;
176 buf
->st_gid
= p
->gid
;
177 buf
->st_mode
= S_IFLNK
| S_IRWXU
| S_IRWXG
| S_IRWXO
;
180 buf
->st_uid
= p
->uid
;
181 buf
->st_gid
= p
->gid
;
182 buf
->st_mode
= S_IFIFO
| S_IRUSR
| S_IWUSR
;
185 buf
->st_uid
= p
->uid
;
186 buf
->st_gid
= p
->gid
;
187 buf
->st_mode
= S_IFSOCK
| S_IRUSR
| S_IWUSR
;
191 buf
->st_uid
= p
->uid
;
192 buf
->st_gid
= p
->gid
;
193 buf
->st_mode
|= S_IFREG
| S_IRUSR
| S_IRGRP
| S_IROTH
;
199 fhandler_process::opendir (int fd
)
201 DIR *dir
= fhandler_virtual::opendir (fd
);
202 if (dir
&& fileid
== PROCESS_FD
)
208 fhandler_process::readdir (DIR *dir
, dirent
*de
)
211 if (fileid
== PROCESS_FD
)
213 if (dir
->__d_position
>= 2 + filesize
/ sizeof (int))
216 else if (dir
->__d_position
>= PROCESS_LINK_COUNT
)
218 if (fileid
== PROCESS_FD
&& dir
->__d_position
> 1)
220 int *p
= (int *) filebuf
;
221 __small_sprintf (de
->d_name
, "%d", p
[dir
->__d_position
++ - 2]);
224 strcpy (de
->d_name
, process_listing
[dir
->__d_position
++]);
225 dir
->__flags
|= dirent_saw_dot
| dirent_saw_dot_dot
;
228 syscall_printf ("%d = readdir (%p, %p) (%s)", res
, dir
, de
, de
->d_name
);
233 fhandler_process::open (int flags
, mode_t mode
)
235 int process_file_no
= -1;
237 int res
= fhandler_virtual::open (flags
, mode
);
244 path
= get_name () + proc_len
+ 1;
246 while (*path
!= 0 && !isdirsep (*path
))
251 if ((flags
& (O_CREAT
| O_EXCL
)) == (O_CREAT
| O_EXCL
))
257 else if (flags
& O_WRONLY
)
270 process_file_no
= -1;
271 for (int i
= 0; process_listing
[i
]; i
++)
274 (process_listing
[i
], path
+ 1, strlen (process_listing
[i
])))
277 if (process_file_no
== -1)
292 if (process_file_no
== PROCESS_FD
)
297 if (flags
& O_WRONLY
)
304 fileid
= process_file_no
;
305 if (!fill_filebuf ())
311 if (flags
& O_APPEND
)
318 set_flags ((flags
& ~O_TEXT
) | O_BINARY
);
321 syscall_printf ("%d = fhandler_proc::open (%p, %d)", res
, flags
, mode
);
326 fhandler_process::fill_filebuf ()
329 path
= get_name () + proc_len
+ 1;
346 char *fdp
= strrchr (path
, '/');
347 if (!fdp
|| *++fdp
== 'f') /* The "fd" directory itself. */
351 filebuf
= p
->fds (fs
);
358 if (fd
< 0 || (fd
== 0 && !isdigit (*fdp
)))
363 filebuf
= p
->fd (fd
, fs
);
364 if (!filebuf
|| !*filebuf
)
380 filebuf
= (char *) crealloc_abort (filebuf
, bufalloc
= 40);
402 default: // what's this here for?
406 __small_sprintf (filebuf
, "%d\n", num
);
407 filesize
= strlen (filebuf
);
412 case PROCESS_CMDLINE
:
423 filebuf
= p
->root (fs
);
426 filebuf
= p
->cwd (fs
);
428 case PROCESS_CMDLINE
:
429 filebuf
= p
->cmdline (fs
);
433 if (!filebuf
|| !*filebuf
)
435 filebuf
= cstrdup ("<defunct>");
436 filesize
= strlen (filebuf
) + 1;
440 case PROCESS_EXENAME
:
443 filebuf
= (char *) crealloc_abort (filebuf
, bufalloc
= NT_MAX_PATH
);
444 if (p
->process_state
& PID_EXITED
)
445 strcpy (filebuf
, "<defunct>");
448 mount_table
->conv_to_posix_path (p
->progname
, filebuf
, 1);
449 /* If transparent_exe isn't set, the link keeps its suffix so that
450 an open(2) call will succeed on /proc/$PID/exe. */
453 int len
= strlen (filebuf
);
456 char *s
= filebuf
+ len
- 4;
457 if (ascii_strcasematch (s
, ".exe"))
462 filesize
= strlen (filebuf
);
467 filebuf
= (char *) crealloc_abort (filebuf
, bufalloc
= 40);
468 __small_sprintf (filebuf
, "%d\n", p
->dwProcessId
);
469 filesize
= strlen (filebuf
);
472 case PROCESS_WINEXENAME
:
474 int len
= strlen (p
->progname
);
475 filebuf
= (char *) crealloc_abort (filebuf
, bufalloc
= (len
+ 2));
476 strcpy (filebuf
, p
->progname
);
483 filebuf
= (char *) crealloc_abort (filebuf
, bufalloc
= 2048);
484 filesize
= format_process_status (*p
, filebuf
, bufalloc
);
489 filebuf
= (char *) crealloc_abort (filebuf
, bufalloc
= 2048);
490 filesize
= format_process_stat (*p
, filebuf
, bufalloc
);
495 filebuf
= (char *) crealloc_abort (filebuf
, bufalloc
= 2048);
496 filesize
= format_process_statm (*p
, filebuf
, bufalloc
);
501 filebuf
= (char *) crealloc_abort (filebuf
, bufalloc
= 2048);
502 filesize
= format_process_maps (*p
, filebuf
, bufalloc
);
511 format_process_maps (_pinfo
*p
, char *&destbuf
, size_t maxsize
)
513 HANDLE proc
= OpenProcess (PROCESS_QUERY_INFORMATION
| PROCESS_VM_READ
,
523 DWORD_PTR
*workingset
= NULL
;
527 PWCHAR modname
= tp
.w_get ();
528 char *posix_modname
= tp
.c_get ();
530 if (!EnumProcessModules (proc
, NULL
, 0, &needed
))
536 modules
= (HMODULE
*) alloca (needed
);
537 if (!EnumProcessModules (proc
, modules
, needed
, &needed
))
544 QueryWorkingSet (proc
, (void *) &wset_size
, sizeof wset_size
);
545 if (GetLastError () == ERROR_BAD_LENGTH
)
547 workingset
= (DWORD_PTR
*) alloca (sizeof (DWORD_PTR
) * ++wset_size
);
548 if (!QueryWorkingSet (proc
, (void *) workingset
,
549 sizeof (DWORD_PTR
) * wset_size
))
552 for (i
= 0; i
< needed
/ sizeof (HMODULE
); i
++)
553 if (GetModuleInformation (proc
, modules
[i
], &info
, sizeof info
)
554 && GetModuleFileNameExW (proc
, modules
[i
], modname
, NT_MAX_PATH
))
557 strcpy (access
, "r--p");
559 if (mount_table
->conv_to_posix_path (modname
, posix_modname
, 0))
560 sys_wcstombs (posix_modname
, NT_MAX_PATH
, modname
);
561 if (stat64 (posix_modname
, &st
))
566 if (len
+ strlen (posix_modname
) + 62 > maxsize
- 1)
567 destbuf
= (char *) crealloc_abort (destbuf
, maxsize
+= 2048);
569 for (unsigned i
= 1; i
<= wset_size
; ++i
)
571 DWORD_PTR addr
= workingset
[i
] & 0xfffff000UL
;
572 if ((char *)addr
>= info
.lpBaseOfDll
573 && (char *)addr
< (char *)info
.lpBaseOfDll
+ info
.SizeOfImage
)
575 access
[0] = (workingset
[i
] & 0x5) ? 'r' : '-';
576 access
[1] = (workingset
[i
] & 0x4) ? 'w' : '-';
577 access
[2] = (workingset
[i
] & 0x2) ? 'x' : '-';
578 access
[3] = (workingset
[i
] & 0x100) ? 's' : 'p';
581 int written
= __small_sprintf (destbuf
+ len
,
582 "%08lx-%08lx %s %08lx %04x:%04x %U ",
584 (unsigned long)info
.lpBaseOfDll
592 destbuf
[len
+ written
++] = ' ';
594 len
+= __small_sprintf (destbuf
+ len
, "%s\n", posix_modname
);
602 format_process_stat (_pinfo
*p
, char *destbuf
, size_t maxsize
)
604 char cmd
[NAME_MAX
+ 1];
606 unsigned long fault_count
= 0UL,
607 utime
= 0UL, stime
= 0UL,
609 vmsize
= 0UL, vmrss
= 0UL, vmmaxrss
= 0UL;
611 if (p
->process_state
& PID_EXITED
)
612 strcpy (cmd
, "<defunct>");
615 char *last_slash
= strrchr (p
->progname
, '\\');
616 strcpy (cmd
, last_slash
? last_slash
+ 1 : p
->progname
);
617 int len
= strlen (cmd
);
620 char *s
= cmd
+ len
- 4;
621 if (ascii_strcasematch (s
, ".exe"))
626 * Note: under Windows, a _process_ is always running - it's only _threads_
627 * that get suspended. Therefore the default state is R (runnable).
629 if (p
->process_state
& PID_EXITED
)
631 else if (p
->process_state
& PID_STOPPED
)
634 state
= get_process_state (p
->dwProcessId
);
635 start_time
= (GetTickCount () / 1000 - time (NULL
) + p
->start_time
) * HZ
;
640 KERNEL_USER_TIMES put
;
641 PROCESS_BASIC_INFORMATION pbi
;
643 SYSTEM_TIME_OF_DAY_INFORMATION stodi
;
644 SYSTEM_PROCESSOR_TIMES spt
;
645 hProcess
= OpenProcess (PROCESS_VM_READ
| PROCESS_QUERY_INFORMATION
,
646 FALSE
, p
->dwProcessId
);
647 if (hProcess
!= NULL
)
649 ret
= NtQueryInformationProcess (hProcess
,
653 if (ret
== STATUS_SUCCESS
)
654 ret
= NtQueryInformationProcess (hProcess
,
658 if (ret
== STATUS_SUCCESS
)
659 ret
= NtQueryInformationProcess (hProcess
,
660 ProcessBasicInformation
,
663 if (ret
== STATUS_SUCCESS
)
664 ret
= NtQueryInformationProcess (hProcess
,
668 CloseHandle (hProcess
);
672 DWORD error
= GetLastError ();
673 __seterrno_from_win_error (error
);
674 debug_printf ("OpenProcess: ret %d", error
);
677 if (ret
== STATUS_SUCCESS
)
678 ret
= NtQuerySystemInformation (SystemTimeOfDayInformation
,
681 if (ret
== STATUS_SUCCESS
)
682 ret
= NtQuerySystemInformation (SystemProcessorTimes
,
685 if (ret
!= STATUS_SUCCESS
)
687 __seterrno_from_nt_status (ret
);
688 debug_printf ("NtQueryInformationProcess: ret %d, Dos(ret) %E", ret
);
691 fault_count
= vmc
.PageFaultCount
;
692 utime
= put
.UserTime
.QuadPart
* HZ
/ 10000000ULL;
693 stime
= put
.KernelTime
.QuadPart
* HZ
/ 10000000ULL;
695 if (stodi
.CurrentTime
.QuadPart
> put
.CreateTime
.QuadPart
)
696 start_time
= (spt
.KernelTime
.QuadPart
+ spt
.UserTime
.QuadPart
-
697 stodi
.CurrentTime
.QuadPart
+ put
.CreateTime
.QuadPart
) * HZ
/ 10000000ULL;
700 * sometimes stodi.CurrentTime is a bit behind
701 * Note: some older versions of procps are broken and can't cope
702 * with process start times > time(NULL).
704 start_time
= (spt
.KernelTme
.QuadPart
+ spt
.UserTime
.QuadPart
) * HZ
/ 10000000ULL;
706 priority
= pbi
.BasePriority
;
707 unsigned page_size
= getsystempagesize ();
708 vmsize
= vmc
.PagefileUsage
;
709 vmrss
= vmc
.WorkingSetSize
/ page_size
;
710 vmmaxrss
= ql
.MaximumWorkingSetSize
/ page_size
;
712 return __small_sprintf (destbuf
, "%d (%s) %c "
714 "%lu %lu %lu %lu %lu %lu %lu "
715 "%ld %ld %ld %ld %ld %ld "
721 p
->ppid
, p
->pgid
, p
->sid
, makedev (FH_TTYS
, p
->ctty
),
722 -1, 0, fault_count
, fault_count
, 0, 0, utime
, stime
,
723 utime
, stime
, priority
, 0, 0, 0,
730 format_process_status (_pinfo
*p
, char *destbuf
, size_t maxsize
)
732 char cmd
[NAME_MAX
+ 1];
734 const char *state_str
= "unknown";
735 unsigned long vmsize
= 0UL, vmrss
= 0UL, vmdata
= 0UL, vmlib
= 0UL, vmtext
= 0UL,
737 if (p
->process_state
& PID_EXITED
)
738 strcpy (cmd
, "<defunct>");
741 char *last_slash
= strrchr (p
->progname
, '\\');
742 strcpy (cmd
, last_slash
? last_slash
+ 1 : p
->progname
);
743 int len
= strlen (cmd
);
746 char *s
= cmd
+ len
- 4;
747 if (ascii_strcasematch (s
, ".exe"))
752 * Note: under Windows, a _process_ is always running - it's only _threads_
753 * that get suspended. Therefore the default state is R (runnable).
755 if (p
->process_state
& PID_EXITED
)
757 else if (p
->process_state
& PID_STOPPED
)
760 state
= get_process_state (p
->dwProcessId
);
764 state_str
= "running";
768 state_str
= "sleeping";
771 state_str
= "runnable";
774 state_str
= "zombie";
777 state_str
= "stopped";
780 if (!get_mem_values (p
->dwProcessId
, &vmsize
, &vmrss
, &vmtext
, &vmdata
,
783 unsigned page_size
= getsystempagesize ();
784 vmsize
*= page_size
; vmrss
*= page_size
; vmdata
*= page_size
;
785 vmtext
*= page_size
; vmlib
*= page_size
;
786 // The real uid value for *this* process is stored at cygheap->user.real_uid
787 // but we can't get at the real uid value for any other process, so
788 // just fake it as p->uid. Similar for p->gid.
789 return __small_sprintf (destbuf
, "Name:\t%s\n"
794 "Uid:\t%d %d %d %d\n"
795 "Gid:\t%d %d %d %d\n"
811 p
->uid
, p
->uid
, p
->uid
, p
->uid
,
812 p
->gid
, p
->gid
, p
->gid
, p
->gid
,
813 vmsize
>> 10, 0, vmrss
>> 10, vmdata
>> 10, 0,
814 vmtext
>> 10, vmlib
>> 10,
815 0, 0, _my_tls
.sigmask
820 format_process_statm (_pinfo
*p
, char *destbuf
, size_t maxsize
)
822 unsigned long vmsize
= 0UL, vmrss
= 0UL, vmtext
= 0UL, vmdata
= 0UL,
823 vmlib
= 0UL, vmshare
= 0UL;
824 if (!get_mem_values (p
->dwProcessId
, &vmsize
, &vmrss
, &vmtext
, &vmdata
,
827 return __small_sprintf (destbuf
, "%ld %ld %ld %ld %ld %ld %ld",
828 vmsize
, vmrss
, vmshare
, vmtext
, vmlib
, vmdata
, 0);
832 get_process_state (DWORD dwProcessId
)
835 * This isn't really heavy magic - just go through the processes'
836 * threads one by one and return a value accordingly
837 * Errors are silently ignored.
840 SYSTEM_PROCESSES
*sp
;
842 PULONG p
= new ULONG
[n
];
844 while (STATUS_INFO_LENGTH_MISMATCH
==
845 (ret
= NtQuerySystemInformation (SystemProcessesAndThreadsInformation
,
847 n
* sizeof *p
, NULL
)))
848 delete [] p
, p
= new ULONG
[n
*= 2];
849 if (ret
!= STATUS_SUCCESS
)
851 debug_printf ("NtQuerySystemInformation: ret %d, Dos(ret) %d",
852 ret
, RtlNtStatusToDosError (ret
));
856 sp
= (SYSTEM_PROCESSES
*) p
;
859 if (sp
->ProcessId
== dwProcessId
)
862 if (wincap
.has_process_io_counters ())
864 * Windows 2000 and XP have an extra member in SYSTEM_PROCESSES
865 * which means the offset of the first SYSTEM_THREADS entry is
866 * different on these operating systems compared to NT 4.
868 st
= &sp
->Threads
[0];
871 * 136 is the offset of the first SYSTEM_THREADS entry on
874 st
= (SYSTEM_THREADS
*) ((char *) sp
+ 136);
876 for (unsigned i
= 0; i
< sp
->ThreadCount
; i
++)
878 if (st
->State
== StateRunning
||
879 st
->State
== StateReady
)
888 if (!sp
->NextEntryDelta
)
890 sp
= (SYSTEM_PROCESSES
*) ((char *) sp
+ sp
->NextEntryDelta
);
898 get_mem_values (DWORD dwProcessId
, unsigned long *vmsize
, unsigned long *vmrss
,
899 unsigned long *vmtext
, unsigned long *vmdata
,
900 unsigned long *vmlib
, unsigned long *vmshare
)
906 MEMORY_WORKING_SET_LIST
*mwsl
;
907 ULONG n
= 0x1000, length
;
908 PULONG p
= (PULONG
) malloc (sizeof (ULONG
) * n
);
909 unsigned page_size
= getsystempagesize ();
910 hProcess
= OpenProcess (PROCESS_QUERY_INFORMATION
,
912 if (hProcess
== NULL
)
914 DWORD error
= GetLastError ();
915 __seterrno_from_win_error (error
);
916 debug_printf ("OpenProcess: ret %d", error
);
919 while ((ret
= NtQueryVirtualMemory (hProcess
, 0,
920 MemoryWorkingSetList
,
922 n
* sizeof *p
, &length
)),
923 (ret
== STATUS_SUCCESS
|| ret
== STATUS_INFO_LENGTH_MISMATCH
) &&
924 length
>= (n
* sizeof (*p
)))
925 p
= (PULONG
) realloc (p
, n
*= (2 * sizeof (ULONG
)));
927 if (ret
!= STATUS_SUCCESS
)
929 debug_printf ("NtQueryVirtualMemory: ret %d, Dos(ret) %d",
930 ret
, RtlNtStatusToDosError (ret
));
934 mwsl
= (MEMORY_WORKING_SET_LIST
*) p
;
935 for (unsigned long i
= 0; i
< mwsl
->NumberOfPages
; i
++)
938 unsigned flags
= mwsl
->WorkingSetList
[i
] & 0x0FFF;
939 if (flags
& (WSLE_PAGE_EXECUTE
| WSLE_PAGE_SHAREABLE
) == (WSLE_PAGE_EXECUTE
| WSLE_PAGE_SHAREABLE
))
941 else if (flags
& WSLE_PAGE_SHAREABLE
)
943 else if (flags
& WSLE_PAGE_EXECUTE
)
948 ret
= NtQueryInformationProcess (hProcess
, ProcessVmCounters
, (PVOID
) &vmc
,
950 if (ret
!= STATUS_SUCCESS
)
952 debug_printf ("NtQueryInformationProcess: ret %d, Dos(ret) %d",
953 ret
, RtlNtStatusToDosError (ret
));
957 *vmsize
= vmc
.PagefileUsage
/ page_size
;
960 CloseHandle (hProcess
);