OSDN Git Service

* loadlib.h: New header implementing safe LoadLibrary calls.
[pf3gnuchains/pf3gnuchains4x.git] / winsup / utils / ps.cc
1 /* ps.cc
2
3    Copyright 1996, 1997, 1998, 1999, 2000, 2001, 2002, 2003, 2004,
4    2008, 2009, 2010 Red Hat, Inc.
5
6 This file is part of Cygwin.
7
8 This software is a copyrighted work licensed under the terms of the
9 Cygwin license.  Please consult the file "CYGWIN_LICENSE" for
10 details. */
11
12 #include <stdio.h>
13 #include <wchar.h>
14 #include <windows.h>
15 #include <time.h>
16 #include <getopt.h>
17 #include <unistd.h>
18 #include <stdlib.h>
19 #include <pwd.h>
20 #include <limits.h>
21 #include <sys/cygwin.h>
22 #include <tlhelp32.h>
23 #include <psapi.h>
24 #include <ddk/ntapi.h>
25 #include <ddk/winddk.h>
26 #include "loadlib.h"
27
28 /* Maximum possible path length under NT.  There's no official define
29    for that value.  Note that PATH_MAX is only 4K. */
30 #define NT_MAX_PATH 32768
31
32 static const char version[] = "$Revision$";
33 static char *prog_name;
34
35 static struct option longopts[] =
36 {
37   {"all", no_argument, NULL, 'a' },
38   {"everyone", no_argument, NULL, 'e' },
39   {"full", no_argument, NULL, 'f' },
40   {"help", no_argument, NULL, 'h' },
41   {"long", no_argument, NULL, 'l' },
42   {"process", required_argument, NULL, 'p'},
43   {"summary", no_argument, NULL, 's' },
44   {"user", required_argument, NULL, 'u'},
45   {"version", no_argument, NULL, 'v'},
46   {"windows", no_argument, NULL, 'W'},
47   {NULL, 0, NULL, 0}
48 };
49
50 static char opts[] = "aefhlp:su:vW";
51
52 typedef BOOL (WINAPI *ENUMPROCESSMODULES)(
53   HANDLE hProcess,      // handle to the process
54   HMODULE * lphModule,  // array to receive the module handles
55   DWORD cb,             // size of the array
56   LPDWORD lpcbNeeded    // receives the number of bytes returned
57 );
58
59 typedef DWORD (WINAPI *GETMODULEFILENAME)(
60   HANDLE hProcess,
61   HMODULE hModule,
62   LPTSTR lpstrFileName,
63   DWORD nSize
64 );
65
66 typedef HANDLE (WINAPI *CREATESNAPSHOT)(
67     DWORD dwFlags,
68     DWORD th32ProcessID
69 );
70
71 // Win95 functions
72 typedef BOOL (WINAPI *PROCESSWALK)(
73     HANDLE hSnapshot,
74     LPPROCESSENTRY32 lppe
75 );
76
77 ENUMPROCESSMODULES myEnumProcessModules;
78 GETMODULEFILENAME myGetModuleFileNameEx;
79 CREATESNAPSHOT myCreateToolhelp32Snapshot;
80 PROCESSWALK myProcess32First;
81 PROCESSWALK myProcess32Next;
82
83 static BOOL WINAPI dummyprocessmodules (
84   HANDLE hProcess,      // handle to the process
85   HMODULE * lphModule,  // array to receive the module handles
86   DWORD cb,             // size of the array
87   LPDWORD lpcbNeeded    // receives the number of bytes returned
88 )
89 {
90   lphModule[0] = (HMODULE) *lpcbNeeded;
91   *lpcbNeeded = 1;
92   return 1;
93 }
94
95 static DWORD WINAPI GetModuleFileNameEx95 (
96   HANDLE hProcess,
97   HMODULE hModule,
98   LPTSTR lpstrFileName,
99   DWORD n
100 )
101 {
102   HANDLE h;
103   DWORD pid = (DWORD) hModule;
104
105   h = myCreateToolhelp32Snapshot (TH32CS_SNAPPROCESS, 0);
106   if (!h)
107     return 0;
108
109   PROCESSENTRY32 proc;
110   proc.dwSize = sizeof (proc);
111   if (myProcess32First(h, &proc))
112     do
113       if (proc.th32ProcessID == pid)
114         {
115           CloseHandle (h);
116           strcpy (lpstrFileName, proc.szExeFile);
117           return 1;
118         }
119     while (myProcess32Next (h, &proc));
120   CloseHandle (h);
121   return 0;
122 }
123
124 int
125 init_win ()
126 {
127   OSVERSIONINFO os_version_info;
128
129   memset (&os_version_info, 0, sizeof os_version_info);
130   os_version_info.dwOSVersionInfoSize = sizeof (OSVERSIONINFO);
131   GetVersionEx (&os_version_info);
132
133   HMODULE h;
134   if (os_version_info.dwPlatformId == VER_PLATFORM_WIN32_NT)
135     {
136       h = LoadLibrary ("psapi.dll");
137       if (!h)
138         return 0;
139       myEnumProcessModules = (ENUMPROCESSMODULES) GetProcAddress (h, "EnumProcessModules");
140       myGetModuleFileNameEx = (GETMODULEFILENAME) GetProcAddress (h, "GetModuleFileNameExA");
141       if (!myEnumProcessModules || !myGetModuleFileNameEx)
142         return 0;
143       return 1;
144     }
145
146   h = GetModuleHandle("KERNEL32.DLL");
147   myCreateToolhelp32Snapshot = (CREATESNAPSHOT)GetProcAddress (h, "CreateToolhelp32Snapshot");
148   myProcess32First = (PROCESSWALK)GetProcAddress (h, "Process32First");
149   myProcess32Next  = (PROCESSWALK)GetProcAddress (h, "Process32Next");
150   if (!myCreateToolhelp32Snapshot || !myProcess32First || !myProcess32Next)
151     return 0;
152
153   myEnumProcessModules = dummyprocessmodules;
154   myGetModuleFileNameEx = GetModuleFileNameEx95;
155   return 1;
156 }
157
158 static char *
159 start_time (external_pinfo *child)
160 {
161   time_t st = child->start_time;
162   time_t t = time (NULL);
163   static char stime[40] = {'\0'};
164   char now[40];
165
166   strncpy (stime, ctime (&st) + 4, 15);
167   strcpy (now, ctime (&t) + 4);
168
169   if ((t - st) < (24 * 3600))
170     return (stime + 7);
171
172   stime[6] = '\0';
173
174   return stime;
175 }
176
177 #define FACTOR (0x19db1ded53ea710LL)
178 #define NSPERSEC 10000000LL
179
180 /* Convert a Win32 time to "UNIX" format. */
181 long __stdcall
182 to_time_t (FILETIME *ptr)
183 {
184   /* A file time is the number of 100ns since jan 1 1601
185      stuffed into two long words.
186      A time_t is the number of seconds since jan 1 1970.  */
187
188   long rem;
189   long long x = ((long long) ptr->dwHighDateTime << 32) + ((unsigned)ptr->dwLowDateTime);
190   x -= FACTOR;                  /* number of 100ns between 1601 and 1970 */
191   rem = x % ((long long)NSPERSEC);
192   rem += (NSPERSEC / 2);
193   x /= (long long) NSPERSEC;            /* number of 100ns in a second */
194   x += (long long) (rem / NSPERSEC);
195   return x;
196 }
197
198 static const char *
199 ttynam (int ntty)
200 {
201   static char buf[5];
202   if (ntty < 0)
203     return "   ?";
204   if (ntty == TTY_CONSOLE)
205     return " con";
206   sprintf (buf, "%4d", ntty);
207   return buf;
208 }
209
210 static void
211 usage (FILE * stream, int status)
212 {
213   fprintf (stream, "\
214 Usage: %s [-aefls] [-u UID] [-p PID]\n\
215 Report process status\n\
216 \n\
217  -a, --all       show processes of all users\n\
218  -e, --everyone  show processes of all users\n\
219  -f, --full      show process uids, ppids\n\
220  -h, --help      output usage information and exit\n\
221  -l, --long      show process uids, ppids, pgids, winpids\n\
222  -p, --process   show information for specified PID\n\
223  -s, --summary   show process summary\n\
224  -u, --user      list processes owned by UID\n\
225  -v, --version   output version information and exit\n\
226  -W, --windows   show windows as well as cygwin processes\n\
227 With no options, %s outputs the long format by default\n",
228            prog_name, prog_name);
229   exit (status);
230 }
231
232 static void
233 print_version ()
234 {
235   const char *v = strchr (version, ':');
236   int len;
237   if (!v)
238     {
239       v = "?";
240       len = 1;
241     }
242   else
243     {
244       v += 2;
245       len = strchr (v, ' ') - v;
246     }
247   printf ("\
248 %s (cygwin) %.*s\n\
249 Process Statistics\n\
250 Copyright 1996, 1997, 1998, 1999, 2000, 2001, 2002 Red Hat, Inc.\n\
251 Compiled on %s\n\
252 ", prog_name, len, v, __DATE__);
253 }
254
255 char dosdevs[32000];
256
257 int
258 main (int argc, char *argv[])
259 {
260   external_pinfo *p;
261   int aflag, lflag, fflag, sflag, uid, proc_id;
262   bool found_proc_id = true;
263   cygwin_getinfo_types query = CW_GETPINFO;
264   const char *dtitle = "    PID TTY     STIME COMMAND\n";
265   const char *dfmt   = "%7d%4s%10s %s\n";
266   const char *ftitle = "     UID     PID    PPID TTY     STIME COMMAND\n";
267   const char *ffmt   = "%8.8s%8d%8d%4s%10s %s\n";
268   const char *ltitle = "      PID    PPID    PGID     WINPID  TTY  UID    STIME COMMAND\n";
269   const char *lfmt   = "%c %7d %7d %7d %10u %4s %4u %8s %s\n";
270   char ch;
271   PUNICODE_STRING uni = NULL;
272
273   aflag = lflag = fflag = sflag = 0;
274   uid = getuid ();
275   proc_id = -1;
276   lflag = 1;
277
278   prog_name = strrchr (argv[0], '/');
279   if (prog_name == NULL)
280     prog_name = strrchr (argv[0], '\\');
281   if (prog_name == NULL)
282     prog_name = argv[0];
283   else
284     prog_name++;
285
286   while ((ch = getopt_long (argc, argv, opts, longopts, NULL)) != EOF)
287     switch (ch)
288       {
289       case 'a':
290       case 'e':
291         aflag = 1;
292         break;
293       case 'f':
294         fflag = 1;
295         break;
296       case 'h':
297         usage (stdout, 0);
298       case 'l':
299         lflag = 1;
300         break;
301       case 'p':
302         proc_id = atoi (optarg);
303         aflag = 1;
304         found_proc_id = false;
305         break;
306       case 's':
307         sflag = 1;
308         break;
309       case 'u':
310         uid = atoi (optarg);
311         if (uid == 0)
312           {
313             struct passwd *pw;
314
315             if ((pw = getpwnam (optarg)))
316               uid = pw->pw_uid;
317             else
318               {
319                 fprintf (stderr, "%s: user %s unknown\n", prog_name, optarg);
320                 exit (1);
321               }
322           }
323         break;
324       case 'v':
325         print_version ();
326         exit (0);
327         break;
328       case 'W':
329         query = CW_GETPINFO_FULL;
330         aflag = 1;
331         break;
332
333       default:
334         usage (stderr, 1);
335       }
336
337   if (sflag)
338     printf (dtitle);
339   else if (fflag)
340     printf (ftitle);
341   else if (lflag)
342     printf (ltitle);
343
344   (void) cygwin_internal (CW_LOCK_PINFO, 1000);
345
346   if (query == CW_GETPINFO_FULL && !init_win ())
347     query = CW_GETPINFO;
348   if (query == CW_GETPINFO_FULL)
349     {
350       /* Enable debug privilege to allow to enumerate all processes,
351          not only processes in current session. */
352       HANDLE tok;
353       if (OpenProcessToken (GetCurrentProcess (),
354                             TOKEN_QUERY | TOKEN_ADJUST_PRIVILEGES,
355                             &tok))
356         {
357           TOKEN_PRIVILEGES priv;
358
359           priv.PrivilegeCount = 1;
360           if (LookupPrivilegeValue (NULL, SE_DEBUG_NAME,
361                                     &priv.Privileges[0].Luid))
362             {
363               priv.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED;
364               AdjustTokenPrivileges (tok, FALSE, &priv, 0, NULL, NULL);
365             }
366         }
367     }
368
369   for (int pid = 0;
370        (p = (external_pinfo *) cygwin_internal (query, pid | CW_NEXTPID));
371        pid = p->pid)
372     {
373       if ((proc_id > 0) && (p->pid != proc_id))
374         continue;
375       else
376         found_proc_id = true;
377
378       if (aflag)
379         /* nothing to do */;
380       else if (p->version >= EXTERNAL_PINFO_VERSION_32_BIT)
381         {
382           if (p->uid32 != (__uid32_t) uid)
383             continue;
384         }
385       else if (p->uid != uid)
386         continue;
387       char status = ' ';
388       if (p->process_state & PID_STOPPED)
389         status = 'S';
390       else if (p->process_state & PID_TTYIN)
391         status = 'I';
392       else if (p->process_state & PID_TTYOU)
393         status = 'O';
394
395       /* Maximum possible path length under NT.  There's no official define
396          for that value. */
397       char pname[NT_MAX_PATH];
398       if (p->process_state & PID_EXITED || (p->exitcode & ~0xffff))
399         strcpy (pname, "<defunct>");
400       else if (p->ppid)
401         {
402           char *s;
403           pname[0] = '\0';
404           if (p->version >= EXTERNAL_PINFO_VERSION_32_LP)
405             cygwin_conv_path (CCP_WIN_A_TO_POSIX | CCP_ABSOLUTE,
406                               p->progname_long, pname, NT_MAX_PATH);
407           else
408             cygwin_conv_path (CCP_WIN_A_TO_POSIX | CCP_ABSOLUTE,
409                               p->progname, pname, NT_MAX_PATH);
410           s = strchr (pname, '\0') - 4;
411           if (s > pname && strcasecmp (s, ".exe") == 0)
412             *s = '\0';
413         }
414       else if (query == CW_GETPINFO_FULL)
415         {
416           HANDLE h = OpenProcess (PROCESS_QUERY_INFORMATION | PROCESS_VM_READ,
417                                   FALSE, p->dwProcessId);
418           if (!h)
419             continue;
420           HMODULE hm[1000];
421           DWORD n = p->dwProcessId;
422           if (!myEnumProcessModules (h, hm, sizeof (hm), &n))
423             n = 0;
424           /* This occurs when trying to enum modules of a 64 bit process.
425              GetModuleFileNameEx with a NULL module will return the same error.
426              Only NtQueryInformationProcess allows to fetch the process image
427              name in that case. */
428           if (!n && GetLastError () == ERROR_PARTIAL_COPY)
429             {
430               NTSTATUS status;
431               char pbuf[PATH_MAX];
432               char dev[256];
433               size_t len = sizeof (UNICODE_STRING) + PATH_MAX * sizeof (WCHAR);
434
435               if (!uni)
436                 {
437                   uni = (PUNICODE_STRING) alloca (len);
438                   QueryDosDevice (NULL, dosdevs, 32000);
439                 }
440               status = NtQueryInformationProcess (h, ProcessImageFileName, uni,
441                                                   len, NULL);
442               if (NT_SUCCESS (status)
443                   && (len = wcsnrtombs (pbuf, (const wchar_t **) &uni->Buffer,
444                                         uni->Length / sizeof (WCHAR), PATH_MAX,
445                                         NULL)) > 0)
446                 {
447                   pbuf[len] = '\0';
448                   if (!strncmp (pbuf, "\\Device\\Mup\\", 12))
449                     {
450                       strcpy (pname, "\\\\");
451                       strcpy (pname + 2, pbuf + 12);
452                     }
453                   else
454                     {
455                       strcpy (pname, pbuf);
456                       for (char *d = dosdevs; *d; d = strchr (d, '\0') + 1)
457                         if (QueryDosDevice (d, dev, 256)
458                             && strlen (d) < 3
459                             && !strncmp (dev, pbuf, strlen (dev)))
460                           {
461                             strcpy (pname, d);
462                             strcat (pname, pbuf + strlen (dev));
463                             break;
464                           }
465                     }
466                 }
467               else
468                 strcpy (pname, "*** unknown ***");
469             }
470           else if (!n || !myGetModuleFileNameEx (h, hm[0], pname, PATH_MAX))
471             strcpy (pname, "*** unknown ***");
472           FILETIME ct, et, kt, ut;
473           if (GetProcessTimes (h, &ct, &et, &kt, &ut))
474             p->start_time = to_time_t (&ct);
475           CloseHandle (h);
476         }
477
478       char uname[128];
479
480       if (fflag)
481         {
482           struct passwd *pw;
483
484           if ((pw = getpwuid (p->version >= EXTERNAL_PINFO_VERSION_32_BIT ?
485                               p->uid32 : p->uid)))
486             strcpy (uname, pw->pw_name);
487           else
488             sprintf (uname, "%u", (unsigned)
489                      (p->version >= EXTERNAL_PINFO_VERSION_32_BIT ?
490                       p->uid32 : p->uid));
491         }
492
493       if (sflag)
494         printf (dfmt, p->pid, ttynam (p->ctty), start_time (p), pname);
495       else if (fflag)
496         printf (ffmt, uname, p->pid, p->ppid, ttynam (p->ctty), start_time (p),
497                 pname);
498       else if (lflag)
499         printf (lfmt, status, p->pid, p->ppid, p->pgid,
500                 p->dwProcessId, ttynam (p->ctty),
501                 p->version >= EXTERNAL_PINFO_VERSION_32_BIT ? p->uid32 : p->uid,
502                 start_time (p), pname);
503
504     }
505   (void) cygwin_internal (CW_UNLOCK_PINFO);
506
507   return found_proc_id ? 0 : 1;
508 }