2009-04-15 Ulrich Drepper <drepper@redhat.com> * sysdeps/unix/sysv/linux/getsysstats.c (next_line): Make sure there are always at least 4 bytes in the returned line. 2009-04-15 Jakub Jelinek <jakub@redhat.com> * sysdeps/unix/sysv/linux/getsysstats.c (__get_nprocs): Check __libc_use_alloca (8192), if the stack is too small use 512 bytes instead of 8K. Stop searching in /proc/stat after hitting first line not starting with cpu. (next_line): Truncate too long lines at buffer size * 3/4 instead of pretending there were line breaks inside of large lines. 2009-02-15 Ulrich Drepper <drepper@redhat.com> * sysdeps/unix/sysv/linux/getsysstats.c (next_line): New function. (GET_NPROCS_PARSER): Change parameters and use next_line. (__get_nprocs): Rewrite to not use stdio routines. * sysdeps/unix/sysv/linux/sparc/getsysstats.c (GET_NPROCS_PARSER): Change parameters and use next_line. --- libc/sysdeps/unix/sysv/linux/getsysstats.c 1 Aug 2007 21:23:37 -0000 1.31 +++ libc/sysdeps/unix/sysv/linux/getsysstats.c 15 Apr 2009 15:57:29 -0000 1.33 @@ -22,6 +22,7 @@ #include <assert.h> #include <ctype.h> #include <errno.h> +#include <fcntl.h> #include <mntent.h> #include <paths.h> #include <stdio.h> @@ -46,10 +47,13 @@ But not all systems have support for the /proc filesystem. If it is not available we simply return 1 since there is no way. */ +#include <not-cancel.h> + + /* Other architectures use different formats for /proc/cpuinfo. This provides a hook for alternative parsers. */ #ifndef GET_NPROCS_PARSER -# define GET_NPROCS_PARSER(FP, BUFFER, RESULT) \ +# define GET_NPROCS_PARSER(FD, BUFFER, CP, RE, BUFFER_END, RESULT) \ do \ { \ (RESULT) = 0; \ @@ -57,44 +61,116 @@ "processor". We don't have to fear extremely long lines since \ the kernel will not generate them. 8192 bytes are really \ enough. */ \ - while (fgets_unlocked (BUFFER, sizeof (BUFFER), FP) != NULL) \ - if (strncmp (BUFFER, "processor", 9) == 0) \ + char *l; \ + while ((l = next_line (FD, BUFFER, &CP, &RE, BUFFER_END)) != NULL) \ + if (strncmp (l, "processor", 9) == 0) \ ++(RESULT); \ } \ while (0) #endif + +static char * +next_line (int fd, char *const buffer, char **cp, char **re, + char *const buffer_end) +{ + char *res = *cp; + char *nl = memchr (*cp, '\n', *re - *cp); + if (nl == NULL) + { + if (*cp != buffer) + { + if (*re == buffer_end) + { + memmove (buffer, *cp, *re - *cp); + *re = buffer + (*re - *cp); + *cp = buffer; + + ssize_t n = read_not_cancel (fd, *re, buffer_end - *re); + if (n < 0) + return NULL; + + *re += n; + + nl = memchr (*cp, '\n', *re - *cp); + while (nl == NULL && *re == buffer_end) + { + /* Truncate too long lines. */ + *re = buffer + 3 * (buffer_end - buffer) / 4; + n = read_not_cancel (fd, *re, buffer_end - *re); + if (n < 0) + return NULL; + + nl = memchr (*re, '\n', n); + **re = '\n'; + *re += n; + } + } + else + nl = memchr (*cp, '\n', *re - *cp); + + res = *cp; + } + + if (nl == NULL) + nl = *re - 1; + } + else if (nl + 5 >= *re) + { + memmove (buffer, nl, *re - nl); + *re = buffer + (*re - nl); + nl = *cp = buffer; + + ssize_t n = read_not_cancel (fd, *re, buffer_end - *re); + if (n < 0) + return NULL; + + *re += n; + } + + *cp = nl + 1; + assert (*cp <= *re); + + return res == *re ? NULL : res; +} + + int __get_nprocs () { - char buffer[8192]; - int result = 1; - /* XXX Here will come a test for the new system call. */ - /* The /proc/stat format is more uniform, use it by default. */ - FILE *fp = fopen ("/proc/stat", "rc"); - if (fp != NULL) - { - /* No threads use this stream. */ - __fsetlocking (fp, FSETLOCKING_BYCALLER); + const size_t buffer_size = __libc_use_alloca (8192) ? 8192 : 512; + char *buffer = alloca (buffer_size); + char *buffer_end = buffer + buffer_size; + char *cp = buffer_end; + char *re = buffer_end; + int result = 1; + /* The /proc/stat format is more uniform, use it by default. */ + int fd = open_not_cancel_2 ("/proc/stat", O_RDONLY); + if (fd != -1) + { result = 0; - while (fgets_unlocked (buffer, sizeof (buffer), fp) != NULL) - if (strncmp (buffer, "cpu", 3) == 0 && isdigit (buffer[3])) + + char *l; + while ((l = next_line (fd, buffer, &cp, &re, buffer_end)) != NULL) + /* The current format of /proc/stat has all the cpu* entries + at the front. We assume here that stays this way. */ + if (strncmp (l, "cpu", 3) != 0) + break; + else if (isdigit (l[3])) ++result; - fclose (fp); + close_not_cancel_no_status (fd); } else { - fp = fopen ("/proc/cpuinfo", "rc"); - if (fp != NULL) + fd = open_not_cancel_2 ("/proc/cpuinfo", O_RDONLY); + if (fd != -1) { - /* No threads use this stream. */ - __fsetlocking (fp, FSETLOCKING_BYCALLER); - GET_NPROCS_PARSER (fp, buffer, result); - fclose (fp); + GET_NPROCS_PARSER (fd, buffer, cp, re, buffer_end, result); + close_not_cancel_no_status (fd); } } --- libc/sysdeps/unix/sysv/linux/sparc/getsysstats.c 6 Jul 2001 04:56:21 -0000 1.2 +++ libc/sysdeps/unix/sysv/linux/sparc/getsysstats.c 15 Feb 2009 23:40:33 -0000 1.3 @@ -21,7 +21,7 @@ /* We need to define a special parser for /proc/cpuinfo. */ -#define GET_NPROCS_PARSER(FP, BUFFER, RESULT) \ +#define GET_NPROCS_PARSER(FD, BUFFER, CP, RE, BUFFER_END, RESULT) \ do \ { \ (RESULT) = 0; \ @@ -29,8 +29,9 @@ active cpus. We don't have to fear extremely long lines since \ the kernel will not generate them. 8192 bytes are really \ enough. */ \ - while (fgets_unlocked (BUFFER, sizeof (BUFFER), FP) != NULL) \ - if (sscanf (BUFFER, "ncpus active : %d", &(RESULT)) == 1) \ + char *l; \ + while ((l = next_line (FD, BUFFER, &CP, &RE, BUFFER_END)) != NULL) \ + if (sscanf (l, "ncpus active : %d", &(RESULT)) == 1) \ break; \ } \ while (0)