385 lines
		
	
	
		
			9.9 KiB
		
	
	
	
		
			C++
		
	
	
	
			
		
		
	
	
			385 lines
		
	
	
		
			9.9 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 <cygwin/version.h>
 | 
						|
#include <psapi.h>
 | 
						|
#include <ntdef.h>
 | 
						|
#include <ntdll.h>
 | 
						|
#include "loadlib.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 32768
 | 
						|
 | 
						|
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 __stdcall
 | 
						|
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)
 | 
						|
{
 | 
						|
  static char buf[9];
 | 
						|
  char buf0[9];
 | 
						|
  if (ntty < 0)
 | 
						|
    strcpy (buf0, "?");
 | 
						|
  else if (ntty & 0xffff0000)
 | 
						|
    sprintf (buf0, "cons%d", ntty & 0xff);
 | 
						|
  else
 | 
						|
    sprintf (buf0, "pty%d", ntty);
 | 
						|
  sprintf (buf, " %-7s", buf0);
 | 
						|
  return buf;
 | 
						|
}
 | 
						|
 | 
						|
static void
 | 
						|
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\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);
 | 
						|
}
 | 
						|
 | 
						|
char unicode_buf[sizeof (UNICODE_STRING) + NT_MAX_PATH];
 | 
						|
 | 
						|
int
 | 
						|
