444 lines
11 KiB
C++
444 lines
11 KiB
C++
/* ps.cc
|
|
|
|
This file is part of Cygwin.
|
|
|
|
This software is a copyrighted work licensed under the terms of the
|
|
Cygwin license. Please consult the file "CYGWIN_LICENSE" for
|
|
details. */
|
|
|
|
#include <errno.h>
|
|
#include <stdio.h>
|
|
#include <locale.h>
|
|
#include <wchar.h>
|
|
#include <windows.h>
|
|
#include <time.h>
|
|
#include <getopt.h>
|
|
#include <unistd.h>
|
|
#include <stdlib.h>
|
|
#include <pwd.h>
|
|
#include <limits.h>
|
|
#include <sys/cygwin.h>
|
|
#include <sys/ioctl.h>
|
|
#include <cygwin/version.h>
|
|
#include <ntdef.h>
|
|
#include <ntdll.h>
|
|
|
|
/* Maximum possible path length under NT. There's no official define
|
|
for that value. Note that PATH_MAX is only 4K. */
|
|
#define NT_MAX_PATH 32767
|
|
|
|
#define OUTPUT_BUFSIZ 65536
|
|
|
|
static char *prog_name;
|
|
|
|
static struct option longopts[] =
|
|
{
|
|
{"all", no_argument, NULL, 'a' },
|
|
{"everyone", no_argument, NULL, 'e' },
|
|
{"full", no_argument, NULL, 'f' },
|
|
{"help", no_argument, NULL, 'h' },
|
|
{"long", no_argument, NULL, 'l' },
|
|
{"process", required_argument, NULL, 'p'},
|
|
{"summary", no_argument, NULL, 's' },
|
|
{"user", required_argument, NULL, 'u'},
|
|
{"version", no_argument, NULL, 'V'},
|
|
{"windows", no_argument, NULL, 'W'},
|
|
{NULL, 0, NULL, 0}
|
|
};
|
|
|
|
static char opts[] = "aefhlp:su:VW";
|
|
|
|
static char *
|
|
start_time (external_pinfo *child)
|
|
{
|
|
time_t st = child->start_time;
|
|
time_t t = time (NULL);
|
|
static char stime[40] = {'\0'};
|
|
char now[40];
|
|
|
|
strncpy (stime, ctime (&st) + 4, 15);
|
|
strcpy (now, ctime (&t) + 4);
|
|
|
|
if ((t - st) < (24 * 3600))
|
|
return (stime + 7);
|
|
|
|
stime[6] = '\0';
|
|
|
|
return stime;
|
|
}
|
|
|
|
#define FACTOR (0x19db1ded53ea710LL)
|
|
#define NSPERSEC 10000000LL
|
|
|
|
/* Convert a Win32 time to "UNIX" format. */
|
|
long
|
|
to_time_t (FILETIME *ptr)
|
|
{
|
|
/* A file time is the number of 100ns since jan 1 1601
|
|
stuffed into two long words.
|
|
A time_t is the number of seconds since jan 1 1970. */
|
|
|
|
long rem;
|
|
long long x = ((long long) ptr->dwHighDateTime << 32) + ((unsigned)ptr->dwLowDateTime);
|
|
x -= FACTOR; /* number of 100ns between 1601 and 1970 */
|
|
rem = x % ((long long)NSPERSEC);
|
|
rem += (NSPERSEC / 2);
|
|
x /= (long long) NSPERSEC; /* number of 100ns in a second */
|
|
x += (long long) (rem / NSPERSEC);
|
|
return x;
|
|
}
|
|
|
|
static const char *
|
|
ttynam (int ntty, char buf[9])
|
|
{
|
|
char buf0[9];
|
|
|
|
if (ntty < 0)
|
|
strcpy (buf0, "?");
|
|
else if (ntty & 0xffff0000)
|
|
snprintf (buf0, 9, "cons%d", ntty & 0xff);
|
|
else
|
|
snprintf (buf0, 9, "pty%d", ntty);
|
|
snprintf (buf, 9, " %-7.7s", buf0);
|
|
return buf;
|
|
}
|
|
|
|
static void __attribute__ ((__noreturn__))
|
|
usage (FILE * stream, int status)
|
|
{
|
|
fprintf (stream, "\
|
|
Usage: %1$s [-aefls] [-u UID] [-p PID]\n\
|
|
\n\
|
|
Report process status\n\
|
|
\n\
|
|
-a, --all show processes of all users\n\
|
|
-e, --everyone show processes of all users\n\
|
|
-f, --full show process uids, ppids and command line\n\
|
|
-h, --help output usage information and exit\n\
|
|
-l, --long show process uids, ppids, pgids, winpids\n\
|
|
-p, --process show information for specified PID\n\
|
|
-s, --summary show process summary\n\
|
|
-u, --user list processes owned by UID\n\
|
|
-V, --version output version information and exit\n\
|
|
-W, --windows show windows as well as cygwin processes\n\
|
|
\n\
|
|
With no options, %1$s outputs the long format by default\n\n",
|
|
prog_name);
|
|
exit (status);
|
|
}
|
|
|
|
static void
|
|
print_version ()
|
|
{
|
|
printf ("ps (cygwin) %d.%d.%d\n"
|
|
"Show process statistics\n"
|
|
"Copyright (C) 1996 - %s Cygwin Authors\n"
|
|
"This is free software; see the source for copying conditions. There is NO\n"
|
|
"warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n",
|
|
CYGWIN_VERSION_DLL_MAJOR / 1000,
|
|
CYGWIN_VERSION_DLL_MAJOR % 1000,
|
|
CYGWIN_VERSION_DLL_MINOR,
|
|
strrchr (__DATE__, ' ') + 1);
|
|
}
|
|
|
|
struct
|
|
{
|
|
SYSTEM_PROCESS_ID_INFORMATION spii;
|
|
WCHAR buf[NT_MAX_PATH + 1];
|
|
} ucbuf;
|
|
|
|
char pname[NT_MAX_PATH + sizeof (" <defunct>") + 1];
|
|
|
|
char output_buffer[OUTPUT_BUFSIZ];
|
|
|
|
void
|
|
ps_print (const char *string, int width)
|
|
{
|
|
printf ("%.*s\n", width, string);
|
|
}
|
|
|
|
int
|
|
main (int argc, char *argv[])
|
|
{
|
|
external_pinfo *p;
|
|
int aflag, lflag, fflag, sflag, proc_id, width, col;
|
|
uid_t uid;
|
|
bool found_proc_id = true;
|
|
cygwin_getinfo_types query = CW_GETPINFO;
|
|
const char *stitle = " PID TTY STIME COMMAND";
|
|
const char *sfmt = "%7d%4s%10s %s";
|
|
const char *ftitle = " UID PID PPID TTY STIME COMMAND";
|
|
const char *ffmt = "%8.8s%8d%8d%4s%10s %s";
|
|
const char *ltitle = " PID PPID PGID WINPID TTY UID STIME COMMAND";
|
|
const char *lfmt = "%c %7d %7d %7d %10u %4s %8u %8s %s";
|
|
char ch;
|
|
void *drive_map = NULL;
|
|
time_t boot_time = -1;
|
|
char *columns, *end;
|
|
struct winsize ws;
|
|
|
|
|
|
aflag = lflag = fflag = sflag = 0;
|
|
uid = getuid ();
|
|
proc_id = -1;
|
|
lflag = 1;
|
|
|
|
setlocale (LC_ALL, "");
|
|
|
|
prog_name = program_invocation_short_name;
|
|
|
|
while ((ch = getopt_long (argc, argv, opts, longopts, NULL)) != EOF)
|
|
switch (ch)
|
|
{
|
|
case 'a':
|
|
case 'e':
|
|
aflag = 1;
|
|
break;
|
|
case 'f':
|
|
fflag = 1;
|
|
lflag = 0;
|
|
sflag = 0;
|
|
break;
|
|
case 'h':
|
|
usage (stdout, 0);
|
|
case 'l':
|
|
fflag = 0;
|
|
lflag = 1;
|
|
sflag = 0;
|
|
break;
|
|
case 'p':
|
|
proc_id = atoi (optarg);
|
|
aflag = 1;
|
|
found_proc_id = false;
|
|
break;
|
|
case 's':
|
|
fflag = 0;
|
|
lflag = 0;
|
|
sflag = 1;
|
|
break;
|
|
case 'u':
|
|
uid = atoi (optarg);
|
|
if (uid == 0)
|
|
{
|
|
struct passwd *pw;
|
|
|
|
if ((pw = getpwnam (optarg)))
|
|
uid = pw->pw_uid;
|
|
else
|
|
{
|
|
fprintf (stderr, "%s: user %s unknown\n", prog_name, optarg);
|
|
exit (1);
|
|
}
|
|
}
|
|
break;
|
|
case 'V':
|
|
print_version ();
|
|
exit (0);
|
|
break;
|
|
case 'W':
|
|
query = CW_GETPINFO_FULL;
|
|
aflag = 1;
|
|
break;
|
|
|
|
default:
|
|
fprintf (stderr, "Try `%s --help' for more information.\n", prog_name);
|
|
exit (1);
|
|
}
|
|
|
|
(void) cygwin_internal (CW_LOCK_PINFO, 1000);
|
|
|
|
if (query == CW_GETPINFO_FULL)
|
|
{
|
|
HANDLE tok;
|
|
NTSTATUS status;
|
|
SYSTEM_TIMEOFDAY_INFORMATION stodi;
|
|
|
|
/* Enable debug privilege to allow to enumerate all processes,
|
|
not only processes in current session. */
|
|
if (OpenProcessToken (GetCurrentProcess (),
|
|
TOKEN_QUERY | TOKEN_ADJUST_PRIVILEGES,
|
|
&tok))
|
|
{
|
|
TOKEN_PRIVILEGES priv;
|
|
|
|
priv.PrivilegeCount = 1;
|
|
if (LookupPrivilegeValue (NULL, SE_DEBUG_NAME,
|
|
&priv.Privileges[0].Luid))
|
|
{
|
|
priv.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED;
|
|
AdjustTokenPrivileges (tok, FALSE, &priv, 0, NULL, NULL);
|
|
}
|
|
}
|
|
|
|
drive_map = (void *) cygwin_internal (CW_ALLOC_DRIVE_MAP);
|
|
|
|
/* Get system boot time to default process start time */
|
|
status = NtQuerySystemInformation (SystemTimeOfDayInformation,
|
|
(PVOID) &stodi, sizeof stodi, NULL);
|
|
if (!NT_SUCCESS (status))
|
|
fprintf (stderr,
|
|
"NtQuerySystemInformation(SystemTimeOfDayInformation), "
|
|
"status %#010x\n", (unsigned int) status);
|
|
boot_time = to_time_t ((FILETIME*)&stodi.BootTime);
|
|
}
|
|
|
|
width = OUTPUT_BUFSIZ;
|
|
if ((columns = getenv ("COLUMNS")) && *columns
|
|
&& (col = strtoul (columns, &end, 0)) > 0 && !*end)
|
|
width = col;
|
|
else if (isatty (STDOUT_FILENO))
|
|
{
|
|
width = 80;
|
|
if (ioctl (STDOUT_FILENO, TIOCGWINSZ, &ws) != -1)
|
|
width = ws.ws_col;
|
|
}
|
|
if (width > OUTPUT_BUFSIZ)
|
|
width = OUTPUT_BUFSIZ;
|
|
|
|
if (sflag)
|
|
ps_print (stitle, width);
|
|
else if (fflag)
|
|
ps_print (ftitle, width);
|
|
else if (lflag)
|
|
ps_print (ltitle, width);
|
|
|
|
for (int pid = 0;
|
|
(p = (external_pinfo *) cygwin_internal (query, pid | CW_NEXTPID));
|
|
pid = p->pid)
|
|
{
|
|
if ((proc_id > 0) && (p->pid != proc_id))
|
|
continue;
|
|
else
|
|
found_proc_id = true;
|
|
|
|
if (aflag)
|
|
/* nothing to do */;
|
|
else if (p->version >= EXTERNAL_PINFO_VERSION_32_BIT)
|
|
{
|
|
if (p->uid32 != uid)
|
|
continue;
|
|
}
|
|
else if (p->uid != uid)
|
|
continue;
|
|
char status = ' ';
|
|
if (p->process_state & PID_STOPPED)
|
|
status = 'S';
|
|
else if (p->process_state & PID_TTYIN)
|
|
status = 'I';
|
|
else if (p->process_state & PID_TTYOU)
|
|
status = 'O';
|
|
|
|
if (p->ppid)
|
|
{
|
|
char *s;
|
|
pname[0] = '\0';
|
|
strncat (pname, p->progname_long, NT_MAX_PATH);
|
|
s = strchr (pname, '\0') - 4;
|
|
if (s > pname && strcasecmp (s, ".exe") == 0)
|
|
*s = '\0';
|
|
if (p->process_state & PID_EXITED || (p->exitcode & ~0xffff))
|
|
strcat (pname, " <defunct>");
|
|
}
|
|
else if (query == CW_GETPINFO_FULL)
|
|
{
|
|
HANDLE h;
|
|
NTSTATUS status;
|
|
wchar_t *win32path = NULL;
|
|
FILETIME ct, et, kt, ut;
|
|
|
|
ucbuf.spii.ProcessId = (PVOID) (ULONG_PTR) p->dwProcessId;
|
|
ucbuf.spii.ImageName.Length = 0;
|
|
ucbuf.spii.ImageName.MaximumLength = NT_MAX_PATH * sizeof (WCHAR);
|
|
ucbuf.spii.ImageName.Buffer = ucbuf.buf;
|
|
status = NtQuerySystemInformation (SystemProcessIdInformation,
|
|
&ucbuf.spii, sizeof ucbuf.spii,
|
|
NULL);
|
|
if (NT_SUCCESS (status))
|
|
{
|
|
if (ucbuf.spii.ImageName.Length)
|
|
ucbuf.spii.ImageName.Buffer[ucbuf.spii.ImageName.Length
|
|
/ sizeof (WCHAR)] = L'\0';
|
|
win32path = ucbuf.spii.ImageName.Buffer;
|
|
}
|
|
if (win32path)
|
|
{
|
|
/* Call CW_MAP_DRIVE_MAP to convert native NT device paths to
|
|
an ordinary Win32 path. The returned pointer points into
|
|
the incoming buffer given as third argument. */
|
|
if (win32path[0] == L'\\')
|
|
win32path = (wchar_t *) cygwin_internal (CW_MAP_DRIVE_MAP,
|
|
drive_map, win32path);
|
|
wcstombs (pname, win32path, sizeof pname);
|
|
}
|
|
else
|
|
strcpy (pname, p->dwProcessId == 4 ? "System" : "*** unknown ***");
|
|
|
|
h = OpenProcess (PROCESS_QUERY_LIMITED_INFORMATION, FALSE,
|
|
p->dwProcessId);
|
|
if (h)
|
|
{
|
|
if (GetProcessTimes (h, &ct, &et, &kt, &ut))
|
|
p->start_time = to_time_t (&ct);
|
|
CloseHandle (h);
|
|
}
|
|
/* Default to boot time when process start time inaccessible, 0, -1 */
|
|
if (!h || 0 == p->start_time || -1 == p->start_time)
|
|
{
|
|
p->start_time = boot_time;
|
|
}
|
|
}
|
|
|
|
char uname[128];
|
|
char ttyname[9];
|
|
char *cmdline = NULL;
|
|
|
|
if (fflag)
|
|
{
|
|
struct passwd *pw;
|
|
|
|
if ((pw = getpwuid (p->version >= EXTERNAL_PINFO_VERSION_32_BIT ?
|
|
p->uid32 : p->uid)))
|
|
strcpy (uname, pw->pw_name);
|
|
else
|
|
sprintf (uname, "%u", (unsigned)
|
|
(p->version >= EXTERNAL_PINFO_VERSION_32_BIT ?
|
|
p->uid32 : p->uid));
|
|
|
|
cmdline = (char *) cygwin_internal (CW_CMDLINE_ALLOC, p->pid);
|
|
if (cmdline) /* Replace \0 with spaces */
|
|
{
|
|
char *p = cmdline;
|
|
while (p && *p)
|
|
if ((p = strchr (p, '\0')))
|
|
*p++ = ' ';
|
|
}
|
|
}
|
|
|
|
if (sflag)
|
|
{
|
|
snprintf (output_buffer, sizeof output_buffer, sfmt,
|
|
p->pid, ttynam (p->ctty, ttyname), start_time (p), pname);
|
|
}
|
|
else if (fflag)
|
|
{
|
|
snprintf (output_buffer, sizeof output_buffer, ffmt,
|
|
uname, p->pid, p->ppid, ttynam (p->ctty, ttyname),
|
|
start_time (p), cmdline ?: pname);
|
|
free (cmdline);
|
|
}
|
|
else if (lflag)
|
|
snprintf (output_buffer, sizeof output_buffer, lfmt,
|
|
status, p->pid, p->ppid, p->pgid,
|
|
p->dwProcessId, ttynam (p->ctty, ttyname),
|
|
p->version >= EXTERNAL_PINFO_VERSION_32_BIT
|
|
? p->uid32 : p->uid,
|
|
start_time (p), pname);
|
|
ps_print (output_buffer, width);
|
|
}
|
|
if (drive_map)
|
|
cygwin_internal (CW_FREE_DRIVE_MAP, drive_map);
|
|
(void) cygwin_internal (CW_UNLOCK_PINFO);
|
|
|
|
return found_proc_id ? 0 : 1;
|
|
}
|