4

I use execv to run lshw command to get the CPU, disk, and memory in C code. But I would like to search another solution to get these information from /proc or any other existed data. Have any suggestion? Here is my code:

char *params[9]  = {"/usr/bin/lshw", "-short", "-c", "disk", 
                  "-c", "memory", "-c", "processor", 0}; //cmd params filled
execv(params[0], params);

Linux command: $ sudo lshw -short -c disk -c processor -c memory

$ sudo lshw -short -c disk -c processor -c memory
H/W path         Device     Class          Description
======================================================
/0/0                        memory         64KiB BIOS
/0/22                       memory         16GiB System Memory
/0/22/0                     memory         DIMM Synchronous [empty]
/0/22/1                     memory         DIMM Synchronous [empty]
/0/22/2                     memory         8GiB DIMM Synchronous 2133 MHz (0.5 ns)
/0/22/3                     memory         8GiB DIMM Synchronous 2133 MHz (0.5 ns)
/0/2a                       memory         256KiB L1 cache
/0/2b                       memory         1MiB L2 cache
/0/2c                       memory         6MiB L3 cache
/0/2d                       processor      Intel(R) Xeon(R) CPU D-1521 @ 2.40GHz
/0/1/0.0.0       /dev/sda   disk           16GB SATADOM-SH 3IE3
/0/2/0.0.0       /dev/sdb   disk           120GB Patriot Blaze

I have two questions:

  • Where to find a guide to parse the files in /proc to get these hardware information?
  • Do I need to trace the source code of lshw to find what does lshw do?

Edit:

Chapter 7 of Advanced Linux Programming is a guide to parse the /proc filesystem.

jww
  • 97,681
  • 90
  • 411
  • 885
charles.cc.hsu
  • 689
  • 11
  • 15

3 Answers3

5

The best way to get hardware information is using sysconf() and sysctl*() functions (Mac OS X, freebsd, openbsd), and sysconf() and sysinfo() on Linux.

Parsing /proc/* is slower and more involved than calling sysinfo( ) or sysconf( )

Below is a small example giving you some information about processor and memory on Mac OS X:

#include <sys/types.h>
#include <sys/sysctl.h>
#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <string.h>

int main()
{
 char *p = NULL;
 size_t len;
 sysctlbyname("hw.model", NULL, &len, NULL, 0);
 p = malloc(len);
 sysctlbyname("hw.model", p, &len, NULL, 0);
 printf("%s\n", p);
 /* CTL_MACHDEP variables are architecture dependent so doesn't work 
 for every one */
 sysctlbyname("machdep.cpu.brand_string", NULL, &len, NULL, 0);
 p = malloc(len);
 sysctlbyname("machdep.cpu.brand_string", p, &len, NULL, 0);
 printf("%s\n", p);
 int64_t mem;
 len = sizeof(mem);
 sysctlbyname("hw.memsize", &mem, &len, NULL, 0);
 printf("System Memory : %lld\n", mem);
 return (0);
}

You have to read man 3 sysctl, or on Linux man 2 sysconf and man 2 sysinfo.

An interesting link : http://nadeausoftware.com/articles/2012/09/c_c_tip_how_get_physical_memory_size_system#Other

You can calculate the CPU load and usage retrieving some sysctl variables, and doing the math by yourself (you can find the formulas to do it on google).

But where to find the physical DIMM information as the report from $ sudo lshw -short -c memory ?

You can execute your command inside your C program to save it as a string like :

#include <stdio.h>
#include <unistd.h>
#include <errno.h>
#include <sys/wait.h>
#include <string.h>
#include <stdlib.h>

char *strjoin(char *s1, char *s2, int n)
{
    int i = strlen(s2);
    int j = 0;
    if ((s2 = realloc(s2, (i + n + 1))) == NULL)
            perror(0);
    while (j < n && s1[j])
    {
            s2[i] = s1[j];
            i++;
            j++;
    }
    s2[i] = 0;
    return (s2);
}

int main()
{
    pid_t father;
    char buf[500] = {0};
    char *str;
    char *argv[5] = {"/usr/bin/lshw", "-short", "-c", "memory"};
    int fd[2];
    int ret;

    if (pipe(fd) == -1)
    {
            perror(NULL);
            return -1;
    }
    father = fork();
    if (father == 0)
    {
            close(fd[1]);
            while ((ret = read(fd[0], buf, 500)))
            {
                    str = strjoin(buf, str, ret);
            }
            close(fd[0]);
    }
    else
    {
            close(fd[0]);
            execv(argv[0], argv);
            close(fd[1]);
            wait(0);
    }
    wait(0);
    printf("%s", str);
    return 0;
}

(I don't check all the function's return in this code, not to have a too long one, but you should do it in your program).