main (int argc, char *argv[])
 | 
						|
{
 | 
						|
  external_pinfo *p;
 | 
						|
  int aflag, lflag, fflag, sflag, proc_id;
 | 
						|
  uid_t uid;
 | 
						|
  bool found_proc_id = true;
 | 
						|
  DWORD proc_access = PROCESS_QUERY_LIMITED_INFORMATION;
 | 
						|
  cygwin_getinfo_types query = CW_GETPINFO;
 | 
						|
  const char *dtitle = "    PID  TTY        STIME COMMAND\n";
 | 
						|
  const char *dfmt   = "%7d%4s%10s %s\n";
 | 
						|
  const char *ftitle = "     UID     PID    PPID  TTY        STIME COMMAND\n";
 | 
						|
  const char *ffmt   = "%8.8s%8d%8d%4s%10s %s\n";
 | 
						|
  const char *ltitle = "      PID    PPID    PGID     WINPID   TTY         UID    STIME COMMAND\n";
 | 
						|
  const char *lfmt   = "%c %7d %7d %7d %10u %4s %8u %8s %s\n";
 | 
						|
  char ch;
 | 
						|
  PUNICODE_STRING uni = (PUNICODE_STRING) unicode_buf;
 | 
						|
  void *drive_map = NULL;
 | 
						|
 | 
						|
  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;
 | 
						|
	break;
 | 
						|
      case 'h':
 | 
						|
	usage (stdout, 0);
 | 
						|
      case 'l':
 | 
						|
	lflag = 1;
 | 
						|
	break;
 | 
						|
      case 'p':
 | 
						|
	proc_id = atoi (optarg);
 | 
						|
	aflag = 1;
 | 
						|
	found_proc_id = false;
 | 
						|
	break;
 | 
						|
      case 's':
 | 
						|
	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);
 | 
						|
      }
 | 
						|
 | 
						|
  if (sflag)
 | 
						|
    printf (dtitle);
 | 
						|
  else if (fflag)
 | 
						|
    printf (ftitle);
 | 
						|
  else if (lflag)
 | 
						|
    printf (ltitle);
 | 
						|
 | 
						|
  (void) cygwin_internal (CW_LOCK_PINFO, 1000);
 | 
						|
 | 
						|
  if (query == CW_GETPINFO_FULL)
 | 
						|
    {
 | 
						|
      /* Enable debug privilege to allow to enumerate all processes,
 | 
						|
	 not only processes in current session. */
 | 
						|
      HANDLE tok;
 | 
						|
      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);
 | 
						|
      /* Check old Cygwin version. */
 | 
						|
      if (drive_map == (void *) -1)
 | 
						|
	drive_map = NULL;
 | 
						|
      /* Allow fallback to GetModuleFileNameEx. */
 | 
						|
      if (!drive_map)
 | 
						|
	proc_access = PROCESS_QUERY_INFORMATION | PROCESS_VM_READ;
 | 
						|
    }
 | 
						|
 | 
						|
  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';
 | 
						|
 | 
						|
      /* Maximum possible path length under NT.  There's no official define
 | 
						|
	 for that value. */
 | 
						|
      char pname[NT_MAX_PATH + sizeof (" <defunct>")];
 | 
						|
      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;
 | 
						|
 | 
						|
	  h = OpenProcess (proc_access, FALSE, p->dwProcessId);
 | 
						|
	  if (!h)
 | 
						|
	    continue;
 | 
						|
	  /* We use NtQueryInformationProcess in the first place, because
 | 
						|
	     GetModuleFileNameEx does not work under WOW64 when trying
 | 
						|
	     to fetch module names of 64 bit processes. */
 | 
						|
	  if (!(proc_access & PROCESS_VM_READ))
 | 
						|
	    {
 | 
						|
	      status = NtQueryInformationProcess (h, ProcessImageFileName, uni,
 | 
						|
						  sizeof unicode_buf, NULL);
 | 
						|
	      if (NT_SUCCESS (status))
 | 
						|
		{
 | 
						|
		  /* NtQueryInformationProcess returns a native NT device path.
 | 
						|
		     Call CW_MAP_DRIVE_MAP to convert the path to an ordinary
 | 
						|
		     Win32 path.  The returned pointer is a pointer into the
 | 
						|
		     incoming buffer given as third argument.  It's expected
 | 
						|
		     to be big enough, which we made sure by defining
 | 
						|
		     unicode_buf to have enough space for a maximum sized
 | 
						|
		     UNICODE_STRING. */
 | 
						|
		  if (uni->Length == 0)	/* System process */
 | 
						|
		    win32path = (wchar_t *) L"System";
 | 
						|
		  else
 | 
						|
		    {
 | 
						|
		      uni->Buffer[uni->Length / sizeof (WCHAR)] = L'\0';
 | 
						|
		      win32path = (wchar_t *) cygwin_internal (CW_MAP_DRIVE_MAP,
 | 
						|
							       drive_map,
 | 
						|
							       uni->Buffer);
 | 
						|
		    }
 | 
						|
		}
 | 
						|
	    }
 | 
						|
	  else if (GetModuleFileNameExW (h, NULL, (PWCHAR) unicode_buf,
 | 
						|
					 NT_MAX_PATH))
 | 
						|
	    win32path = (wchar_t *) unicode_buf;
 | 
						|
	  if (win32path)
 | 
						|
	    wcstombs (pname, win32path, sizeof pname);
 | 
						|
	  else
 | 
						|
	    strcpy (pname, "*** unknown ***");
 | 
						|
	  FILETIME ct, et, kt, ut;
 | 
						|
	  if (GetProcessTimes (h, &ct, &et, &kt, &ut))
 | 
						|
	    p->start_time = to_time_t (&ct);
 | 
						|
	  CloseHandle (h);
 | 
						|
	}
 | 
						|
 | 
						|
      char uname[128];
 | 
						|
 | 
						|
      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));
 | 
						|
	}
 | 
						|
 | 
						|
      if (sflag)
 | 
						|
	printf (dfmt, p->pid, ttynam (p->ctty), start_time (p), pname);
 | 
						|
      else if (fflag)
 | 
						|
	printf (ffmt, uname, p->pid, p->ppid, ttynam (p->ctty), start_time (p),
 | 
						|
		pname);
 | 
						|
      else if (lflag)
 | 
						|
	printf (lfmt, status, p->pid, p->ppid, p->pgid,
 | 
						|
		p->dwProcessId, ttynam (p->ctty),
 | 
						|
		p->version >= EXTERNAL_PINFO_VERSION_32_BIT ? p->uid32 : p->uid,
 | 
						|
		start_time (p), pname);
 | 
						|
 | 
						|
    }
 | 
						|
  if (drive_map)
 | 
						|
    cygwin_internal (CW_FREE_DRIVE_MAP, drive_map);
 | 
						|
  (void) cygwin_internal (CW_UNLOCK_PINFO);
 | 
						|
 | 
						|
  return found_proc_id ? 0 : 1;
 | 
						|
}
 |