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