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