Here is an example of parsing the file /proc/meminfo to save in a double array 2 strings I want, and then printing them out :

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

int main()
{
    FILE *f;
    char *line = NULL;
    ssize_t read;
    size_t len = 0;
    char    **info;
    int i = 0;

    info = malloc(3 * sizeof(char*));
    f = fopen("/proc/meminfo", "r");
    while ((read = getline(&line, &len, f)) != -1)
    {
            if (strstr(line, "MemTotal") != NULL)
                    info[i] = strdup(line);
            else if (strstr(line, "MemFree") != NULL)
                    info[i] = strdup(line);
            i++;
    }
    info[i] = 0;
    fclose(f);
    i = 0;
    while (info[i])
    {
            printf("%s", info[i]);
            free (info[i]);
            i++;
    }
    free (info);
    return 0;
}

If you want to save more strings, malloc more space in the double array info, and add them with else if inside the read loop. You can do that with any files from /proc/ to get the information you need.

CaptainBli
  • 4,121
  • 4
  • 39
  • 58
MIG-23
  • 511
  • 3
  • 11
  • Yes on Linux, the functions sysconf() and sysinfo() are better (man 2). In the link I gave, there is also an exemple on parsing the file, if your information aren't all in these two functions – MIG-23 Jun 07 '17 at 09:06
  • I've read the functions sysconf() and sysinfo() in man page and read the link you provided. But I still can not get what I want. I'll try to read the code of [lsscsi](https://github.com/hreinecke/lsscsi/blob/master/src/lsscsi.c), and the code of [lscpu](https://github.com/karelzak/util-linux/blob/master/sys-utils/lscpu.c), or just parse the file `/proc/cpuinfo`. But where to find the physical DIMM information as the report from `$ sudo lshw -short -c memory`. – charles.cc.hsu Jun 07 '17 at 10:03
  • Do you have access to /dev/mem? – MIG-23 Jun 07 '17 at 10:13
  • If you want help to parse one of the files you need to parse, paste one of the file content here and I will write you an exemple of how to parse it to get the information you want – MIG-23 Jun 07 '17 at 11:30
  • 1
    I modified my answer to show you at the end an exemple of how to save in a string the result of a command in C (if you execute this code it will output you the string). You can do that with any kind of command you want. – MIG-23 Jun 07 '17 at 14:19
  • Yes, that's what I have done. I `execv` the `lshw` and parse the result to get what I need. But my leader ask me to do it by C code. Maybe I need to trace the source code of `lshw` to get the memory information. Why you mention `/dev/mem`? – charles.cc.hsu Jun 07 '17 at 15:22
  • I have edited my answer, so it's an easy way to get your information like lshw command does, you can parse each file you need from /proc/ using the same way – MIG-23 Jun 07 '17 at 15:48
1

By reading the source code of lshw, I found that lshw read raw data from /sys/class/dmi/. Because lshw is written in CPP that I am not familiar with, there is a question Where does dmidecode get the SMBIOS table? mentioned that dmidecode.c read raw data from /sys/class/dmi that's same as lshw does.

Here are the definitions in dmidecode.c

#define SYS_ENTRY_FILE "/sys/firmware/dmi/tables/smbios_entry_point"
#define SYS_TABLE_FILE "/sys/firmware/dmi/tables/DMI"

I extract code from dmidecode.c to get the CPU and memory information, and use lsscsi to get disk information.

Thanks for your help.

charles.cc.hsu
  • 689
  • 11
  • 15
-2

1: To get the CPU load use this command :

top -bn1 | grep load

this will give you output like :

top - 12:26:20 up 35 min,  2 users,  load average: 0.02, 0.01, 0.00

now parse the load average from above string. 2: To get memory info use this command :

free -m

This will give you:

total       used       free     shared    buffers     cached
Mem:         15926        308      15617          6         15        122
-/+ buffers/cache:        171      15755
Swap:            0          0          0

To get the disk info , use this :

df -H /home/test

This will give you :

Filesystem      Size  Used Avail Use% Mounted on
/dev/sda1       102G  5.4G   91G   6% /

Now from above result parse the content what you want.

suraj
  • 403
  • 4
  • 15
  • Thanks for your information, but I want to do that in pure C code. – charles.cc.hsu Jun 06 '17 at 07:36
  • redirect the output of above command to the file then parse the data from that file. For redirection use dup2() sys call. e.g. int fd = open(file, O_RDWR | O_CREAT, S_IRUSR | S_IWUSR); then duplicate the file_descriptor for I/O buffer. e. g. dup2(fd, 1); // make stdout go to file and dup2(fd, 2); // make stderr go to file. – suraj Jun 06 '17 at 08:54