776 lines
27 KiB
C
776 lines
27 KiB
C
/*
|
|
* UwUfetch is free software: you can redistribute it and/or modify
|
|
* it under the terms of the GNU General Public License as published by
|
|
* the Free Software Foundation, either version 3 of the License, or
|
|
* (at your option) any later version.
|
|
*
|
|
* This program is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
* GNU General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU General Public License
|
|
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
|
*/
|
|
|
|
#ifdef __APPLE__
|
|
#include <TargetConditionals.h> // for checking iOS
|
|
#endif
|
|
#include <dirent.h>
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
#include <unistd.h>
|
|
#if defined(__APPLE__) || defined(__BSD__)
|
|
#include <sys/sysctl.h>
|
|
#if defined(__OPENBSD__)
|
|
#include <sys/time.h>
|
|
#else
|
|
#include <time.h>
|
|
#endif // defined(__OPENBSD__)
|
|
#else // defined(__APPLE__) || defined(__BSD__)
|
|
#ifdef __BSD__
|
|
#else // defined(__BSD__) || defined(_WIN32)
|
|
#ifndef _WIN32
|
|
#ifndef __OPENBSD__
|
|
#include <sys/sysinfo.h>
|
|
#else // __OPENBSD__
|
|
#endif // __OPENBSD__
|
|
#else // _WIN32
|
|
#include <sysinfoapi.h>
|
|
#endif // _WIN32
|
|
#endif // defined(__BSD__) || defined(_WIN32)
|
|
#endif // defined(__APPLE__) || defined(__BSD__)
|
|
#ifndef _WIN32
|
|
#include <pthread.h> // linux only right now
|
|
#include <sys/ioctl.h>
|
|
#include <sys/utsname.h>
|
|
#else // _WIN32
|
|
#include <windows.h>
|
|
CONSOLE_SCREEN_BUFFER_INFO csbi;
|
|
#endif // _WIN32
|
|
|
|
#define LIBFETCH_INTERNAL // to do certain things only when included from the library itself
|
|
#include "fetch.h"
|
|
#define BUFFER_SIZE 256
|
|
#ifdef __DEBUG__
|
|
static bool verbose_enabled = false;
|
|
bool* get_verbose_handle() { return &verbose_enabled; }
|
|
#endif
|
|
|
|
#ifndef PKGPATH
|
|
#ifdef __APPLE__
|
|
#define PKGPATH "/usr/local/bin/"
|
|
#else
|
|
#define PKGPATH "/usr/bin/"
|
|
#endif
|
|
#endif
|
|
|
|
#ifdef __APPLE__
|
|
// buffers where data fetched from sysctl are stored
|
|
#define CPUBUFFERLEN 128
|
|
|
|
char cpu_buffer[CPUBUFFERLEN];
|
|
size_t cpu_buffer_len = CPUBUFFERLEN;
|
|
|
|
// Installed RAM
|
|
int64_t mem_buffer = 0;
|
|
size_t mem_buffer_len = sizeof(mem_buffer);
|
|
|
|
// uptime
|
|
struct timeval time_buffer;
|
|
size_t time_buffer_len = sizeof(time_buffer);
|
|
#endif // __APPLE__
|
|
|
|
struct package_manager {
|
|
char* command_path;
|
|
char* command_string; // command to get number of packages installed
|
|
char* pkgman_name; // name of the package manager
|
|
};
|
|
|
|
// truncates the given string
|
|
void truncate_str(char* string, int target_width) {
|
|
char arr[target_width];
|
|
for (int i = 0; i < target_width; i++) arr[i] = string[i];
|
|
string = arr;
|
|
}
|
|
|
|
// remove square brackets (for gpu names)
|
|
void remove_brackets(char* str) {
|
|
int i = 0, j = 0;
|
|
while (i < (int)strlen(str))
|
|
if (str[i] == '[' || str[i] == ']')
|
|
for (j = i; j < (int)strlen(str); j++) str[j] = str[j + 1];
|
|
else
|
|
i++;
|
|
}
|
|
|
|
void get_twidth(struct info* user_info) {
|
|
LOG_I("getting terminal width");
|
|
// get terminal width used to truncate long names
|
|
#ifndef _WIN32
|
|
ioctl(STDOUT_FILENO, TIOCGWINSZ, &user_info->win);
|
|
user_info->target_width = user_info->win.ws_col - 30;
|
|
LOG_V(user_info->target_width);
|
|
#else // _WIN32
|
|
GetConsoleScreenBufferInfo(GetStdHandle(STD_OUTPUT_HANDLE), &csbi);
|
|
user_info->ws_col = csbi.srWindow.Right - csbi.srWindow.Left - 29;
|
|
user_info->ws_rows = csbi.srWindow.Bottom - csbi.srWindow.Top + 1;
|
|
LOG_V(user_info->ws_col);
|
|
LOG_V(user_info->ws_rows);
|
|
#endif // _WIN32
|
|
}
|
|
|
|
void get_sys(struct info* user_info) {
|
|
LOG_I("getting sys_var struct");
|
|
#ifndef _WIN32
|
|
uname(&user_info->sys_var);
|
|
#endif // _WIN32
|
|
#ifndef __APPLE__
|
|
#ifndef __BSD__
|
|
#ifndef _WIN32
|
|
sysinfo(&user_info->sys);
|
|
#else
|
|
GetSystemInfo(&user_info->sys);
|
|
#endif
|
|
#endif
|
|
#endif
|
|
}
|
|
|
|
// tries to get cpu name
|
|
void* get_cpu(void* argp) {
|
|
if (!((struct thread_varg*)argp)->thread_flags[0]) return 0;
|
|
char* buffer = ((struct thread_varg*)argp)->buffer;
|
|
struct info* user_info = ((struct thread_varg*)argp)->user_info;
|
|
FILE* cpuinfo = ((struct thread_varg*)argp)->cpuinfo;
|
|
LOG_I("getting cpu name");
|
|
if (cpuinfo) {
|
|
while (fgets(buffer, BUFFER_SIZE, cpuinfo)) {
|
|
#ifdef __BSD__
|
|
if (sscanf(buffer, "hw.model"
|
|
#ifdef __FREEBSD__
|
|
": "
|
|
#elif defined(__OPENBSD__)
|
|
"="
|
|
#endif
|
|
"%[^\n]",
|
|
user_info->cpu_model))
|
|
break;
|
|
#else
|
|
if (sscanf(buffer, "model name : %[^\n]", user_info->cpu_model)) break;
|
|
#endif // __BSD__
|
|
}
|
|
}
|
|
if (strlen(user_info->cpu_model) == 0) {
|
|
LOG_E("failed to get cpu name");
|
|
rewind(cpuinfo);
|
|
char cores[4] = "";
|
|
while (fgets(buffer, BUFFER_SIZE, cpuinfo)) // get the last core number
|
|
sscanf(buffer, "processor%*[ | ]: %[^\n]", cores);
|
|
cores[strlen(cores) - 1] += 1; // should be a number
|
|
sprintf(user_info->cpu_model, "%s Cores", cores);
|
|
}
|
|
LOG_V(user_info->cpu_model);
|
|
return 0;
|
|
}
|
|
|
|
// tries to get memory usage
|
|
void* get_ram(void* argp) {
|
|
if (!((struct thread_varg*)argp)->thread_flags[1]) return 0;
|
|
LOG_I("getting ram");
|
|
struct info* user_info = ((struct thread_varg*)argp)->user_info;
|
|
#ifndef __APPLE__
|
|
#ifdef _WIN32
|
|
FILE* mem_used_fp = popen("wmic os get freevirtualmemory", "r"); // free memory
|
|
FILE* mem_total_fp = popen("wmic os get totalvirtualmemorysize", "r"); // total memory
|
|
char mem_used_ch[2137] = {0}, mem_total_ch[2137] = {0};
|
|
|
|
while (fgets(mem_total_ch, sizeof(mem_total_ch), mem_total_fp) != NULL) {
|
|
if (strstr(mem_total_ch, "TotalVirtualMemorySize") != 0)
|
|
continue;
|
|
else if (strstr(mem_total_ch, " ") == 0)
|
|
continue;
|
|
else
|
|
user_info->ram_total = atoi(mem_total_ch) / 1024;
|
|
}
|
|
LOG_V(user_info->ram_total);
|
|
while (fgets(mem_used_ch, sizeof(mem_used_ch), mem_used_fp) != NULL) {
|
|
if (strstr(mem_used_ch, "FreeVirtualMemory") != 0)
|
|
continue;
|
|
else if (strstr(mem_used_ch, " ") == 0)
|
|
continue;
|
|
else
|
|
user_info->ram_used = user_info->ram_total - (atoi(mem_used_ch) / 1024);
|
|
}
|
|
LOG_V(user_info->ram_used);
|
|
pclose(mem_used_fp);
|
|
pclose(mem_total_fp);
|
|
#else // if not _WIN32
|
|
char* buffer = ((struct thread_varg*)argp)->buffer;
|
|
FILE* meminfo;
|
|
|
|
#ifdef __BSD__
|
|
#ifndef __OPENBSD__
|
|
meminfo = popen("LANG=EN_us freecolor -om 2> /dev/null", "r"); // free alternative for freebsd
|
|
#else
|
|
meminfo = popen("LANG=EN_us vmstat 2> /dev/null | grep -v 'procs' | grep -v 'r' | awk '{print $3 "
|
|
"\" / \" $4}'",
|
|
"r"); // free alternative for openbsd
|
|
#endif
|
|
#else
|
|
// getting memory info from /proc/meminfo: https://github.com/KittyKatt/screenFetch/issues/386#issuecomment-249312716
|
|
meminfo = fopen("/proc/meminfo",
|
|
"r"); // popen("LANG=EN_us free -m 2> /dev/null", "r"); // get ram info with free
|
|
#endif
|
|
// brackets are here to restrict the access to this int variables, which are temporary
|
|
{
|
|
#ifndef __OPENBSD__
|
|
int memtotal = 0, shmem = 0, memfree = 0, buffers = 0, cached = 0, sreclaimable = 0;
|
|
#endif
|
|
while (fgets(buffer, BUFFER_SIZE, meminfo)) {
|
|
#ifndef __OPENBSD__
|
|
sscanf(buffer, "MemTotal: %d", &memtotal);
|
|
sscanf(buffer, "Shmem: %d", &shmem);
|
|
sscanf(buffer, "MemFree: %d", &memfree);
|
|
sscanf(buffer, "Buffers: %d", &buffers);
|
|
sscanf(buffer, "Cached: %d", &cached);
|
|
sscanf(buffer, "SReclaimable: %d", &sreclaimable);
|
|
#else
|
|
sscanf(buffer, "%dM / %dM", &user_info->ram_used, &user_info->ram_total);
|
|
#endif
|
|
}
|
|
#ifndef __OPENBSD__
|
|
user_info->ram_total = memtotal / 1024;
|
|
user_info->ram_used = ((memtotal + shmem) - (memfree + buffers + cached + sreclaimable)) / 1024;
|
|
#endif
|
|
LOG_V(user_info->ram_total);
|
|
LOG_V(user_info->ram_used);
|
|
}
|
|
|
|
fclose(meminfo);
|
|
#endif
|
|
#else // if __APPLE__
|
|
// Used
|
|
FILE *mem_wired_fp, *mem_active_fp, *mem_compressed_fp;
|
|
mem_wired_fp = popen("vm_stat | awk '/wired/ { printf $4 }' | cut -d '.' -f 1", "r");
|
|
mem_active_fp = popen("vm_stat | awk '/active/ { printf $3 }' | cut -d '.' -f 1", "r");
|
|
mem_compressed_fp = popen("vm_stat | awk '/occupied/ { printf $5 }' | cut -d '.' -f 1", "r");
|
|
char mem_wired_ch[2137], mem_active_ch[2137], mem_compressed_ch[2137];
|
|
while (fgets(mem_wired_ch, sizeof(mem_wired_ch), mem_wired_fp) != NULL)
|
|
while (fgets(mem_active_ch, sizeof(mem_active_ch), mem_active_fp) != NULL)
|
|
while (fgets(mem_compressed_ch, sizeof(mem_compressed_ch), mem_compressed_fp) != NULL)
|
|
;
|
|
|
|
pclose(mem_wired_fp);
|
|
pclose(mem_active_fp);
|
|
pclose(mem_compressed_fp);
|
|
|
|
int mem_wired = atoi(mem_wired_ch);
|
|
int mem_active = atoi(mem_active_ch);
|
|
int mem_compressed = atoi(mem_compressed_ch);
|
|
|
|
// Total
|
|
sysctlbyname("hw.memsize", &mem_buffer, &mem_buffer_len, NULL, 0);
|
|
user_info->ram_used = ((mem_wired + mem_active + mem_compressed) * 4 / 1024);
|
|
user_info->ram_total = mem_buffer / 1024 / 1024;
|
|
LOG_V(user_info->ram_total);
|
|
LOG_V(user_info->ram_used);
|
|
#endif
|
|
return 0;
|
|
}
|
|
|
|
// tries to get installed gpu(s)
|
|
void* get_gpu(void* argp) {
|
|
if (!((struct thread_varg*)argp)->thread_flags[2]) return 0;
|
|
LOG_I("getting gpu(s)");
|
|
char* buffer = ((struct thread_varg*)argp)->buffer;
|
|
struct info* user_info = ((struct thread_varg*)argp)->user_info;
|
|
int gpuc = 0; // gpu counter
|
|
#ifndef _WIN32
|
|
setenv("LANG", "en_US", 1); // force language to english
|
|
#endif
|
|
FILE* gpu;
|
|
#ifndef _WIN32
|
|
LOG_I("getting gpus with lshw");
|
|
gpu = popen("lshw -class display 2> /dev/null", "r");
|
|
|
|
// add all gpus to the array gpu_model
|
|
while (fgets(buffer, BUFFER_SIZE, gpu))
|
|
if (sscanf(buffer, " product: %[^\n]", user_info->gpu_model[gpuc])) gpuc++;
|
|
#endif
|
|
|
|
if (strlen(user_info->gpu_model[0]) < 2) {
|
|
// get gpus with lspci command
|
|
if (strcmp(user_info->os_name, "android") != 0) {
|
|
#ifndef __APPLE__
|
|
#ifdef _WIN32
|
|
gpu = popen("wmic PATH Win32_VideoController GET Name", "r");
|
|
#else
|
|
gpu = popen("lspci -mm 2> /dev/null | grep \"VGA\" | awk -F '\"' '{print $4 $5 $6}'", "r");
|
|
#endif
|
|
#else
|
|
gpu = popen(
|
|
"system_profiler SPDisplaysDataType | awk -F ': ' '/Chipset Model: /{ print $2 }'", "r");
|
|
#endif
|
|
} else
|
|
gpu = popen("getprop ro.hardware.vulkan 2> /dev/null", "r"); // for android
|
|
}
|
|
|
|
// get all the gpus
|
|
while (fgets(buffer, BUFFER_SIZE, gpu)) {
|
|
// windows
|
|
if (strstr(buffer, "Name") || (strlen(buffer) == 2))
|
|
continue;
|
|
else if (sscanf(buffer, "%[^\n]", user_info->gpu_model[gpuc]))
|
|
gpuc++;
|
|
}
|
|
fclose(gpu);
|
|
|
|
// format gpu names
|
|
for (int i = 0; i < gpuc; i++) {
|
|
remove_brackets(user_info->gpu_model[i]);
|
|
truncate_str(user_info->gpu_model[i], user_info->target_width);
|
|
LOG_V(user_info->gpu_model[i]);
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
// tries to get screen resolution
|
|
#ifndef _WIN32
|
|
void* get_res(void* argp) {
|
|
if (!((struct thread_varg*)argp)->thread_flags[3]) return 0;
|
|
LOG_I("getting resolution");
|
|
char* buffer = ((struct thread_varg*)argp)->buffer;
|
|
struct info* user_info = ((struct thread_varg*)argp)->user_info;
|
|
FILE* resolution = popen("xwininfo -root 2> /dev/null | grep -E 'Width|Height'", "r");
|
|
while (fgets(buffer, BUFFER_SIZE, resolution)) {
|
|
sscanf(buffer, " Width: %d", &user_info->screen_width);
|
|
sscanf(buffer, " Height: %d", &user_info->screen_height);
|
|
}
|
|
LOG_V(user_info->screen_width);
|
|
LOG_V(user_info->screen_height);
|
|
#else
|
|
void* get_res() {
|
|
// TODO: get resolution on windows
|
|
#endif
|
|
return 0;
|
|
}
|
|
|
|
// tries to get the installed package count and package managers name
|
|
void* get_pkg(void* argp) { // this is just a function that returns the total of installed packages
|
|
if (!((struct thread_varg*)argp)->thread_flags[4]) return 0;
|
|
LOG_I("getting pkgs");
|
|
struct info* user_info = ((struct thread_varg*)argp)->user_info;
|
|
user_info->pkgs = 0;
|
|
#ifndef __APPLE__
|
|
#ifndef _WIN32
|
|
// all supported package managers
|
|
struct package_manager pkgmans[] = {
|
|
{PKGPATH "apt", "apt list --installed 2> /dev/null | wc -l", "(apt)"},
|
|
{PKGPATH "apk", "apk info 2> /dev/null | wc -l", "(apk)"},
|
|
// {PKGPATH"dnf","dnf list installed 2> /dev/null | wc -l", "(dnf)"}, // according to https://stackoverflow.com/questions/48570019/advantages-of-dnf-vs-rpm-on-fedora, dnf and rpm return the same number of packages
|
|
{PKGPATH "qlist", "qlist -I 2> /dev/null | wc -l", "(emerge)"},
|
|
{PKGPATH "flatpak", "flatpak list 2> /dev/null | wc -l", "(flatpak)"},
|
|
{PKGPATH "snap", "snap list 2> /dev/null | wc -l", "(snap)"},
|
|
{PKGPATH "guix", "guix package --list-installed 2> /dev/null | wc -l", "(guix)"},
|
|
{PKGPATH "nix-store", "nix-store -q --requisites /run/current-system/sw 2> /dev/null | wc -l", "(nix)"},
|
|
{PKGPATH "pacman", "pacman -Qq 2> /dev/null | wc -l", "(pacman)"},
|
|
{PKGPATH "pkg", "pkg info 2>/dev/null | wc -l", "(pkg)"},
|
|
{PKGPATH "pkg_info", "pkg_info 2>/dev/null | wc -l | sed \"s/ //g\"", "(pkg)"},
|
|
{PKGPATH "port", "port installed 2> /dev/null | tail -n +2 | wc -l", "(port)"},
|
|
{PKGPATH "brew", "find $(brew --cellar 2>/dev/stdout) -maxdepth 1 -type d 2> /dev/null | wc -l | awk '{print $1}'", "(brew-cellar)"},
|
|
{PKGPATH "brew", "find $(brew --caskroom 2>/dev/stdout) -maxdepth 1 -type d 2> /dev/null | wc -l | awk '{print $1}'", "(brew-cask)"},
|
|
{PKGPATH "rpm", "rpm -qa --last 2> /dev/null | wc -l", "(rpm)"},
|
|
{PKGPATH "xbps-query", "xbps-query -l 2> /dev/null | wc -l", "(xbps)"}};
|
|
#endif
|
|
#else
|
|
struct package_manager pkgmans[] = {{"/usr/local/bin/brew", "find $(brew --cellar 2>/dev/stdout) -maxdepth 1 -type d 2> /dev/null | wc -l | awk '{print $1}' > /tmp/uwufetch_brew_tmp", "(brew-cellar)"},
|
|
{"/usr/local/bin/brew", "find $(brew --caskroom 2>/dev/stdout) -maxdepth 1 -type d 2> /dev/null | wc -l | awk '{print $1}' > /tmp/uwufetch_brew_tmp", "(brew-cask)"}};
|
|
#endif
|
|
#ifndef _WIN32
|
|
const int pkgman_count = sizeof(pkgmans) / sizeof(pkgmans[0]); // number of package managers
|
|
int comma_separator = 0;
|
|
for (int i = 0; i < pkgman_count; i++) {
|
|
struct package_manager* current = &pkgmans[i]; // pointer to current package manager
|
|
|
|
unsigned int pkg_count = 0;
|
|
LOG_I("trying pkgman %d: %s", i, current->pkgman_name);
|
|
LOG_V(current->command_path);
|
|
if (access(current->command_path, F_OK) != -1) {
|
|
#ifndef __APPLE__
|
|
FILE* fp = popen(current->command_string, "r"); // trying current package manager
|
|
#else
|
|
system(current->command_string); // writes to a temporary file: for some reason popen() does not intercept the stdout, so i have to read from a temporary file
|
|
FILE* fp = fopen("/tmp/uwufetch_brew_tmp", "r");
|
|
#endif
|
|
if (fscanf(fp, "%u", &pkg_count) == 3) continue;
|
|
|
|
#ifndef __APPLE__
|
|
pclose(fp);
|
|
#else
|
|
// remove("/tmp/uwufetch_brew_tmp");
|
|
fclose(fp);
|
|
#endif
|
|
}
|
|
#ifdef __DEBUG__
|
|
else
|
|
LOG_W("pkgman %s executable not found!", current->pkgman_name);
|
|
#endif
|
|
|
|
// adding a package manager with its package count to user_info->pkgman_name
|
|
user_info->pkgs += pkg_count;
|
|
if (pkg_count > 0) {
|
|
if (comma_separator++) strcat(user_info->pkgman_name, ", ");
|
|
char spkg_count[16];
|
|
sprintf(spkg_count, "%u", pkg_count);
|
|
strcat(user_info->pkgman_name, spkg_count);
|
|
strcat(user_info->pkgman_name, " ");
|
|
strcat(user_info->pkgman_name, current->pkgman_name);
|
|
LOG_V(user_info->pkgman_name);
|
|
}
|
|
}
|
|
#else // _WIN32
|
|
// chocolatey for windows
|
|
FILE* fp = popen("choco list -l --no-color 2> nul", "r");
|
|
unsigned int pkg_count;
|
|
char buffer[7562] = {0};
|
|
while (fgets(buffer, BUFFER_SIZE, fp)) {
|
|
sscanf(buffer, "%u packages installed.", &pkg_count);
|
|
}
|
|
if (fp) pclose(fp);
|
|
|
|
user_info->pkgs = pkg_count;
|
|
char spkg_count[16];
|
|
sprintf(spkg_count, "%u", pkg_count);
|
|
strcat(user_info->pkgman_name, spkg_count);
|
|
strcat(user_info->pkgman_name, " ");
|
|
strcat(user_info->pkgman_name, "(chocolatey)");
|
|
LOG_V(user_info->pkgman_name);
|
|
#endif // _WIN32
|
|
return 0;
|
|
}
|
|
|
|
void* get_model(void* argp) {
|
|
if (!((struct thread_varg*)argp)->thread_flags[5]) return 0;
|
|
LOG_I("getting model");
|
|
struct info* user_info = ((struct thread_varg*)argp)->user_info;
|
|
char* buffer = ((struct thread_varg*)argp)->buffer;
|
|
FILE* model_fp;
|
|
#ifdef _WIN32
|
|
// all the previous files obviously did not exist on windows
|
|
model_fp = popen("wmic computersystem get model", "r");
|
|
while (fgets(buffer, BUFFER_SIZE, model_fp)) {
|
|
if (strstr(buffer, "Model") != 0)
|
|
continue;
|
|
else {
|
|
sprintf(user_info->model, "%s", buffer);
|
|
user_info->model[strlen(user_info->model) - 2] = '\0';
|
|
break;
|
|
}
|
|
}
|
|
#elif defined(__BSD__) || defined(__APPLE__)
|
|
#if defined(__BSD__) && !defined(__OPENBSD__)
|
|
#define HOSTCTL "hw.hv_vendor"
|
|
#elif defined(__APPLE__)
|
|
#define HOSTCTL "hw.model"
|
|
#elif defined(__OPENBSD__)
|
|
#define HOSTCTL "hw.product"
|
|
#endif
|
|
model_fp = popen("sysctl " HOSTCTL, "r");
|
|
while (fgets(buffer, BUFFER_SIZE, model_fp))
|
|
if (sscanf(buffer,
|
|
HOSTCTL
|
|
#ifdef __OPENBSD__
|
|
"="
|
|
#else
|
|
": "
|
|
#endif
|
|
"%[^\n]",
|
|
user_info->model))
|
|
break;
|
|
pclose(model_fp);
|
|
#else
|
|
char model_filename[4][256] = {
|
|
"/sys/devices/virtual/dmi/id/product_version",
|
|
"/sys/devices/virtual/dmi/id/product_name",
|
|
"/sys/devices/virtual/dmi/id/board_name",
|
|
"getprop ro.product.vendor.marketname 2>/dev/null",
|
|
};
|
|
|
|
char tmp_model[4][BUFFER_SIZE] = {0}; // temporary variable to store the contents of all 3 files
|
|
int longest_model = 0, best_len = 0, currentlen = 0;
|
|
FILE* (*tocall[])(const char*, const char*) = {fopen, fopen, fopen, popen}; // open a process or a file, depending on the model_filename
|
|
int (*tocall_close[])(FILE*) = {fclose, fclose, fclose, pclose};
|
|
for (int i = 0; i < 4; i++) {
|
|
// read file
|
|
model_fp = tocall[i](model_filename[i], "r");
|
|
if (model_fp) {
|
|
fgets(tmp_model[i], BUFFER_SIZE, model_fp);
|
|
tmp_model[i][strlen(tmp_model[i]) - 1] = '\0';
|
|
tocall_close[i](model_fp);
|
|
}
|
|
LOG_V(tmp_model[i]);
|
|
// choose the file with the longest name
|
|
currentlen = strlen(tmp_model[i]);
|
|
if (currentlen > best_len) {
|
|
best_len = currentlen;
|
|
longest_model = i;
|
|
}
|
|
}
|
|
if (strlen(tmp_model[longest_model]) == 0) {
|
|
model_fp = popen("lscpu 2>/dev/null", "r");
|
|
while (fgets(buffer, BUFFER_SIZE, model_fp))
|
|
if (sscanf(buffer, "Model name:%*[ | ]%[^\n]", tmp_model[longest_model]) == 1) break;
|
|
pclose(model_fp);
|
|
LOG_V(tmp_model[longest_model]);
|
|
if (strcmp(tmp_model[longest_model], "Icestorm") == 0) sprintf(tmp_model[longest_model], "Apple MacBook Air (M1)");
|
|
}
|
|
sprintf(user_info->model, "%s", tmp_model[longest_model]);
|
|
LOG_V(user_info->model);
|
|
#endif
|
|
return 0;
|
|
}
|
|
|
|
void* get_ker(void* argp) {
|
|
if (!((struct thread_varg*)argp)->thread_flags[6]) return 0;
|
|
LOG_I("getting kernel");
|
|
struct info* user_info = ((struct thread_varg*)argp)->user_info;
|
|
|
|
#ifndef _WIN32
|
|
truncate_str(user_info->sys_var.release, user_info->target_width);
|
|
sprintf(user_info->kernel, "%s %s %s", user_info->sys_var.sysname, user_info->sys_var.release, user_info->sys_var.machine); // kernel name
|
|
truncate_str(user_info->kernel, user_info->target_width);
|
|
LOG_V(user_info->kernel);
|
|
#else // _WIN32
|
|
// windows version
|
|
FILE* kernel_fp = popen("wmic computersystem get systemtype", "r");
|
|
char* buffer = ((struct thread_varg*)argp)->buffer;
|
|
while (fgets(buffer, BUFFER_SIZE, kernel_fp)) {
|
|
if (strstr(buffer, "SystemType") != 0)
|
|
continue;
|
|
else {
|
|
sprintf(user_info->kernel, "%s", buffer);
|
|
user_info->kernel[strlen(user_info->kernel) - 2] = '\0';
|
|
break;
|
|
}
|
|
}
|
|
if (kernel_fp) pclose(kernel_fp);
|
|
#endif // _WIN32
|
|
return 0;
|
|
}
|
|
|
|
void* get_upt(void* argp) {
|
|
LOG_V(((struct thread_varg*)argp)->thread_flags[7]);
|
|
if (!((struct thread_varg*)argp)->thread_flags[7]) return 0;
|
|
LOG_I("getting uptime");
|
|
struct info* user_info = ((struct thread_varg*)argp)->user_info;
|
|
#ifdef __APPLE__
|
|
int mib[2] = {CTL_KERN, KERN_BOOTTIME};
|
|
sysctl(mib, 2, &time_buffer, &time_buffer_len, NULL, 0);
|
|
|
|
time_t bsec = time_buffer.tv_sec;
|
|
time_t csec = time(NULL);
|
|
|
|
user_info->uptime = difftime(csec, bsec);
|
|
#else
|
|
#ifdef __BSD__
|
|
// https://github.com/coreutils/coreutils/blob/master/src/uptime.c
|
|
int boot_time = 0;
|
|
static int request[2] = {CTL_KERN, KERN_BOOTTIME};
|
|
struct timeval result;
|
|
size_t result_len = sizeof result;
|
|
|
|
if (sysctl(request, 2, &result, &result_len, NULL, 0) >= 0) boot_time = result.tv_sec;
|
|
int time_now = time(NULL);
|
|
user_info->uptime = time_now - boot_time;
|
|
#else
|
|
#ifdef _WIN32
|
|
user_info->uptime = GetTickCount() / 1000;
|
|
#else // _WIN32
|
|
user_info->uptime = user_info->sys.uptime;
|
|
#endif // _WIN32
|
|
#endif
|
|
#endif
|
|
LOG_V(user_info->uptime);
|
|
return 0;
|
|
}
|
|
|
|
// Retrieves system information
|
|
void get_info(struct flags flags, struct info* user_info) {
|
|
char buffer[BUFFER_SIZE]; // line buffer
|
|
get_twidth(user_info);
|
|
// os version, cpu and board info
|
|
#ifdef __OPENBSD__
|
|
FILE* os_release = popen("echo ID=openbsd", "r"); // os-release does not exist in OpenBSD
|
|
#else
|
|
FILE* os_release = fopen("/etc/os-release", "r"); // os name file
|
|
#endif
|
|
#ifndef __BSD__
|
|
FILE* cpuinfo = fopen("/proc/cpuinfo", "r"); // cpu name file for not-freebsd systems
|
|
#else
|
|
FILE* cpuinfo = popen("sysctl hw.model", "r"); // cpu name command for freebsd
|
|
#endif
|
|
// trying to get some kind of information about the name of the computer (hopefully a product full name)
|
|
if (os_release) { // get normal vars if os_release exists
|
|
if (flags.os) {
|
|
LOG_I("getting os name from /etc/os-release");
|
|
while (fgets(buffer, BUFFER_SIZE, os_release) &&
|
|
!(sscanf(buffer, "\nID=\"%s\"", user_info->os_name) ||
|
|
sscanf(buffer, "\nID=%s", user_info->os_name)))
|
|
;
|
|
// sometimes for some reason sscanf reads the last '\"' too
|
|
int os_name_len = strlen(user_info->os_name);
|
|
if (user_info->os_name[os_name_len - 1] == '\"') {
|
|
user_info->os_name[os_name_len - 1] = '\0';
|
|
}
|
|
// trying to detect amogos because in its os-release file ID value is just "debian", will be removed when amogos will have an os-release file with ID=amogos
|
|
if (strcmp(user_info->os_name, "debian") == 0 ||
|
|
strcmp(user_info->os_name, "raspbian") == 0) {
|
|
DIR* amogos_plymouth = opendir("/usr/share/plymouth/themes/amogos");
|
|
if (amogos_plymouth) {
|
|
closedir(amogos_plymouth);
|
|
sprintf(user_info->os_name, "amogos");
|
|
LOG_V(user_info->os_name);
|
|
}
|
|
}
|
|
LOG_V(user_info->os_name);
|
|
}
|
|
} else { // try for android vars, next for Apple var, or unknown system
|
|
// android
|
|
DIR* system_app = opendir("/system/app/");
|
|
DIR* system_priv_app = opendir("/system/priv-app/");
|
|
DIR* library = opendir("/Library/");
|
|
if (system_app && system_priv_app) {
|
|
closedir(system_app);
|
|
closedir(system_priv_app);
|
|
if (flags.os) sprintf(user_info->os_name, "android");
|
|
LOG_V(user_info->os_name);
|
|
if (flags.user) {
|
|
// username
|
|
FILE* whoami = popen("whoami", "r");
|
|
if (fscanf(whoami, "%s", user_info->user) == 3) {
|
|
}
|
|
LOG_V(user_info->user);
|
|
pclose(whoami);
|
|
}
|
|
} else if (library) { // Apple
|
|
closedir(library);
|
|
#ifdef __APPLE__
|
|
if (flags.cpu) {
|
|
sysctlbyname("machdep.cpu.brand_string", &cpu_buffer, &cpu_buffer_len, NULL,
|
|
0); // cpu name
|
|
sprintf(user_info->cpu_model, "%s", cpu_buffer);
|
|
}
|
|
if (flags.os) {
|
|
#ifndef __IPHONE__
|
|
sprintf(user_info->os_name, "macos");
|
|
#else
|
|
sprintf(user_info->os_name, "ios");
|
|
#endif
|
|
}
|
|
#endif
|
|
} else // if no option before is working, the system is unknown
|
|
sprintf(user_info->os_name, "unknown");
|
|
}
|
|
#ifndef __BSD__
|
|
#endif
|
|
#ifndef _WIN32
|
|
// getting username and hostname
|
|
if (flags.user) {
|
|
LOG_I("getting username and hostname");
|
|
gethostname(user_info->host, 256);
|
|
LOG_V(user_info->host);
|
|
char* tmp_user = getenv("USER");
|
|
LOG_V(tmp_user);
|
|
if (tmp_user == NULL)
|
|
sprintf(user_info->user, "%s", "");
|
|
else
|
|
sprintf(user_info->user, "%s", tmp_user);
|
|
LOG_V(user_info->user);
|
|
if (os_release) fclose(os_release);
|
|
}
|
|
if (flags.shell) {
|
|
LOG_I("getting shell");
|
|
char* tmp_shell = getenv("SHELL"); // shell name
|
|
LOG_V(tmp_shell);
|
|
if (!tmp_shell)
|
|
sprintf(user_info->shell, "%s", "");
|
|
else
|
|
snprintf(user_info->shell, sizeof user_info->shell, "%s", tmp_shell);
|
|
#ifdef __linux__
|
|
if (strlen(user_info->shell) > 16) // android shell name was too long
|
|
memmove(&user_info->shell, &user_info->shell[27], strlen(user_info->shell));
|
|
#endif
|
|
LOG_V(user_info->shell);
|
|
}
|
|
#else // if _WIN32
|
|
// cpu name
|
|
if (flags.cpu) {
|
|
cpuinfo = popen("wmic cpu get caption", "r");
|
|
while (fgets(buffer, BUFFER_SIZE, cpuinfo)) {
|
|
if (strstr(buffer, "Caption") != 0)
|
|
continue;
|
|
else {
|
|
sprintf(user_info->cpu_model, "%s", buffer);
|
|
user_info->cpu_model[strlen(user_info->cpu_model) - 2] = '\0';
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
// username
|
|
if (flags.user) {
|
|
FILE* user_host_fp = popen("wmic computersystem get username", "r");
|
|
while (fgets(buffer, BUFFER_SIZE, user_host_fp)) {
|
|
if (strstr(buffer, "UserName") != 0)
|
|
continue;
|
|
else {
|
|
sscanf(buffer, "%[^\\]%s", user_info->host, user_info->user);
|
|
memmove(user_info->user, user_info->user + 1, sizeof(user_info->user) - 1);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
// powershell version
|
|
if (flags.shell) {
|
|
FILE* shell_fp = popen("powershell $PSVersionTable", "r");
|
|
sprintf(user_info->shell, "PowerShell ");
|
|
char tmp_shell[64];
|
|
while (fgets(buffer, BUFFER_SIZE, shell_fp) &&
|
|
sscanf(buffer, "PSVersion %s", tmp_shell) == 0)
|
|
;
|
|
strcat(user_info->shell, tmp_shell);
|
|
}
|
|
#endif // _WIN32
|
|
|
|
#ifdef _WIN32
|
|
if (flags.os) sprintf(user_info->os_name, "windows");
|
|
#endif
|
|
get_sys(user_info);
|
|
// are threads overpowered? nah
|
|
void* (*fnptrs[])(void*) = {get_cpu, get_ram, get_gpu, get_res, get_pkg, get_model, get_ker, get_upt};
|
|
struct thread_varg args =
|
|
(struct thread_varg){buffer,
|
|
user_info,
|
|
cpuinfo,
|
|
{flags.cpu, flags.ram, flags.gpu, flags.resolution, flags.pkgs, flags.model, flags.kernel, flags.uptime}};
|
|
#define THREAD_COUNT 8
|
|
#ifndef _WIN32
|
|
pthread_t tids[THREAD_COUNT] = {0};
|
|
#endif
|
|
for (int i = 0; i < THREAD_COUNT; i++) {
|
|
LOG_I("STARTING thread %d", i);
|
|
#ifdef _WIN32
|
|
fnptrs[i](&args);
|
|
#else
|
|
pthread_create(&tids[i], NULL, fnptrs[i], &args);
|
|
#endif
|
|
}
|
|
#ifndef _WIN32
|
|
for (int i = 0; i < THREAD_COUNT; i++) {
|
|
if (tids[i] != 0) pthread_join(tids[i], NULL);
|
|
LOG_I("JOINING thread %d", i);
|
|
}
|
|
#endif
|
|
fclose(cpuinfo);
|
|
}
|