]> sourceware.org Git - newlib-cygwin.git/blob - winsup/utils/ps.cc
b51608317c18e32d415b8069c49f4e14572c5356
[newlib-cygwin.git] / winsup / utils / ps.cc
1 /* ps.cc
2
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
9 #include <errno.h>
10 #include <stdio.h>
11 #include <locale.h>
12 #include <wchar.h>
13 #include <windows.h>
14 #include <time.h>
15 #include <getopt.h>
16 #include <unistd.h>
17 #include <stdlib.h>
18 #include <pwd.h>
19 #include <limits.h>
20 #include <sys/cygwin.h>
21 #include <sys/ioctl.h>
22 #include <cygwin/version.h>
23 #include <ntdef.h>
24 #include <ntdll.h>
25
26 /* Maximum possible path length under NT. There's no official define
27 for that value. Note that PATH_MAX is only 4K. */
28 #define NT_MAX_PATH 32767
29
30 #define OUTPUT_BUFSIZ 65536
31
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' },
41 {"process", required_argument, NULL, 'p'},
42 {"summary", no_argument, NULL, 's' },
43 {"user", required_argument, NULL, 'u'},
44 {"version", no_argument, NULL, 'V'},
45 {"windows", no_argument, NULL, 'W'},
46 {NULL, 0, NULL, 0}
47 };
48
49 static char opts[] = "aefhlp:su:VW";
50
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
70 #define FACTOR (0x19db1ded53ea710LL)
71 #define NSPERSEC 10000000LL
72
73 /* Convert a Win32 time to "UNIX" format. */
74 long
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
91 static const char *
92 ttynam (int ntty, char buf[9])
93 {
94 char buf0[9];
95
96 if (ntty < 0)
97 strcpy (buf0, "?");
98 else if (ntty & 0xffff0000)
99 snprintf (buf0, 9, "cons%d", ntty & 0xff);
100 else
101 snprintf (buf0, 9, "pty%d", ntty);
102 snprintf (buf, 9, " %-7.7s", buf0);
103 return buf;
104 }
105
106 static void __attribute__ ((__noreturn__))
107 usage (FILE * stream, int status)
108 {
109 fprintf (stream, "\
110 Usage: %1$s [-aefls] [-u UID] [-p PID]\n\
111 \n\
112 Report process status\n\
113 \n\
114 -a, --all show processes of all users\n\
115 -e, --everyone show processes of all users\n\
116 -f, --full show process uids, ppids and command line\n\
117 -h, --help output usage information and exit\n\
118 -l, --long show process uids, ppids, pgids, winpids\n\
119 -p, --process show information for specified PID\n\
120 -s, --summary show process summary\n\
121 -u, --user list processes owned by UID\n\
122 -V, --version output version information and exit\n\
123 -W, --windows show windows as well as cygwin processes\n\
124 \n\
125 With no options, %1$s outputs the long format by default\n\n",
126 prog_name);
127 exit (status);
128 }
129
130 static void
131 print_version ()
132 {
133 printf ("ps (cygwin) %d.%d.%d\n"
134 "Show process statistics\n"
135 "Copyright (C) 1996 - %s Cygwin Authors\n"
136 "This is free software; see the source for copying conditions. There is NO\n"
137 "warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n",
138 CYGWIN_VERSION_DLL_MAJOR / 1000,
139 CYGWIN_VERSION_DLL_MAJOR % 1000,
140 CYGWIN_VERSION_DLL_MINOR,
141 strrchr (__DATE__, ' ') + 1);
142 }
143
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];
151
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
160 int
161 main (int argc, char *argv[])
162 {
163 external_pinfo *p;
164 int aflag, lflag, fflag, sflag, proc_id, width, col;
165 uid_t uid;
166 bool found_proc_id = true;
167 cygwin_getinfo_types query = CW_GETPINFO;
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";
174 char ch;
175 void *drive_map = NULL;
176 time_t boot_time = -1;
177 char *columns, *end;
178 struct winsize ws;
179
180
181 aflag = lflag = fflag = sflag = 0;
182 uid = getuid ();
183 proc_id = -1;
184 lflag = 1;
185
186 setlocale (LC_ALL, "");
187
188 prog_name = program_invocation_short_name;
189
190 while ((ch = getopt_long (argc, argv, opts, longopts, NULL)) != EOF)
191 switch (ch)
192 {
193 case 'a':
194 case 'e':
195 aflag = 1;
196 break;
197 case 'f':
198 fflag = 1;
199 lflag = 0;
200 sflag = 0;
201 break;
202 case 'h':
203 usage (stdout, 0);
204 case 'l':
205 fflag = 0;
206 lflag = 1;
207 sflag = 0;
208 break;
209 case 'p':
210 proc_id = atoi (optarg);
211 aflag = 1;
212 found_proc_id = false;
213 break;
214 case 's':
215 fflag = 0;
216 lflag = 0;
217 sflag = 1;
218 break;
219 case 'u':
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;
234 case 'V':
235 print_version ();
236 exit (0);
237 break;
238 case 'W':
239 query = CW_GETPINFO_FULL;
240 aflag = 1;
241 break;
242
243 default:
244 fprintf (stderr, "Try `%s --help' for more information.\n", prog_name);
245 exit (1);
246 }
247
248 (void) cygwin_internal (CW_LOCK_PINFO, 1000);
249
250 if (query == CW_GETPINFO_FULL)
251 {
252 HANDLE tok;
253 NTSTATUS status;
254 SYSTEM_TIMEOFDAY_INFORMATION stodi;
255
256 /* Enable debug privilege to allow to enumerate all processes,
257 not only processes in current session. */
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 }
272
273 drive_map = (void *) cygwin_internal (CW_ALLOC_DRIVE_MAP);
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,
280 "NtQuerySystemInformation(SystemTimeOfDayInformation), "
281 "status %#010x\n", (unsigned int) status);
282 boot_time = to_time_t ((FILETIME*)&stodi.BootTime);
283 }
284
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
305 for (int pid = 0;
306 (p = (external_pinfo *) cygwin_internal (query, pid | CW_NEXTPID));
307 pid = p->pid)
308 {
309 if ((proc_id > 0) && (p->pid != proc_id))
310 continue;
311 else
312 found_proc_id = true;
313
314 if (aflag)
315 /* nothing to do */;
316 else if (p->version >= EXTERNAL_PINFO_VERSION_32_BIT)
317 {
318 if (p->uid32 != uid)
319 continue;
320 }
321 else if (p->uid != uid)
322 continue;
323 char status = ' ';
324 if (p->process_state & PID_STOPPED)
325 status = 'S';
326 else if (p->process_state & PID_TTYIN)
327 status = 'I';
328 else if (p->process_state & PID_TTYOU)
329 status = 'O';
330
331 if (p->ppid)
332 {
333 char *s;
334 pname[0] = '\0';
335 strncat (pname, p->progname_long, NT_MAX_PATH);
336 s = strchr (pname, '\0') - 4;
337 if (s > pname && strcasecmp (s, ".exe") == 0)
338 *s = '\0';
339 if (p->process_state & PID_EXITED || (p->exitcode & ~0xffff))
340 strcat (pname, " <defunct>");
341 }
342 else if (query == CW_GETPINFO_FULL)
343 {
344 HANDLE h;
345 NTSTATUS status;
346 wchar_t *win32path = NULL;
347 FILETIME ct, et, kt, ut;
348
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))
357 {
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;
362 }
363 if (win32path)
364 {
365 /* Call CW_MAP_DRIVE_MAP to convert native NT device paths to
366 an ordinary Win32 path. The returned pointer points into
367 the incoming buffer given as third argument. */
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 }
373 else
374 strcpy (pname, p->dwProcessId == 4 ? "System" : "*** unknown ***");
375
376 h = OpenProcess (PROCESS_QUERY_LIMITED_INFORMATION, FALSE,
377 p->dwProcessId);
378 if (h)
379 {
380 if (GetProcessTimes (h, &ct, &et, &kt, &ut))
381 p->start_time = to_time_t (&ct);
382 CloseHandle (h);
383 }
384 /* Default to boot time when process start time inaccessible, 0, -1 */
385 if (!h || 0 == p->start_time || -1 == p->start_time)
386 {
387 p->start_time = boot_time;
388 }
389 }
390
391 char uname[128];
392 char ttyname[9];
393 char *cmdline = NULL;
394
395 if (fflag)
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 ?
405 p->uid32 : p->uid));
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 }
415 }
416
417 if (sflag)
418 {
419 snprintf (output_buffer, sizeof output_buffer, sfmt,
420 p->pid, ttynam (p->ctty, ttyname), start_time (p), pname);
421 }
422 else if (fflag)
423 {
424 snprintf (output_buffer, sizeof output_buffer, ffmt,
425 uname, p->pid, p->ppid, ttynam (p->ctty, ttyname),
426 start_time (p), cmdline ?: pname);
427 free (cmdline);
428 }
429 else if (lflag)
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);
437 }
438 if (drive_map)
439 cygwin_internal (CW_FREE_DRIVE_MAP, drive_map);
440 (void) cygwin_internal (CW_UNLOCK_PINFO);
441
442 return found_proc_id ? 0 : 1;
443 }
This page took 0.057544 seconds and 6 git commands to generate.