]>
Commit | Line | Data |
---|---|---|
1fd5e000 CF |
1 | /* ps.cc |
2 | ||
df0f949c CF |
3 | Copyright 1996, 1997, 1998, 1999, 2000, 2001, 2002, 2003, 2004, 2005, 2006, |
4 | 2007, 2008, 2009, 2010, 2011, 2012 Red Hat, Inc. | |
1fd5e000 CF |
5 | |
6 | This file is part of Cygwin. | |
7 | ||
8 | This software is a copyrighted work licensed under the terms of the | |
9 | Cygwin license. Please consult the file "CYGWIN_LICENSE" for | |
10 | details. */ | |
11 | ||
92b499ac | 12 | #include <errno.h> |
1fd5e000 | 13 | #include <stdio.h> |
c47ec78d | 14 | #include <locale.h> |
46777e0a | 15 | #include <wchar.h> |
e73a56e9 | 16 | #include <windows.h> |
1fd5e000 CF |
17 | #include <time.h> |
18 | #include <getopt.h> | |
19 | #include <unistd.h> | |
20 | #include <stdlib.h> | |
21 | #include <pwd.h> | |
9408cf38 | 22 | #include <limits.h> |
1fd5e000 | 23 | #include <sys/cygwin.h> |
92b499ac | 24 | #include <cygwin/version.h> |
185ad82d | 25 | #include <psapi.h> |
9cfc9511 CV |
26 | #include <ntdef.h> |
27 | #include <ntdll.h> | |
71d8f118 | 28 | #include "loadlib.h" |
cc631726 | 29 | |
2b2b42cf CV |
30 | /* Maximum possible path length under NT. There's no official define |
31 | for that value. Note that PATH_MAX is only 4K. */ | |
32 | #define NT_MAX_PATH 32768 | |
33 | ||
ad39fa8c CF |
34 | static char *prog_name; |
35 | ||
36 | static struct option longopts[] = | |
37 | { | |
38 | {"all", no_argument, NULL, 'a' }, | |
39 | {"everyone", no_argument, NULL, 'e' }, | |
40 | {"full", no_argument, NULL, 'f' }, | |
41 | {"help", no_argument, NULL, 'h' }, | |
42 | {"long", no_argument, NULL, 'l' }, | |
df4f13b7 | 43 | {"process", required_argument, NULL, 'p'}, |
ad39fa8c CF |
44 | {"summary", no_argument, NULL, 's' }, |
45 | {"user", required_argument, NULL, 'u'}, | |
92b499ac | 46 | {"version", no_argument, NULL, 'V'}, |
ad39fa8c CF |
47 | {"windows", no_argument, NULL, 'W'}, |
48 | {NULL, 0, NULL, 0} | |
49 | }; | |
50 | ||
92b499ac | 51 | static char opts[] = "aefhlp:su:VW"; |
ad39fa8c | 52 | |
1fd5e000 CF |
53 | static char * |
54 | start_time (external_pinfo *child) | |
55 | { | |
56 | time_t st = child->start_time; | |
57 | time_t t = time (NULL); | |
58 | static char stime[40] = {'\0'}; | |
59 | char now[40]; | |
60 | ||
61 | strncpy (stime, ctime (&st) + 4, 15); | |
62 | strcpy (now, ctime (&t) + 4); | |
63 | ||
64 | if ((t - st) < (24 * 3600)) | |
65 | return (stime + 7); | |
66 | ||
67 | stime[6] = '\0'; | |
68 | ||
69 | return stime; | |
70 | } | |
71 | ||
cc631726 CF |
72 | #define FACTOR (0x19db1ded53ea710LL) |
73 | #define NSPERSEC 10000000LL | |
74 | ||
75 | /* Convert a Win32 time to "UNIX" format. */ | |
76 | long __stdcall | |
77 | to_time_t (FILETIME *ptr) | |
78 | { | |
79 | /* A file time is the number of 100ns since jan 1 1601 | |
80 | stuffed into two long words. | |
81 | A time_t is the number of seconds since jan 1 1970. */ | |
82 | ||
83 | long rem; | |
84 | long long x = ((long long) ptr->dwHighDateTime << 32) + ((unsigned)ptr->dwLowDateTime); | |
85 | x -= FACTOR; /* number of 100ns between 1601 and 1970 */ | |
86 | rem = x % ((long long)NSPERSEC); | |
87 | rem += (NSPERSEC / 2); | |
88 | x /= (long long) NSPERSEC; /* number of 100ns in a second */ | |
89 | x += (long long) (rem / NSPERSEC); | |
90 | return x; | |
91 | } | |
92 | ||
64c41c76 CF |
93 | static const char * |
94 | ttynam (int ntty) | |
95 | { | |
a4fddeb9 CF |
96 | static char buf[9]; |
97 | char buf0[9]; | |
64c41c76 | 98 | if (ntty < 0) |
a4fddeb9 CF |
99 | strcpy (buf0, "?"); |
100 | else if (ntty & 0xffff0000) | |
101 | sprintf (buf0, "cons%d", ntty & 0xff); | |
102 | else | |
c47ec78d | 103 | sprintf (buf0, "pty%d", ntty); |
a4fddeb9 | 104 | sprintf (buf, " %-7s", buf0); |
64c41c76 CF |
105 | return buf; |
106 | } | |
107 | ||
ad39fa8c CF |
108 | static void |
109 | usage (FILE * stream, int status) | |
110 | { | |
111 | fprintf (stream, "\ | |
92b499ac CV |
112 | Usage: %1$s [-aefls] [-u UID] [-p PID]\n\ |
113 | \n\ | |
aa275fe0 JDF |
114 | Report process status\n\ |
115 | \n\ | |
ad39fa8c CF |
116 | -a, --all show processes of all users\n\ |
117 | -e, --everyone show processes of all users\n\ | |
118 | -f, --full show process uids, ppids\n\ | |
119 | -h, --help output usage information and exit\n\ | |
120 | -l, --long show process uids, ppids, pgids, winpids\n\ | |
df4f13b7 | 121 | -p, --process show information for specified PID\n\ |
ad39fa8c CF |
122 | -s, --summary show process summary\n\ |
123 | -u, --user list processes owned by UID\n\ | |
92b499ac | 124 | -V, --version output version information and exit\n\ |
ad39fa8c | 125 | -W, --windows show windows as well as cygwin processes\n\ |
92b499ac CV |
126 | \n\ |
127 | With no options, %1$s outputs the long format by default\n\n", | |
128 | prog_name); | |
ad39fa8c CF |
129 | exit (status); |
130 | } | |
131 | ||
132 | static void | |
133 | print_version () | |
134 | { | |
92b499ac | 135 | printf ("ps (cygwin) %d.%d.%d\n" |
1b23b30b CF |
136 | "Show process statistics\n" |
137 | "Copyright (C) 1996 - %s Red Hat, Inc.\n" | |
138 | "This is free software; see the source for copying conditions. There is NO\n" | |
92b499ac | 139 | "warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n", |
1b23b30b CF |
140 | CYGWIN_VERSION_DLL_MAJOR / 1000, |
141 | CYGWIN_VERSION_DLL_MAJOR % 1000, | |
142 | CYGWIN_VERSION_DLL_MINOR, | |
143 | strrchr (__DATE__, ' ') + 1); | |
ad39fa8c CF |
144 | } |
145 | ||
1281d5fc | 146 | char unicode_buf[sizeof (UNICODE_STRING) + NT_MAX_PATH]; |
46777e0a | 147 | |
1fd5e000 CF |
148 | int |
149 | main (int argc, char *argv[]) | |
150 | { | |
151 | external_pinfo *p; | |
61522196 CV |
152 | int aflag, lflag, fflag, sflag, proc_id; |
153 | uid_t uid; | |
d0e99ecc | 154 | bool found_proc_id = true; |
185ad82d | 155 | DWORD proc_access = PROCESS_QUERY_LIMITED_INFORMATION; |
cc631726 | 156 | cygwin_getinfo_types query = CW_GETPINFO; |
a4fddeb9 | 157 | const char *dtitle = " PID TTY STIME COMMAND\n"; |
64c41c76 | 158 | const char *dfmt = "%7d%4s%10s %s\n"; |
a4fddeb9 | 159 | const char *ftitle = " UID PID PPID TTY STIME COMMAND\n"; |
64c41c76 | 160 | const char *ffmt = "%8.8s%8d%8d%4s%10s %s\n"; |
a4fddeb9 | 161 | const char *ltitle = " PID PPID PGID WINPID TTY UID STIME COMMAND\n"; |
64274544 | 162 | const char *lfmt = "%c %7d %7d %7d %10u %4s %4u %8s %s\n"; |
1fd5e000 | 163 | char ch; |
1281d5fc CV |
164 | PUNICODE_STRING uni = (PUNICODE_STRING) unicode_buf; |
165 | void *drive_map = NULL; | |
1fd5e000 | 166 | |
cc631726 | 167 | aflag = lflag = fflag = sflag = 0; |
1fd5e000 | 168 | uid = getuid (); |
df4f13b7 | 169 | proc_id = -1; |
cc631726 | 170 | lflag = 1; |
1fd5e000 | 171 | |
c47ec78d CV |
172 | setlocale (LC_ALL, ""); |
173 | ||
92b499ac | 174 | prog_name = program_invocation_short_name; |
ad39fa8c CF |
175 | |
176 | while ((ch = getopt_long (argc, argv, opts, longopts, NULL)) != EOF) | |
1fd5e000 CF |
177 | switch (ch) |
178 | { | |
179 | case 'a': | |
180 | case 'e': | |
4bfc614b CF |
181 | aflag = 1; |
182 | break; | |
1fd5e000 | 183 | case 'f': |
4bfc614b CF |
184 | fflag = 1; |
185 | break; | |
ad39fa8c | 186 | case 'h': |
4bfc614b | 187 | usage (stdout, 0); |
1fd5e000 | 188 | case 'l': |
4bfc614b CF |
189 | lflag = 1; |
190 | break; | |
df4f13b7 CV |
191 | case 'p': |
192 | proc_id = atoi (optarg); | |
32410d21 | 193 | aflag = 1; |
d0e99ecc | 194 | found_proc_id = false; |
df4f13b7 | 195 | break; |
cc631726 CF |
196 | case 's': |
197 | sflag = 1; | |
198 | break; | |
1fd5e000 | 199 | case 'u': |
4bfc614b CF |
200 | uid = atoi (optarg); |
201 | if (uid == 0) | |
202 | { | |
203 | struct passwd *pw; | |
204 | ||
205 | if ((pw = getpwnam (optarg))) | |
206 | uid = pw->pw_uid; | |
207 | else | |
208 | { | |
209 | fprintf (stderr, "%s: user %s unknown\n", prog_name, optarg); | |
210 | exit (1); | |
211 | } | |
212 | } | |
213 | break; | |
92b499ac | 214 | case 'V': |
4bfc614b CF |
215 | print_version (); |
216 | exit (0); | |
217 | break; | |
cc631726 CF |
218 | case 'W': |
219 | query = CW_GETPINFO_FULL; | |
220 | aflag = 1; | |
221 | break; | |
222 | ||
1fd5e000 | 223 | default: |
92b499ac CV |
224 | fprintf (stderr, "Try `%s --help' for more information.\n", prog_name); |
225 | exit (1); | |
1fd5e000 CF |
226 | } |
227 | ||
cc631726 CF |
228 | if (sflag) |
229 | printf (dtitle); | |
1fd5e000 CF |
230 | else if (fflag) |
231 | printf (ftitle); | |
cc631726 CF |
232 | else if (lflag) |
233 | printf (ltitle); | |
1fd5e000 CF |
234 | |
235 | (void) cygwin_internal (CW_LOCK_PINFO, 1000); | |
236 | ||
46777e0a CV |
237 | if (query == CW_GETPINFO_FULL) |
238 | { | |
239 | /* Enable debug privilege to allow to enumerate all processes, | |
240 | not only processes in current session. */ | |
241 | HANDLE tok; | |
242 | if (OpenProcessToken (GetCurrentProcess (), | |
243 | TOKEN_QUERY | TOKEN_ADJUST_PRIVILEGES, | |
244 | &tok)) | |
245 | { | |
246 | TOKEN_PRIVILEGES priv; | |
247 | ||
248 | priv.PrivilegeCount = 1; | |
249 | if (LookupPrivilegeValue (NULL, SE_DEBUG_NAME, | |
250 | &priv.Privileges[0].Luid)) | |
251 | { | |
252 | priv.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED; | |
253 | AdjustTokenPrivileges (tok, FALSE, &priv, 0, NULL, NULL); | |
254 | } | |
255 | } | |
185ad82d CV |
256 | |
257 | /* Check process query capabilities. */ | |
258 | OSVERSIONINFO version; | |
259 | version.dwOSVersionInfoSize = sizeof version; | |
260 | GetVersionEx (&version); | |
261 | if (version.dwMajorVersion <= 5) /* pre-Vista */ | |
262 | { | |
263 | proc_access = PROCESS_QUERY_INFORMATION; | |
264 | if (version.dwMinorVersion < 1) /* Windows 2000 */ | |
265 | proc_access |= PROCESS_VM_READ; | |
266 | else | |
267 | { | |
268 | } | |
269 | } | |
270 | ||
271 | /* Except on Windows 2000, fetch an opaque drive mapping object from the | |
272 | Cygwin DLL. This is used to map NT device paths to Win32 paths. */ | |
273 | if (!(proc_access & PROCESS_VM_READ)) | |
274 | { | |
275 | drive_map = (void *) cygwin_internal (CW_ALLOC_DRIVE_MAP); | |
276 | /* Check old Cygwin version. */ | |
277 | if (drive_map == (void *) -1) | |
278 | drive_map = NULL; | |
279 | /* Allow fallback to GetModuleFileNameEx for post-W2K. */ | |
280 | if (!drive_map) | |
281 | proc_access = PROCESS_QUERY_INFORMATION | PROCESS_VM_READ; | |
282 | } | |
46777e0a | 283 | } |
cc631726 | 284 | |
1fd5e000 | 285 | for (int pid = 0; |
cc631726 | 286 | (p = (external_pinfo *) cygwin_internal (query, pid | CW_NEXTPID)); |
1fd5e000 CF |
287 | pid = p->pid) |
288 | { | |
df4f13b7 CV |
289 | if ((proc_id > 0) && (p->pid != proc_id)) |
290 | continue; | |
d0e99ecc CF |
291 | else |
292 | found_proc_id = true; | |
df4f13b7 | 293 | |
a35d9f1a CF |
294 | if (aflag) |
295 | /* nothing to do */; | |
296 | else if (p->version >= EXTERNAL_PINFO_VERSION_32_BIT) | |
297 | { | |
61522196 | 298 | if (p->uid32 != uid) |
a35d9f1a CF |
299 | continue; |
300 | } | |
301 | else if (p->uid != uid) | |
302 | continue; | |
1fd5e000 CF |
303 | char status = ' '; |
304 | if (p->process_state & PID_STOPPED) | |
4bfc614b | 305 | status = 'S'; |
1fd5e000 | 306 | else if (p->process_state & PID_TTYIN) |
4bfc614b | 307 | status = 'I'; |
1fd5e000 | 308 | else if (p->process_state & PID_TTYOU) |
4bfc614b | 309 | status = 'O'; |
1fd5e000 | 310 | |
2b2b42cf | 311 | /* Maximum possible path length under NT. There's no official define |
1b23b30b | 312 | for that value. */ |
56a85b9c CV |
313 | char pname[NT_MAX_PATH + sizeof (" <defunct>")]; |
314 | if (p->ppid) | |
cc631726 CF |
315 | { |
316 | char *s; | |
0ad10c0f | 317 | pname[0] = '\0'; |
c47ec78d | 318 | strncat (pname, p->progname_long, NT_MAX_PATH); |
cc631726 CF |
319 | s = strchr (pname, '\0') - 4; |
320 | if (s > pname && strcasecmp (s, ".exe") == 0) | |
321 | *s = '\0'; | |
56a85b9c CV |
322 | if (p->process_state & PID_EXITED || (p->exitcode & ~0xffff)) |
323 | strcat (pname, " <defunct>"); | |
cc631726 | 324 | } |
185ad82d | 325 | else if (query == CW_GETPINFO_FULL) |
cc631726 | 326 | { |
1281d5fc CV |
327 | HANDLE h; |
328 | NTSTATUS status; | |
329 | wchar_t *win32path = NULL; | |
330 | ||
185ad82d | 331 | h = OpenProcess (proc_access, FALSE, p->dwProcessId); |
cc631726 CF |
332 | if (!h) |
333 | continue; | |
185ad82d CV |
334 | /* We use NtQueryInformationProcess in the first place, because |
335 | GetModuleFileNameEx does not work on 64 bit systems when trying | |
336 | to fetch module names of 64 bit processes. */ | |
337 | if (!(proc_access & PROCESS_VM_READ)) /* Windows 2000 */ | |
338 | { | |
339 | status = NtQueryInformationProcess (h, ProcessImageFileName, uni, | |
340 | sizeof unicode_buf, NULL); | |
341 | if (NT_SUCCESS (status)) | |
342 | { | |
343 | /* NtQueryInformationProcess returns a native NT device path. | |
344 | Call CW_MAP_DRIVE_MAP to convert the path to an ordinary | |
345 | Win32 path. The returned pointer is a pointer into the | |
346 | incoming buffer given as third argument. It's expected | |
347 | to be big enough, which we made sure by defining | |
348 | unicode_buf to have enough space for a maximum sized | |
349 | UNICODE_STRING. */ | |
350 | if (uni->Length == 0) /* System process */ | |
9cfc9511 | 351 | win32path = (wchar_t *) L"System"; |
185ad82d CV |
352 | else |
353 | { | |
354 | uni->Buffer[uni->Length / sizeof (WCHAR)] = L'\0'; | |
355 | win32path = (wchar_t *) cygwin_internal (CW_MAP_DRIVE_MAP, | |
356 | drive_map, | |
357 | uni->Buffer); | |
358 | } | |
359 | } | |
360 | } | |
361 | else | |
46777e0a | 362 | { |
185ad82d CV |
363 | if (GetModuleFileNameExW (h, NULL, (PWCHAR) unicode_buf, |
364 | NT_MAX_PATH)) | |
365 | win32path = (wchar_t *) unicode_buf; | |
46777e0a | 366 | } |
1281d5fc CV |
367 | if (win32path) |
368 | wcstombs (pname, win32path, sizeof pname); | |
c47ec78d | 369 | else |
1281d5fc | 370 | strcpy (pname, "*** unknown ***"); |
cc631726 CF |
371 | FILETIME ct, et, kt, ut; |
372 | if (GetProcessTimes (h, &ct, &et, &kt, &ut)) | |
373 | p->start_time = to_time_t (&ct); | |
374 | CloseHandle (h); | |
375 | } | |
1fd5e000 CF |
376 | |
377 | char uname[128]; | |
378 | ||
379 | if (fflag) | |
4bfc614b CF |
380 | { |
381 | struct passwd *pw; | |
382 | ||
383 | if ((pw = getpwuid (p->version >= EXTERNAL_PINFO_VERSION_32_BIT ? | |
384 | p->uid32 : p->uid))) | |
385 | strcpy (uname, pw->pw_name); | |
386 | else | |
387 | sprintf (uname, "%u", (unsigned) | |
388 | (p->version >= EXTERNAL_PINFO_VERSION_32_BIT ? | |
50674f2d | 389 | p->uid32 : p->uid)); |
4bfc614b | 390 | } |
1fd5e000 | 391 | |
cc631726 | 392 | if (sflag) |
4bfc614b | 393 | printf (dfmt, p->pid, ttynam (p->ctty), start_time (p), pname); |
1fd5e000 | 394 | else if (fflag) |
4bfc614b CF |
395 | printf (ffmt, uname, p->pid, p->ppid, ttynam (p->ctty), start_time (p), |
396 | pname); | |
cc631726 | 397 | else if (lflag) |
4bfc614b CF |
398 | printf (lfmt, status, p->pid, p->ppid, p->pgid, |
399 | p->dwProcessId, ttynam (p->ctty), | |
50674f2d CV |
400 | p->version >= EXTERNAL_PINFO_VERSION_32_BIT ? p->uid32 : p->uid, |
401 | start_time (p), pname); | |
1fd5e000 CF |
402 | |
403 | } | |
1281d5fc CV |
404 | if (drive_map) |
405 | cygwin_internal (CW_FREE_DRIVE_MAP, drive_map); | |
1fd5e000 CF |
406 | (void) cygwin_internal (CW_UNLOCK_PINFO); |
407 | ||
d0e99ecc | 408 | return found_proc_id ? 0 : 1; |
1fd5e000 | 409 | } |