55ef5b661a701815485420c65304ab196415b705
[kopensolaris-gnu/glibc.git] / sysdeps / unix / sysv / linux / getsysstats.c
1 /* Determine various system internal values, Linux version.
2    Copyright (C) 1996-2001, 2002 Free Software Foundation, Inc.
3    This file is part of the GNU C Library.
4    Contributed by Ulrich Drepper <drepper@cygnus.com>, 1996.
5
6    The GNU C Library is free software; you can redistribute it and/or
7    modify it under the terms of the GNU Lesser General Public
8    License as published by the Free Software Foundation; either
9    version 2.1 of the License, or (at your option) any later version.
10
11    The GNU C Library is distributed in the hope that it will be useful,
12    but WITHOUT ANY WARRANTY; without even the implied warranty of
13    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14    Lesser General Public License for more details.
15
16    You should have received a copy of the GNU Lesser General Public
17    License along with the GNU C Library; if not, write to the Free
18    Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
19    02111-1307 USA.  */
20
21 #include <alloca.h>
22 #include <assert.h>
23 #include <ctype.h>
24 #include <errno.h>
25 #include <mntent.h>
26 #include <paths.h>
27 #include <stdio.h>
28 #include <stdio_ext.h>
29 #include <stdlib.h>
30 #include <string.h>
31 #include <unistd.h>
32 #include <sys/sysinfo.h>
33
34 #include <atomicity.h>
35
36
37 /* The default value for the /proc filesystem mount point.  */
38 static const char path_proc[] = "/proc";
39
40 /* Actual mount point of /proc filesystem.  */
41 libc_freeres_ptr (static char *mount_proc);
42
43 /* Determine the path to the /proc filesystem if available.  */
44 static const char *
45 internal_function
46 get_proc_path (char *buffer, size_t bufsize)
47 {
48   struct mntent mount_point;
49   struct mntent *entry;
50   char *result = NULL;
51   char *copy_result;
52   FILE *fp;
53
54   /* First find the mount point of the proc filesystem.  */
55   fp = __setmntent (_PATH_MOUNTED, "r");
56   if (fp == NULL)
57     fp = __setmntent (_PATH_MNTTAB, "r");
58   if (fp != NULL)
59     {
60       /* We don't need locking.  */
61       (void) __fsetlocking (fp, FSETLOCKING_BYCALLER);
62
63       while ((entry = __getmntent_r (fp, &mount_point, buffer, bufsize))
64              != NULL)
65         if (strcmp (mount_point.mnt_type, "proc") == 0)
66           {
67             result = mount_point.mnt_dir;
68             break;
69           }
70       __endmntent (fp);
71     }
72
73   /* If we haven't found anything this is generally a bad sign but we
74      handle it gracefully.  We return what is hopefully the right
75      answer (/proc) but we don't remember this.  This will enable
76      programs which started before the system is fully running to
77      adjust themselves.  */
78   if (result == NULL)
79     return path_proc;
80
81   /* Make a copy we can keep around.  */
82   copy_result = __strdup (result);
83   if (copy_result == NULL)
84     return result;
85
86   /* Now store the copied value.  But do it atomically.  */
87   assert (sizeof (long int) == sizeof (void *__unbounded));
88   if (compare_and_swap ((long int *) &mount_proc, (long int) 0,
89                         (long int) copy_result) == 0)
90     /* Replacing the value failed.  This means another thread was
91        faster and we don't need the copy anymore.  */
92     free (copy_result);
93 #if __BOUNDED_POINTERS__
94   else
95     {
96       /* compare_and_swap only copied the pointer value, so we must
97          now copy the bounds as well.  */
98       __ptrlow (mount_proc) = __ptrlow (copy_result);
99       __ptrhigh (mount_proc) = __ptrhigh (copy_result);
100     }
101 #endif
102
103   return mount_proc;
104 }
105
106
107 /* How we can determine the number of available processors depends on
108    the configuration.  There is currently (as of version 2.0.21) no
109    system call to determine the number.  It is planned for the 2.1.x
110    series to add this, though.
111
112    One possibility to implement it for systems using Linux 2.0 is to
113    examine the pseudo file /proc/cpuinfo.  Here we have one entry for
114    each processor.
115
116    But not all systems have support for the /proc filesystem.  If it
117    is not available we simply return 1 since there is no way.  */
118
119 /* Other architectures use different formats for /proc/cpuinfo.  This
120    provides a hook for alternative parsers.  */
121 #ifndef GET_NPROCS_PARSER
122 # define GET_NPROCS_PARSER(FP, BUFFER, RESULT)                          \
123   do                                                                    \
124     {                                                                   \
125       (RESULT) = 0;                                                     \
126       /* Read all lines and count the lines starting with the string    \
127          "processor".  We don't have to fear extremely long lines since \
128          the kernel will not generate them.  8192 bytes are really      \
129          enough.  */                                                    \
130       while (fgets_unlocked (BUFFER, sizeof (BUFFER), FP) != NULL)      \
131         if (strncmp (BUFFER, "processor", 9) == 0)                      \
132           ++(RESULT);                                                   \
133     }                                                                   \
134   while (0)
135 #endif
136
137 int
138 __get_nprocs ()
139 {
140   FILE *fp;
141   char buffer[8192];
142   const char *proc_path;
143   int result = 1;
144
145   /* XXX Here will come a test for the new system call.  */
146
147   /* Get mount point of proc filesystem.  */
148   proc_path = get_proc_path (buffer, sizeof buffer);
149
150   /* If we haven't found an appropriate entry return 1.  */
151   if (proc_path != NULL)
152     {
153       char *proc_fname = alloca (strlen (proc_path) + sizeof ("/cpuinfo"));
154
155       /* The /proc/stat format is more uniform, use it by default.  */
156       __stpcpy (__stpcpy (proc_fname, proc_path), "/stat");
157
158       fp = fopen (proc_fname, "r");
159       if (fp != NULL)
160         {
161           /* No threads use this stream.  */
162           __fsetlocking (fp, FSETLOCKING_BYCALLER);
163
164           result = 0;
165           while (fgets_unlocked (buffer, sizeof (buffer), fp) != NULL)
166             if (strncmp (buffer, "cpu", 3) == 0 && isdigit (buffer[3]))
167               ++result;
168
169           fclose (fp);
170         }
171       else
172         {
173           __stpcpy (__stpcpy (proc_fname, proc_path), "/cpuinfo");
174
175           fp = fopen (proc_fname, "r");
176           if (fp != NULL)
177             {
178               /* No threads use this stream.  */
179               __fsetlocking (fp, FSETLOCKING_BYCALLER);
180               GET_NPROCS_PARSER (fp, buffer, result);
181               fclose (fp);
182             }
183         }
184     }
185
186   return result;
187 }
188 weak_alias (__get_nprocs, get_nprocs)
189
190
191 #ifdef GET_NPROCS_CONF_PARSER
192 /* On some architectures it is possible to distinguish between configured
193    and active cpus.  */
194 int
195 __get_nprocs_conf ()
196 {
197   FILE *fp;
198   char buffer[8192];
199   const char *proc_path;
200   int result = 1;
201
202   /* XXX Here will come a test for the new system call.  */
203
204   /* Get mount point of proc filesystem.  */
205   proc_path = get_proc_path (buffer, sizeof buffer);
206
207   /* If we haven't found an appropriate entry return 1.  */
208   if (proc_path != NULL)
209     {
210       char *proc_cpuinfo = alloca (strlen (proc_path) + sizeof ("/cpuinfo"));
211       __stpcpy (__stpcpy (proc_cpuinfo, proc_path), "/cpuinfo");
212
213       fp = fopen (proc_cpuinfo, "r");
214       if (fp != NULL)
215         {
216           /* No threads use this stream.  */
217           __fsetlocking (fp, FSETLOCKING_BYCALLER);
218           GET_NPROCS_CONF_PARSER (fp, buffer, result);
219           fclose (fp);
220         }
221     }
222
223   return result;
224 }
225 #else
226 /* As far as I know Linux has no separate numbers for configured and
227    available processors.  So make the `get_nprocs_conf' function an
228    alias.  */
229 strong_alias (__get_nprocs, __get_nprocs_conf)
230 #endif
231 weak_alias (__get_nprocs_conf, get_nprocs_conf)
232
233 /* General function to get information about memory status from proc
234    filesystem.  */
235 static long int
236 internal_function
237 phys_pages_info (const char *format)
238 {
239   FILE *fp;
240   char buffer[8192];
241   const char *proc_path;
242   long int result = -1;
243
244   /* Get mount point of proc filesystem.  */
245   proc_path = get_proc_path (buffer, sizeof buffer);
246
247   /* If we haven't found an appropriate entry return 1.  */
248   if (proc_path != NULL)
249     {
250       char *proc_meminfo = alloca (strlen (proc_path) + sizeof ("/meminfo"));
251       __stpcpy (__stpcpy (proc_meminfo, proc_path), "/meminfo");
252
253       fp = fopen (proc_meminfo, "r");
254       if (fp != NULL)
255         {
256           /* No threads use this stream.  */
257           __fsetlocking (fp, FSETLOCKING_BYCALLER);
258
259           result = 0;
260           /* Read all lines and count the lines starting with the
261              string "processor".  We don't have to fear extremely long
262              lines since the kernel will not generate them.  8192
263              bytes are really enough.  */
264           while (fgets_unlocked (buffer, sizeof buffer, fp) != NULL)
265             if (sscanf (buffer, format, &result) == 1)
266               {
267                 result /= (__getpagesize () / 1024);
268                 break;
269               }
270
271           fclose (fp);
272         }
273     }
274
275   if (result == -1)
276     /* We cannot get the needed value: signal an error.  */
277     __set_errno (ENOSYS);
278
279   return result;
280 }
281
282
283 /* Return the number of pages of physical memory in the system.  There
284    is currently (as of version 2.0.21) no system call to determine the
285    number.  It is planned for the 2.1.x series to add this, though.
286
287    One possibility to implement it for systems using Linux 2.0 is to
288    examine the pseudo file /proc/cpuinfo.  Here we have one entry for
289    each processor.
290
291    But not all systems have support for the /proc filesystem.  If it
292    is not available we return -1 as an error signal.  */
293 long int
294 __get_phys_pages ()
295 {
296   /* XXX Here will come a test for the new system call.  */
297
298   return phys_pages_info ("MemTotal: %ld kB");
299 }
300 weak_alias (__get_phys_pages, get_phys_pages)
301
302
303 /* Return the number of available pages of physical memory in the
304    system.  There is currently (as of version 2.0.21) no system call
305    to determine the number.  It is planned for the 2.1.x series to add
306    this, though.
307
308    One possibility to implement it for systems using Linux 2.0 is to
309    examine the pseudo file /proc/cpuinfo.  Here we have one entry for
310    each processor.
311
312    But not all systems have support for the /proc filesystem.  If it
313    is not available we return -1 as an error signal.  */
314 long int
315 __get_avphys_pages ()
316 {
317   /* XXX Here will come a test for the new system call.  */
318
319   return phys_pages_info ("MemFree: %ld kB");
320 }
321 weak_alias (__get_avphys_pages, get_avphys_pages)