OSDN Git Service

* cygwin.sc: Add recent changes from ld sources.
[pf3gnuchains/sourceware.git] / winsup / cygwin / fhandler_proc.cc
1 /* fhandler_proc.cc: fhandler for /proc virtual filesystem
2
3    Copyright 2002 Red Hat, Inc.
4
5 This file is part of Cygwin.
6
7 This software is a copyrighted work licensed under the terms of the
8 Cygwin license.  Please consult the file "CYGWIN_LICENSE" for
9 details. */
10
11 #include "winsup.h"
12 #include <errno.h>
13 #include <unistd.h>
14 #include <stdlib.h>
15 #include <sys/cygwin.h>
16 #include <ntdef.h>
17 #include "cygerrno.h"
18 #include "security.h"
19 #include "fhandler.h"
20 #include "path.h"
21 #include "pinfo.h"
22 #include "dtable.h"
23 #include "cygheap.h"
24 #include <assert.h>
25 #include <sys/utsname.h>
26 #include "ntdll.h"
27
28 #define _COMPILING_NEWLIB
29 #include <dirent.h>
30
31 /* offsets in proc_listing */
32 static const int PROC_LOADAVG  = 2;     // /proc/loadavg
33 static const int PROC_MEMINFO  = 3;     // /proc/meminfo
34 static const int PROC_REGISTRY = 4;     // /proc/registry
35 static const int PROC_STAT     = 5;     // /proc/stat
36 static const int PROC_VERSION  = 6;     // /proc/version
37 static const int PROC_UPTIME   = 7;     // /proc/uptime
38
39 /* names of objects in /proc */
40 static const char *proc_listing[] = {
41   ".",
42   "..",
43   "loadavg",
44   "meminfo",
45   "registry",
46   "stat",
47   "version",
48   "uptime",
49   NULL
50 };
51
52 static const int PROC_LINK_COUNT = (sizeof(proc_listing) / sizeof(const char *)) - 1;
53
54 /* FH_PROC in the table below means the file/directory is handles by
55  * fhandler_proc.
56  */
57 static const DWORD proc_fhandlers[PROC_LINK_COUNT] = {
58   FH_PROC,
59   FH_PROC,
60   FH_PROC,
61   FH_PROC,
62   FH_REGISTRY,
63   FH_PROC,
64   FH_PROC,
65   FH_PROC
66 };
67
68 /* name of the /proc filesystem */
69 const char proc[] = "/proc";
70 const int proc_len = sizeof (proc) - 1;
71
72 static off_t format_proc_meminfo (char *destbuf, size_t maxsize);
73 static off_t format_proc_stat (char *destbuf, size_t maxsize);
74 static off_t format_proc_uptime (char *destbuf, size_t maxsize);
75
76 /* auxillary function that returns the fhandler associated with the given path
77  * this is where it would be nice to have pattern matching in C - polymorphism
78  * just doesn't cut it
79  */
80 DWORD
81 fhandler_proc::get_proc_fhandler (const char *path)
82 {
83   debug_printf ("get_proc_fhandler(%s)", path);
84   path += proc_len;
85   /* Since this method is called from path_conv::check we can't rely on
86    * it being normalised and therefore the path may have runs of slashes
87    * in it.
88    */
89   while (SLASH_P (*path))
90     path++;
91
92   /* Check if this is the root of the virtual filesystem (i.e. /proc).  */
93   if (*path == 0)
94     return FH_PROC;
95
96   for (int i = 0; proc_listing[i]; i++)
97     {
98       if (path_prefix_p (proc_listing[i], path, strlen (proc_listing[i])))
99         return proc_fhandlers[i];
100     }
101
102   if (pinfo (atoi (path)))
103     return FH_PROCESS;
104
105   bool has_subdir = false;
106   while (*path)
107     if (SLASH_P (*path++))
108       {
109         has_subdir = true;
110         break;
111       }
112
113   if (has_subdir)
114     /* The user is trying to access a non-existent subdirectory of /proc. */
115     return FH_BAD;
116   else
117     /* Return FH_PROC so that we can return EROFS if the user is trying to create
118        a file. */
119     return FH_PROC;
120 }
121
122 /* Returns 0 if path doesn't exist, >0 if path is a directory,
123  * <0 if path is a file.
124  */
125 int
126 fhandler_proc::exists ()
127 {
128   const char *path = get_name ();
129   debug_printf ("exists (%s)", path);
130   path += proc_len;
131   if (*path == 0)
132     return 2;
133   for (int i = 0; proc_listing[i]; i++)
134     if (pathmatch (path + 1, proc_listing[i]))
135       return (proc_fhandlers[i] == FH_PROC) ? -1 : 1;
136   return 0;
137 }
138
139 fhandler_proc::fhandler_proc ():
140   fhandler_virtual (FH_PROC)
141 {
142 }
143
144 fhandler_proc::fhandler_proc (DWORD devtype):
145   fhandler_virtual (devtype)
146 {
147 }
148
149 int
150 fhandler_proc::fstat (struct __stat64 *buf, path_conv *pc)
151 {
152   const char *path = get_name ();
153   debug_printf ("fstat (%s)", path);
154
155   path += proc_len;
156   (void) fhandler_base::fstat (buf, pc);
157
158   buf->st_mode &= ~_IFMT & NO_W;
159
160   if (!*path)
161     {
162       buf->st_nlink = PROC_LINK_COUNT;
163       buf->st_mode |= S_IFDIR | S_IXUSR | S_IXGRP | S_IXOTH;
164       return 0;
165     }
166   else
167     {
168       path++;
169       for (int i = 0; proc_listing[i]; i++)
170         if (pathmatch (path, proc_listing[i]))
171           {
172             if (proc_fhandlers[i] != FH_PROC)
173               buf->st_mode |= S_IFDIR | S_IXUSR | S_IXGRP | S_IXOTH;
174             else
175               {
176                 buf->st_mode &= NO_X;
177                 buf->st_mode |= S_IFREG;
178               }
179             return 0;
180           }
181     }
182   set_errno (ENOENT);
183   return -1;
184 }
185
186 struct dirent *
187 fhandler_proc::readdir (DIR * dir)
188 {
189   if (dir->__d_position >= PROC_LINK_COUNT)
190     {
191       winpids pids;
192       int found = 0;
193       for (unsigned i = 0; i < pids.npids; i++)
194         if (found++ == dir->__d_position - PROC_LINK_COUNT)
195           {
196             __small_sprintf (dir->__d_dirent->d_name, "%d", pids[i]->pid);
197             dir->__d_position++;
198             return dir->__d_dirent;
199           }
200       set_errno (ENMFILE);
201       return NULL;
202     }
203
204   strcpy (dir->__d_dirent->d_name, proc_listing[dir->__d_position++]);
205   syscall_printf ("%p = readdir (%p) (%s)", &dir->__d_dirent, dir,
206                   dir->__d_dirent->d_name);
207   return dir->__d_dirent;
208 }
209
210 int
211 fhandler_proc::open (path_conv *pc, int flags, mode_t mode)
212 {
213   int proc_file_no = -1;
214
215   int res = fhandler_virtual::open (pc, flags, mode);
216   if (!res)
217     goto out;
218
219   const char *path;
220
221   path = get_name () + proc_len;
222
223   if (!*path)
224     {
225       if ((flags & (O_CREAT | O_EXCL)) == (O_CREAT | O_EXCL))
226         {
227           set_errno (EEXIST);
228           res = 0;
229           goto out;
230         }
231       else if (flags & O_WRONLY)
232         {
233           set_errno (EISDIR);
234           res = 0;
235           goto out;
236         }
237       else
238         {
239           flags |= O_DIROPEN;
240           goto success;
241         }
242     }
243
244   proc_file_no = -1;
245   for (int i = 0; proc_listing[i]; i++)
246     if (path_prefix_p (proc_listing[i], path + 1, strlen (proc_listing[i])))
247       {
248         proc_file_no = i;
249         if (proc_fhandlers[i] != FH_PROC)
250           {
251             if ((flags & (O_CREAT | O_EXCL)) == (O_CREAT | O_EXCL))
252               {
253                 set_errno (EEXIST);
254                 res = 0;
255                 goto out;
256               }
257             else if (flags & O_WRONLY)
258               {
259                 set_errno (EISDIR);
260                 res = 0;
261                 goto out;
262               }
263             else
264               {
265                 flags |= O_DIROPEN;
266                 goto success;
267               }
268           }
269       }
270
271   if (proc_file_no == -1)
272     {
273       if (flags & O_CREAT)
274         {
275           set_errno (EROFS);
276           res = 0;
277           goto out;
278         }
279       else
280         {
281           set_errno (ENOENT);
282           res = 0;
283           goto out;
284         }
285     }
286   if (flags & O_WRONLY)
287     {
288       set_errno (EROFS);
289       res = 0;
290       goto out;
291     }
292
293   fileid = proc_file_no;
294   fill_filebuf ();
295
296   if (flags & O_APPEND)
297     position = filesize;
298   else
299     position = 0;
300
301 success:
302   res = 1;
303   set_flags (flags & ~O_TEXT, O_BINARY);
304   set_open_status ();
305 out:
306   syscall_printf ("%d = fhandler_proc::open (%p, %d)", res, flags, mode);
307   return res;
308 }
309
310 void
311 fhandler_proc::fill_filebuf ()
312 {
313   switch (fileid)
314     {
315     case PROC_VERSION:
316       {
317         if (!filebuf)
318           {
319             struct utsname uts_name;
320             uname (&uts_name);
321                 bufalloc = strlen (uts_name.sysname) + 1 + strlen (uts_name.release) +
322                           1 + strlen (uts_name.version) + 2;
323             filebuf = (char *) cmalloc (HEAP_BUF, bufalloc);
324                 filesize = __small_sprintf (filebuf, "%s %s %s\n", uts_name.sysname,
325                              uts_name.release, uts_name.version);
326           }
327         break;
328       }
329     case PROC_UPTIME:
330       {
331         if (!filebuf)
332           filebuf = (char *) cmalloc (HEAP_BUF, bufalloc = 80);
333         filesize = format_proc_uptime (filebuf, bufalloc);
334         break;
335       }
336     case PROC_STAT:
337       {
338         if (!filebuf)
339           filebuf = (char *) cmalloc (HEAP_BUF, bufalloc = 2048);
340         filesize = format_proc_stat (filebuf, bufalloc);
341         break;
342       }
343     case PROC_LOADAVG:
344       {
345         /*
346          * not really supported - Windows doesn't keep track of these values
347          * Windows 95/98/me does have the KERNEL/CPUUsage performance counter
348          * which is similar.
349          */
350         if (!filebuf)
351           filebuf = (char *) cmalloc (HEAP_BUF, bufalloc = 16);
352         filesize = __small_sprintf (filebuf, "%u.%02u %u.%02u %u.%02u\n",
353                                     0, 0, 0, 0, 0, 0);
354         break;
355       }
356     case PROC_MEMINFO:
357       {
358         if (!filebuf)
359           filebuf = (char *) cmalloc (HEAP_BUF, bufalloc = 2048);
360         filesize = format_proc_meminfo (filebuf, bufalloc);
361         break;
362       }
363     }
364 }
365
366 static
367 off_t
368 format_proc_meminfo (char *destbuf, size_t maxsize)
369 {
370   unsigned long mem_total = 0UL, mem_free = 0UL, swap_total = 0UL,
371                 swap_free = 0UL;
372   MEMORYSTATUS memory_status;
373   GlobalMemoryStatus (&memory_status);
374   mem_total = memory_status.dwTotalPhys;
375   mem_free = memory_status.dwAvailPhys;
376   swap_total = memory_status.dwTotalPageFile;
377   swap_free = memory_status.dwAvailPageFile;
378   return __small_sprintf (destbuf, "         total:      used:      free:\n"
379                                    "Mem:  %10lu %10lu %10lu\n"
380                                    "Swap: %10lu %10lu %10lu\n"
381                                    "MemTotal:     %10lu kB\n"
382                                    "MemFree:      %10lu kB\n"
383                                    "MemShared:             0 kB\n"
384                                    "HighTotal:             0 kB\n"
385                                    "HighFree:              0 kB\n"
386                                    "LowTotal:     %10lu kB\n"
387                                    "LowFree:      %10lu kB\n"
388                                    "SwapTotal:    %10lu kB\n"
389                                    "SwapFree:     %10lu kB\n",
390                                    mem_total, mem_total - mem_free, mem_free,
391                                    swap_total, swap_total - swap_free, swap_free,
392                                    mem_total >> 10, mem_free >> 10,
393                                    mem_total >> 10, mem_free >> 10,
394                                    swap_total >> 10, swap_free >> 10);
395 }
396
397 static
398 off_t
399 format_proc_uptime (char *destbuf, size_t maxsize)
400 {
401   unsigned long long uptime = 0ULL, idle_time = 0ULL;
402   SYSTEM_PROCESSOR_TIMES spt;
403
404   NTSTATUS ret = NtQuerySystemInformation (SystemProcessorTimes, (PVOID) &spt,
405                                            sizeof spt, NULL);
406   if (!ret && GetLastError () == ERROR_PROC_NOT_FOUND)
407     uptime = GetTickCount() / 10;
408   else if (ret != STATUS_SUCCESS)
409     {
410       __seterrno_from_win_error (RtlNtStatusToDosError (ret));
411       debug_printf("NtQuerySystemInformation: ret = %d, "
412                    "Dos(ret) = %d",
413                    ret, RtlNtStatusToDosError (ret));
414       return 0;
415     }
416   else
417     {
418       idle_time = spt.IdleTime.QuadPart / 100000ULL;
419       uptime = (spt.InterruptTime.QuadPart + spt.KernelTime.QuadPart +
420                 spt.IdleTime.QuadPart + spt.UserTime.QuadPart +
421                 spt.DpcTime.QuadPart) / 100000ULL;
422     }
423
424   return __small_sprintf (destbuf, "%U.%02u %U.%02u\n",
425                           uptime / 100, long (uptime % 100),
426                           idle_time / 100, long (idle_time % 100));
427 }
428
429 static
430 off_t
431 format_proc_stat (char *destbuf, size_t maxsize)
432 {
433   unsigned long long user_time = 0ULL, kernel_time = 0ULL, idle_time = 0ULL;
434   unsigned long pages_in = 0UL, pages_out = 0UL, interrupt_count = 0UL,
435                 context_switches = 0UL, swap_in = 0UL, swap_out = 0UL;
436   time_t boot_time = 0;
437
438   if (wincap.is_winnt ())
439     {
440       NTSTATUS ret;
441       SYSTEM_PROCESSOR_TIMES spt;
442       SYSTEM_PERFORMANCE_INFORMATION spi;
443       SYSTEM_TIME_OF_DAY_INFORMATION stodi;
444       ret = NtQuerySystemInformation (SystemProcessorTimes,
445                                       (PVOID) &spt,
446                                       sizeof spt, NULL);
447       if (ret == STATUS_SUCCESS)
448         ret = NtQuerySystemInformation (SystemPerformanceInformation,
449                                         (PVOID) &spi,
450                                         sizeof spi, NULL);
451       if (ret == STATUS_SUCCESS)
452         ret = NtQuerySystemInformation (SystemTimeOfDayInformation,
453                                         (PVOID) &stodi,
454                                         sizeof stodi, NULL);
455       if (ret != STATUS_SUCCESS)
456         {
457           __seterrno_from_win_error (RtlNtStatusToDosError (ret));
458           debug_printf("NtQuerySystemInformation: ret = %d, "
459                        "Dos(ret) = %d",
460                        ret, RtlNtStatusToDosError (ret));
461           return 0;
462         }
463       kernel_time = (spt.KernelTime.QuadPart + spt.InterruptTime.QuadPart + spt.DpcTime.QuadPart) / 100000ULL;
464       user_time = spt.UserTime.QuadPart / 100000ULL;
465       idle_time = spt.IdleTime.QuadPart / 100000ULL;
466       interrupt_count = spt.InterruptCount;
467       pages_in = spi.PagesRead;
468       pages_out = spi.PagefilePagesWritten + spi.MappedFilePagesWritten;
469       /*
470        * Note: there is no distinction made in this structure between pages
471        * read from the page file and pages read from mapped files, but there
472        * is such a distinction made when it comes to writing. Goodness knows
473        * why. The value of swap_in, then, will obviously be wrong but its our
474        * best guess.
475        */
476       swap_in = spi.PagesRead;
477       swap_out = spi.PagefilePagesWritten;
478       context_switches = spi.ContextSwitches;
479       boot_time = to_time_t ((FILETIME *) &stodi.BootTime.QuadPart);
480     }
481   /*
482    * else
483    *   {
484    * There are only two relevant performance counters on Windows 95/98/me,
485    * VMM/cPageIns and VMM/cPageOuts. The extra effort needed to read these
486    * counters is by no means worth it.
487    *   }
488    */
489   return __small_sprintf (destbuf, "cpu %U %U %U %U\n"
490                                    "page %u %u\n"
491                                    "swap %u %u\n"
492                                    "intr %u\n"
493                                    "ctxt %u\n"
494                                    "btime %u\n",
495                           user_time, 0ULL,
496                           kernel_time, idle_time,
497                           pages_in, pages_out,
498                           swap_in, swap_out,
499                           interrupt_count,
500                           context_switches,
501                           boot_time);
502 }