#include #include #include #include #include #define likely(x) __builtin_expect(!!(x), 1) #define unlikely(x) __builtin_expect(!!(x), 0) static char *read_cpuinfo(void) { const size_t bufstep = 64*1024; size_t bufsize = 0; size_t len = 0; ssize_t rlen; char *buf = NULL; char *newbuf; int fd; int err; char *p; fd = open("/proc/cpuinfo", O_RDONLY); if (fd < 0) return NULL; do { if (len >= bufsize) { bufsize += bufstep; newbuf = realloc(buf, bufsize); if (!newbuf) goto error; buf = newbuf; } rlen = read(fd, buf + len, bufsize - len); if (rlen < 0) { if (errno == EINTR || errno == EAGAIN) continue; else goto error; } len += rlen; } while (rlen); /* * There should always be a blank line after each CPU, * including at the end. */ if (len < 2 || memcmp(buf + len - 2, "\n\n", 2)) { errno = EPROTO; goto error; } close(fd); /* * buf will have at least one free byte at the end. As a result: * - Each processor data block is terminated by an empty string. * - The entire file is terminated by two empty strings in a row. */ buf[len] = '\0'; /* Make each line a separate string */ for (p = buf; p < buf + len; p++) { if (*p == '\n') *p = '\0'; } newbuf = realloc(buf, len+1); if (newbuf) buf = newbuf; return buf; error: err = errno; close(fd); free(buf); errno = err; return NULL; } const char *get_cpuinfo(void) { static char *cpuinfo; if (unlikely(!cpuinfo)) cpuinfo = read_cpuinfo(); return cpuinfo; } /* * Sets *value to the contents of the field for a specific CPU or NULL. * Returns 0 on success or: * -errno - any file I/O error * -EPROTO - parsing error * -ESRCH - no such CPU * -ENOMSG - no such tag */ int cpuinfo_get_field(const char **value, const char *field, unsigned long cpu) { static const char processor_prefix[] = "processor\t: "; const size_t pfxlen = sizeof processor_prefix - 1; size_t fieldlen; const char *p; *value = NULL; p = get_cpuinfo(); if (!p) return -errno; while (1) { /* Blank line at start -> no more processors */ if (!*p) return -ESRCH; /* First line must be processor */ if (strncmp(p, processor_prefix, pfxlen)) return -EPROTO; if (strtoul(p + pfxlen, NULL, 10) == cpu) break; /* Found our CPU */ /* * Skip to the next processor. * Each processor block is terminated by a blank line. */ do { p = strchr(p, '\0') + 1; } while (*p); p++; /* Skip the blank line */ } /* Look for the requested field inside the processor block. */ fieldlen = strlen(field); while (*p) { if (!strncmp(p, field, fieldlen)) { p += fieldlen; if (!*p) return -EPROTO; /* No field data present */ if (*p == '\t' || *p == ':') { /* Field matches */ while (*p == '\t') p++; if (*p != ':') return -EPROTO; /* Colon missing */ p++; /* Skip space after colon */ if (*p == ' ') p++; *value = p; return 0; /* Field found */ } } p = strchr(p, '\0') + 1; /* Next field */ } return -ENOMSG; /* CPU found but no such field */ } /* * Look for a specific flag inside a specific multi-token field; * this currently applies to the "flags" or "bugs" fields. * * Return 1 if the given flag is present, 0 if it is not, or * a negative error code returned from cpuinfo_get_field(). */ int cpuinfo_has_flag(const char *flag, const char *field, unsigned long cpu) { const char *p; size_t flaglen; int err; err = cpuinfo_get_field(&p, field, cpu); if (err) return err; flaglen = strlen(flag); while (*p) { if (!strncmp(p, flag, flaglen) && (!p[flaglen] || p[flaglen] == ' ')) return 1; /* Flag found */ /* Skip to the next flag */ p = strchr(p, ' '); if (!p) break; p++; } return 0; /* Not found */ } #ifdef TEST_CPUINFO #include #include int main(int argc, char **argv) { const char *field, *flag; unsigned long cpu; int rv; if (argc < 3) { fprintf(stderr, "Usage: %s cpu field\n", argv[0]); exit(EX_USAGE); } cpu = strtoul(argv[1], NULL, 10); field = argv[2]; flag = argv[3]; if (flag) { rv = cpuinfo_has_flag(flag, field, cpu); if (rv >= 0) { printf("CPU %lu %s(%s) = %d\n", cpu, field, flag, rv); exit(rv); } } else { const char *data; rv = cpuinfo_get_field(&data, field, cpu); if (!rv) { printf("CPU %lu %s = %s\n", cpu, field, data); exit(0); } } /* * On error. Errors that should never happen no matter what the * input are sent to stderr. */ switch (rv) { case -ESRCH: printf("CPU %lu does not exist\n", cpu); exit(EX_NOUSER); case -ENOMSG: printf("CPU %lu %s : field does not exist\n", cpu, field); exit(EX_NOINPUT); case -EPROTO: fprintf(stderr, "%s: could not parse /proc/cpuinfo\n", argv[0]); exit(EX_PROTOCOL); default: fprintf(stderr, "/proc/cpuinfo: %s\n", strerror(-rv)); exit(EX_OSFILE); } } #endif /* TEST_CPUINFO */