/* 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 #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include /* 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 (" ") + 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, " "); } 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; }