hxtools/suser/sysinfo.c

453 lines
10 KiB
C

// SPDX-License-Identifier: GPL-2.0-or-later
// SPDX-FileCopyrightText: 2005-2015
/*
* sysinfo.c - print banner with general system information
*/
#ifdef HAVE_CONFIG_H
# include "config.h"
#endif
#include <errno.h>
#include <stdbool.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/stat.h>
#include <sys/statvfs.h>
#include <sys/utsname.h>
#include <libHX/ctype_helper.h>
#include <libHX/defs.h>
#include <libHX/io.h>
#include <libHX/map.h>
#include <libHX/option.h>
#include <libHX/string.h>
#ifdef HAVE_LIBPCI
# include <pci/header.h>
# include <pci/pci.h>
#endif
#ifdef HAVE_LIBXCB
# include <xcb/xcb.h>
#endif
#ifdef HAVE_LIBMOUNT
# include <libmount.h>
#endif
struct sy_block {
struct utsname uts;
struct HXmap *osrel;
unsigned int num_cpu_threads, num_tasks;
char *cpu_vendor, *cpu_arch;
hxmc_t *cpu_model_name;
double cpu_mhz, load_avg1;
unsigned long long mem_used, mem_total;
unsigned long long disk_used, disk_total;
char gfx_hardware_buf[128];
const char *gfx_hardware;
unsigned int display_width, display_height;
};
static const char *sy_osrelease_file = "/etc/os-release";
static const char *sy_cpuinfo_file = COMPAT_PROC "/proc/cpuinfo";
static const char sysfs_cpu_dir[] = COMPAT_PROC "/sys/devices/system/cpu";
static unsigned int sy_verbose;
static void sy_num_cpu_threads(struct sy_block *sib)
{
struct HXdir *dh = HXdir_open(sysfs_cpu_dir);
unsigned int thr = 0;
const char *de;
struct stat sb;
char path[80];
if (dh == NULL)
return;
while ((de = HXdir_read(dh)) != NULL) {
if (strncmp(de, "cpu", 3) != 0)
continue;
snprintf(path, sizeof(path), "%s/%s/topology",
sysfs_cpu_dir, de);
if (stat(path, &sb) == 0)
++thr;
}
HXdir_close(dh);
sib->num_cpu_threads = thr;
}
static char *__HX_strmtrim(char *i)
{
char *orig, *out, last = '\0';
for (orig = out = i; *i != '\0'; ++i) {
if (HX_isspace(last) && *i == last)
continue;
*out++ = last = *i;
}
*out++ = '\0';
return orig;
}
static hxmc_t *sy_cpu_model(const char *name)
{
hxmc_t *buf;
char *p;
if (strncmp(name, "Dual-Core", 9) == 0 ||
strncmp(name, "Quad-Core", 9) == 0)
name += 9;
while (HX_isspace(*name))
++name;
buf = HXmc_strinit(name);
if (buf == NULL)
return NULL;
/* Kill ", altivec supported" and "@ 2.67 GHz" */
p = strpbrk(buf, "@,");
if (p != NULL)
*p = '\0';
HX_strrtrim(buf);
__HX_strmtrim(buf);
return buf;
}
static char *sy_hop_colon(char *key, char *value)
{
while (*value != ':' && *value != '\0')
++value;
if (*value != ':')
return NULL;
*value++ = '\0';
HX_strrtrim(key);
while (HX_isspace(*value))
++value;
return value;
}
static void sy_proc_cpuinfo(struct sy_block *sib)
{
FILE *fp = fopen(sy_cpuinfo_file, "r");
hxmc_t *line = NULL;
char *key, *value;
unsigned int dummy_uint;
if (fp == NULL)
return;
while (HX_getl(&line, fp) != NULL) {
value = key = line;
value = sy_hop_colon(key, value);
if (value == NULL)
continue;
HX_chomp(value);
if (strcmp(key, "cpu") == 0 || strcmp(key, "model name") == 0)
sib->cpu_model_name = sy_cpu_model(value);
else if (strcmp(key, "vendor") == 0)
sib->cpu_vendor = HX_strdup(value);
else if (strcmp(key, "arch") == 0)
sib->cpu_arch = HX_strdup(value);
else if (strcmp(key, "clock") == 0 ||
strcmp(key, "cpu MHz") == 0)
sib->cpu_mhz = strtod(value, NULL);
else if (sscanf(key, "Cpu%udClkTck", &dummy_uint) == 1)
sib->cpu_mhz = strtoull(value, NULL, 16) / 1000000;
}
HXmc_free(line);
fclose(fp);
}
static void sy_cpupower(struct sy_block *sib)
{
struct HXdir *dh = HXdir_open(sysfs_cpu_dir);
hxmc_t *line = NULL;
const char *de;
char path[80];
FILE *fp;
if (dh == NULL)
return;
while ((de = HXdir_read(dh)) != NULL) {
if (strncmp(de, "cpu", 3) != 0)
continue;
snprintf(path, sizeof(path), "%s/%s/cpufreq/cpuinfo_max_freq",
sysfs_cpu_dir, de);
fp = fopen(path, "r");
if (fp == NULL)
continue;
if (HX_getl(&line, fp) != NULL) {
/* this is in KHz */
sib->cpu_mhz = strtoull(line, NULL, 0) / 1000;
HXmc_free(line);
}
fclose(fp);
break;
}
HXdir_close(dh);
}
static void sy_loadavg(struct sy_block *sib)
{
double avg5, avg15;
unsigned int run;
FILE *fp = fopen(COMPAT_PROC "/proc/loadavg", "r");
if (fp == NULL)
return;
fscanf(fp, "%lf %lf %lf %u/%u",
&sib->load_avg1, &avg5, &avg15, &run, &sib->num_tasks);
fclose(fp);
}
static void sy_memory(struct sy_block *sib)
{
unsigned long long mem_free = 0, mem_buf = 0, mem_cac = 0, mem_shm = 0;
FILE *fp = fopen(COMPAT_PROC "/proc/meminfo", "r");
hxmc_t *line = NULL;
char *key, *value;
if (fp == NULL)
return;
while (HX_getl(&line, fp) != NULL) {
value = key = line;
value = sy_hop_colon(key, value);
if (value == NULL)
continue;
if (strcmp(key, "MemTotal") == 0)
sib->mem_total = strtoull(value, NULL, 0);
else if (strcmp(key, "MemFree") == 0)
mem_free = strtoull(value, NULL, 0);
else if (strcmp(key, "Buffers") == 0)
mem_buf = strtoull(value, NULL, 0);
else if (strcmp(key, "Cached") == 0)
mem_cac = strtoull(value, NULL, 0);
else if (strcmp(key, "Shmem") == 0)
mem_shm = strtoull(value, NULL, 0);
}
sib->mem_used = sib->mem_total - mem_free - mem_buf - mem_cac + mem_shm;
HXmc_free(line);
fclose(fp);
}
static void sy_disk(struct sy_block *sib)
{
#ifdef HAVE_LIBMOUNT
struct libmnt_context *ctx;
struct libmnt_table *table;
struct libmnt_iter *iter;
struct libmnt_fs *fs;
struct HXmap *seen;
seen = HXmap_init(HXMAPT_DEFAULT, HXMAP_SCDATA);
if (seen == NULL)
return;
ctx = mnt_new_context();
if (ctx == NULL)
goto out;
if (mnt_context_get_mtab(ctx, &table) != 0)
goto out;
iter = mnt_new_iter(MNT_ITER_FORWARD);
if (iter == NULL)
goto out;
sib->disk_total = 0;
while (mnt_table_next_fs(table, iter, &fs) == 0) {
const char *source = mnt_fs_get_source(fs);
const char *mntpt = mnt_fs_get_target(fs);
if (source == NULL || *source != '/')
continue;
if (HXmap_add(seen, reinterpret_cast(const void *,
static_cast(uintptr_t, mnt_fs_get_devno(fs))), mntpt) < 0)
goto out;
}
mnt_reset_iter(iter, MNT_ITER_FORWARD);
while (mnt_table_next_fs(table, iter, &fs) == 0) {
const char *mntpt, *saved_mntpt;
struct statvfs sb;
mntpt = mnt_fs_get_target(fs);
saved_mntpt = HXmap_get(seen, reinterpret_cast(const void *,
static_cast(uintptr_t, mnt_fs_get_devno(fs))));
if (mntpt == NULL || *mntpt != '/')
continue;
if (saved_mntpt == NULL ||
strcmp(mntpt, saved_mntpt) != 0)
continue;
if (statvfs(mntpt, &sb) < 0)
continue;
sib->disk_used += (sb.f_blocks - sb.f_bavail) * sb.f_bsize;
sib->disk_total += sb.f_blocks * sb.f_bsize;
}
out:
if (ctx != NULL)
mnt_free_context(ctx);
HXmap_free(seen);
#endif
}
static void sy_gfx_hardware(struct sy_block *sib)
{
#ifdef HAVE_LIBPCI
struct pci_dev *pd;
struct pci_access *pacc;
const char *ret = NULL;
pacc = pci_alloc();
if (pacc == NULL)
return;
pci_init(pacc);
pci_scan_bus(pacc);
for (pd = pacc->devices; pd != NULL; pd = pd->next) {
if ((pd->device_class >> 8) != PCI_BASE_CLASS_DISPLAY)
continue;
break;
}
if (pd != NULL)
ret = pci_lookup_name(pacc, sib->gfx_hardware_buf,
sizeof(sib->gfx_hardware_buf),
PCI_LOOKUP_VENDOR | PCI_LOOKUP_DEVICE,
pd->vendor_id, pd->device_id);
pci_cleanup(pacc);
sib->gfx_hardware = ret;
#endif
}
static void sy_display_size(struct sy_block *sib)
{
#ifdef HAVE_LIBXCB
xcb_connection_t *conn;
const xcb_setup_t *setup;
xcb_screen_iterator_t iter;
const xcb_screen_t *screen;
conn = xcb_connect(NULL, NULL);
if (conn == NULL)
return;
if (xcb_connection_has_error(conn))
goto out;
setup = xcb_get_setup(conn);
if (setup == NULL)
goto out;
iter = xcb_setup_roots_iterator(setup);
screen = iter.data;
if (screen != NULL) {
sib->display_width = screen->width_in_pixels;
sib->display_height = screen->height_in_pixels;
}
out:
xcb_disconnect(conn);
#endif
}
static void sy_dump(const struct sy_block *sib)
{
/* system */
printf("(by hxtools sysinfo) [%s]", sib->uts.nodename);
if (sib->osrel != NULL) {
const char *s = HXmap_get(sib->osrel, "NAME");
if (s != NULL) {
printf(" %s", s);
s = HXmap_get(sib->osrel, "VERSION_ID");
if (s == NULL)
s = HXmap_get(sib->osrel, "VERSION");
if (s != NULL)
printf(" %s", s);
printf(" |");
}
}
printf(" %s %s %s",
sib->uts.sysname, sib->uts.release, sib->uts.machine);
/* cpu */
if (sib->num_cpu_threads == 0)
printf(" |");
else
printf(" | %u-thr", sib->num_cpu_threads);
if (sib->cpu_model_name != NULL) {
printf(" %s", sib->cpu_model_name);
} else {
if (sib->cpu_vendor != NULL)
printf(" %s", sib->cpu_vendor);
if (sib->cpu_arch != NULL)
printf(" %s", sib->cpu_arch);
}
if (sib->cpu_mhz != 0)
printf(" %.0fMHz", sib->cpu_mhz);
/* load */
printf(" | Load: %.2f Tasks: %u", sib->load_avg1, sib->num_tasks);
/* memory */
printf(" | Mem: %llu/%lluMB", sib->mem_used / 1024,
sib->mem_total / 1024);
/* disk */
if (sib->disk_total != -1)
printf(" | Disk: %llu/%lluGB",
sib->disk_used / (1024 * 1048576),
sib->disk_total / (1024 * 1048576));
else if (sy_verbose)
printf(" | Disk: <libmount missing>");
/* gfx */
if (sib->gfx_hardware != NULL || sib->display_width != 0 ||
sib->display_height != 0) {
printf(" | Gfx:");
if (sib->gfx_hardware != NULL)
printf(" %s", sib->gfx_hardware);
if (sib->display_width != 0 || sib->display_height != 0)
printf(" @ %ux%u", sib->display_width,
sib->display_height);
} else if (sy_verbose) {
printf(" | Gfx:");
#ifndef HAVE_LIBPCI
printf(" <libpci missing>");
#endif
#ifndef HAVE_LIBXCB
printf(" <libxcb missing>");
#endif
}
printf("\n");
}
static bool sy_get_options(int *argc, const char ***argv)
{
static const struct HXoption options_table[] = {
{.sh = 'P', .type = HXTYPE_STRING, .ptr = &sy_cpuinfo_file,
.help = "Debug: specify alternate cpuinfo file", .htyp = "FILE"},
{.sh = 'v', .type = HXTYPE_NONE, .ptr = &sy_verbose,
.help = "Verbose reporting"},
HXOPT_AUTOHELP,
HXOPT_TABLEEND,
};
if (HX_getopt(options_table, argc, argv, HXOPT_USAGEONERR) !=
HXOPT_ERR_SUCCESS)
return false;
return true;
}
int main(int argc, const char **argv)
{
struct sy_block sib;
if (!sy_get_options(&argc, &argv))
return EXIT_FAILURE;
memset(&sib, 0, sizeof(sib));
sib.disk_total = -1;
uname(&sib.uts);
sib.osrel = HX_shconfig_map(sy_osrelease_file);
sy_num_cpu_threads(&sib);
sy_proc_cpuinfo(&sib);
sy_cpupower(&sib);
sy_loadavg(&sib);
sy_memory(&sib);
sy_disk(&sib);
sy_gfx_hardware(&sib);
sy_display_size(&sib);
sy_dump(&sib);
if (sib.osrel != NULL)
HXmap_free(sib.osrel);
return EXIT_SUCCESS;
}