OSDN Git Service

* path.cc (conv_path_list): Take cygwin_conv_path_t as third parameter.
[pf3gnuchains/pf3gnuchains4x.git] / winsup / cygwin / fhandler_process.cc
1 /* fhandler_process.cc: fhandler for /proc/<pid> virtual filesystem
2
3    Copyright 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009,
4    2010, 2011 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 "winsup.h"
13 #include <stdlib.h>
14 #include <stdio.h>
15 #include <sys/cygwin.h>
16 #include "cygerrno.h"
17 #include "security.h"
18 #include "path.h"
19 #include "fhandler.h"
20 #include "fhandler_virtual.h"
21 #include "pinfo.h"
22 #include "shared_info.h"
23 #include "dtable.h"
24 #include "cygheap.h"
25 #include "ntdll.h"
26 #include "cygtls.h"
27 #include "pwdgrp.h"
28 #include "tls_pbuf.h"
29 #include <sys/param.h>
30 #include <ctype.h>
31 #include <psapi.h>
32 #include <tlhelp32.h>
33
34 #define _COMPILING_NEWLIB
35 #include <dirent.h>
36
37 static _off64_t format_process_maps (void *, char *&);
38 static _off64_t format_process_stat (void *, char *&);
39 static _off64_t format_process_status (void *, char *&);
40 static _off64_t format_process_statm (void *, char *&);
41 static _off64_t format_process_winexename (void *, char *&);
42 static _off64_t format_process_winpid (void *, char *&);
43 static _off64_t format_process_exename (void *, char *&);
44 static _off64_t format_process_root (void *, char *&);
45 static _off64_t format_process_cwd (void *, char *&);
46 static _off64_t format_process_cmdline (void *, char *&);
47 static _off64_t format_process_ppid (void *, char *&);
48 static _off64_t format_process_uid (void *, char *&);
49 static _off64_t format_process_pgid (void *, char *&);
50 static _off64_t format_process_sid (void *, char *&);
51 static _off64_t format_process_gid (void *, char *&);
52 static _off64_t format_process_ctty (void *, char *&);
53 static _off64_t format_process_fd (void *, char *&);
54 static _off64_t format_process_mounts (void *, char *&);
55
56 static const virt_tab_t process_tab[] =
57 {
58   { _VN ("."),          FH_PROCESS,   virt_directory, NULL },
59   { _VN (".."),         FH_PROCESS,   virt_directory, NULL },
60   { _VN ("cmdline"),    FH_PROCESS,   virt_file,      format_process_cmdline },
61   { _VN ("ctty"),       FH_PROCESS,   virt_file,      format_process_ctty },
62   { _VN ("cwd"),        FH_PROCESS,   virt_symlink,   format_process_cwd },
63   { _VN ("exe"),        FH_PROCESS,   virt_symlink,   format_process_exename },
64   { _VN ("exename"),    FH_PROCESS,   virt_file,      format_process_exename },
65   { _VN ("fd"),         FH_PROCESSFD, virt_directory, format_process_fd },
66   { _VN ("gid"),        FH_PROCESS,   virt_file,      format_process_gid },
67   { _VN ("maps"),       FH_PROCESS,   virt_file,      format_process_maps },
68   { _VN ("mounts"),     FH_PROCESS,   virt_file,      format_process_mounts },
69   { _VN ("pgid"),       FH_PROCESS,   virt_file,      format_process_pgid },
70   { _VN ("ppid"),       FH_PROCESS,   virt_file,      format_process_ppid },
71   { _VN ("root"),       FH_PROCESS,   virt_symlink,   format_process_root },
72   { _VN ("sid"),        FH_PROCESS,   virt_file,      format_process_sid },
73   { _VN ("stat"),       FH_PROCESS,   virt_file,      format_process_stat },
74   { _VN ("statm"),      FH_PROCESS,   virt_file,      format_process_statm },
75   { _VN ("status"),     FH_PROCESS,   virt_file,      format_process_status },
76   { _VN ("uid"),        FH_PROCESS,   virt_file,      format_process_uid },
77   { _VN ("winexename"), FH_PROCESS,   virt_file,      format_process_winexename },
78   { _VN ("winpid"),     FH_PROCESS,   virt_file,      format_process_winpid },
79   { NULL, 0,            FH_NADA,       virt_none,      NULL }
80 };
81
82 static const int PROCESS_LINK_COUNT =
83   (sizeof (process_tab) / sizeof (virt_tab_t)) - 1;
84 int get_process_state (DWORD dwProcessId);
85 static bool get_mem_values (DWORD dwProcessId, unsigned long *vmsize,
86                             unsigned long *vmrss, unsigned long *vmtext,
87                             unsigned long *vmdata, unsigned long *vmlib,
88                             unsigned long *vmshare);
89
90 /* Returns 0 if path doesn't exist, >0 if path is a directory,
91  * -1 if path is a file, -2 if path is a symlink, -3 if path is a pipe,
92  * -4 if path is a socket.
93  */
94 virtual_ftype_t
95 fhandler_process::exists ()
96 {
97   const char *path = get_name ();
98   debug_printf ("exists (%s)", path);
99   path += proc_len + 1;
100   while (*path != 0 && !isdirsep (*path))
101     path++;
102   if (*path == 0)
103     return virt_rootdir;
104
105   virt_tab_t *entry = virt_tab_search (path + 1, true, process_tab,
106                                        PROCESS_LINK_COUNT);
107   if (entry)
108     {
109       if (!path[entry->name_len + 1])
110         {
111           fileid = entry - process_tab;
112           return entry->type;
113         }
114       if (entry->type == virt_directory)
115         {
116           fileid = entry - process_tab;
117           if (fill_filebuf ())
118             return virt_symlink;
119           /* Check for nameless device entries. */
120           path = strrchr (path, '/');
121           if (path && *++path)
122             {
123               if (!strncmp (path, "pipe:[", 6))
124                 return virt_pipe;
125               else if (!strncmp (path, "socket:[", 8))
126                 return virt_socket;
127             }
128         }
129     }
130   return virt_none;
131 }
132
133 fhandler_process::fhandler_process ():
134   fhandler_proc ()
135 {
136 }
137
138 int
139 fhandler_process::fstat (struct __stat64 *buf)
140 {
141   const char *path = get_name ();
142   int file_type = exists ();
143   fhandler_base::fstat (buf);
144   path += proc_len + 1;
145   pid = atoi (path);
146
147   pinfo p (pid);
148   /* If p->pid != pid, then pid is actually the Windows PID for an execed
149      Cygwin process, and the pinfo entry is the additional entry created
150      at exec time.  We don't want to enable the user to access a process
151      entry by using the Win32 PID, though. */
152   if (!p || p->pid != pid)
153     {
154       set_errno (ENOENT);
155       return -1;
156     }
157
158   buf->st_mode &= ~_IFMT & NO_W;
159
160   switch (file_type)
161     {
162     case virt_none:
163       set_errno (ENOENT);
164       return -1;
165     case virt_directory:
166     case virt_rootdir:
167       buf->st_ctime = buf->st_mtime = buf->st_birthtime = p->start_time;
168       buf->st_ctim.tv_nsec = buf->st_mtim.tv_nsec
169         = buf->st_birthtim.tv_nsec = 0;
170       time_as_timestruc_t (&buf->st_atim);
171       buf->st_uid = p->uid;
172       buf->st_gid = p->gid;
173       buf->st_mode |= S_IFDIR | S_IXUSR | S_IXGRP | S_IXOTH;
174       if (file_type == 1)
175         buf->st_nlink = 2;
176       else
177         buf->st_nlink = 3;
178       return 0;
179     case virt_symlink:
180       buf->st_uid = p->uid;
181       buf->st_gid = p->gid;
182       buf->st_mode = S_IFLNK | S_IRWXU | S_IRWXG | S_IRWXO;
183       return 0;
184     case virt_pipe:
185       buf->st_uid = p->uid;
186       buf->st_gid = p->gid;
187       buf->st_mode = S_IFIFO | S_IRUSR | S_IWUSR;
188       return 0;
189     case virt_socket:
190       buf->st_uid = p->uid;
191       buf->st_gid = p->gid;
192       buf->st_mode = S_IFSOCK | S_IRUSR | S_IWUSR;
193       return 0;
194     case virt_file:
195     default:
196       buf->st_uid = p->uid;
197       buf->st_gid = p->gid;
198       buf->st_mode |= S_IFREG | S_IRUSR | S_IRGRP | S_IROTH;
199       return 0;
200     }
201 }
202
203 DIR *
204 fhandler_process::opendir (int fd)
205 {
206   DIR *dir = fhandler_virtual::opendir (fd);
207   if (dir && process_tab[fileid].fhandler == FH_PROCESSFD)
208     fill_filebuf ();
209   return dir;
210 }
211
212 int
213 fhandler_process::closedir (DIR *dir)
214 {
215   return fhandler_virtual::closedir (dir);
216 }
217
218 int
219 fhandler_process::readdir (DIR *dir, dirent *de)
220 {
221   int res = ENMFILE;
222   if (process_tab[fileid].fhandler == FH_PROCESSFD)
223     {
224       if (dir->__d_position >= 2 + filesize / sizeof (int))
225         goto out;
226     }
227   else if (dir->__d_position >= PROCESS_LINK_COUNT)
228     goto out;
229   if (process_tab[fileid].fhandler == FH_PROCESSFD && dir->__d_position > 1)
230     {
231       int *p = (int *) filebuf;
232       __small_sprintf (de->d_name, "%d", p[dir->__d_position++ - 2]);
233     }
234   else
235     strcpy (de->d_name, process_tab[dir->__d_position++].name);
236   dir->__flags |= dirent_saw_dot | dirent_saw_dot_dot;
237   res = 0;
238 out:
239   syscall_printf ("%d = readdir (%p, %p) (%s)", res, dir, de, de->d_name);
240   return res;
241 }
242
243 int
244 fhandler_process::open (int flags, mode_t mode)
245 {
246   int res = fhandler_virtual::open (flags, mode);
247   if (!res)
248     goto out;
249
250   nohandle (true);
251
252   const char *path;
253   path = get_name () + proc_len + 1;
254   pid = atoi (path);
255   while (*path != 0 && !isdirsep (*path))
256     path++;
257
258   if (*path == 0)
259     {
260       if ((flags & (O_CREAT | O_EXCL)) == (O_CREAT | O_EXCL))
261         {
262           set_errno (EEXIST);
263           res = 0;
264           goto out;
265         }
266       else if (flags & O_WRONLY)
267         {
268           set_errno (EISDIR);
269           res = 0;
270           goto out;
271         }
272       else
273         {
274           flags |= O_DIROPEN;
275           goto success;
276         }
277     }
278
279   virt_tab_t *entry;
280   entry = virt_tab_search (path + 1, true, process_tab, PROCESS_LINK_COUNT);
281   if (!entry)
282     {
283       set_errno ((flags & O_CREAT) ? EROFS : ENOENT);
284       res = 0;
285       goto out;
286     }
287   if (entry->fhandler == FH_PROCESSFD)
288     {
289       flags |= O_DIROPEN;
290       goto success;
291     }
292   if (flags & O_WRONLY)
293     {
294       set_errno (EROFS);
295       res = 0;
296       goto out;
297     }
298
299   fileid = entry - process_tab;
300   if (!fill_filebuf ())
301         {
302           res = 0;
303           goto out;
304         }
305
306   if (flags & O_APPEND)
307     position = filesize;
308   else
309     position = 0;
310
311 success:
312   res = 1;
313   set_flags ((flags & ~O_TEXT) | O_BINARY);
314   set_open_status ();
315 out:
316   syscall_printf ("%d = fhandler_proc::open (%p, %d)", res, flags, mode);
317   return res;
318 }
319
320 struct process_fd_t {
321   const char *path;
322   _pinfo *p;
323 };
324
325 bool
326 fhandler_process::fill_filebuf ()
327 {
328   const char *path;
329   path = get_name () + proc_len + 1;
330   if (!pid)
331     pid = atoi (path);
332
333   pinfo p (pid);
334   /* If p->pid != pid, then pid is actually the Windows PID for an execed
335      Cygwin process, and the pinfo entry is the additional entry created
336      at exec time.  We don't want to enable the user to access a process
337      entry by using the Win32 PID, though. */
338   if (!p || p->pid != pid)
339     {
340       set_errno (ENOENT);
341       return false;
342     }
343
344   if (process_tab[fileid].format_func)
345     {
346       if (process_tab[fileid].fhandler == FH_PROCESSFD)
347         {
348           process_fd_t fd = { path, p };
349           filesize = process_tab[fileid].format_func (&fd, filebuf);
350         }
351       else
352         filesize = process_tab[fileid].format_func (p, filebuf);
353       return !filesize ? false : true;
354     }
355   return false;
356 }
357
358 static _off64_t
359 format_process_fd (void *data, char *&destbuf)
360 {
361   _pinfo *p = ((process_fd_t *) data)->p;
362   const char *path = ((process_fd_t *) data)->path;
363   size_t fs = 0;
364   char *fdp = strrchr (path, '/');
365
366   if (!fdp || *++fdp == 'f') /* The "fd" directory itself. */
367     {
368       if (destbuf)
369         cfree (destbuf);
370       destbuf = p->fds (fs);
371     }
372   else
373     {
374       if (destbuf)
375         cfree (destbuf);
376       int fd = atoi (fdp);
377       if (fd < 0 || (fd == 0 && !isdigit (*fdp)))
378         {
379           set_errno (ENOENT);
380           return 0;
381         }
382       destbuf = p->fd (fd, fs);
383       if (!destbuf || !*destbuf)
384         {
385           set_errno (ENOENT);
386           return 0;
387         }
388     }
389   return fs;
390 }
391
392 static _off64_t
393 format_process_ppid (void *data, char *&destbuf)
394 {
395   _pinfo *p = (_pinfo *) data;
396   destbuf = (char *) crealloc_abort (destbuf, 40);
397   return __small_sprintf (destbuf, "%d\n", p->ppid);
398 }
399
400 static _off64_t
401 format_process_uid (void *data, char *&destbuf)
402 {
403   _pinfo *p = (_pinfo *) data;
404   destbuf = (char *) crealloc_abort (destbuf, 40);
405   return __small_sprintf (destbuf, "%d\n", p->uid);
406 }
407
408 static _off64_t
409 format_process_pgid (void *data, char *&destbuf)
410 {
411   _pinfo *p = (_pinfo *) data;
412   destbuf = (char *) crealloc_abort (destbuf, 40);
413   return __small_sprintf (destbuf, "%d\n", p->pgid);
414 }
415
416 static _off64_t
417 format_process_sid (void *data, char *&destbuf)
418 {
419   _pinfo *p = (_pinfo *) data;
420   destbuf = (char *) crealloc_abort (destbuf, 40);
421   return __small_sprintf (destbuf, "%d\n", p->sid);
422 }
423
424 static _off64_t
425 format_process_gid (void *data, char *&destbuf)
426 {
427   _pinfo *p = (_pinfo *) data;
428   destbuf = (char *) crealloc_abort (destbuf, 40);
429   return __small_sprintf (destbuf, "%d\n", p->gid);
430 }
431
432 static _off64_t
433 format_process_ctty (void *data, char *&destbuf)
434 {
435   device d;
436   _pinfo *p = (_pinfo *) data;
437   d.parse (p->ctty);
438   destbuf = (char *) crealloc_abort (destbuf, strlen (d.name) + 2);
439   return __small_sprintf (destbuf, "%s\n", d.name);
440 }
441
442 static _off64_t
443 format_process_root (void *data, char *&destbuf)
444 {
445   _pinfo *p = (_pinfo *) data;
446   size_t fs;
447
448   if (destbuf)
449     {
450       cfree (destbuf);
451       destbuf = NULL;
452     }
453   destbuf = p->root (fs);
454   if (!destbuf || !*destbuf)
455     {
456       destbuf = cstrdup ("<defunct>");
457       fs = strlen (destbuf) + 1;
458     }
459   return fs;
460 }
461
462 static _off64_t
463 format_process_cwd (void *data, char *&destbuf)
464 {
465   _pinfo *p = (_pinfo *) data;
466   size_t fs;
467
468   if (destbuf)
469     {
470       cfree (destbuf);
471       destbuf = NULL;
472     }
473   destbuf = p->cwd (fs);
474   if (!destbuf || !*destbuf)
475     {
476       destbuf = cstrdup ("<defunct>");
477       fs = strlen (destbuf) + 1;
478     }
479   return fs;
480 }
481
482 static _off64_t
483 format_process_cmdline (void *data, char *&destbuf)
484 {
485   _pinfo *p = (_pinfo *) data;
486   size_t fs;
487
488   if (destbuf)
489     {
490       cfree (destbuf);
491       destbuf = NULL;
492     }
493   destbuf = p->cmdline (fs);
494   if (!destbuf || !*destbuf)
495     {
496       destbuf = cstrdup ("<defunct>");
497       fs = strlen (destbuf) + 1;
498     }
499   return fs;
500 }
501
502 static _off64_t
503 format_process_exename (void *data, char *&destbuf)
504 {
505   _pinfo *p = (_pinfo *) data;
506   int len;
507   tmp_pathbuf tp;
508
509   char *buf = tp.c_get ();
510   if (p->process_state & PID_EXITED)
511     stpcpy (buf, "<defunct>");
512   else
513     {
514       mount_table->conv_to_posix_path (p->progname, buf, 1);
515       len = strlen (buf);
516       if (len > 4)
517         {
518           char *s = buf + len - 4;
519           if (ascii_strcasematch (s, ".exe"))
520             *s = 0;
521         }
522     }
523   destbuf = (char *) crealloc_abort (destbuf, (len = strlen (buf)) + 1);
524   stpcpy (destbuf, buf);
525   return len;
526 }
527
528 static _off64_t
529 format_process_winpid (void *data, char *&destbuf)
530 {
531   _pinfo *p = (_pinfo *) data;
532   destbuf = (char *) crealloc_abort (destbuf, 20);
533   return __small_sprintf (destbuf, "%d\n", p->dwProcessId);
534 }
535
536 static _off64_t
537 format_process_winexename (void *data, char *&destbuf)
538 {
539   _pinfo *p = (_pinfo *) data;
540   size_t len = sys_wcstombs (NULL, 0, p->progname);
541   destbuf = (char *) crealloc_abort (destbuf, len + 1);
542   sys_wcstombs (destbuf, len, p->progname);
543   destbuf[len] = '\n';
544   return len + 1;
545 }
546
547 struct dos_drive_mappings
548 {
549   struct mapping
550   {
551     mapping *next;
552     int len;
553     wchar_t drive_letter;
554     wchar_t mapping[1];
555   };
556   mapping *mappings;
557
558   dos_drive_mappings ()
559     : mappings(0)
560   {
561     /* The logical drive strings buffer holds a list of (at most 26)
562        drive names separated by nulls and terminated by a double-null:
563
564        "a:\\\0b:\\\0...z:\\\0"
565
566        The annoying part is, QueryDosDeviceW wants only "x:" rather
567        than the "x:\" we get back from GetLogicalDriveStringsW, so
568        we'll have to strip out the trailing slash for each mapping.
569
570        The returned mapping a native NT pathname (\Device\...) which
571        we can use to fix up the output of GetMappedFileNameW
572     */
573     static unsigned const DBUFLEN = 26 * 4;
574     wchar_t dbuf[DBUFLEN + 1];
575     wchar_t pbuf[NT_MAX_PATH];
576     wchar_t drive[] = {L'x', L':', 0};
577     unsigned result = GetLogicalDriveStringsW (DBUFLEN * sizeof (wchar_t),
578                                                dbuf);
579     if (!result)
580       debug_printf ("Failed to get logical DOS drive names: %lu",
581                     GetLastError ());
582     else if (result > DBUFLEN)
583       debug_printf ("Too many mapped drive letters: %u", result);
584     else
585       for (wchar_t *cur = dbuf; (*drive = *cur); cur = wcschr (cur, L'\0')+1)
586         if (QueryDosDeviceW (drive, pbuf, NT_MAX_PATH))
587           {
588             size_t plen = wcslen (pbuf);
589             size_t psize = plen * sizeof (wchar_t);
590             debug_printf ("DOS drive %ls maps to %ls", drive, pbuf);
591             mapping *m = (mapping*) malloc (sizeof (mapping) + psize);
592             if (m)
593               {
594                 m->next = mappings;
595                 m->len = plen;
596                 m->drive_letter = *drive;
597                 memcpy (m->mapping, pbuf, psize + sizeof (wchar_t));
598                 mappings = m;
599               }
600           }
601         else
602           debug_printf ("Unable to determine the native mapping for %ls "
603                         "(error %lu)", drive, GetLastError ());
604   }
605
606   wchar_t *fixup_if_match (wchar_t *path)
607   {
608     /* Check for network drive first. */
609     if (!wcsncmp (path, L"\\Device\\Mup\\", 12))
610       {
611         path += 10;
612         path[0] = L'\\';
613         return path;
614       }
615     /* Then test local drives. */
616     for (mapping *m = mappings; m; m = m->next)
617       if (!wcsncmp (m->mapping, path, m->len))
618         {
619           path += m->len - 2;
620           path[0] = m->drive_letter;
621           path[1] = L':';
622           break;
623         }
624     return path;
625   }
626
627   ~dos_drive_mappings ()
628   {
629     mapping *n = 0;
630     for (mapping *m = mappings; m; m = n)
631       {
632         n = m->next;
633         free (m);
634       }
635   }
636 };
637
638 struct heap_info
639 {
640   struct heap
641   {
642     heap *next;
643     unsigned heap_id;
644     char *base;
645     char *end;
646     unsigned long flags;
647   };
648   heap *heap_vm_chunks;
649
650   heap_info (DWORD pid)
651     : heap_vm_chunks (NULL)
652   {
653     PDEBUG_BUFFER buf;
654     NTSTATUS status;
655     PDEBUG_HEAP_ARRAY harray;
656
657     buf = RtlCreateQueryDebugBuffer (0, FALSE);
658     if (!buf)
659       return;
660     status = RtlQueryProcessDebugInformation (pid, PDI_HEAPS | PDI_HEAP_BLOCKS,
661                                               buf);
662     if (NT_SUCCESS (status)
663         && (harray = (PDEBUG_HEAP_ARRAY) buf->HeapInformation) != NULL)
664       for (ULONG hcnt = 0; hcnt < harray->Count; ++hcnt)
665         {
666           PDEBUG_HEAP_BLOCK barray = (PDEBUG_HEAP_BLOCK)
667                                      harray->Heaps[hcnt].Blocks;
668           if (!barray)
669             continue;
670           for (ULONG bcnt = 0; bcnt < harray->Heaps[hcnt].BlockCount; ++bcnt)
671             if (barray[bcnt].Flags & 2)
672               {
673                 heap *h = (heap *) malloc (sizeof (heap));
674                 if (h)
675                   {
676                     *h = (heap) { heap_vm_chunks,
677                                   hcnt, (char *) barray[bcnt].Address,
678                                   (char *) barray[bcnt].Address
679                                            + barray[bcnt].Size,
680                                   harray->Heaps[hcnt].Flags };
681                     heap_vm_chunks = h;
682                   }
683               }
684         }
685     RtlDestroyQueryDebugBuffer (buf);
686   }
687
688   char *fill_if_match (char *base, ULONG type, char *dest)
689   {
690     for (heap *h = heap_vm_chunks; h; h = h->next)
691       if (base >= h->base && base < h->end)
692         {
693           char *p = dest + __small_sprintf (dest, "[win heap %ld", h->heap_id);
694           if (!(h->flags & HEAP_FLAG_NONDEFAULT))
695             p = stpcpy (p, " default");
696           if ((h->flags & HEAP_FLAG_SHAREABLE) && (type & MEM_MAPPED))
697             p = stpcpy (p, " shared");
698           if (h->flags & HEAP_FLAG_EXECUTABLE)
699             p = stpcpy (p, " exec");
700           if (h->flags & HEAP_FLAG_GROWABLE)
701             p = stpcpy (p, " grow");
702           if (h->flags & HEAP_FLAG_NOSERIALIZE)
703             p = stpcpy (p, " noserial");
704           if (h->flags == HEAP_FLAG_DEBUGGED)
705             p = stpcpy (p, " debug");
706           stpcpy (p, "]");
707           return dest;
708         }
709     return 0;
710   }
711
712   ~heap_info ()
713   {
714     heap *n = 0;
715     for (heap *m = heap_vm_chunks; m; m = n)
716       {
717         n = m->next;
718         free (m);
719       }
720   }
721 };
722
723 struct thread_info
724 {
725   struct region
726   {
727     region *next;
728     ULONG thread_id;
729     char *start;
730     char *end;
731     bool teb;
732   };
733   region *regions;
734
735   thread_info (DWORD pid, HANDLE process)
736     : regions (NULL)
737   {
738     NTSTATUS status;
739     PVOID buf = NULL;
740     size_t size = 50 * (sizeof (SYSTEM_PROCESSES)
741                         + 16 * sizeof (SYSTEM_THREADS));
742     PSYSTEM_PROCESSES proc;
743     PSYSTEM_THREADS thread;
744
745     do
746       {
747         buf = realloc (buf, size);
748         status = NtQuerySystemInformation (SystemProcessesAndThreadsInformation,
749                                            buf, size, NULL);
750         size <<= 1;
751       }
752     while (status == STATUS_INFO_LENGTH_MISMATCH);
753     if (!NT_SUCCESS (status))
754       {
755         if (buf)
756           free (buf);
757         debug_printf ("NtQuerySystemInformation, %p", status);
758         return;
759       }
760     proc = (PSYSTEM_PROCESSES) buf;
761     while (true)
762       {
763         if (proc->ProcessId == pid)
764           break;
765         if (!proc->NextEntryDelta)
766           {
767             free (buf);
768             return;
769           }
770         proc = (PSYSTEM_PROCESSES) ((PBYTE) proc + proc->NextEntryDelta);
771       }
772     thread = proc->Threads;
773     for (ULONG i = 0; i < proc->ThreadCount; ++i)
774       {
775         THREAD_BASIC_INFORMATION tbi;
776         TEB teb;
777         HANDLE thread_h;
778
779         if (!(thread_h = OpenThread (THREAD_QUERY_INFORMATION, FALSE,
780                                      (ULONG) thread[i].ClientId.UniqueThread)))
781           continue;
782         status = NtQueryInformationThread (thread_h, ThreadBasicInformation,
783                                            &tbi, sizeof tbi, NULL);
784         CloseHandle (thread_h);
785         if (!NT_SUCCESS (status))
786           continue;
787         region *r = (region *) malloc (sizeof (region));
788         if (r)
789           {
790             *r = (region) { regions, (ULONG) thread[i].ClientId.UniqueThread,
791                             (char *) tbi.TebBaseAddress,
792                             (char *) tbi.TebBaseAddress + wincap.page_size (),
793                             true };
794             regions = r;
795           }
796         if (!ReadProcessMemory (process, (PVOID) tbi.TebBaseAddress,
797                                 &teb, sizeof teb, NULL))
798           continue;
799         r = (region *) malloc (sizeof (region));
800         if (r)
801           {
802             *r = (region) { regions, (ULONG) thread[i].ClientId.UniqueThread,
803                             (char *) (teb.DeallocationStack
804                                       ?: teb.Tib.StackLimit),
805                             (char *) teb.Tib.StackBase,
806                             false };
807             regions = r;
808           }
809       }
810     free (buf);
811   }
812
813   char *fill_if_match (char *base, ULONG type, char *dest)
814   {
815     for (region *r = regions; r; r = r->next)
816       if ((base >= r->start && base < r->end)
817           /* Special case WOW64.  The TEB is 8K within the region reserved
818              for it.  No idea what the lower 8K are used for. */
819           || (r->teb && wincap.is_wow64 ()
820               && r->start == base + 2 * wincap.page_size ()))
821         {
822           char *p = dest + __small_sprintf (dest, "[%s (tid %ld)",
823                                             r->teb ? "teb" : "stack",
824                                             r->thread_id);
825           if (type & MEM_MAPPED)
826             p = stpcpy (p, " shared");
827           stpcpy (p, "]");
828           return dest;
829         }
830     return 0;
831   }
832
833   ~thread_info ()
834   {
835     region *n = 0;
836     for (region *m = regions; m; m = n)
837       {
838         n = m->next;
839         free (m);
840       }
841   }
842 };
843
844 static _off64_t
845 format_process_maps (void *data, char *&destbuf)
846 {
847   _pinfo *p = (_pinfo *) data;
848   HANDLE proc = OpenProcess (PROCESS_QUERY_INFORMATION | PROCESS_VM_READ,
849                              FALSE, p->dwProcessId);
850   if (!proc)
851     return 0;
852
853   NTSTATUS status;
854   PROCESS_BASIC_INFORMATION pbi;
855   PPEB peb = NULL;
856
857   memset (&pbi, 0, sizeof (pbi));
858   status = NtQueryInformationProcess (proc, ProcessBasicInformation,
859                                       &pbi, sizeof pbi, NULL);
860   if (NT_SUCCESS (status))
861     peb = pbi.PebBaseAddress;
862   /* myself is in the same spot in every process, so is the pointer to the
863      procinfo.  But make sure the destructor doesn't try to release procinfo! */
864   pinfo proc_pinfo;
865   if (ReadProcessMemory (proc, &myself, &proc_pinfo, sizeof proc_pinfo, NULL))
866     proc_pinfo.preserve ();
867   /* The heap info on the cygheap is also in the same spot in each process
868      because the cygheap is located at the same address. */
869   user_heap_info user_heap;
870   ReadProcessMemory (proc, &cygheap->user_heap, &user_heap,
871                      sizeof user_heap, NULL);
872
873   _off64_t len = 0;
874
875   union access
876   {
877     char flags[8];
878     _off64_t word;
879   } a;
880
881   struct region {
882     access a;
883     char *abase;
884     char *rbase;
885     char *rend;
886   } cur = {{{'\0'}}, (char *)1, 0, 0};
887
888   MEMORY_BASIC_INFORMATION mb;
889   dos_drive_mappings drive_maps;
890   heap_info heaps (p->dwProcessId);
891   thread_info threads (p->dwProcessId, proc);
892   struct __stat64 st;
893   long last_pass = 0;
894
895   tmp_pathbuf tp;
896   PMEMORY_SECTION_NAME msi = (PMEMORY_SECTION_NAME) tp.w_get ();
897   char *posix_modname = tp.c_get ();
898   size_t maxsize = 0;
899
900   if (destbuf)
901     {
902       cfree (destbuf);
903       destbuf = NULL;
904     }
905
906   /* Iterate over each VM region in the address space, coalescing
907      memory regions with the same permissions. Once we run out, do one
908      last_pass to trigger output of the last accumulated region. */
909   for (char *i = 0;
910        VirtualQueryEx (proc, i, &mb, sizeof(mb)) || (1 == ++last_pass);
911        i = cur.rend)
912     {
913       if (last_pass)
914         posix_modname[0] = '\0';
915       if (mb.State == MEM_FREE)
916         a.word = 0;
917       else if (mb.State == MEM_RESERVE)
918         {
919           char *p = stpcpy (a.flags, "===");
920           stpcpy (p, (mb.Type & MEM_MAPPED) ? "s" : "p");
921         }
922       else
923         {
924           static DWORD const RO = (PAGE_EXECUTE_READ | PAGE_READONLY);
925           static DWORD const RW = (PAGE_EXECUTE_READWRITE | PAGE_READWRITE
926                                    | PAGE_EXECUTE_WRITECOPY | PAGE_WRITECOPY);
927           static DWORD const X = (PAGE_EXECUTE | PAGE_EXECUTE_READ
928                                   | PAGE_EXECUTE_READWRITE | PAGE_EXECUTE_WRITECOPY);
929           static DWORD const WC = (PAGE_EXECUTE_WRITECOPY | PAGE_WRITECOPY);
930           DWORD p = mb.Protect;
931           a = (access) {{
932               (p & (RO | RW))                           ? 'r' : '-',
933               (p & (RW))                                ? 'w' : '-',
934               (p & (X))                                 ? 'x' : '-',
935               (mb.Type & MEM_MAPPED) && !(p & (WC))     ? 's'
936               : (p & PAGE_GUARD)                        ? 'g' : 'p',
937               '\0', // zero-fill the remaining bytes
938             }};
939         }
940
941       region next = { a,
942                       (char *) mb.AllocationBase,
943                       (char *) mb.BaseAddress,
944                       (char *) mb.BaseAddress+mb.RegionSize
945       };
946
947       /* Windows permissions are more fine-grained than the unix rwxp,
948          so we reduce clutter by manually coalescing regions sharing
949          the same allocation base and effective permissions. */
950       bool newbase = (next.abase != cur.abase);
951       if (!last_pass && !newbase && next.a.word == cur.a.word)
952           cur.rend = next.rend; // merge with previous
953       else
954         {
955           // output the current region if it's "interesting"
956           if (cur.a.word)
957             {
958               size_t newlen = strlen (posix_modname) + 62;
959               if (len + newlen >= maxsize)
960                 destbuf = (char *) crealloc_abort (destbuf,
961                                                    maxsize += roundup2 (newlen,
962                                                                         2048));
963               int written = __small_sprintf (destbuf + len,
964                                              "%08lx-%08lx %s %08lx %04x:%04x %U   ",
965                                              cur.rbase, cur.rend, cur.a.flags,
966                                              cur.rbase - cur.abase,
967                                              st.st_dev >> 16,
968                                              st.st_dev & 0xffff,
969                                              st.st_ino);
970               while (written < 62)
971                 destbuf[len + written++] = ' ';
972               len += written;
973               len += __small_sprintf (destbuf + len, "%s\n", posix_modname);
974             }
975           // start of a new region (but possibly still the same allocation)
976           cur = next;
977           // if a new allocation, figure out what kind it is
978           if (newbase && !last_pass && mb.State != MEM_FREE)
979             {
980               /* If the return length pointer is missing, NtQueryVirtualMemory
981                  returns with STATUS_ACCESS_VIOLATION on Windows 2000. */
982               ULONG ret_len = 0;
983
984               st.st_dev = 0;
985               st.st_ino = 0;
986               if ((mb.Type & (MEM_MAPPED | MEM_IMAGE))
987                   && NT_SUCCESS (status = NtQueryVirtualMemory (proc, cur.abase,
988                                                        MemorySectionName,
989                                                        msi, 65536, &ret_len)))
990                 {
991                   PWCHAR dosname =
992                       drive_maps.fixup_if_match (msi->SectionFileName.Buffer);
993                   if (mount_table->conv_to_posix_path (dosname,
994                                                        posix_modname, 0))
995                     sys_wcstombs (posix_modname, NT_MAX_PATH, dosname);
996                   stat64 (posix_modname, &st);
997                 }
998               else if (!threads.fill_if_match (cur.abase, mb.Type,
999                                                posix_modname)
1000                        && !heaps.fill_if_match (cur.abase, mb.Type,
1001                                                 posix_modname))
1002                 {
1003                   if (cur.abase == (char *) peb)
1004                     strcpy (posix_modname, "[peb]");
1005                   else if (cur.abase == (char *) &SharedUserData)
1006                     strcpy (posix_modname, "[shared-user-data]");
1007                   else if (cur.abase == (char *) cygwin_shared)
1008                     strcpy (posix_modname, "[cygwin-shared]");
1009                   else if (cur.abase == (char *) user_shared)
1010                     strcpy (posix_modname, "[cygwin-user-shared]");
1011                   else if (cur.abase == (char *) *proc_pinfo)
1012                     strcpy (posix_modname, "[procinfo]");
1013                   else if (cur.abase == user_heap.base)
1014                     strcpy (posix_modname, "[heap]");
1015                   else
1016                     posix_modname[0] = 0;
1017                 }
1018             }
1019         }
1020     }
1021   CloseHandle (proc);
1022   return len;
1023 }
1024
1025 static _off64_t
1026 format_process_stat (void *data, char *&destbuf)
1027 {
1028   _pinfo *p = (_pinfo *) data;
1029   char cmd[NAME_MAX + 1];
1030   WCHAR wcmd[NAME_MAX + 1];
1031   int state = 'R';
1032   unsigned long fault_count = 0UL,
1033                 utime = 0UL, stime = 0UL,
1034                 start_time = 0UL,
1035                 vmsize = 0UL, vmrss = 0UL, vmmaxrss = 0UL;
1036   int priority = 0;
1037   if (p->process_state & PID_EXITED)
1038     strcpy (cmd, "<defunct>");
1039   else
1040     {
1041       PWCHAR last_slash = wcsrchr (p->progname, L'\\');
1042       wcscpy (wcmd, last_slash ? last_slash + 1 : p->progname);
1043       sys_wcstombs (cmd, NAME_MAX + 1, wcmd);
1044       int len = strlen (cmd);
1045       if (len > 4)
1046         {
1047           char *s = cmd + len - 4;
1048           if (ascii_strcasematch (s, ".exe"))
1049             *s = 0;
1050          }
1051     }
1052   /*
1053    * Note: under Windows, a _process_ is always running - it's only _threads_
1054    * that get suspended. Therefore the default state is R (runnable).
1055    */
1056   if (p->process_state & PID_EXITED)
1057     state = 'Z';
1058   else if (p->process_state & PID_STOPPED)
1059     state = 'T';
1060   else
1061     state = get_process_state (p->dwProcessId);
1062   start_time = (GetTickCount () / 1000 - time (NULL) + p->start_time) * HZ;
1063
1064   NTSTATUS ret;
1065   HANDLE hProcess;
1066   VM_COUNTERS vmc;
1067   KERNEL_USER_TIMES put;
1068   PROCESS_BASIC_INFORMATION pbi;
1069   QUOTA_LIMITS ql;
1070   SYSTEM_TIME_OF_DAY_INFORMATION stodi;
1071   SYSTEM_PROCESSOR_TIMES spt;
1072   hProcess = OpenProcess (PROCESS_VM_READ | PROCESS_QUERY_INFORMATION,
1073                           FALSE, p->dwProcessId);
1074   if (hProcess != NULL)
1075     {
1076       ret = NtQueryInformationProcess (hProcess,
1077                                        ProcessVmCounters,
1078                                        (PVOID) &vmc,
1079                                        sizeof vmc, NULL);
1080       if (ret == STATUS_SUCCESS)
1081         ret = NtQueryInformationProcess (hProcess,
1082                                          ProcessTimes,
1083                                          (PVOID) &put,
1084                                          sizeof put, NULL);
1085       if (ret == STATUS_SUCCESS)
1086         ret = NtQueryInformationProcess (hProcess,
1087                                          ProcessBasicInformation,
1088                                          (PVOID) &pbi,
1089                                          sizeof pbi, NULL);
1090       if (ret == STATUS_SUCCESS)
1091         ret = NtQueryInformationProcess (hProcess,
1092                                          ProcessQuotaLimits,
1093                                          (PVOID) &ql,
1094                                          sizeof ql, NULL);
1095       CloseHandle (hProcess);
1096     }
1097   else
1098     {
1099       DWORD error = GetLastError ();
1100       __seterrno_from_win_error (error);
1101       debug_printf ("OpenProcess: ret %d", error);
1102       return 0;
1103     }
1104   if (ret == STATUS_SUCCESS)
1105     ret = NtQuerySystemInformation (SystemTimeOfDayInformation,
1106                                     (PVOID) &stodi,
1107                                     sizeof stodi, NULL);
1108   if (ret == STATUS_SUCCESS)
1109     ret = NtQuerySystemInformation (SystemProcessorTimes,
1110                                     (PVOID) &spt,
1111                                     sizeof spt, NULL);
1112   if (ret != STATUS_SUCCESS)
1113     {
1114       __seterrno_from_nt_status (ret);
1115       debug_printf ("NtQueryInformationProcess: ret %d, Dos(ret) %E", ret);
1116       return 0;
1117     }
1118   fault_count = vmc.PageFaultCount;
1119   utime = put.UserTime.QuadPart * HZ / 10000000ULL;
1120   stime = put.KernelTime.QuadPart * HZ / 10000000ULL;
1121 #if 0
1122    if (stodi.CurrentTime.QuadPart > put.CreateTime.QuadPart)
1123      start_time = (spt.KernelTime.QuadPart + spt.UserTime.QuadPart -
1124                    stodi.CurrentTime.QuadPart + put.CreateTime.QuadPart) * HZ / 10000000ULL;
1125    else
1126      /*
1127       * sometimes stodi.CurrentTime is a bit behind
1128       * Note: some older versions of procps are broken and can't cope
1129       * with process start times > time(NULL).
1130       */
1131      start_time = (spt.KernelTme.QuadPart + spt.UserTime.QuadPart) * HZ / 10000000ULL;
1132 #endif
1133   priority = pbi.BasePriority;
1134   unsigned page_size = getsystempagesize ();
1135   vmsize = vmc.PagefileUsage;
1136   vmrss = vmc.WorkingSetSize / page_size;
1137   vmmaxrss = ql.MaximumWorkingSetSize / page_size;
1138
1139   destbuf = (char *) crealloc_abort (destbuf, strlen (cmd) + 320);
1140   return __small_sprintf (destbuf, "%d (%s) %c "
1141                                    "%d %d %d %d %d "
1142                                    "%lu %lu %lu %lu %lu %lu %lu "
1143                                    "%ld %ld %ld %ld %ld %ld "
1144                                    "%lu %lu "
1145                                    "%ld "
1146                                    "%lu",
1147                           p->pid, cmd,
1148                           state,
1149                           p->ppid, p->pgid, p->sid, p->ctty,
1150                           -1, 0, fault_count, fault_count, 0, 0, utime, stime,
1151                           utime, stime, priority, 0, 0, 0,
1152                           start_time, vmsize,
1153                           vmrss, vmmaxrss
1154                           );
1155 }
1156
1157 static _off64_t
1158 format_process_status (void *data, char *&destbuf)
1159 {
1160   _pinfo *p = (_pinfo *) data;
1161   char cmd[NAME_MAX + 1];
1162   WCHAR wcmd[NAME_MAX + 1];
1163   int state = 'R';
1164   const char *state_str = "unknown";
1165   unsigned long vmsize = 0UL, vmrss = 0UL, vmdata = 0UL, vmlib = 0UL, vmtext = 0UL,
1166                 vmshare = 0UL;
1167   PWCHAR last_slash = wcsrchr (p->progname, L'\\');
1168   wcscpy (wcmd, last_slash ? last_slash + 1 : p->progname);
1169   sys_wcstombs (cmd, NAME_MAX + 1, wcmd);
1170   int len = strlen (cmd);
1171   if (len > 4)
1172     {
1173       char *s = cmd + len - 4;
1174       if (ascii_strcasematch (s, ".exe"))
1175         *s = 0;
1176      }
1177   /*
1178    * Note: under Windows, a _process_ is always running - it's only _threads_
1179    * that get suspended. Therefore the default state is R (runnable).
1180    */
1181   if (p->process_state & PID_EXITED)
1182     state = 'Z';
1183   else if (p->process_state & PID_STOPPED)
1184     state = 'T';
1185   else
1186     state = get_process_state (p->dwProcessId);
1187   switch (state)
1188     {
1189     case 'O':
1190       state_str = "running";
1191       break;
1192     case 'D':
1193     case 'S':
1194       state_str = "sleeping";
1195       break;
1196     case 'R':
1197       state_str = "runnable";
1198       break;
1199     case 'Z':
1200       state_str = "zombie";
1201       break;
1202     case 'T':
1203       state_str = "stopped";
1204       break;
1205     }
1206   if (!get_mem_values (p->dwProcessId, &vmsize, &vmrss, &vmtext, &vmdata,
1207                        &vmlib, &vmshare))
1208     return 0;
1209   unsigned page_size = getsystempagesize ();
1210   vmsize *= page_size; vmrss *= page_size; vmdata *= page_size;
1211   vmtext *= page_size; vmlib *= page_size;
1212   // The real uid value for *this* process is stored at cygheap->user.real_uid
1213   // but we can't get at the real uid value for any other process, so
1214   // just fake it as p->uid. Similar for p->gid.
1215   destbuf = (char *) crealloc_abort (destbuf, strlen (cmd) + 320);
1216   return __small_sprintf (destbuf, "Name:\t%s\n"
1217                                    "State:\t%c (%s)\n"
1218                                    "Tgid:\t%d\n"
1219                                    "Pid:\t%d\n"
1220                                    "PPid:\t%d\n"
1221                                    "Uid:\t%d %d %d %d\n"
1222                                    "Gid:\t%d %d %d %d\n"
1223                                    "VmSize:\t%8d kB\n"
1224                                    "VmLck:\t%8d kB\n"
1225                                    "VmRSS:\t%8d kB\n"
1226                                    "VmData:\t%8d kB\n"
1227                                    "VmStk:\t%8d kB\n"
1228                                    "VmExe:\t%8d kB\n"
1229                                    "VmLib:\t%8d kB\n"
1230                                    "SigPnd:\t%016x\n"
1231                                    "SigBlk:\t%016x\n"
1232                                    "SigIgn:\t%016x\n",
1233                           cmd,
1234                           state, state_str,
1235                           p->pgid,
1236                           p->pid,
1237                           p->ppid,
1238                           p->uid, p->uid, p->uid, p->uid,
1239                           p->gid, p->gid, p->gid, p->gid,
1240                           vmsize >> 10, 0, vmrss >> 10, vmdata >> 10, 0,
1241                           vmtext >> 10, vmlib >> 10,
1242                           0, 0, _my_tls.sigmask
1243                           );
1244 }
1245
1246 static _off64_t
1247 format_process_statm (void *data, char *&destbuf)
1248 {
1249   _pinfo *p = (_pinfo *) data;
1250   unsigned long vmsize = 0UL, vmrss = 0UL, vmtext = 0UL, vmdata = 0UL,
1251                 vmlib = 0UL, vmshare = 0UL;
1252   if (!get_mem_values (p->dwProcessId, &vmsize, &vmrss, &vmtext, &vmdata,
1253                        &vmlib, &vmshare))
1254     return 0;
1255   destbuf = (char *) crealloc_abort (destbuf, 96);
1256   return __small_sprintf (destbuf, "%ld %ld %ld %ld %ld %ld %ld",
1257                           vmsize, vmrss, vmshare, vmtext, vmlib, vmdata, 0);
1258 }
1259
1260 extern "C" {
1261   FILE *setmntent (const char *, const char *);
1262   struct mntent *getmntent (FILE *);
1263 };
1264
1265 static _off64_t
1266 format_process_mounts (void *data, char *&destbuf)
1267 {
1268   _pinfo *p = (_pinfo *) data;
1269   user_info *u_shared = NULL;
1270   HANDLE u_hdl = NULL;
1271   _off64_t len = 0;
1272   struct mntent *mnt;
1273
1274   if (p->pid != myself->pid)
1275     {
1276       WCHAR sid_string[UNLEN + 1] = L""; /* Large enough for SID */
1277
1278       cygsid p_sid;
1279
1280       if (!p_sid.getfrompw (internal_getpwuid (p->uid)))
1281         return 0;
1282       p_sid.string (sid_string);
1283       u_shared = (user_info *) open_shared (sid_string, USER_VERSION, u_hdl,
1284                                             sizeof (user_info), SH_JUSTOPEN,
1285                                             &sec_none_nih);
1286       if (!u_shared)
1287         return 0;
1288     }
1289   else
1290     u_shared = user_shared;
1291
1292   /* Store old value of _my_tls.locals here. */
1293   int iteration = _my_tls.locals.iteration;
1294   unsigned available_drives = _my_tls.locals.available_drives;
1295   /* This reinitializes the above values in _my_tls. */
1296   setmntent (NULL, NULL);
1297   while ((mnt = getmntent (NULL)))
1298     {
1299       destbuf = (char *) crealloc_abort (destbuf, len
1300                                                   + strlen (mnt->mnt_fsname)
1301                                                   + strlen (mnt->mnt_dir)
1302                                                   + strlen (mnt->mnt_type)
1303                                                   + strlen (mnt->mnt_opts)
1304                                                   + 28);
1305       len += __small_sprintf (destbuf + len, "%s %s %s %s %d %d\n",
1306                               mnt->mnt_fsname, mnt->mnt_dir, mnt->mnt_type,
1307                               mnt->mnt_opts, mnt->mnt_freq, mnt->mnt_passno);
1308     }
1309   /* Restore old value of _my_tls.locals here. */
1310   _my_tls.locals.iteration = iteration;
1311   _my_tls.locals.available_drives = available_drives;
1312
1313   if (u_hdl) /* Only not-NULL if open_shared has been called. */
1314     {
1315       UnmapViewOfFile (u_shared);
1316       CloseHandle (u_hdl);
1317     }
1318   return len;
1319 }
1320
1321 int
1322 get_process_state (DWORD dwProcessId)
1323 {
1324   /*
1325    * This isn't really heavy magic - just go through the processes'
1326    * threads one by one and return a value accordingly
1327    * Errors are silently ignored.
1328    */
1329   NTSTATUS ret;
1330   SYSTEM_PROCESSES *sp;
1331   ULONG n = 0x1000;
1332   PULONG p = new ULONG[n];
1333   int state =' ';
1334   while (STATUS_INFO_LENGTH_MISMATCH ==
1335          (ret = NtQuerySystemInformation (SystemProcessesAndThreadsInformation,
1336                                          (PVOID) p,
1337                                          n * sizeof *p, NULL)))
1338     delete [] p, p = new ULONG[n *= 2];
1339   if (ret != STATUS_SUCCESS)
1340     {
1341       debug_printf ("NtQuerySystemInformation: ret %d, Dos(ret) %d",
1342                     ret, RtlNtStatusToDosError (ret));
1343       goto out;
1344     }
1345   state = 'Z';
1346   sp = (SYSTEM_PROCESSES *) p;
1347   for (;;)
1348     {
1349       if (sp->ProcessId == dwProcessId)
1350         {
1351           SYSTEM_THREADS *st;
1352           st = &sp->Threads[0];
1353           state = 'S';
1354           for (unsigned i = 0; i < sp->ThreadCount; i++)
1355             {
1356               /* FIXME: at some point we should consider generating 'O' */
1357               if (st->State == StateRunning ||
1358                   st->State == StateReady)
1359                 {
1360                   state = 'R';
1361                   goto out;
1362                 }
1363               st++;
1364             }
1365           break;
1366         }
1367       if (!sp->NextEntryDelta)
1368          break;
1369       sp = (SYSTEM_PROCESSES *) ((char *) sp + sp->NextEntryDelta);
1370     }
1371 out:
1372   delete [] p;
1373   return state;
1374 }
1375
1376 static bool
1377 get_mem_values (DWORD dwProcessId, unsigned long *vmsize, unsigned long *vmrss,
1378                 unsigned long *vmtext, unsigned long *vmdata,
1379                 unsigned long *vmlib, unsigned long *vmshare)
1380 {
1381   bool res = false;
1382   NTSTATUS ret;
1383   HANDLE hProcess;
1384   VM_COUNTERS vmc;
1385   MEMORY_WORKING_SET_LIST *mwsl;
1386   ULONG n = 0x4000, length;
1387   PMEMORY_WORKING_SET_LIST p = (PMEMORY_WORKING_SET_LIST) malloc (n);
1388   unsigned page_size = getsystempagesize ();
1389   hProcess = OpenProcess (PROCESS_QUERY_INFORMATION,
1390                           FALSE, dwProcessId);
1391   if (hProcess == NULL)
1392     {
1393       __seterrno ();
1394       debug_printf ("OpenProcess, %E");
1395       return false;
1396     }
1397   while (true)
1398     {
1399       ret = NtQueryVirtualMemory (hProcess, 0, MemoryWorkingSetList,
1400                                   (PVOID) p, n, (length = ULONG_MAX, &length));
1401       if (ret != STATUS_INFO_LENGTH_MISMATCH)
1402         break;
1403       n <<= 1;
1404       PMEMORY_WORKING_SET_LIST new_p = (PMEMORY_WORKING_SET_LIST)
1405                                        realloc (p, n);
1406       if (!new_p)
1407         goto out;
1408       p = new_p;
1409     }
1410   if (!NT_SUCCESS (ret))
1411     {
1412       debug_printf ("NtQueryVirtualMemory: ret %p", ret);
1413       if (ret == STATUS_PROCESS_IS_TERMINATING)
1414         {
1415           *vmsize = *vmrss = *vmtext = *vmdata = *vmlib = *vmshare = 0;
1416           res = true;
1417         }
1418       else
1419         __seterrno_from_nt_status (ret);
1420       goto out;
1421     }
1422   mwsl = (MEMORY_WORKING_SET_LIST *) p;
1423   for (unsigned long i = 0; i < mwsl->NumberOfPages; i++)
1424     {
1425       ++*vmrss;
1426       unsigned flags = mwsl->WorkingSetList[i] & 0x0FFF;
1427       if ((flags & (WSLE_PAGE_EXECUTE | WSLE_PAGE_SHAREABLE))
1428           == (WSLE_PAGE_EXECUTE | WSLE_PAGE_SHAREABLE))
1429         ++*vmlib;
1430       else if (flags & WSLE_PAGE_SHAREABLE)
1431         ++*vmshare;
1432       else if (flags & WSLE_PAGE_EXECUTE)
1433         ++*vmtext;
1434       else
1435         ++*vmdata;
1436     }
1437   ret = NtQueryInformationProcess (hProcess, ProcessVmCounters, (PVOID) &vmc,
1438                                    sizeof vmc, NULL);
1439   if (!NT_SUCCESS (ret))
1440     {
1441       debug_printf ("NtQueryInformationProcess: ret %p", ret);
1442       __seterrno_from_nt_status (ret);
1443       goto out;
1444     }
1445   *vmsize = vmc.PagefileUsage / page_size;
1446   res = true;
1447 out:
1448   free (p);
1449   CloseHandle (hProcess);
1450   return res;
1451 }