diff options
Diffstat (limited to 'hardinfo2')
-rw-r--r-- | hardinfo2/binreloc.c | 668 | ||||
-rw-r--r-- | hardinfo2/cpu_util.c | 252 | ||||
-rw-r--r-- | hardinfo2/dmi_util.c | 450 | ||||
-rw-r--r-- | hardinfo2/dt_util.c | 1187 | ||||
-rw-r--r-- | hardinfo2/expr.c | 250 | ||||
-rw-r--r-- | hardinfo2/gg_key_file_parse_string_as_value.c | 112 | ||||
-rw-r--r-- | hardinfo2/gg_strescape.c | 139 | ||||
-rw-r--r-- | hardinfo2/gpu_util.c | 477 | ||||
-rw-r--r-- | hardinfo2/hardinfo.c | 175 | ||||
-rw-r--r-- | hardinfo2/hinote_util.c | 27 | ||||
-rw-r--r-- | hardinfo2/info.c | 584 | ||||
-rw-r--r-- | hardinfo2/pci_util.c | 463 | ||||
-rw-r--r-- | hardinfo2/problem_marker.c | 13 | ||||
-rw-r--r-- | hardinfo2/socket.c | 127 | ||||
-rw-r--r-- | hardinfo2/storage_util.c | 250 | ||||
-rw-r--r-- | hardinfo2/udisks2_util.c | 641 | ||||
-rw-r--r-- | hardinfo2/usb_util.c | 557 | ||||
-rw-r--r-- | hardinfo2/util.c | 1452 | ||||
-rw-r--r-- | hardinfo2/vendor.c | 655 | ||||
-rw-r--r-- | hardinfo2/x_util.c | 342 |
20 files changed, 8821 insertions, 0 deletions
diff --git a/hardinfo2/binreloc.c b/hardinfo2/binreloc.c new file mode 100644 index 00000000..f47a3fae --- /dev/null +++ b/hardinfo2/binreloc.c @@ -0,0 +1,668 @@ +/* + * BinReloc - a library for creating relocatable executables + * Written by: Hongli Lai <h.lai@chello.nl> + * http://autopackage.org/ + * + * This source code is public domain. You can relicense this code + * under whatever license you want. + * + * See http://autopackage.org/docs/binreloc/ for + * more information and how to use this. + */ + +#ifndef __BINRELOC_C__ +#define __BINRELOC_C__ + +#include <sys/types.h> +#include <sys/stat.h> +#include <unistd.h> +#include <stdio.h> +#include <stdlib.h> +#include <limits.h> +#include <string.h> +#include "binreloc.h" +#include "config.h" + +G_BEGIN_DECLS +/** @internal + * Find the canonical filename of the executable. Returns the filename + * (which must be freed) or NULL on error. If the parameter 'error' is + * not NULL, the error code will be stored there, if an error occured. + */ +static char *_br_find_exe(GbrInitError * error) +{ + char *path, *path2, *line, *result; + size_t buf_size; + ssize_t size; + struct stat stat_buf; + FILE *f; + + /* Read from /proc/self/exe (symlink) */ + if (sizeof(path) > SSIZE_MAX) + buf_size = SSIZE_MAX - 1; + else + buf_size = PATH_MAX - 1; + path = (char *) g_try_malloc(buf_size); + if (path == NULL) { + /* Cannot allocate memory. */ + if (error) + *error = GBR_INIT_ERROR_NOMEM; + return NULL; + } + path2 = (char *) g_try_malloc(buf_size); + if (path2 == NULL) { + /* Cannot allocate memory. */ + if (error) + *error = GBR_INIT_ERROR_NOMEM; + g_free(path); + return NULL; + } + + strncpy(path2, "/proc/self/exe", buf_size - 1); + + while (1) { + int i; + + size = readlink(path2, path, buf_size - 1); + if (size == -1) { + /* Error. */ + g_free(path2); + break; + } + + /* readlink() success. */ + path[size] = '\0'; + + /* Check whether the symlink's target is also a symlink. + * We want to get the final target. */ + i = stat(path, &stat_buf); + if (i == -1) { + /* Error. */ + g_free(path2); + break; + } + + /* stat() success. */ + if (!S_ISLNK(stat_buf.st_mode)) { + /* path is not a symlink. Done. */ + g_free(path2); + return path; + } + + /* path is a symlink. Continue loop and resolve this. */ + strncpy(path, path2, buf_size - 1); + } + + + /* readlink() or stat() failed; this can happen when the program is + * running in Valgrind 2.2. Read from /proc/self/maps as fallback. */ + + buf_size = PATH_MAX + 128; + line = (char *) g_try_realloc(path, buf_size); + if (line == NULL) { + /* Cannot allocate memory. */ + g_free(path); + if (error) + *error = GBR_INIT_ERROR_NOMEM; + return NULL; + } + + f = fopen("/proc/self/maps", "r"); + if (f == NULL) { + g_free(line); + if (error) + *error = GBR_INIT_ERROR_OPEN_MAPS; + return NULL; + } + + /* The first entry should be the executable name. */ + result = fgets(line, (int) buf_size, f); + if (result == NULL) { + fclose(f); + g_free(line); + if (error) + *error = GBR_INIT_ERROR_READ_MAPS; + return NULL; + } + + /* Get rid of newline character. */ + buf_size = strlen(line); + if (buf_size <= 0) { + /* Huh? An empty string? */ + fclose(f); + g_free(line); + if (error) + *error = GBR_INIT_ERROR_INVALID_MAPS; + return NULL; + } + if (line[buf_size - 1] == 10) + line[buf_size - 1] = 0; + + /* Extract the filename; it is always an absolute path. */ + path = strchr(line, '/'); + + /* Sanity check. */ + if (strstr(line, " r-xp ") == NULL || path == NULL) { + fclose(f); + g_free(line); + if (error) + *error = GBR_INIT_ERROR_INVALID_MAPS; + return NULL; + } + + path = g_strdup(path); + g_free(line); + fclose(f); + return path; +} + + +/** @internal + * Find the canonical filename of the executable which owns symbol. + * Returns a filename which must be freed, or NULL on error. + */ +static char *_br_find_exe_for_symbol(const void *symbol, + GbrInitError * error) +{ +#define SIZE PATH_MAX + 100 + FILE *f; + size_t address_string_len; + char *address_string, line[SIZE], *found; + + if (symbol == NULL) + return (char *) NULL; + + f = fopen("/proc/self/maps", "r"); + if (f == NULL) + return (char *) NULL; + + address_string_len = 4; + address_string = (char *) g_try_malloc(address_string_len); + found = (char *) NULL; + + while (!feof(f)) { + char *start_addr, *end_addr, *end_addr_end, *file; + void *start_addr_p, *end_addr_p; + size_t len; + + if (fgets(line, SIZE, f) == NULL) + break; + + /* Sanity check. */ + if (strstr(line, " r-xp ") == NULL || strchr(line, '/') == NULL) + continue; + + /* Parse line. */ + start_addr = line; + end_addr = strchr(line, '-'); + file = strchr(line, '/'); + + /* More sanity check. */ + if (!(file > end_addr && end_addr != NULL && end_addr[0] == '-')) + continue; + + end_addr[0] = '\0'; + end_addr++; + end_addr_end = strchr(end_addr, ' '); + if (end_addr_end == NULL) + continue; + + end_addr_end[0] = '\0'; + len = strlen(file); + if (len == 0) + continue; + if (file[len - 1] == '\n') + file[len - 1] = '\0'; + + /* Get rid of "(deleted)" from the filename. */ + len = strlen(file); + if (len > 10 && strcmp(file + len - 10, " (deleted)") == 0) + file[len - 10] = '\0'; + + /* I don't know whether this can happen but better safe than sorry. */ + len = strlen(start_addr); + if (len != strlen(end_addr)) + continue; + + + /* Transform the addresses into a string in the form of 0xdeadbeef, + * then transform that into a pointer. */ + if (address_string_len < len + 3) { + address_string_len = len + 3; + address_string = + (char *) g_try_realloc(address_string, address_string_len); + } + + memcpy(address_string, "0x", 2); + memcpy(address_string + 2, start_addr, len); + address_string[2 + len] = '\0'; + sscanf(address_string, "%p", &start_addr_p); + + memcpy(address_string, "0x", 2); + memcpy(address_string + 2, end_addr, len); + address_string[2 + len] = '\0'; + sscanf(address_string, "%p", &end_addr_p); + + + if (symbol >= start_addr_p && symbol < end_addr_p) { + found = file; + break; + } + } + + g_free(address_string); + fclose(f); + + if (found == NULL) + return (char *) NULL; + else + return g_strdup(found); +} + + +static gchar *exe = NULL; + +static void set_gerror(GError ** error, GbrInitError errcode); + + +/** Initialize the BinReloc library (for applications). + * + * This function must be called before using any other BinReloc functions. + * It attempts to locate the application's canonical filename. + * + * @note If you want to use BinReloc for a library, then you should call + * gbr_init_lib() instead. + * + * @param error If BinReloc failed to initialize, then the error report will + * be stored in this variable. Set to NULL if you don't want an + * error report. See the #GbrInitError for a list of error + * codes. + * + * @returns TRUE on success, FALSE if BinReloc failed to initialize. + */ +gboolean gbr_init(GError ** error) +{ + GbrInitError errcode = 0; + + /* Locate the application's filename. */ + exe = _br_find_exe(&errcode); + if (exe != NULL) + /* Success! */ + return TRUE; + else { + /* Failed :-( */ + set_gerror(error, errcode); + return FALSE; + } +} + + +/** Initialize the BinReloc library (for libraries). + * + * This function must be called before using any other BinReloc functions. + * It attempts to locate the calling library's canonical filename. + * + * @note The BinReloc source code MUST be included in your library, or this + * function won't work correctly. + * + * @returns TRUE on success, FALSE if a filename cannot be found. + */ +gboolean gbr_init_lib(GError ** error) +{ + GbrInitError errcode = 0; + + exe = _br_find_exe_for_symbol((const void *) "", &errcode); + if (exe != NULL) + /* Success! */ + return TRUE; + else { + /* Failed :-( */ + set_gerror(error, errcode); + return exe != NULL; + } +} + + +static void set_gerror(GError ** error, GbrInitError errcode) +{ + gchar *error_message; + + if (error == NULL) + return; + + switch (errcode) { + case GBR_INIT_ERROR_NOMEM: + error_message = "Cannot allocate memory."; + break; + case GBR_INIT_ERROR_OPEN_MAPS: + error_message = "Unable to open /proc/self/maps for reading."; + break; + case GBR_INIT_ERROR_READ_MAPS: + error_message = "Unable to read from /proc/self/maps."; + break; + case GBR_INIT_ERROR_INVALID_MAPS: + error_message = "The file format of /proc/self/maps is invalid."; + break; + case GBR_INIT_ERROR_DISABLED: + error_message = "Binary relocation support is disabled."; + break; + default: + error_message = "Unknown error."; + break; + }; + g_set_error(error, g_quark_from_static_string("GBinReloc"), + errcode, "%s", error_message); +} + + +/** Find the canonical filename of the current application. + * + * @param default_exe A default filename which will be used as fallback. + * @returns A string containing the application's canonical filename, + * which must be freed when no longer necessary. If BinReloc is + * not initialized, or if the initialization function failed, + * then a copy of default_exe will be returned. If default_exe + * is NULL, then NULL will be returned. + */ +gchar *gbr_find_exe(const gchar * default_exe) +{ + if (exe == NULL) { + /* BinReloc is not initialized. */ + if (default_exe != NULL) + return g_strdup(default_exe); + else + return NULL; + } + return g_strdup(exe); +} + + +/** Locate the directory in which the current application is installed. + * + * The prefix is generated by the following pseudo-code evaluation: + * \code + * dirname(exename) + * \endcode + * + * @param default_dir A default directory which will used as fallback. + * @return A string containing the directory, which must be freed when no + * longer necessary. If BinReloc is not initialized, or if the + * initialization function failed, then a copy of default_dir + * will be returned. If default_dir is NULL, then NULL will be + * returned. + */ +gchar *gbr_find_exe_dir(const gchar * default_dir) +{ + if (exe == NULL) { + /* BinReloc not initialized. */ + if (default_dir != NULL) + return g_strdup(default_dir); + else + return NULL; + } + + return g_path_get_dirname(exe); +} + + +/** Locate the prefix in which the current application is installed. + * + * The prefix is generated by the following pseudo-code evaluation: + * \code + * dirname(dirname(exename)) + * \endcode + * + * @param default_prefix A default prefix which will used as fallback. + * @return A string containing the prefix, which must be freed when no + * longer necessary. If BinReloc is not initialized, or if the + * initialization function failed, then a copy of default_prefix + * will be returned. If default_prefix is NULL, then NULL will be + * returned. + */ +gchar *gbr_find_prefix(const gchar * default_prefix) +{ + gchar *dir1, *dir2; + + if (exe == NULL) { + /* BinReloc not initialized. */ + if (default_prefix != NULL) + return g_strdup(default_prefix); + else + return NULL; + } + + dir1 = g_path_get_dirname(exe); + dir2 = g_path_get_dirname(dir1); + g_free(dir1); + return dir2; +} + + +/** Locate the application's binary folder. + * + * The path is generated by the following pseudo-code evaluation: + * \code + * prefix + "/bin" + * \endcode + * + * @param default_bin_dir A default path which will used as fallback. + * @return A string containing the bin folder's path, which must be freed when + * no longer necessary. If BinReloc is not initialized, or if the + * initialization function failed, then a copy of default_bin_dir will + * be returned. If default_bin_dir is NULL, then NULL will be returned. + */ +gchar *gbr_find_bin_dir(const gchar * default_bin_dir) +{ + gchar *prefix, *dir; + + prefix = gbr_find_prefix(NULL); + if (prefix == NULL) { + /* BinReloc not initialized. */ + if (default_bin_dir != NULL) + return g_strdup(default_bin_dir); + else + return NULL; + } + + dir = g_build_filename(prefix, "bin", NULL); + g_free(prefix); + return dir; +} + + +/** Locate the application's superuser binary folder. + * + * The path is generated by the following pseudo-code evaluation: + * \code + * prefix + "/sbin" + * \endcode + * + * @param default_sbin_dir A default path which will used as fallback. + * @return A string containing the sbin folder's path, which must be freed when + * no longer necessary. If BinReloc is not initialized, or if the + * initialization function failed, then a copy of default_sbin_dir will + * be returned. If default_bin_dir is NULL, then NULL will be returned. + */ +gchar *gbr_find_sbin_dir(const gchar * default_sbin_dir) +{ + gchar *prefix, *dir; + + prefix = gbr_find_prefix(NULL); + if (prefix == NULL) { + /* BinReloc not initialized. */ + if (default_sbin_dir != NULL) + return g_strdup(default_sbin_dir); + else + return NULL; + } + + dir = g_build_filename(prefix, "sbin", NULL); + g_free(prefix); + return dir; +} + + +/** Locate the application's data folder. + * + * The path is generated by the following pseudo-code evaluation: + * \code + * prefix + "/share" + * \endcode + * + * @param default_data_dir A default path which will used as fallback. + * @return A string containing the data folder's path, which must be freed when + * no longer necessary. If BinReloc is not initialized, or if the + * initialization function failed, then a copy of default_data_dir + * will be returned. If default_data_dir is NULL, then NULL will be + * returned. + */ +gchar *gbr_find_data_dir(const gchar * default_data_dir) +{ + gchar *prefix, *dir; + + prefix = gbr_find_prefix(NULL); + if (prefix == NULL) { + /* BinReloc not initialized. */ + if (default_data_dir != NULL) + return g_strdup(default_data_dir); + else + return NULL; + } + + dir = g_build_filename(prefix, "share", NULL); + g_free(prefix); + return dir; +} + + +/** Locate the application's localization folder. + * + * The path is generated by the following pseudo-code evaluation: + * \code + * prefix + "/share/locale" + * \endcode + * + * @param default_locale_dir A default path which will used as fallback. + * @return A string containing the localization folder's path, which must be freed when + * no longer necessary. If BinReloc is not initialized, or if the + * initialization function failed, then a copy of default_locale_dir will be returned. + * If default_locale_dir is NULL, then NULL will be returned. + */ +gchar *gbr_find_locale_dir(const gchar * default_locale_dir) +{ + gchar *data_dir, *dir; + + data_dir = gbr_find_data_dir(NULL); + if (data_dir == NULL) { + /* BinReloc not initialized. */ + if (default_locale_dir != NULL) + return g_strdup(default_locale_dir); + else + return NULL; + } + + dir = g_build_filename(data_dir, "locale", NULL); + g_free(data_dir); + return dir; +} + + +/** Locate the application's library folder. + * + * The path is generated by the following pseudo-code evaluation: + * \code + * prefix + "/lib" + * \endcode + * + * @param default_lib_dir A default path which will used as fallback. + * @return A string containing the library folder's path, which must be freed when + * no longer necessary. If BinReloc is not initialized, or if the + * initialization function failed, then a copy of default_lib_dir will be returned. + * If default_lib_dir is NULL, then NULL will be returned. + */ +gchar *gbr_find_lib_dir(const gchar * default_lib_dir) +{ + gchar *prefix, *dir; + + prefix = gbr_find_prefix(NULL); + if (prefix == NULL) { + /* BinReloc not initialized. */ + if (default_lib_dir != NULL) + return g_strdup(default_lib_dir); + else + return NULL; + } + + dir = g_build_filename(prefix, LIBDIR, NULL); + + g_free(prefix); + return dir; +} + + +/** Locate the application's libexec folder. + * + * The path is generated by the following pseudo-code evaluation: + * \code + * prefix + "/libexec" + * \endcode + * + * @param default_libexec_dir A default path which will used as fallback. + * @return A string containing the libexec folder's path, which must be freed when + * no longer necessary. If BinReloc is not initialized, or if the initialization + * function failed, then a copy of default_libexec_dir will be returned. + * If default_libexec_dir is NULL, then NULL will be returned. + */ +gchar *gbr_find_libexec_dir(const gchar * default_libexec_dir) +{ + gchar *prefix, *dir; + + prefix = gbr_find_prefix(NULL); + if (prefix == NULL) { + /* BinReloc not initialized. */ + if (default_libexec_dir != NULL) + return g_strdup(default_libexec_dir); + else + return NULL; + } + + dir = g_build_filename(prefix, "libexec", NULL); + g_free(prefix); + return dir; +} + + +/** Locate the application's configuration files folder. + * + * The path is generated by the following pseudo-code evaluation: + * \code + * prefix + "/etc" + * \endcode + * + * @param default_etc_dir A default path which will used as fallback. + * @return A string containing the etc folder's path, which must be freed when + * no longer necessary. If BinReloc is not initialized, or if the initialization + * function failed, then a copy of default_etc_dir will be returned. + * If default_etc_dir is NULL, then NULL will be returned. + */ +gchar *gbr_find_etc_dir(const gchar * default_etc_dir) +{ + gchar *prefix, *dir; + + prefix = gbr_find_prefix(NULL); + if (prefix == NULL) { + /* BinReloc not initialized. */ + if (default_etc_dir != NULL) + return g_strdup(default_etc_dir); + else + return NULL; + } + + dir = g_build_filename(prefix, "etc", NULL); + g_free(prefix); + return dir; +} + + +G_END_DECLS +#endif /* __BINRELOC_C__ */ diff --git a/hardinfo2/cpu_util.c b/hardinfo2/cpu_util.c new file mode 100644 index 00000000..acbe63db --- /dev/null +++ b/hardinfo2/cpu_util.c @@ -0,0 +1,252 @@ +/* + * HardInfo - Displays System Information + * Copyright (C) 2003-2006 L. A. F. Pereira <l@tia.mat.br> + * This file by Burt P. <pburt0@gmail.com> + * + * This program 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, version 2 or later. + * + * 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, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include <string.h> +#include "hardinfo.h" +#include "cpu_util.h" +#include "cpubits.h" + +#define CPU_TOPO_NULL -9877 + +const gchar *byte_order_str() { +#if G_BYTE_ORDER == G_LITTLE_ENDIAN + return _("Little Endian"); +#else + return _("Big Endian"); +#endif +} + +int processor_has_flag(gchar * strflags, gchar * strflag) +{ + gchar **flags; + gint ret = 0; + if (strflags == NULL || strflag == NULL) + return 0; + flags = g_strsplit(strflags, " ", 0); + ret = g_strv_contains((const gchar * const *)flags, strflag); + g_strfreev(flags); + return ret; +} + +gchar* get_cpu_str(const gchar* file, gint cpuid) { + gchar *tmp0 = NULL; + gchar *tmp1 = NULL; + tmp0 = g_strdup_printf("/sys/devices/system/cpu/cpu%d/%s", cpuid, file); + g_file_get_contents(tmp0, &tmp1, NULL, NULL); + g_free(tmp0); + return tmp1; +} + +gint get_cpu_int(const char* item, int cpuid, int null_val) { + gchar *fc = NULL; + int ret = null_val; + fc = get_cpu_str(item, cpuid); + if (fc) { + ret = atol(fc); + g_free(fc); + } + return ret; +} + +/* cpubits is 32768 bits long + * core_ids are not unique among physical_ids + * hack up cpubits into 128 packs of 256 cores + * to make cores unique in cpubits */ +#define MAX_CORES_PER_PACK 256 +#define MAX_PACKS 128 + +int cpu_procs_cores_threads_nodes(int *p, int *c, int *t, int *n) +{ + cpubits *threads, *cores, *packs; + char *tmp; + int i, m, pack_id, core_id; + + g_file_get_contents("/sys/devices/system/cpu/present", &tmp, NULL, NULL); + if (tmp == NULL) { + *p = *c = *t = *n = -1; + return 0; + } + + threads = cpubits_from_str(tmp); + cores = cpubits_from_str(""); + packs = cpubits_from_str(""); + m = cpubits_max(threads); + for (i = 0; i <= m; i++) { + pack_id = get_cpu_int("topology/physical_package_id", i, CPU_TOPO_NULL); + core_id = get_cpu_int("topology/core_id", i, CPU_TOPO_NULL); + if (pack_id < 0) + pack_id = 0; + CPUBIT_SET(packs, pack_id); + if (core_id >= 0) { + CPUBIT_SET(cores, (pack_id * MAX_CORES_PER_PACK) + core_id); + } + } + + *t = cpubits_count(threads); + *c = cpubits_count(cores); + *p = cpubits_count(packs); + *n = 1; + + g_free(tmp); + g_file_get_contents("/sys/devices/system/node/possible", &tmp, NULL, NULL); + if (tmp != NULL) { + cpubits *nodes = cpubits_from_str(tmp); + if (nodes) + *n = cpubits_count(nodes); + free(nodes); + } + + if (!*c) + *c = 1; + if (!*p) + *p = 1; + if (!*n) + *n = 1; + + g_free(threads); + g_free(cores); + g_free(packs); + g_free(tmp); + return 1; +} + +cpufreq_data *cpufreq_new(gint id) +{ + cpufreq_data *cpufd; + cpufd = malloc(sizeof(cpufreq_data)); + if (cpufd) { + memset(cpufd, 0, sizeof(cpufreq_data)); + cpufd->id = id; + cpufreq_update(cpufd, 0); + } + return cpufd; +} + +void cpufreq_update(cpufreq_data *cpufd, int cur_only) +{ + if (cpufd) { + cpufd->cpukhz_cur = get_cpu_int("cpufreq/scaling_cur_freq", cpufd->id, 0); + if (cur_only) return; + cpufd->scaling_driver = get_cpu_str("cpufreq/scaling_driver", cpufd->id); + cpufd->scaling_governor = get_cpu_str("cpufreq/scaling_governor", cpufd->id); + cpufd->transition_latency = get_cpu_int("cpufreq/cpuinfo_transition_latency", cpufd->id, 0); + cpufd->cpukhz_min = get_cpu_int("cpufreq/scaling_min_freq", cpufd->id, 0); + cpufd->cpukhz_max = get_cpu_int("cpufreq/scaling_max_freq", cpufd->id, 0); + if (cpufd->scaling_driver == NULL) cpufd->scaling_driver = g_strdup("(Unknown)"); + if (cpufd->scaling_governor == NULL) cpufd->scaling_governor = g_strdup("(Unknown)"); + + /* x86 uses freqdomain_cpus, all others use affected_cpus */ + cpufd->shared_list = get_cpu_str("cpufreq/freqdomain_cpus", cpufd->id); + if (cpufd->shared_list == NULL) cpufd->shared_list = get_cpu_str("cpufreq/affected_cpus", cpufd->id); + if (cpufd->shared_list == NULL) cpufd->shared_list = g_strdup_printf("%d", cpufd->id); + } +} + +void cpufreq_free(cpufreq_data *cpufd) +{ + if (cpufd) { + g_free(cpufd->scaling_driver); + g_free(cpufd->scaling_governor); + } + g_free(cpufd); +} + +cpu_topology_data *cputopo_new(gint id) +{ + cpu_topology_data *cputd; + cputd = malloc(sizeof(cpu_topology_data)); + if (cputd) { + memset(cputd, 0, sizeof(cpu_topology_data)); + cputd->id = id; + cputd->socket_id = get_cpu_int("topology/physical_package_id", id, CPU_TOPO_NULL); + cputd->core_id = get_cpu_int("topology/core_id", id, CPU_TOPO_NULL); + cputd->book_id = get_cpu_int("topology/book_id", id, CPU_TOPO_NULL); + cputd->drawer_id = get_cpu_int("topology/drawer_id", id, CPU_TOPO_NULL); + } + return cputd; + +} + +void cputopo_free(cpu_topology_data *cputd) +{ + g_free(cputd); +} + +gchar *cpufreq_section_str(cpufreq_data *cpufd) +{ + if (cpufd == NULL) + return g_strdup(""); + + if (cpufd->cpukhz_min || cpufd->cpukhz_max || cpufd->cpukhz_cur) { + return g_strdup_printf( + "[%s]\n" + "%s=%d %s\n" + "%s=%d %s\n" + "%s=%d %s\n" + "%s=%d %s\n" + "%s=%s\n" + "%s=%s\n", + _("Frequency Scaling"), + _("Minimum"), cpufd->cpukhz_min, _("kHz"), + _("Maximum"), cpufd->cpukhz_max, _("kHz"), + _("Current"), cpufd->cpukhz_cur, _("kHz"), + _("Transition Latency"), cpufd->transition_latency, _("ns"), + _("Governor"), cpufd->scaling_governor, + _("Driver"), cpufd->scaling_driver); + } else { + return g_strdup_printf( + "[%s]\n" + "%s=%s\n", + _("Frequency Scaling"), + _("Driver"), cpufd->scaling_driver); + } +} + +gchar *cputopo_section_str(cpu_topology_data *cputd) +{ + static const char na[] = N_("(Not Available)"); + char sock_str[64] = "", core_str[64] = ""; + char book_str[64] = "", drawer_str[64] = ""; + + if (cputd == NULL) + return g_strdup(""); + + if (cputd->socket_id != CPU_TOPO_NULL && cputd->socket_id != -1) + sprintf(sock_str, "%s=%d\n", _("Socket"), cputd->socket_id); + else + sprintf(sock_str, "%s=%s\n", _("Socket"), na); + + if (cputd->core_id != CPU_TOPO_NULL) + sprintf(core_str, "%s=%d\n", _("Core"), cputd->core_id); + else + sprintf(core_str, "%s=%s\n", _("Core"), na); + + if (cputd->book_id != CPU_TOPO_NULL) + sprintf(core_str, "%s=%d\n", _("Book"), cputd->book_id); + if (cputd->book_id != CPU_TOPO_NULL) + sprintf(core_str, "%s=%d\n", _("Drawer"), cputd->drawer_id); + + return g_strdup_printf( + "[%s]\n" + "%s=%d\n" + "%s%s%s%s", + _("Topology"), + _("ID"), cputd->id, + sock_str, core_str, book_str, drawer_str ); +} diff --git a/hardinfo2/dmi_util.c b/hardinfo2/dmi_util.c new file mode 100644 index 00000000..35f9282f --- /dev/null +++ b/hardinfo2/dmi_util.c @@ -0,0 +1,450 @@ +/* + * HardInfo - Displays System Information + * Copyright (C) 2003-2017 L. A. F. Pereira <l@tia.mat.br> + * This file + * Copyright (C) 2017 Burt P. <pburt0@gmail.com> + * + * This program 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, version 2 or later. + * + * 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, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include "hardinfo.h" +#include "dmi_util.h" + +static const char *dmi_type_strings[] = { + [0] = N_("BIOS Information"), + [1] = N_("System"), + [2] = N_("Base Board"), + [3] = N_("Chassis"), + [4] = N_("Processor"), + [5] = N_("Memory Controller"), + [6] = N_("Memory Module"), + [7] = N_("Cache"), + [8] = N_("Port Connector"), + [9] = N_("System Slots"), + [10] = N_("On Board Devices"), + [11] = N_("OEM Strings"), + [12] = N_("System Configuration Options"), + [13] = N_("BIOS Language"), + [14] = N_("Group Associations"), + [15] = N_("System Event Log"), + [16] = N_("Physical Memory Array"), + [17] = N_("Memory Device"), + [18] = N_("32-bit Memory Error"), + [19] = N_("Memory Array Mapped Address"), + [20] = N_("Memory Device Mapped Address"), + [21] = N_("Built-in Pointing Device"), + [22] = N_("Portable Battery"), + [23] = N_("System Reset"), + [24] = N_("Hardware Security"), + [25] = N_("System Power Controls"), + [26] = N_("Voltage Probe"), + [27] = N_("Cooling Device"), + [28] = N_("Temperature Probe"), + [29] = N_("Electrical Current Probe"), + [30] = N_("Out-of-band Remote Access"), + [31] = N_("Boot Integrity Services"), + [32] = N_("System Boot"), + [33] = N_("64-bit Memory Error"), + [34] = N_("Management Device"), + [35] = N_("Management Device Component"), + [36] = N_("Management Device Threshold Data"), + [37] = N_("Memory Channel"), + [38] = N_("IPMI Device"), + [39] = N_("Power Supply"), + [40] = N_("Additional Information"), + [41] = N_("Onboard Device"), + //127 = End of Table +}; + +/* frees the string and sets it NULL if it is to be ignored + * returns -1 if error, 0 if ok, 1 if ignored */ +static int ignore_placeholder_strings(gchar **pstr) { + gchar *chk, *p; + if (pstr == NULL || *pstr == NULL) + return -1; + chk = g_strdup(*pstr); + +#define DMI_IGNORE(m) if (strcasecmp(m, *pstr) == 0) { g_free(chk); g_free(*pstr); *pstr = NULL; return 1; } + DMI_IGNORE("To be filled by O.E.M."); + DMI_IGNORE("Default String"); + DMI_IGNORE("System Product Name"); + DMI_IGNORE("System Manufacturer"); + DMI_IGNORE("System Version"); + DMI_IGNORE("System Serial Number"); + DMI_IGNORE("Rev X.0x"); /* ASUS board version nonsense */ + DMI_IGNORE("x.x"); /* Gigabyte board version nonsense */ + DMI_IGNORE("NA"); + DMI_IGNORE("SKU"); + + /* noticed on an HP x360 with Insyde BIOS */ + DMI_IGNORE("Type2 - Board Asset Tag"); + DMI_IGNORE("Type1ProductConfigId"); + + /* Toshiba Laptop with Insyde BIOS */ + DMI_IGNORE("Base Board Version"); + DMI_IGNORE("No Asset Tag"); + DMI_IGNORE("None"); + DMI_IGNORE("Type1Family"); + DMI_IGNORE("123456789"); + + /* ASUS socket 775 MB */ + DMI_IGNORE("Asset-1234567890"); + DMI_IGNORE("MB-1234567890"); + DMI_IGNORE("Chassis Serial Number"); + DMI_IGNORE("Chassis Version"); + DMI_IGNORE("Chassis Manufacture"); + + /* Zotac version nonsense */ + p = chk; + while (*p != 0) { *p = 'x'; p++; } /* all X */ + DMI_IGNORE(chk); + p = chk; + while (*p != 0) { *p = '0'; p++; } /* all 0 */ + DMI_IGNORE(chk); + + /*... more, I'm sure. */ + + g_free(chk); + return 0; +} + +static const char *dmi_sysfs_root(void) { + char *candidates[] = { + "/sys/devices/virtual/dmi", + "/sys/class/dmi", + NULL + }; + int i = 0; + while (candidates[i] != NULL) { + if(access(candidates[i], F_OK) != -1) + return candidates[i]; + i++; + } + return NULL; +} + +int dmi_str_status(const char *id_str) { + gchar *str = dmi_get_str_abs(id_str); + int ret = 1; + + if (!str) + ret = 0; + + if ( ignore_placeholder_strings(&str) > 0 ) + ret = -1; + + g_free(str); + return ret; +} + +char *dmi_get_str(const char *id_str) { + gchar *ret = dmi_get_str_abs(id_str); + /* return NULL on empty */ + if (ret && *ret == 0) { + g_free(ret); + ret = NULL; + } + ignore_placeholder_strings(&ret); + return ret; +} + +char *dmi_get_str_abs(const char *id_str) { + static struct { + char *id; + char *path; + } tab_dmi_sysfs[] = { + /* dmidecode -> sysfs */ + { "bios-release-date", "id/bios_date" }, + { "bios-vendor", "id/bios_vendor" }, + { "bios-version", "id/bios_version" }, + { "baseboard-product-name", "id/board_name" }, + { "baseboard-manufacturer", "id/board_vendor" }, + { "baseboard-version", "id/board_version" }, + { "baseboard-serial-number", "id/board_serial" }, + { "baseboard-asset-tag", "id/board_asset_tag" }, + { "system-product-name", "id/product_name" }, + { "system-manufacturer", "id/sys_vendor" }, + { "system-serial-number", "id/product_serial" }, + { "system-product-family", "id/product_family" }, + { "system-version", "id/product_version" }, + { "system-uuid", "product_uuid" }, + { "system-sku", "id/product_sku" }, /*dmidecode doesn't actually support this one*/ + { "chassis-type", "id/chassis_type" }, + { "chassis-serial-number", "id/chassis_serial" }, + { "chassis-manufacturer", "id/chassis_vendor" }, + { "chassis-version", "id/chassis_version" }, + { "chassis-asset-tag", "id/chassis_asset_tag" }, + { NULL, NULL } + }; + const gchar *dmi_root = dmi_sysfs_root(); + gchar *ret = NULL; + gchar full_path[PATH_MAX]; + gboolean spawned; + gchar *out, *err; + + int i = 0; + + /* try sysfs first */ + if (dmi_root) { + while (tab_dmi_sysfs[i].id != NULL) { + if (strcmp(id_str, tab_dmi_sysfs[i].id) == 0) { + snprintf(full_path, PATH_MAX, "%s/%s", dmi_root, tab_dmi_sysfs[i].path); + if (g_file_get_contents(full_path, &ret, NULL, NULL) ) + goto dmi_str_done; + } + i++; + } + } + + /* try dmidecode, but may require root */ + snprintf(full_path, PATH_MAX, "dmidecode -s %s", id_str); + spawned = hardinfo_spawn_command_line_sync(full_path, + &out, &err, &i, NULL); + if (spawned) { + if (i == 0) + ret = out; + else + g_free(out); + + g_free(err); + } + +dmi_str_done: + if (ret != NULL) { + ret = strend(ret, '\n'); + ret = g_strstrip(ret); + } + return ret; +} + +char *dmi_chassis_type_str(int chassis_type, gboolean with_val) { + static const char *types[] = { + N_("Invalid chassis type (0)"), + N_("Unknown chassis type"), /* 1 is "Other", but not helpful in HardInfo */ + N_("Unknown chassis type"), + N_("Desktop"), + N_("Low-profile Desktop"), + N_("Pizza Box"), + N_("Mini Tower"), + N_("Tower"), + N_("Portable"), + N_("Laptop"), + N_("Notebook"), + N_("Handheld"), + N_("Docking Station"), + N_("All-in-one"), + N_("Subnotebook"), + N_("Space-saving"), + N_("Lunch Box"), + N_("Main Server Chassis"), + N_("Expansion Chassis"), + N_("Sub Chassis"), + N_("Bus Expansion Chassis"), + N_("Peripheral Chassis"), + N_("RAID Chassis"), + N_("Rack Mount Chassis"), + N_("Sealed-case PC"), + N_("Multi-system"), + N_("CompactPCI"), + N_("AdvancedTCA"), + N_("Blade"), + N_("Blade Enclosing"), + N_("Tablet"), + N_("Convertible"), + N_("Detachable"), + N_("IoT Gateway"), + N_("Embedded PC"), + N_("Mini PC"), + N_("Stick PC"), + }; + + if (chassis_type <= 0) { + gchar *chassis = dmi_get_str("chassis-type"); + if (chassis) { + chassis_type = atoi(chassis); + g_free(chassis); + } else + chassis_type = -1; + } + + if (chassis_type >= 0 && chassis_type < (int)G_N_ELEMENTS(types)) { + if (with_val) + return g_strdup_printf("[%d] %s", chassis_type, _(types[chassis_type])); + + return g_strdup(_(types[chassis_type])); + } + return NULL; +} + +/* TODO: something better maybe */ +static char *dd_cache[128] = {}; +void dmidecode_cache_free() +{ int i; for(i = 0; i < 128; i++) g_free(dd_cache[i]); } + +char *dmidecode_read(const dmi_type *type) { + gchar *ret = NULL; + gchar full_path[PATH_MAX]; + gboolean spawned; + gchar *out, *err; + + int i = 0; + + if (type) { + if (dd_cache[*type]) + return g_strdup(dd_cache[*type]); + snprintf(full_path, PATH_MAX, "dmidecode -t %"PRId32, *type); + } else { + if (dd_cache[127]) + return g_strdup(dd_cache[127]); + snprintf(full_path, PATH_MAX, "dmidecode"); + } + + spawned = hardinfo_spawn_command_line_sync(full_path, + &out, &err, &i, NULL); + if (spawned) { + if (i == 0) + ret = out; + else + g_free(out); + + g_free(err); + } + + if (ret) { + if (type) + dd_cache[*type] = g_strdup(ret); + else + dd_cache[127] = g_strdup(ret); + } + + return ret; +} + +dmi_handle_list *dmi_handle_list_add(dmi_handle_list *hl, dmi_handle_ext new_handle_ext) { + if (new_handle_ext.type < G_N_ELEMENTS(dmi_type_strings) ) + new_handle_ext.type_str = dmi_type_strings[new_handle_ext.type]; + if (!hl) { + hl = malloc(sizeof(dmi_handle_list)); + hl->count = 1; + hl->handles = malloc(sizeof(dmi_handle) * hl->count); + hl->handles_ext = malloc(sizeof(dmi_handle_ext) * hl->count); + } else { + hl->count++; + hl->handles = realloc(hl->handles, sizeof(dmi_handle) * hl->count); + hl->handles_ext = realloc(hl->handles_ext, sizeof(dmi_handle_ext) * hl->count); + } + hl->handles_ext[hl->count - 1] = new_handle_ext; + hl->handles[hl->count - 1] = new_handle_ext.id; + + return hl; +} + +dmi_handle_list *dmidecode_handles(const dmi_type *type) { + gchar *full = NULL, *p = NULL, *next_nl = NULL; + dmi_handle_list *hl = NULL; + + // Handle 0x003B, DMI type 9, 17 bytes + + full = dmidecode_read(type); + if (full) { + p = full; + while(next_nl = strchr(p, '\n')) { + unsigned int ch = 0, ct = 0, cb = 0; + strend(p, '\n'); + if (sscanf(p, "Handle 0x%X, DMI type %u, %u bytes", &ch, &ct, &cb) > 0) { + if (type && !ct) ct = *type; + hl = dmi_handle_list_add(hl, (dmi_handle_ext){.id = ch, .type = ct, .size = cb}); + } + p = next_nl + 1; + } + free(full); + } + return hl; +} + +void dmi_handle_list_free(dmi_handle_list *hl) { + if (hl) { + free(hl->handles); + free(hl->handles_ext); + } + free(hl); +} + +char *dmidecode_match(const char *name, const dmi_type *type, const dmi_handle *handle) { + gchar *ret = NULL, *full = NULL, *p = NULL, *next_nl = NULL; + unsigned int ch = 0; + int ln = 0; + + if (!name) return NULL; + ln = strlen(name); + + full = dmidecode_read(type); + if (full) { + p = full; + while(next_nl = strchr(p, '\n')) { + strend(p, '\n'); + if (!(sscanf(p, "Handle 0x%X", &ch) > 0) ) { + if (!handle || *handle == ch) { + while(*p == '\t') p++; + if (strncmp(p, name, ln) == 0) { + if (*(p + ln) == ':') { + p = p + ln + 1; + while(*p == ' ') p++; + ret = strdup(p); + break; + } + } + } + } + p = next_nl + 1; + } + free(full); + } + + return ret; +} + +dmi_handle_list *dmidecode_match_value(const char *name, const char *value, const dmi_type *type) { + dmi_handle_list *hl = NULL; + gchar *full = NULL, *p = NULL, *next_nl = NULL; + unsigned int ch = 0, ct = 0, cb = 0; + int ln = 0, lnv = 0; + + if (!name) return NULL; + ln = strlen(name); + lnv = (value) ? strlen(value) : 0; + + full = dmidecode_read(type); + if (full) { + p = full; + while(next_nl = strchr(p, '\n')) { + strend(p, '\n'); + if (!(sscanf(p, "Handle 0x%X, DMI type %u, %u bytes", &ch, &ct, &cb) > 0)) { + while(*p == '\t') p++; + if (strncmp(p, name, ln) == 0) { + if (*(p + ln) == ':') { + p = p + ln + 1; + while(*p == ' ') p++; + if (!value || strncmp(p, value, lnv) == 0) + hl = dmi_handle_list_add(hl, (dmi_handle_ext){.id = ch, .type = ct, .size = cb}); + } + } + } + p = next_nl + 1; + } + free(full); + } + + return hl; +} diff --git a/hardinfo2/dt_util.c b/hardinfo2/dt_util.c new file mode 100644 index 00000000..08b0e317 --- /dev/null +++ b/hardinfo2/dt_util.c @@ -0,0 +1,1187 @@ +/* + * HardInfo - Displays System Information + * Copyright (C) 2003-2007 L. A. F. Pereira <l@tia.mat.br> + * + * This program 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, version 2 or later. + * + * 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, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ +/* + * Device Tree support by Burt P. <pburt0@gmail.com> + * Sources: + * http://elinux.org/Device_Tree_Usage + * http://elinux.org/Device_Tree_Mysteries + */ +#include <unistd.h> +#include <sys/types.h> +#include <inttypes.h> /* for PRIu64 */ +#include <endian.h> +#include "hardinfo.h" +#include "dt_util.h" +#include "appf.h" + +static struct { + char *name; int type; +} prop_types[] = { + { "name", DTP_STR }, + { "compatible", DTP_STR }, + { "model", DTP_STR }, + { "reg", DTP_REG }, + { "clocks", DTP_CLOCKS }, + { "gpios", DTP_GPIOS }, + { "cs-gpios", DTP_GPIOS }, + { "phandle", DTP_PH }, + { "interrupts", DTP_INTRUPT }, + { "interrupts-extended", DTP_INTRUPT_EX }, + { "interrupt-parent", DTP_PH_REF }, + { "interrupt-controller", DTP_EMPTY }, + { "regulator-min-microvolt", DTP_UINT }, + { "regulator-max-microvolt", DTP_UINT }, + { "clock-frequency", DTP_UINT }, + { "dmas", DTP_DMAS }, + { "dma-channels", DTP_UINT }, + { "dma-requests", DTP_UINT }, + + /* operating-points-v2: */ + /* https://www.kernel.org/doc/Documentation/devicetree/bindings/opp/opp.txt */ + { "operating-points-v2", DTP_PH_REF_OPP2 }, + { "opp-hz", DTP_UINT64 }, + { "opp-microvolt", DTP_UINT }, + { "opp-microvolt-L0", DTP_UINT }, /* opp-microvolt-<named>, but this kind of */ + { "opp-microvolt-L1", DTP_UINT }, /* wildcard matching isn't supported yet */ + { "opp-microamp", DTP_UINT }, + { "clock-latency-ns", DTP_UINT }, + + { NULL, 0 }, +}; + +static struct { + char *name; uint32_t v; +} default_values[] = { + { "#address-cells", 2 }, + { "#size-cells", 1 }, + { "#dma-cells", 1 }, + { NULL, 0 }, +}; + +struct _dtr_map { + uint32_t v; /* phandle */ + char *label; /* alias */ + char *path; + struct _dtr_map *next; +}; +typedef struct _dtr_map dtr_map; + +struct _dtr { + dtr_map *aliases; + dtr_map *symbols; + dtr_map *phandles; + char *base_path; + char *log; +}; + +struct _dtr_obj { + char *path; + union { + void *data; + char *data_str; + dt_uint *data_int; + dt_uint64 *data_int64; + }; + char *name; + uint32_t length; + int type; + char *prefix; /* if the name has a manufacturer's prefix or null */ + char *np_name; /* the name without any prefix. points into .prefix or .name, do not free */ + const char *alias; /* null until first dtr_obj_alias(). do not free */ + const char *symbol; /* null until first dtr_obj_symbol(). do not free */ + dtr *dt; +}; + +dtr_map *dtr_map_add(dtr_map *map, uint32_t v, const char *label, const char *path) { + dtr_map *it; + dtr_map *nmap = malloc(sizeof(dtr_map)); + memset(nmap, 0, sizeof(dtr_map)); + nmap->v = v; + + if (label != NULL) nmap->label = strdup(label); + if (path != NULL) nmap->path = strdup(path); + if (map == NULL) + return nmap; + + it = map; + while(it->next != NULL) + it = it->next; + it->next = nmap; + + return nmap; +} + +void dtr_map_free(dtr_map *map) { + dtr_map *it; + while(map != NULL) { + it = map->next; + free(map->label); + free(map->path); + free(map); + map = it; + } +} + +/* simple sort for maps + * sv: 1 = sort by v, 0 = sort by label */ +void dtr_map_sort(dtr_map *map, int sv) +{ + int done = 0, cmp; + dtr_map *this, *next, *top = NULL, *next_top; + uint32_t tmp_v; + char *tmp_l, *tmp_p; + if (map == NULL) return; + do { + this = map; + next_top = NULL; + while(this != NULL) { + next = this->next; + if (this == top) + break; + if (next == NULL) + break; + if (sv) + cmp = (this->v > next->v) ? 1 : 0; + else + cmp = strcmp(this->label, next->label); + if (cmp > 0) { + tmp_v = this->v; this->v = next->v; next->v = tmp_v; + tmp_l = this->label; this->label = next->label; next->label = tmp_l; + tmp_p = this->path; this->path = next->path; next->path = tmp_p; + next_top = this; + } + this = next; + } + if (next_top == NULL) + done = 1; + else + top = next_top; + } while (!done); +} + +const char *dtr_phandle_lookup(dtr *s, uint32_t v) { + dtr_map *phi = s->phandles; + /* 0 and 0xffffffff are invalid phandle values */ + /* TODO: perhaps "INVALID" or something */ + if (v == 0 || v == 0xffffffff) + return NULL; + while(phi != NULL) { + if (phi->v == v) + return phi->path; + phi = phi->next; + } + return NULL; +} + +const char *dtr_alias_lookup(dtr *s, const char* label) { + dtr_map *ali = s->aliases; + while(ali != NULL) { + if (strcmp(ali->label, label) == 0) + return ali->path; + ali = ali->next; + } + return NULL; +} + +const char *dtr_alias_lookup_by_path(dtr *s, const char* path) { + dtr_map *ali = s->aliases; + while(ali != NULL) { + if (strcmp(ali->path, path) == 0) + return ali->label; + ali = ali->next; + } + return NULL; +} + +const char *dtr_symbol_lookup_by_path(dtr *s, const char* path) { + dtr_map *ali = s->symbols; + while(ali != NULL) { + if (strcmp(ali->path, path) == 0) + return ali->label; + ali = ali->next; + } + return NULL; +} + +void _dtr_read_aliases(dtr *); +void _dtr_read_symbols(dtr *); +void _dtr_map_phandles(dtr *, char *np); +int dtr_inh_find(dtr_obj *obj, char *qprop, int limit); +#define UMIN(a,b) MIN(((uint32_t)(a)), ((uint32_t)(b))) + +const char *dtr_find_device_tree_root() { + char *candidates[] = { + "/proc/device-tree", + "/sys/firmware/devicetree/base", + /* others? */ + NULL + }; + int i = 0; + while (candidates[i] != NULL) { + if(access(candidates[i], F_OK) != -1) + return candidates[i]; + i++; + } + return NULL; +} + +dtr *dtr_new_x(const char *base_path, int fast) { + dtr *dt = malloc(sizeof(dtr)); + if (dt != NULL) { + memset(dt, 0, sizeof(dtr)); + dt->log = strdup(""); + + if (base_path == NULL) + base_path = DTR_ROOT; + + if (base_path != NULL) + dt->base_path = strdup(base_path); + else { + dtr_msg(dt, "%s", "Device Tree not found."); + return dt; + } + + /* build alias and phandle lists */ + dt->aliases = NULL; + dt->symbols = NULL; + dt->phandles = NULL; + if (!fast) { + _dtr_read_aliases(dt); + _dtr_read_symbols(dt); + _dtr_map_phandles(dt, ""); + } + } + return dt; +} + +dtr *dtr_new(const char *base_path) { + return dtr_new_x(base_path, 0); +} + +void dtr_free(dtr *s) { + if (s != NULL) { + dtr_map_free(s->aliases); + dtr_map_free(s->symbols); + dtr_map_free(s->phandles); + free(s->base_path); + free(s->log); + free(s); + } +} + +int dtr_was_found(dtr *s) { + if (s != NULL) { + if (s->base_path != NULL) + return 1; + } + return 0; +} + +void dtr_msg(dtr *s, char *fmt, ...) { + gchar *buf, *tmp; + va_list args; + + va_start(args, fmt); + buf = g_strdup_vprintf(fmt, args); + va_end(args); + + tmp = g_strdup_printf("%s%s", s->log, buf); + g_free(s->log); + s->log = tmp; +} + +char *dtr_messages(dtr *s) { + if (s != NULL) + return strdup(s->log); + else + return NULL; +} + +const char *dtr_base_path(dtr *s) { + if (s) + return s->base_path; + return NULL; +} + +/*glib, but _dt_obj *prop uses malloc() and std types */ +dtr_obj *dtr_obj_read(dtr *s, const char *dtp) { + char *full_path; + char *slash, *coma; + dtr_obj *obj; + + if (dtp == NULL) + return NULL; + + obj = malloc(sizeof(dtr_obj)); + if (obj != NULL) { + memset(obj, 0, sizeof(dtr_obj)); + + obj->dt = s; + if (*dtp != '/') { + /* doesn't start with slash, use alias */ + obj->path = (char*)dtr_alias_lookup(s, dtp); + if (obj->path != NULL) + obj->path = strdup(obj->path); + else { + dtr_obj_free(obj); + return NULL; + } + } else + obj->path = strdup(dtp); + + /* find name after last slash, or start */ + slash = strrchr(obj->path, '/'); + if (slash != NULL) + obj->name = strdup(slash + 1); + else + obj->name = strdup(obj->path); + + /* find manufacturer prefix */ + obj->prefix = strdup(obj->name); + coma = strchr(obj->prefix, ','); + if (coma != NULL) { + /* coma -> null; .np_name starts after */ + *coma = 0; + obj->np_name = coma + 1; + } else { + obj->np_name = obj->name; + free(obj->prefix); + obj->prefix = NULL; + } + + /* read data */ + full_path = g_strdup_printf("%s%s", s->base_path, obj->path); + if ( g_file_test(full_path, G_FILE_TEST_IS_DIR) ) { + obj->type = DT_NODE; + } else { + if (!g_file_get_contents(full_path, (gchar**)&obj->data, (gsize*)&obj->length, NULL)) { + dtr_obj_free(obj); + g_free(full_path); + return NULL; + } + obj->type = dtr_guess_type(obj); + } + g_free(full_path); + + return obj; + } + return NULL; +} + +void dtr_obj_free(dtr_obj *s) { + if (s != NULL) { + free(s->path); + free(s->name); + free(s->prefix); + free(s->data); + free(s); + } +} + +int dtr_obj_type(dtr_obj *s) { + if (s) + return s->type; + return DT_TYPE_ERR; +} + +char *dtr_obj_alias(dtr_obj *s) { + if (s) { + if (s->alias == NULL) + s->alias = dtr_alias_lookup_by_path(s->dt, s->path); + return (char*)s->alias; + } + return NULL; +} + +char *dtr_obj_symbol(dtr_obj *s) { + if (s) { + if (s->symbol == NULL) + s->symbol = dtr_symbol_lookup_by_path(s->dt, s->path); + return (char*)s->symbol; + } + return NULL; +} + +char *dtr_obj_path(dtr_obj *s) { + if (s) + return s->path; + return NULL; +} + +char *dtr_obj_full_path(dtr_obj *s) { + if (s) { + if (strcmp(s->path, "/") == 0) + return g_strdup_printf("%s", s->dt->base_path); + else + return g_strdup_printf("%s%s", s->dt->base_path, s->path); + } + return NULL; +} + +dtr_obj *dtr_get_prop_obj(dtr *s, dtr_obj *node, const char *name) { + dtr_obj *prop; + char *ptmp; + ptmp = g_strdup_printf("%s/%s", (node == NULL) ? "" : node->path, name); + prop = dtr_obj_read(s, ptmp); + g_free(ptmp); + return prop; +} + +char *dtr_get_prop_str(dtr *s, dtr_obj *node, const char *name) { + dtr_obj *prop; + char *ptmp; + char *ret = NULL; + + ptmp = g_strdup_printf("%s/%s", (node == NULL) ? "" : node->path, name); + prop = dtr_obj_read(s, ptmp); + if (prop != NULL && prop->data != NULL) { + ret = strdup(prop->data_str); + dtr_obj_free(prop); + } + g_free(ptmp); + return ret; +} + +char *dtr_get_string(const char *p, int decode) { + dtr *dt = dtr_new_x(NULL, 1); + dtr_obj *obj; + char *ret = NULL; + if (decode) { + obj = dtr_get_prop_obj(dt, NULL, p); + ret = dtr_str(obj); + dtr_obj_free(obj); + } else + ret = dtr_get_prop_str(dt, NULL, p); + dtr_free(dt); + return ret; +} + +uint32_t dtr_get_prop_u32(dtr *s, dtr_obj *node, const char *name) { + dtr_obj *prop; + char *ptmp; + uint32_t ret = 0; + + ptmp = g_strdup_printf("%s/%s", (node == NULL) ? "" : node->path, name); + prop = dtr_obj_read(s, ptmp); + if (prop != NULL) { + if (prop->data != NULL) + ret = be32toh(*prop->data_int); + dtr_obj_free(prop); + } + g_free(ptmp); + return ret; +} + +uint64_t dtr_get_prop_u64(dtr *s, dtr_obj *node, const char *name) { + dtr_obj *prop; + char *ptmp; + uint64_t ret = 0; + + ptmp = g_strdup_printf("%s/%s", (node == NULL) ? "" : node->path, name); + prop = dtr_obj_read(s, ptmp); + if (prop != NULL) { + if (prop->data != NULL) + ret = be64toh(*prop->data_int64); + dtr_obj_free(prop); + } + g_free(ptmp); + return ret; +} + +int dtr_guess_type(dtr_obj *obj) { + char *tmp, *dash; + uint32_t i = 0, anc = 0, might_be_str = 1; + + if (obj->length == 0) + return DTP_EMPTY; + + /* special #(.*)-cells names are UINT */ + if (*obj->name == '#') { + dash = strrchr(obj->name, '-'); + if (dash != NULL) { + if (strcmp(dash, "-cells") == 0) + return DTP_UINT; + } + } + + /* /aliases/ * and /__symbols__/ * are always strings */ + if (strncmp(obj->path, "/aliases/", strlen("/aliases/") ) == 0) + return DTP_STR; + if (strncmp(obj->path, "/__symbols__/", strlen("/__symbols__/") ) == 0) + return DTP_STR; + + /* /__overrides__/ * */ + if (strncmp(obj->path, "/__overrides__/", strlen("/__overrides__/") ) == 0) + if (strcmp(obj->name, "name") != 0) + return DTP_OVR; + + /* lookup known type */ + while (prop_types[i].name != NULL) { + if (strcmp(obj->name, prop_types[i].name) == 0) + return prop_types[i].type; + i++; + } + + /* maybe a string? */ + for (i = 0; i < obj->length; i++) { + tmp = obj->data_str + i; + if ( isalnum(*tmp) ) anc++; /* count the alpha-nums */ + if ( isprint(*tmp) || *tmp == 0 ) + continue; + might_be_str = 0; + break; + } + if (might_be_str && + ( anc >= obj->length - 2 /* all alpha-nums but ^/ and \0$ */ + || anc >= 5 ) /*arbitrary*/) + return DTP_STR; + + /* multiple of 4 bytes, try list of hex values */ + if (!(obj->length % 4)) + return DTP_HEX; + + return DTP_UNK; +} + +char *dtr_elem_phref(dtr *s, dt_uint e, int show_path, const char *extra) { + const char *ph_path, *al_label; + char *ret = NULL; + ph_path = dtr_phandle_lookup(s, be32toh(e)); + if (ph_path != NULL) { + /* TODO: alias or symbol? */ + al_label = dtr_symbol_lookup_by_path(s, ph_path); + if (al_label != NULL) { + if (show_path) + ret = g_strdup_printf("&%s (%s) %s", al_label, ph_path, extra ? extra : ""); + else + ret = g_strdup_printf("&%s %s", al_label, extra ? extra : ""); + } else { + if (show_path) + ret = g_strdup_printf("0x%x (%s) %s", be32toh(e), ph_path, extra ? extra : ""); + } + } + if (ret == NULL) + ret = dtr_elem_hex(e); + return ret; +} + +char *dtr_elem_oppv2(dtr_obj* obj) { + char opp_str[512] = ""; + dtr_obj *parent = dtr_get_parent_obj(obj); + if (parent) { + dt_opp_range *opp = dtr_get_opp_range(obj->dt, parent->path); + if (opp) { + snprintf(opp_str, 511, "[%d - %d %s]", opp->khz_min, opp->khz_max, _("kHz")); + g_free(opp); + } + dtr_obj_free(parent); + } + return dtr_elem_phref(obj->dt, *obj->data_int, 1, opp_str); +} + +char *dtr_elem_hex(dt_uint e) { + return g_strdup_printf("0x%x", be32toh(e) ); +} + +char *dtr_elem_byte(uint8_t e) { + return g_strdup_printf("%x", e); +} + +char *dtr_elem_uint(dt_uint e) { + return g_strdup_printf("%u", be32toh(e) ); +} + +char *dtr_elem_uint64(dt_uint64 e) { + return g_strdup_printf("%" PRIu64, be64toh(e) ); +} + +char *dtr_list_byte(uint8_t *bytes, unsigned long count) { + char *ret, *dest; + char buff[4] = ""; /* max element: " 00\0" */ + uint32_t v; + unsigned long i, l; + l = count * 4 + 1; + ret = malloc(l); + memset(ret, 0, l); + strcpy(ret, "["); + dest = ret + 1; + for (i = 0; i < count; i++) { + v = bytes[i]; + l = sprintf(buff, "%s%02x", (i) ? " " : "", v); + strncpy(dest, buff, l); + dest += l; + } + strcpy(dest, "]"); + return ret; +} + +char *dtr_list_hex(dt_uint *list, unsigned long count) { + char *ret, *dest; + char buff[12] = ""; /* max element: " 0x00000000\0" */ + unsigned long i, l; + l = count * 12 + 1; + dest = ret = malloc(l); + memset(ret, 0, l); + for (i = 0; i < count; i++) { + l = sprintf(buff, "%s0x%x", (i) ? " " : "", be32toh(list[i])); + strncpy(dest, buff, l); + dest += l; + } + return ret; +} + +/*cstd, except for g_strescape()*/ +char *dtr_list_str0(const char *data, uint32_t length) { + char *tmp, *esc, *next_str; + char ret[1024] = ""; + uint32_t l, tl; + + /* treat as null-separated string list */ + tl = 0; + strcpy(ret, ""); + tmp = ret; + next_str = (char*)data; + while(next_str != NULL) { + l = strlen(next_str); + esc = g_strescape(next_str, NULL); + sprintf(tmp, "%s\"%s\"", + strlen(ret) ? ", " : "", esc); + free(esc); + tmp += strlen(tmp); + tl += l + 1; next_str += l + 1; + if (tl >= length) break; + } + + return strdup(ret); +} + +char *dtr_list_override(dtr_obj *obj) { + /* <phref, string "property:value"> ... */ + char *ret = NULL; + char *ph, *str; + char *src; + uint32_t consumed = 0; + int l; + src = obj->data_str; + while (consumed + 5 <= obj->length) { + ph = dtr_elem_phref(obj->dt, *(dt_uint*)src, 1, NULL); + src += 4; consumed += 4; + l = strlen(src) + 1; /* consume the null */ + str = dtr_list_str0(src, l); + ret = appfsp(ret, "<%s -> %s>", ph, str); + src += l; consumed += l; + free(ph); + free(str); + } + if (consumed < obj->length) { + str = dtr_list_byte((uint8_t*)src, obj->length - consumed); + ret = appfsp(ret, "%s", str); + free(str); + } + return ret; +} + +uint32_t dtr_get_phref_prop(dtr *s, uint32_t phandle, char *prop) { + const char *ph_path; + char *tmp; + uint32_t ret; + ph_path = dtr_phandle_lookup(s, phandle); + tmp = g_strdup_printf("%s/%s", ph_path, prop); + ret = dtr_get_prop_u32(s, NULL, tmp); + free(tmp); + return ret; +} + +char *dtr_list_phref(dtr_obj *obj, char *ext_cell_prop) { + /* <phref, #XXX-cells> */ + int count = obj->length / 4; + int i = 0, ext_cells = 0; + char *ph, *ext, *ret = NULL; + + while (i < count) { + if (ext_cell_prop == NULL) + ext_cells = 0; + else + ext_cells = dtr_get_phref_prop(obj->dt, be32toh(obj->data_int[i]), ext_cell_prop); + ph = dtr_elem_phref(obj->dt, obj->data_int[i], 0, NULL); i++; + if (ext_cells > count - i) ext_cells = count - i; + ext = dtr_list_hex((obj->data_int + i), ext_cells); i+=ext_cells; + ret = appfsp(ret, "<%s%s%s>", + ph, (ext_cells) ? " " : "", ext); + g_free(ph); + g_free(ext); + } + return ret; +} + +char *dtr_list_interrupts(dtr_obj *obj) { + char *ext, *ret = NULL; + uint32_t iparent, icells; + int count, i = 0; + + iparent = dtr_inh_find(obj, "interrupt-parent", 0); + if (!iparent) { + dtr_msg(obj->dt, "Did not find an interrupt-parent for %s", obj->path); + goto intr_err; + } + icells = dtr_get_phref_prop(obj->dt, iparent, "#interrupt-cells"); + if (!icells) { + dtr_msg(obj->dt, "Invalid #interrupt-cells value %d for %s", icells, obj->path); + goto intr_err; + } + + count = obj->length / 4; + while (i < count) { + icells = UMIN(icells, count - i); + ext = dtr_list_hex((obj->data_int + i), icells); i+=icells; + ret = appfsp(ret, "<%s>", ext); + } + return ret; + +intr_err: + return dtr_list_hex(obj->data_int, obj->length); + +} + +char *dtr_list_reg(dtr_obj *obj) { + char *tup_str, *ret = NULL; + uint32_t acells, scells, tup_len; + uint32_t extra, consumed; /* bytes */ + uint32_t *next; + + acells = dtr_inh_find(obj, "#address-cells", 2); + scells = dtr_inh_find(obj, "#size-cells", 2); + tup_len = acells + scells; + extra = obj->length % (tup_len * 4); + consumed = 0; /* bytes */ + + //printf("list reg: %s\n ... acells: %u, scells: %u, extra: %u\n", obj->path, acells, scells, extra); + + if (extra) { + /* error: length is not a multiple of tuples */ + dtr_msg(obj->dt, "Data length (%u) is not a multiple of (#address-cells:%u + #size-cells:%u) for %s\n", + obj->length, acells, scells, obj->path); + return dtr_list_hex(obj->data, obj->length / 4); + } + + next = obj->data_int; + consumed = 0; + while (consumed + (tup_len * 4) <= obj->length) { + tup_str = dtr_list_hex(next, tup_len); + ret = appfsp(ret, "<%s>", tup_str); + free(tup_str); + consumed += (tup_len * 4); + next += tup_len; + } + + //printf(" ... %s\n", ret); + return ret; +} + +char* dtr_str(dtr_obj *obj) { + char *ret; + int type; + + if (obj == NULL) return NULL; + type = obj->type; + + if (type == DTP_PH_REF) { + if (!DTEX_PHREFS || obj->length != 4) + type = DTP_HEX; + } + + switch(type) { + case DT_NODE: + ret = strdup("{node}"); + break; + case DTP_EMPTY: + ret = strdup("{empty}"); + break; + case DTP_STR: + ret = dtr_list_str0(obj->data_str, obj->length); + break; + case DTP_OVR: + ret = dtr_list_override(obj); + break; + case DTP_REG: + /* <#address-cells #size-cells> */ + ret = dtr_list_reg(obj); + break; + case DTP_INTRUPT: + ret = dtr_list_interrupts(obj); + break; + case DTP_INTRUPT_EX: + /* <phref, #interrupt-cells"> */ + ret = dtr_list_phref(obj, "#interrupt-cells"); + break; + case DTP_CLOCKS: + /* <phref, #clock-cells"> */ + ret = dtr_list_phref(obj, "#clock-cells"); + break; + case DTP_GPIOS: + /* <phref, #gpio-cells"> */ + ret = dtr_list_phref(obj, "#gpio-cells"); + break; + case DTP_DMAS: + /* <phref, #dma-cells"> */ + ret = dtr_list_phref(obj, "#dma-cells"); + break; + case DTP_PH: + case DTP_HEX: + if (obj->length % 4) + ret = dtr_list_byte((uint8_t*)obj->data, obj->length); + else + ret = dtr_list_hex(obj->data, obj->length / 4); + break; + case DTP_PH_REF: + ret = dtr_elem_phref(obj->dt, *obj->data_int, 1, NULL); + break; + case DTP_PH_REF_OPP2: + ret = dtr_elem_oppv2(obj); + break; + case DTP_UINT: + ret = dtr_elem_uint(*obj->data_int); + break; + case DTP_UINT64: + ret = dtr_elem_uint64(*obj->data_int64); + break; + case DTP_UNK: + default: + if (obj->length > 64) /* maybe should use #define at the top */ + ret = g_strdup_printf("{data} (%lu bytes)", (long unsigned int)obj->length); + else + ret = dtr_list_byte((uint8_t*)obj->data, obj->length); + break; + } + + return ret; +} + +dtr_obj *dtr_get_parent_obj(dtr_obj *obj) { + char *slash, *parent; + dtr_obj *ret = NULL; + + if (obj == NULL) + return NULL; + + parent = strdup(obj->path); + slash = strrchr(parent, '/'); + if (slash != NULL) { + *slash = 0; + if (strlen(parent) > 0) + ret = dtr_obj_read(obj->dt, parent); + else + ret = dtr_obj_read(obj->dt, "/"); + } + free(parent); + return ret; +} + +/* find the value of a path-inherited property by climbing the path */ +int dtr_inh_find(dtr_obj *obj, char *qprop, int limit) { + dtr_obj *tobj, *pobj = NULL, *qobj; + uint32_t ret = 0; + int i, found = 0; + + if (!limit) limit = 1000; + + tobj = obj; + while (tobj != NULL) { + pobj = dtr_get_parent_obj(tobj); + if (tobj != obj) + dtr_obj_free(tobj); + if (!limit || pobj == NULL) break; + qobj = dtr_get_prop_obj(obj->dt, pobj, qprop); + if (qobj != NULL) { + ret = be32toh(*qobj->data_int); + found = 1; + dtr_obj_free(qobj); + break; + } + tobj = pobj; + limit--; + } + dtr_obj_free(pobj); + + if (!found) { + i = 0; + while(default_values[i].name != NULL) { + if (strcmp(default_values[i].name, qprop) == 0) { + ret = default_values[i].v; + dtr_msg(obj->dt, "Using default value %d for %s in %s\n", ret, qprop, obj->path); + break; + } + i++; + } + } + + return ret; +} + +dt_opp_range *dtr_get_opp_range(dtr *s, const char *name) { + dt_opp_range *ret = NULL; + dtr_obj *obj = NULL, *table_obj = NULL, *row_obj = NULL; + uint32_t opp_ph = 0; + const char *opp_table_path = NULL; + char *tab_compat = NULL, *tab_status = NULL; + const gchar *fn; + gchar *full_path; + GDir *dir; + uint64_t khz = 0; + uint32_t lns = 0; + char *row_status = NULL; + uint32_t i = 0; + + if (!s) + return NULL; + + obj = dtr_obj_read(s, name); + if (!obj) + goto get_opp_finish; + + opp_ph = dtr_get_prop_u32(s, obj, "operating-points-v2"); /* OPPv2 */ + table_obj = dtr_get_prop_obj(s, obj, "operating-points"); /* OPPv1 */ + if (!opp_ph) { + if (table_obj) { + /* only v1 */ + ret = g_new0(dt_opp_range, 1); + ret->version = 1; + ret->clock_latency_ns = dtr_get_prop_u32(s, obj, "clock-latency"); + + /* pairs of (kHz,uV) */ + for (i = 0; i < table_obj->length; i += 2) { + khz = table_obj->data_int[i]; + if (khz > ret->khz_max) + ret->khz_max = khz; + if (khz < ret->khz_min || ret->khz_min == 0) + ret->khz_min = khz; + } + + } else { + /* is clock-frequency available? */ + khz = dtr_get_prop_u32(s, obj, "clock-frequency"); + if (khz) { + ret = g_new0(dt_opp_range, 1); + ret->version = 0; + ret->khz_max = khz; + ret->clock_latency_ns = dtr_get_prop_u32(s, obj, "clock-latency"); + } + } + goto get_opp_finish; + } else { + /* use v2 if both available */ + dtr_obj_free(table_obj); + table_obj = NULL; + } + + opp_table_path = dtr_phandle_lookup(s, opp_ph); + if (!opp_table_path) + goto get_opp_finish; + + table_obj = dtr_obj_read(s, opp_table_path); + if (!table_obj) + goto get_opp_finish; + + tab_compat = dtr_get_prop_str(s, table_obj, "compatible"); + tab_status = dtr_get_prop_str(s, table_obj, "status"); + + if (!tab_compat || strcmp(tab_compat, "operating-points-v2") != 0) + goto get_opp_finish; + if (tab_status && strcmp(tab_status, "disabled") == 0) + goto get_opp_finish; + + ret = g_new0(dt_opp_range, 1); + ret->version = 2; + ret->phandle = opp_ph; + + full_path = dtr_obj_full_path(table_obj); + dir = g_dir_open(full_path, 0 , NULL); + if (dir) { + while((fn = g_dir_read_name(dir)) != NULL) { + row_obj = dtr_get_prop_obj(s, table_obj, fn); + if (row_obj->type == DT_NODE) { + row_status = dtr_get_prop_str(s, row_obj, "status"); + if (!row_status || strcmp(row_status, "disabled") != 0) { + khz = dtr_get_prop_u64(s, row_obj, "opp-hz"); + khz /= 1000; /* 64b hz -> 32b khz */ + lns = dtr_get_prop_u32(s, row_obj, "clock-latency-ns"); + if (khz > ret->khz_max) + ret->khz_max = khz; + if (khz < ret->khz_min || ret->khz_min == 0) + ret->khz_min = khz; + ret->clock_latency_ns = lns; + } + } + free(row_status); row_status = NULL; + dtr_obj_free(row_obj); + row_obj = NULL; + } + g_dir_close(dir); + } + g_free(full_path); + +get_opp_finish: + dtr_obj_free(obj); + dtr_obj_free(table_obj); + free(tab_status); + free(tab_compat); + free(row_status); + return ret; +} + + +void _dtr_read_aliases(dtr *s) { + gchar *dir_path; + GDir *dir; + const gchar *fn; + dtr_obj *anode, *prop; + dtr_map *al; + anode = dtr_obj_read(s, "/aliases"); + + dir_path = g_strdup_printf("%s/aliases", s->base_path); + dir = g_dir_open(dir_path, 0 , NULL); + if (dir) { + while((fn = g_dir_read_name(dir)) != NULL) { + prop = dtr_get_prop_obj(s, anode, fn); + if (prop->type == DTP_STR) { + if (*prop->data_str == '/') { + al = dtr_map_add(s->aliases, 0, prop->name, prop->data_str); + if (s->aliases == NULL) + s->aliases = al; + } + } + dtr_obj_free(prop); + } + g_dir_close(dir); + } + g_free(dir_path); + dtr_obj_free(anode); + dtr_map_sort(s->aliases, 0); +} + +void _dtr_read_symbols(dtr *s) { + gchar *dir_path; + GDir *dir; + const gchar *fn; + dtr_obj *anode, *prop; + dtr_map *al; + anode = dtr_obj_read(s, "/__symbols__"); + + dir_path = g_strdup_printf("%s/__symbols__", s->base_path); + dir = g_dir_open(dir_path, 0 , NULL); + if (dir) { + while((fn = g_dir_read_name(dir)) != NULL) { + prop = dtr_get_prop_obj(s, anode, fn); + if (prop->type == DTP_STR) { + if (*prop->data_str == '/') { + al = dtr_map_add(s->symbols, 0, prop->name, prop->data_str); + if (s->symbols == NULL) + s->symbols = al; + } + } + dtr_obj_free(prop); + } + g_dir_close(dir); + } + g_free(dir_path); + dtr_obj_free(anode); + dtr_map_sort(s->symbols, 0); +} + +/* TODO: rewrite */ +void _dtr_map_phandles(dtr *s, char *np) { + gchar *dir_path; + gchar *ftmp, *ntmp, *ptmp; + const gchar *fn; + GDir *dir; + dtr_obj *prop, *ph_prop; + dtr_map *ph; + + if (np == NULL) np = ""; + dir_path = g_strdup_printf("%s/%s", s->base_path, np); + + prop = dtr_obj_read(s, np); + dir = g_dir_open(dir_path, 0 , NULL); + if (dir) { + while((fn = g_dir_read_name(dir)) != NULL) { + ftmp = g_strdup_printf("%s/%s", dir_path, fn); + if ( g_file_test(ftmp, G_FILE_TEST_IS_DIR) ) { + ntmp = g_strdup_printf("%s/%s", np, fn); + ptmp = g_strdup_printf("%s/phandle", ntmp); + ph_prop = dtr_obj_read(s, ptmp); + if (ph_prop != NULL) { + ph = dtr_map_add(s->phandles, be32toh(*ph_prop->data_int), NULL, ntmp); + if (s->phandles == NULL) + s->phandles = ph; + } + _dtr_map_phandles(s, ntmp); + g_free(ptmp); + g_free(ntmp); + dtr_obj_free(ph_prop); + } + g_free(ftmp); + } + g_dir_close(dir); + } + dtr_obj_free(prop); + dtr_map_sort(s->phandles, 1); +} + +/* + * Maybe these should move to devicetree.c, but would have to expose + * struct internals. + */ + +/* kvl: 0 = key is label, 1 = key is v */ +char *dtr_map_info_section(dtr *s, dtr_map *map, char *title, int kvl) { + gchar *tmp, *ret; + const gchar *sym; + ret = g_strdup_printf("[%s]\n", _(title)); + dtr_map *it = map; + while(it != NULL) { + if (kvl) { + sym = dtr_symbol_lookup_by_path(s, it->path); + if (sym != NULL) + tmp = g_strdup_printf("%s0x%x (%s)=%s\n", ret, + it->v, sym, it->path); + else + tmp = g_strdup_printf("%s0x%x=%s\n", ret, + it->v, it->path); + } else + tmp = g_strdup_printf("%s%s=%s\n", ret, + it->label, it->path); + g_free(ret); + ret = tmp; + it = it->next; + } + + return ret; +} + +char *dtr_maps_info(dtr *s) { + gchar *ph_map, *al_map, *sy_map, *ret; + + ph_map = dtr_map_info_section(s, s->phandles, _("phandle Map"), 1); + al_map = dtr_map_info_section(s, s->aliases, _("Alias Map"), 0); + sy_map = dtr_map_info_section(s, s->symbols, _("Symbol Map"), 0); + ret = g_strdup_printf("%s%s%s", ph_map, sy_map, al_map); + g_free(ph_map); + g_free(al_map); + g_free(sy_map); + return ret; +} diff --git a/hardinfo2/expr.c b/hardinfo2/expr.c new file mode 100644 index 00000000..26284aee --- /dev/null +++ b/hardinfo2/expr.c @@ -0,0 +1,250 @@ +/* + * HardInfo - Displays System Information + * Copyright (C) 2003-2007 L. A. F. Pereira <l@tia.mat.br> + * + * This program 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, version 2 or later. + * + * 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, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ +/* + * This is only used to compute sensor values, hence the only variable supported is '@'. + * The '`' operator (ln(x)) is not available, nor multi-line formulas. + */ + +#include <glib.h> +#include <string.h> +#include <stdio.h> +#include <ctype.h> +#include <math.h> + +#include "expr.h" +#include "config.h" + +static MathToken *new_operator(gchar op) +{ + MathToken *t = g_new0(MathToken, 1); + + t->val.op = op; + t->type = TOKEN_OPERATOR; /* operator */ + + return t; +} + +static MathToken *new_variable(gchar var) +{ + MathToken *t = g_new0(MathToken, 1); + + t->val.op = '@'; + t->type = TOKEN_VARIABLE; /* variable */ + + return t; +} + +static MathToken *new_value(gfloat value) +{ + MathToken *t = g_new0(MathToken, 1); + + t->val.value = value; + t->type = TOKEN_VALUE; /* value */ + + return t; +} + +static inline gint priority(char operation) +{ + switch (operation) { + case '^': + return 3; + case '*': + case '/': + return 2; + case '+': + case '-': + return 1; + case '(': + return 0; + } + + return 0; +} + +GSList *math_infix_to_postfix(GSList * infix) +{ + MathToken *stack[500]; + gint t_sp = 0; + + GSList *postfix = NULL, *p; + MathToken *top; + + for (p = infix; p; p = p->next) { + MathToken *t = (MathToken *) p->data; + + if (t->type == TOKEN_OPERATOR && t->val.op == '(') { + stack[++t_sp] = t; + } else if (t->type == TOKEN_OPERATOR && t->val.op == ')') { + for (top = stack[t_sp]; t_sp != 0 && top->val.op != '('; + top = stack[t_sp]) { + postfix = g_slist_append(postfix, stack[t_sp--]); + } + t_sp--; + } else if (t->type != TOKEN_OPERATOR) { + postfix = g_slist_append(postfix, t); + } else if (t_sp == 0) { + stack[++t_sp] = t; + } else { + while (t_sp != 0 + && priority(t->val.op) <= priority(stack[t_sp]->val.op)) + postfix = g_slist_append(postfix, stack[t_sp--]); + stack[++t_sp] = t; + } + } + + while (t_sp) + postfix = g_slist_append(postfix, stack[t_sp--]); + + return postfix; +} + +static inline gfloat __result(gfloat op1, gfloat op2, gchar operation) +{ + switch (operation) { + case '^': + return powf(op1, op2); + case '+': + return op1 + op2; + case '-': + return op1 - op2; + case '/': + return op1 / op2; + case '*': + return op1 * op2; + } + + return 0; +} + +gfloat math_postfix_eval(GSList * postfix, gfloat at_value) +{ + GSList *p; + gfloat stack[500]; + gint sp = 0; + + memset(stack, 0, sizeof(gfloat) * 500); + + for (p = postfix; p; p = p->next) { + MathToken *t = (MathToken *) p->data; + + if (t->type == TOKEN_VARIABLE) { + stack[++sp] = at_value; + } else if (t->type == TOKEN_VALUE) { + stack[++sp] = t->val.value; + } else { + gfloat op1, op2; + + op2 = stack[sp--]; + op1 = stack[sp]; + + stack[sp] = __result(op1, op2, t->val.op); + } + } + + return stack[sp]; +} + +GSList *math_string_to_infix(gchar * string) +{ + GSList *infix = NULL; + gchar *expr = string; + + for (; *expr; expr++) { + if (strchr("+-/*^()", *expr)) { + infix = g_slist_append(infix, new_operator(*expr)); + } else if (strchr("@", *expr)) { + infix = g_slist_append(infix, new_variable(*expr)); + } else if (strchr("-.1234567890", *expr)) { + gchar value[32], *v = value; + gfloat floatval; + + do { + *v++ = *expr++; + } while (*expr && strchr("-.1234567890", *expr)); + expr--; + *v = '\0'; + + sscanf(value, "%f", &floatval); + + infix = g_slist_append(infix, new_value(floatval)); + } else if (!isspace(*expr)) { + g_print("Invalid token: [%c][%d]\n", *expr, *expr); + math_infix_free(infix, TRUE); + return NULL; + } + } + + return infix; +} + +void math_infix_free(GSList * infix, gboolean free_tokens) +{ + GSList *p; + + if (!free_tokens) + for (p = infix; p; p = g_slist_delete_link(p, p)); + else + for (p = infix; p; p = g_slist_delete_link(p, p)) { + MathToken *t = (MathToken *) p->data; + g_free(t); + } +} + +GSList *math_string_to_postfix(gchar * string) +{ + GSList *infix; + GSList *postfix; + + infix = math_string_to_infix(string); + if (!infix) + return NULL; + + postfix = math_infix_to_postfix(infix); + math_infix_free(infix, FALSE); + + return postfix; +} + +gfloat math_string_eval(gchar * string, gfloat at_value) +{ + GSList *postfix; + gfloat val; + + postfix = math_string_to_postfix(string); + val = math_postfix_eval(postfix, at_value); + math_postfix_free(postfix, TRUE); + + return val; +} + +#ifdef MATH_TEST +int main(void) +{ + GSList *postfix; + + gchar *expr = "0.9*(@+(5.2*0.923+3*(2.0)))"; + + postfix = math_string_to_postfix(expr); + g_print("%s = %f (must be 18.71964)\n", expr, + math_postfix_eval(postfix, 10)); + math_postfix_free(postfix, TRUE); + + return 0; +} +#endif diff --git a/hardinfo2/gg_key_file_parse_string_as_value.c b/hardinfo2/gg_key_file_parse_string_as_value.c new file mode 100644 index 00000000..6eba2af3 --- /dev/null +++ b/hardinfo2/gg_key_file_parse_string_as_value.c @@ -0,0 +1,112 @@ +/* From: gkeyfile.c - key file parser + * + * Copyright 2004 Red Hat, Inc. + * Copyright 2009-2010 Collabora Ltd. + * Copyright 2009 Nokia Corporation + * + * Written by Ray Strode <rstrode@redhat.com> + * Matthias Clasen <mclasen@redhat.com> + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this library; if not, see <http://www.gnu.org/licenses/>. + */ + +#include <glib.h> +#include <string.h> + +gchar * +gg_key_file_parse_string_as_value (const gchar *string, const gchar list_separator) +{ + gchar *value, *p, *q; + gsize length; + gboolean parsing_leading_space; + + length = strlen (string) + 1; + + /* Worst case would be that every character needs to be escaped. + * In other words every character turns to two characters. */ + value = g_new (gchar, 2 * length); + + p = (gchar *) string; + q = value; + parsing_leading_space = TRUE; + while (p < (string + length - 1)) + { + gchar escaped_character[3] = { '\\', 0, 0 }; + + switch (*p) + { + case ' ': + if (parsing_leading_space) + { + escaped_character[1] = 's'; + strcpy (q, escaped_character); + q += 2; + } + else + { + *q = *p; + q++; + } + break; + case '\t': + if (parsing_leading_space) + { + escaped_character[1] = 't'; + strcpy (q, escaped_character); + q += 2; + } + else + { + *q = *p; + q++; + } + break; + case '\n': + escaped_character[1] = 'n'; + strcpy (q, escaped_character); + q += 2; + break; + case '\r': + escaped_character[1] = 'r'; + strcpy (q, escaped_character); + q += 2; + break; + case '\\': + escaped_character[1] = '\\'; + strcpy (q, escaped_character); + q += 2; + parsing_leading_space = FALSE; + break; + default: + if (list_separator && *p == list_separator) + { + escaped_character[1] = list_separator; + strcpy (q, escaped_character); + q += 2; + parsing_leading_space = TRUE; + } + else + { + *q = *p; + q++; + parsing_leading_space = FALSE; + } + break; + } + p++; + } + *q = '\0'; + + return value; +} diff --git a/hardinfo2/gg_strescape.c b/hardinfo2/gg_strescape.c new file mode 100644 index 00000000..93efe381 --- /dev/null +++ b/hardinfo2/gg_strescape.c @@ -0,0 +1,139 @@ +/* Base on: GLIB - Library of useful routines for C programming + * Copyright (C) 1995-1997 Peter Mattis, Spencer Kimball and Josh MacDonald + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#include <glib.h> +#include <string.h> + +guchar excmap_def[256] = {1,0}; +static void make_excmap_def() { + int i; + for(i=0; i<256; i++){ + switch ((guchar)i) + { + case '\b': + case '\f': + case '\n': + case '\r': + case '\t': + case '\\': + case '"': + excmap_def[i] = 0; + break; + default: + if ((i < ' ') || (i >= 0177)) + excmap_def[i] = 0; + else + excmap_def[i] = 1; + break; + } + } +} + +gchar * +gg_strescape (const gchar *source, + const gchar *exceptions, + const gchar *extra) +{ + const guchar *p; + gchar *dest; + gchar *q; + guchar excmap[256]; + + g_return_val_if_fail (source != NULL, NULL); + + if (excmap_def[0]) /* [0] should be 0 or it isn't initialized */ + make_excmap_def(); + + memcpy(excmap, excmap_def, 256); + + p = (guchar *) source; + /* Each source byte needs maximally four destination chars (\777) */ + q = dest = g_malloc (strlen (source) * 4 + 1); + + if (exceptions) + { + guchar *e = (guchar *) exceptions; + + while (*e) + { + excmap[*e] = 1; + e++; + } + } + + if (extra) + { + guchar *e = (guchar *) extra; + + while (*e) + { + excmap[*e] = 0; + e++; + } + } + + while (*p) + { + if (excmap[*p]) + *q++ = *p; + else + { + switch (*p) + { + case '\b': + *q++ = '\\'; + *q++ = 'b'; + break; + case '\f': + *q++ = '\\'; + *q++ = 'f'; + break; + case '\n': + *q++ = '\\'; + *q++ = 'n'; + break; + case '\r': + *q++ = '\\'; + *q++ = 'r'; + break; + case '\t': + *q++ = '\\'; + *q++ = 't'; + break; + case '\\': + *q++ = '\\'; + *q++ = '\\'; + break; + case '"': + *q++ = '\\'; + *q++ = '"'; + break; + default: + *q++ = '\\'; + *q++ = '0' + (((*p) >> 6) & 07); + *q++ = '0' + (((*p) >> 3) & 07); + *q++ = '0' + ((*p) & 07); + break; + } + } + p++; + } + *q = 0; + return dest; +} diff --git a/hardinfo2/gpu_util.c b/hardinfo2/gpu_util.c new file mode 100644 index 00000000..5d32f9e2 --- /dev/null +++ b/hardinfo2/gpu_util.c @@ -0,0 +1,477 @@ +/* + * HardInfo - Displays System Information + * Copyright (C) 2003-2017 L. A. F. Pereira <l@tia.mat.br> + * This file + * Copyright (C) 2018 Burt P. <pburt0@gmail.com> + * + * This program 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, version 2 or later. + * + * 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, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include "hardinfo.h" +#include "gpu_util.h" +#include "nice_name.h" +#include "cpu_util.h" /* for EMPIFNULL() */ + +nvgpu *nvgpu_new() { + return g_new0(nvgpu, 1); +} + +void nvgpu_free(nvgpu *s) { + if (s) { + free(s->model); + free(s->bios_version); + free(s->uuid); + } +} + +static char *_line_value(char *line, const char *prefix) { + if (g_str_has_prefix(g_strstrip(line), prefix)) { + line += strlen(prefix) + 1; + return g_strstrip(line); + } else + return NULL; +} + +static gboolean nv_fill_procfs_info(gpud *s) { + gchar *data, *p, *l, *next_nl; + gchar *pci_loc = pci_address_str(s->pci_dev->domain, s->pci_dev->bus, s->pci_dev->device, s->pci_dev->function); + gchar *nvi_file = g_strdup_printf("/proc/driver/nvidia/gpus/%s/information", pci_loc); + + g_file_get_contents(nvi_file, &data, NULL, NULL); + g_free(pci_loc); + g_free(nvi_file); + + if (data) { + s->nv_info = nvgpu_new(); + p = data; + while(next_nl = strchr(p, '\n')) { + strend(p, '\n'); + g_strstrip(p); + if (l = _line_value(p, "Model")) { + s->nv_info->model = g_strdup(l); + goto nv_details_next; + } + if (l = _line_value(p, "GPU UUID")) { + s->nv_info->uuid = g_strdup(l); + goto nv_details_next; + } + if (l = _line_value(p, "Video BIOS")) { + s->nv_info->bios_version = g_strdup(l); + goto nv_details_next; + } + + /* TODO: more details */ + + nv_details_next: + p = next_nl + 1; + } + g_free(data); + return TRUE; + } + return FALSE; +} + +static void intel_fill_freq(gpud *s) { + gchar path[256] = ""; + gchar *min_mhz = NULL, *max_mhz = NULL; + if (s->sysfs_drm_path) { + snprintf(path, 255, "%s/%s/gt_min_freq_mhz", s->sysfs_drm_path, s->id); + g_file_get_contents(path, &min_mhz, NULL, NULL); + snprintf(path, 255, "%s/%s/gt_max_freq_mhz", s->sysfs_drm_path, s->id); + g_file_get_contents(path, &max_mhz, NULL, NULL); + + if (min_mhz) + s->khz_min = atoi(min_mhz) * 1000; + if (max_mhz) + s->khz_max = atoi(max_mhz) * 1000; + + g_free(min_mhz); + g_free(max_mhz); + } +} + +static void amdgpu_parse_dpmclk(gchar *path, int *min, int *max) { + gchar *data = NULL, *p, *next_nl; + int sp, i, clk; + + *min = -1; + *max = -1; + + g_file_get_contents(path, &data, NULL, NULL); + if (data) { + p = data; + + while(next_nl = strchr(p, '\n')) { + strend(p, '\n'); + sp = sscanf(p, "%d: %dMhz", &i, &clk); + + if (sp == 2) { + if (clk > 0) { + if (*min < 0 || clk < *min) + *min = clk; + if (clk > *max) + *max = clk; + } + } + + p = next_nl + 1; + } + } + g_free(data); +} + +static void amdgpu_fill_freq(gpud *s) { + gchar path[256] = ""; + int clk_min = -1, clk_max = -1, mem_clk_min = -1, mem_clk_max = -1; + + if (s->sysfs_drm_path) { + /* core */ + snprintf(path, 255, "%s/%s/device/pp_dpm_sclk", s->sysfs_drm_path, s->id); + amdgpu_parse_dpmclk(path, &clk_min, &clk_max); + + if (clk_max > 0) + s->khz_max = clk_max * 1000; + if (clk_min > 0) + s->khz_min = clk_min * 1000; + + /* memory */ + snprintf(path, 255, "%s/%s/device/pp_dpm_mclk", s->sysfs_drm_path, s->id); + amdgpu_parse_dpmclk(path, &mem_clk_min, &mem_clk_max); + + if (mem_clk_max > 0) + s->mem_khz_max = mem_clk_max * 1000; + if (mem_clk_min > 0) + s->mem_khz_min = mem_clk_min * 1000; + } +} + +gpud *gpud_new() { + return g_new0(gpud, 1); +} + +void gpud_free(gpud *s) { + if (s) { + free(s->id); + free(s->nice_name); + free(s->vendor_str); + free(s->device_str); + free(s->location); + free(s->drm_dev); + free(s->sysfs_drm_path); + free(s->dt_compat); + g_free(s->dt_opp); + pcid_free(s->pci_dev); + nvgpu_free(s->nv_info); + g_free(s); + } +} + +void gpud_list_free(gpud *s) { + gpud *n; + while(s != NULL) { + n = s->next; + gpud_free(s); + s = n; + } +} + +/* returns number of items after append */ +static int gpud_list_append(gpud *l, gpud *n) { + int c = 0; + while(l != NULL) { + c++; + if (l->next == NULL) { + if (n != NULL) { + l->next = n; + c++; + } + break; + } + l = l->next; + } + return c; +} + +int gpud_list_count(gpud *s) { + return gpud_list_append(s, NULL); +} + +/* TODO: In the future, when there is more vendor specific information available in + * the gpu struct, then more precise names can be given to each gpu */ +static void make_nice_name(gpud *s) { + + /* NV information available */ + if (s->nv_info && s->nv_info->model) { + s->nice_name = g_strdup_printf("%s %s", "NVIDIA", s->nv_info->model); + return; + } + + static const char unk_v[] = "Unknown"; /* do not... */ + static const char unk_d[] = "Device"; /* ...translate */ + const char *vendor_str = s->vendor_str; + const char *device_str = s->device_str; + if (!vendor_str) + vendor_str = unk_v; + if (!device_str) + device_str = unk_d; + + /* try and a get a "short name" for the vendor */ + vendor_str = vendor_get_shortest_name(vendor_str); + + if (strstr(vendor_str, "Intel")) { + gchar *device_str_clean = strdup(device_str); + nice_name_intel_gpu_device(device_str_clean); + s->nice_name = g_strdup_printf("%s %s", vendor_str, device_str_clean); + g_free(device_str_clean); + } else if (strstr(vendor_str, "AMD")) { + /* AMD PCI strings are crazy stupid because they use the exact same + * chip and device id for a zillion "different products" */ + char *full_name = strdup(device_str); + /* Try and shorten it to the chip code name only, at least */ + char *b = strchr(full_name, '['); + if (b) *b = '\0'; + s->nice_name = g_strdup_printf("%s %s", "AMD/ATI", g_strstrip(full_name)); + free(full_name); + } else { + /* nothing nicer */ + s->nice_name = g_strdup_printf("%s %s", vendor_str, device_str); + } + +} + +/* Look for this kind of thing: + * * /soc/gpu + * * /gpu@ff300000 + * + * Usually a gpu dt node will have ./name = "gpu" + */ +static gchar *dt_find_gpu(dtr *dt, char *np) { + gchar *dir_path, *dt_path, *ret; + gchar *ftmp, *ntmp; + const gchar *fn; + GDir *dir; + dtr_obj *obj; + + /* consider self */ + obj = dtr_obj_read(dt, np); + dt_path = dtr_obj_path(obj); + ntmp = strstr(dt_path, "/gpu"); + if (ntmp) { + /* was found in node name */ + if ( strchr(ntmp+1, '/') == NULL) { + ftmp = ntmp + 4; + /* should either be NULL or @ */ + if (*ftmp == '\0' || *ftmp == '@') + return g_strdup(dt_path); + } + } + + /* search children ... */ + dir_path = g_strdup_printf("%s/%s", dtr_base_path(dt), np); + dir = g_dir_open(dir_path, 0 , NULL); + if (dir) { + while((fn = g_dir_read_name(dir)) != NULL) { + ftmp = g_strdup_printf("%s/%s", dir_path, fn); + if ( g_file_test(ftmp, G_FILE_TEST_IS_DIR) ) { + if (strcmp(np, "/") == 0) + ntmp = g_strdup_printf("/%s", fn); + else + ntmp = g_strdup_printf("%s/%s", np, fn); + ret = dt_find_gpu(dt, ntmp); + g_free(ntmp); + if (ret != NULL) { + g_free(ftmp); + g_dir_close(dir); + return ret; + } + } + g_free(ftmp); + } + g_dir_close(dir); + } + + return NULL; +} + +gpud *dt_soc_gpu() { + static const char std_soc_gpu_drm_path[] = "/sys/devices/platform/soc/soc:gpu/drm"; + + /* compatible contains a list of compatible hardware, so be careful + * with matching order. + * ex: "ti,omap3-beagleboard-xm", "ti,omap3450", "ti,omap3"; + * matches "omap3 family" first. + * ex: "brcm,bcm2837", "brcm,bcm2836"; + * would match 2836 when it is a 2837. + */ + const struct { + char *search_str; + char *vendor; + char *soc; + } dt_compat_searches[] = { + { "brcm,bcm2837-vc4", "Broadcom", "VideoCore IV" }, + { "brcm,bcm2836-vc4", "Broadcom", "VideoCore IV" }, + { "brcm,bcm2835-vc4", "Broadcom", "VideoCore IV" }, + { "arm,mali-450", "ARM", "Mali 450" }, + { "arm,mali", "ARM", "Mali family" }, + { NULL, NULL, NULL } + }; + char tmp_path[256] = ""; + char *dt_gpu_path = NULL; + char *compat = NULL; + char *vendor = NULL, *device = NULL; + int i; + + gpud *gpu = NULL; + + dtr *dt = dtr_new(NULL); + if (!dtr_was_found(dt)) + goto dt_gpu_end; + + dt_gpu_path = dt_find_gpu(dt, "/"); + + if (dt_gpu_path == NULL) + goto dt_gpu_end; + + snprintf(tmp_path, 255, "%s/compatible", dt_gpu_path); + compat = dtr_get_string(tmp_path, 1); + + if (compat == NULL) + goto dt_gpu_end; + + gpu = gpud_new(); + + i = 0; + while(dt_compat_searches[i].search_str != NULL) { + if (strstr(compat, dt_compat_searches[i].search_str) != NULL) { + vendor = dt_compat_searches[i].vendor; + device = dt_compat_searches[i].soc; + break; + } + i++; + } + + gpu->dt_compat = compat; + gpu->dt_vendor = vendor; + gpu->dt_device = device; + gpu->dt_path = dt_gpu_path; + snprintf(tmp_path, 255, "%s/status", dt_gpu_path); + gpu->dt_status = dtr_get_string(tmp_path, 1); + snprintf(tmp_path, 255, "%s/name", dt_gpu_path); + gpu->dt_name = dtr_get_string(tmp_path, 1); + gpu->dt_opp = dtr_get_opp_range(dt, dt_gpu_path); + if (gpu->dt_opp) { + gpu->khz_max = gpu->dt_opp->khz_max; + gpu->khz_min = gpu->dt_opp->khz_min; + } + EMPIFNULL(gpu->dt_name); + EMPIFNULL(gpu->dt_status); + + gpu->id = strdup("dt-soc-gpu"); + gpu->location = strdup("SOC"); + + if (access(std_soc_gpu_drm_path, F_OK) != -1) + gpu->sysfs_drm_path = strdup(std_soc_gpu_drm_path); + if (vendor) gpu->vendor_str = strdup(vendor); + if (device) gpu->device_str = strdup(device); + make_nice_name(gpu); + + +dt_gpu_end: + dtr_free(dt); + return gpu; +} + +gpud *gpu_get_device_list() { + int cn = 0; + gpud *list = NULL; + +/* Can we just ask DRM someway? ... */ + /* TODO: yes. /sys/class/drm/card* */ + +/* Try PCI ... */ + pcid_list pci_list = pci_get_device_list(0x300,0x3ff); + GSList *l = pci_list; + + if (l) { + while(l) { + pcid *curr = (pcid*)l->data; + char *pci_loc = NULL; + gpud *new_gpu = gpud_new(); + new_gpu->pci_dev = curr; + + pci_loc = pci_address_str(curr->domain, curr->bus, curr->device, curr->function); + + int len; + char drm_id[512] = "", card_id[64] = ""; + char *drm_dev = NULL; + gchar *drm_path = + g_strdup_printf("/dev/dri/by-path/pci-%s-card", pci_loc); + memset(drm_id, 0, 512); + if ((len = readlink(drm_path, drm_id, sizeof(drm_id)-1)) != -1) + drm_id[len] = '\0'; + g_free(drm_path); + + if (strlen(drm_id) != 0) { + /* drm has the card */ + drm_dev = strstr(drm_id, "card"); + if (drm_dev) + snprintf(card_id, 64, "%s", drm_dev); + } + + if (strlen(card_id) == 0) { + /* fallback to our own counter */ + snprintf(card_id, 64, "pci-dc%d", cn); + cn++; + } + + if (drm_dev) + new_gpu->drm_dev = strdup(drm_dev); + + char *sysfs_path_candidate = g_strdup_printf("%s/%s/drm", SYSFS_PCI_ROOT, pci_loc); + if (access(sysfs_path_candidate, F_OK) != -1) { + new_gpu->sysfs_drm_path = sysfs_path_candidate; + } else + free(sysfs_path_candidate); + new_gpu->location = g_strdup_printf("PCI/%s", pci_loc); + new_gpu->id = strdup(card_id); + if (curr->vendor_id_str) new_gpu->vendor_str = strdup(curr->vendor_id_str); + if (curr->device_id_str) new_gpu->device_str = strdup(curr->device_id_str); + nv_fill_procfs_info(new_gpu); + intel_fill_freq(new_gpu); + amdgpu_fill_freq(new_gpu); + make_nice_name(new_gpu); + if (list == NULL) + list = new_gpu; + else + gpud_list_append(list, new_gpu); + + free(pci_loc); + l=l->next; + } + + /* don't pcid_list_free(pci_list); They will be freed by gpud_free() */ + g_slist_free(pci_list); /* just the linking data */ + return list; + } + +/* Try Device Tree ... */ + list = dt_soc_gpu(); + if (list) return list; + +/* Try other things ... */ + + return list; +} + + diff --git a/hardinfo2/hardinfo.c b/hardinfo2/hardinfo.c new file mode 100644 index 00000000..edbad308 --- /dev/null +++ b/hardinfo2/hardinfo.c @@ -0,0 +1,175 @@ +/* + * HardInfo - Displays System Information + * Copyright (C) 2003-2009 L. A. F. Pereira <l@tia.mat.br> + * + * This program 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, version 2 or later. + * + * 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, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include <config.h> +#include <shell.h> + +#include <report.h> +#include <hardinfo.h> +#include <iconcache.h> +#include <stock.h> +#include <vendor.h> + +#include <binreloc.h> +#include "dmi_util.h" + +ProgramParameters params = { 0 }; + +int main(int argc, char **argv) +{ + int exit_code = 0; + GSList *modules; + + setlocale(LC_ALL, ""); + bindtextdomain("hardinfo2", LOCALEDIR); + textdomain("hardinfo2"); + + DEBUG("Hardinfo2 version " VERSION ". Debug version."); + +#if GLIB_CHECK_VERSION(2,32,0) +#else + if (!g_thread_supported ()) g_thread_init (NULL); +#endif + + /* parse all command line parameters */ + parameters_init(&argc, &argv, ¶ms); + + /* show version information and quit */ + if (params.show_version) { + g_print("Hardinfo2 version " VERSION "\n"); + g_print + (_(/*/ %d will be latest year of copyright*/ "Copyright (C) 2003-2023 L. A. F. Pereira. 2024-%d Hardinfo2 Project.\n\n"), HARDINFO2_COPYRIGHT_LATEST_YEAR ); + + g_print(_("Compile-time options:\n" + " Release version: %s (%s)\n" + " BinReloc enabled: %s\n" + " Data prefix: %s\n" + " Library prefix: %s\n" + " Compiled for: %s\n"), + RELEASE ? _("Yes") : "No (" VERSION ")", ARCH, + ENABLE_BINRELOC ? _("Yes") : _("No"), + PREFIX, LIBPREFIX, PLATFORM); + + DEBUG(" Debugging is enabled."); + + /* show also available modules */ + params.list_modules = TRUE; + } + + /* initialize the binreloc library, so we can load program data */ + if (!binreloc_init(FALSE)) + g_error(_("Failed to find runtime data.\n\n" + "\342\200\242 Is HardInfo2 correctly installed?\n" + "\342\200\242 See if %s and %s exists and you have read permission."), + PREFIX, LIBPREFIX); + + /* list all module names */ + if (params.list_modules) { + g_print(_("Modules:\n" + "%-20s %-15s %-12s\n"), _("File Name"), _("Name"), _("Version")); + + for (modules = modules_load_all(); modules; + modules = modules->next) { + ShellModule *module = (ShellModule *) modules->data; + const ModuleAbout *ma = module_get_about(module); + gchar *name = g_path_get_basename(g_module_name(module->dll)); + + g_print("%-20s %-15s %-12s\n", name, module->name, ma->version); + + g_free(name); + } + + return 0; + } + + if (!params.create_report && !params.run_benchmark) { + /* we only try to open the UI if the user didn't ask for a report. */ + params.gui_running = ui_init(&argc, &argv); + + /* as a fallback, if GTK+ initialization failed, run in report + generation mode. */ + if (!params.gui_running) { + params.create_report = TRUE; + /* ... it is possible to -f html without -r */ + if (params.report_format != REPORT_FORMAT_HTML) + params.markup_ok = FALSE; + } + } + + if (params.use_modules) { + /* load only selected modules */ + DEBUG("loading user-selected modules"); + modules = modules_load_selected(); + } else { + /* load all modules */ + DEBUG("loading all modules"); + modules = modules_load_all(); + } + + /* initialize vendor database */ + vendor_init(); + + /* initialize moreinfo */ + moreinfo_init(); + + if (params.run_benchmark) { + gchar *result; + + result = module_call_method_param("benchmark::runBenchmark", params.run_benchmark); + if (!result) { + fprintf(stderr, _("Unknown benchmark ``%s'' or benchmark.so not loaded"), params.run_benchmark); + exit_code = 1; + } else { + fprintf(stderr, "\n"); + g_print("%s\n", result); + g_free(result); + } + } else if (params.gui_running) { + /* initialize gui and start gtk+ main loop */ + icon_cache_init(); + stock_icons_init(); + + shell_init(modules); + + DEBUG("entering gtk+ main loop"); + + gtk_main(); + } else if (params.create_report) { + /* generate report */ + gchar *report; + + DEBUG("generating report"); + + report = report_create_from_module_list_format(modules, + params. + report_format); + g_print("%s", report); + + g_free(report); + } else { + g_error(_("Don't know what to do. Exiting.")); + } + + moreinfo_shutdown(); + vendor_cleanup(); + dmidecode_cache_free(); + free_auto_free_final(); + + DEBUG("finished"); + return exit_code; +} diff --git a/hardinfo2/hinote_util.c b/hardinfo2/hinote_util.c new file mode 100644 index 00000000..efc85be0 --- /dev/null +++ b/hardinfo2/hinote_util.c @@ -0,0 +1,27 @@ + +#include "hardinfo.h" + +/* requires COMPILE_FLAGS "-std=c99" */ + +static const char bullet_yes[] = "<big><b>\u2713</b></big>"; +static const char bullet_no[] = "<big><b>\u2022<tt> </tt></b></big>"; +static const char bullet_yes_text[] = "[X]"; +static const char bullet_no_text[] = "[ ]"; + +gboolean note_cond_bullet(gboolean cond, gchar *note_buff, const gchar *desc_str) { + int l = strlen(note_buff); + if (params.markup_ok) + snprintf(note_buff + l, note_max_len - l - 1, "%s %s\n", + cond ? bullet_yes : bullet_no, desc_str); + else + snprintf(note_buff + l, note_max_len - l - 1, "%s %s\n", + cond ? bullet_yes_text : bullet_no_text, desc_str); + return cond; +} + +gboolean note_require_tool(const gchar *tool, gchar *note_buff, const gchar *desc_str) { + gchar *tl = find_program((gchar*)tool); + gboolean found = note_cond_bullet(!!tl, note_buff, desc_str); + g_free(tl); + return found; +} diff --git a/hardinfo2/info.c b/hardinfo2/info.c new file mode 100644 index 00000000..fae16497 --- /dev/null +++ b/hardinfo2/info.c @@ -0,0 +1,584 @@ +/* + * HardInfo - Displays System Information + * Copyright (C) 2017 L. A. F. Pereira <l@tia.mat.br> + * + * This program 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, version 2 or later. + * + * 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, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include "hardinfo.h" +#include "util_sysobj.h" /* for SEQ() */ + +/* Using a slightly modified gg_key_file_parse_string_as_value() + * from GLib in flatten(), to escape characters and the separator. + * The function is not public in GLib and we don't have a GKeyFile + * to pass it anyway. */ +/* Now in hardinfo.h -- #include "gg_key_file_parse_string_as_value.c" */ + +static const gchar *info_column_titles[] = { + "TextValue", "Value", "Progress", "Extra1", "Extra2" +}; + +struct Info *info_new(void) +{ + struct Info *info = g_new0(struct Info, 1); + + info->groups = g_array_new(FALSE, FALSE, sizeof(struct InfoGroup)); + info->view_type = SHELL_VIEW_NORMAL; + info->column_headers_visible = FALSE; + info->zebra_visible = FALSE; + info->normalize_percentage = TRUE; + + return info; +} + +void info_group_add_field(struct InfoGroup *group, struct InfoField field) +{ + if (!group) + return; + + /* info_field_last() */ + if (!field.name) + return; + + g_array_append_val(group->fields, field); +} + +void info_group_add_fieldsv(struct InfoGroup *group, va_list ap) +{ + while (1) { + struct InfoField field = va_arg(ap, struct InfoField); + + /* info_field_last() */ + if (!field.name) + break; + + g_array_append_val(group->fields, field); + } +} + +void info_group_add_fields(struct InfoGroup *group, ...) +{ + va_list ap; + + va_start(ap, group); + info_group_add_fieldsv(group, ap); + va_end(ap); +} + +struct InfoGroup *info_add_group(struct Info *info, const gchar *group_name, ...) +{ + struct InfoGroup group = { + .name = group_name, + .fields = g_array_new(FALSE, FALSE, sizeof(struct InfoField)) + }; + va_list ap; + + va_start(ap, group_name); + info_group_add_fieldsv(&group, ap); + va_end(ap); + + g_array_append_val(info->groups, group); + + return &g_array_index(info->groups, struct InfoGroup, info->groups->len - 1); +} + +struct InfoField info_field_printf(const gchar *name, const gchar *format, ...) +{ + gchar *value; + va_list ap; + + va_start(ap, format); + value = g_strdup_vprintf(format, ap); + va_end(ap); + + return (struct InfoField) { + .name = name, + .value = value, + .free_value_on_flatten = TRUE, + }; +} + +void info_group_strip_extra(struct InfoGroup *group) +{ + guint fi; + char *val, *oldval; + struct InfoField *field; + + for (fi = 0; fi < group->fields->len; fi++) { + field = &g_array_index(group->fields, struct InfoField, fi); + if (field->value){ + val = strrchr(field->value, '|'); + if (val) { + oldval = (gchar*)field->value; + field->value = strdup(val + 1); + g_free(oldval); + } + } + } +} + +void info_add_computed_group(struct Info *info, const gchar *name, const gchar *value) +{ + /* This is a scaffolding method: HardInfo should move away from pre-computing + * the strings in favor of storing InfoGroups instead; while modules are not + * fully converted, use this instead. */ + struct Info *tmp_info = NULL; + struct InfoGroup donor = {}; + gchar *tmp_str = NULL; + + if (name) + tmp_str = g_strdup_printf("[%s]\n%s", name, value); + else + tmp_str = g_strdup(value); + + tmp_info = info_unflatten(tmp_str); + if (tmp_info->groups->len != 1) { + fprintf(stderr, + "info_add_computed_group(): expected only one group in value! (actual: %d)\n", + tmp_info->groups->len); + } else { + donor = g_array_index(tmp_info->groups, struct InfoGroup, 0); + g_array_append_val(info->groups, donor); + } + + g_free(tmp_info); // TODO: doesn't do enough + g_free(tmp_str); +} + +void info_add_computed_group_wo_extra(struct Info *info, const gchar *name, const gchar *value) +{ + /* This is a scaffolding method: HardInfo should move away from pre-computing + * the strings in favor of storing InfoGroups instead; while modules are not + * fully converted, use this instead. */ + struct InfoGroup *agroup; + + info_add_computed_group(info, name, value); + if (info->groups->len > 0) { + agroup = &g_array_index(info->groups, struct InfoGroup, info->groups->len - 1); + info_group_strip_extra(agroup); + } +} + +void info_set_column_title(struct Info *info, const gchar *column, const gchar *title) +{ + guint i; + + for (i = 0; i < G_N_ELEMENTS(info_column_titles); i++) { + if (g_str_equal(info_column_titles[i], column)) { + info->column_titles[i] = title; + return; + } + } +} + +void info_set_column_headers_visible(struct Info *info, gboolean setting) +{ + info->column_headers_visible = setting; +} + +void info_set_zebra_visible(struct Info *info, gboolean setting) +{ + info->zebra_visible = setting; +} + +void info_set_normalize_percentage(struct Info *info, gboolean setting) +{ + info->normalize_percentage = setting; +} + +void info_set_view_type(struct Info *info, ShellViewType setting) +{ + info->view_type = setting; +} + +void info_set_reload_interval(struct Info *info, int setting) +{ + info->reload_interval = setting; +} + +static int info_field_cmp_name_ascending(const void *a, const void *b) +{ + const struct InfoField *aa = a, *bb = b; + return g_strcmp0(aa->name, bb->name); +} + +static int info_field_cmp_name_descending(const void *a, const void *b) +{ + const struct InfoField *aa = a, *bb = b; + return g_strcmp0(bb->name, aa->name); +} + +static int info_field_cmp_value_ascending(const void *a, const void *b) +{ + const struct InfoField *aa = a, *bb = b; + return g_strcmp0(aa->value, bb->value); +} + +static int info_field_cmp_value_descending(const void *a, const void *b) +{ + const struct InfoField *aa = a, *bb = b; + return g_strcmp0(bb->value, aa->value); +} + +static int info_field_cmp_tag_ascending(const void *a, const void *b) +{ + const struct InfoField *aa = a, *bb = b; + return g_strcmp0(aa->tag, bb->tag); +} + +static int info_field_cmp_tag_descending(const void *a, const void *b) +{ + const struct InfoField *aa = a, *bb = b; + return g_strcmp0(bb->tag, aa->tag); +} + +static const GCompareFunc sort_functions[INFO_GROUP_SORT_MAX] = { + [INFO_GROUP_SORT_NONE] = NULL, + [INFO_GROUP_SORT_NAME_ASCENDING] = info_field_cmp_name_ascending, + [INFO_GROUP_SORT_NAME_DESCENDING] = info_field_cmp_name_descending, + [INFO_GROUP_SORT_VALUE_ASCENDING] = info_field_cmp_value_ascending, + [INFO_GROUP_SORT_VALUE_DESCENDING] = info_field_cmp_value_descending, + [INFO_GROUP_SORT_TAG_ASCENDING] = info_field_cmp_tag_ascending, + [INFO_GROUP_SORT_TAG_DESCENDING] = info_field_cmp_tag_descending, +}; + +static void field_free_strings(struct InfoField *field) +{ + if (field->free_value_on_flatten) + g_free((gchar *)field->value); + if (field->free_name_on_flatten) + g_free((gchar *)field->name); + g_free(field->tag); +} + +static void free_group_fields(struct InfoGroup *group) +{ + if (group && group->fields) { + guint i; + + for (i = 0; i < group->fields->len; i++) { + struct InfoField *field = + &g_array_index(group->fields, struct InfoField, i); + field_free_strings(field); + } + + g_array_free(group->fields, TRUE); + } +} + +static void flatten_group(GString *output, const struct InfoGroup *group, guint group_count) +{ + guint i; + + if (group->name != NULL) + g_string_append_printf(output, "[%s#%d]\n", group->name, group_count); + + if (group->sort != INFO_GROUP_SORT_NONE) + g_array_sort(group->fields, sort_functions[group->sort]); + + if (group->fields) { + for (i = 0; i < group->fields->len; i++) { + struct InfoField *field = &g_array_index(group->fields, struct InfoField, i); + gchar tmp_tag[256] = ""; /* for generated tag */ + + gboolean do_escape = TRUE; /* refers to the value side only */ + if (field->value && strchr(field->value, '|') ) { + /* turning off escaping for values that may have columns */ + do_escape = FALSE; + /* TODO:/FIXME: struct InfoField should store the column values + * in an array instead of packing them into one value with '|'. + * Then each value can be escaped and joined together with '|' + * for flatten(). unflatten() can then split on non-escaped '|', + * and unescape the result values into the column value array. + * Another way to do this might be to check + * .column_headers_visible in struct Info, but that is not + * available here. + */ + } + + const gchar *tp = field->tag; + gboolean tagged = !!tp; + gboolean flagged = field->highlight || field->report_details || field->value_has_vendor; + if (!tp) { + snprintf(tmp_tag, 255, "ITEM%d-%d", group_count, i); + tp = tmp_tag; + } + if (!field->label_is_escaped) { + if (strchr(field->name, '=') + || strchr(field->name, '$')) { + // TODO: what about # ? + gchar *ofn = (gchar*)field->name; + field->name = key_label_escape(ofn); + g_free(ofn); + field->label_is_escaped = TRUE; + } + } + if (tagged || flagged || field->icon) { + g_string_append_printf(output, "$%s%s%s%s%s$", + field->label_is_escaped ? "@" : "", + field->highlight ? "*" : "", + field->report_details ? "!" : "", + field->value_has_vendor ? "^" : "", + tp); + } + + if (do_escape) { + gchar *escaped_value = gg_key_file_parse_string_as_value(field->value, '|'); + g_string_append_printf(output, "%s=%s\n", field->name, escaped_value); + g_free(escaped_value); + } else { + g_string_append_printf(output, "%s=%s\n", field->name, field->value); + } + } + } else if (group->computed) { + g_string_append_printf(output, "%s\n", group->computed); + } +} + +static void flatten_shell_param(GString *output, const struct InfoGroup *group, guint group_count) +{ + guint i; + + if (!group->fields) + return; + + for (i = 0; i < group->fields->len; i++) { + struct InfoField *field = &g_array_index(group->fields, struct InfoField, i); + gchar tmp_tag[256] = ""; /* for generated tag */ + + const gchar *tp = field->tag; + gboolean tagged = !!tp; + if (!tp) { + snprintf(tmp_tag, 255, "ITEM%d-%d", group_count, i); + tp = tmp_tag; + } + + if (field->update_interval) { + g_string_append_printf(output, "UpdateInterval$%s%s%s=%d\n", + tagged ? tp : "", tagged ? "$" : "", /* tag and close or nothing */ + field->name, + field->update_interval); + } + + if (field->icon) { + g_string_append_printf(output, "Icon$%s$=%s\n", + tp, field->icon); + } + } +} + +static void flatten_shell_param_global(GString *output, const struct Info *info) +{ + guint i; + + g_string_append_printf(output, "ViewType=%d\n", info->view_type); + g_string_append_printf(output, "ShowColumnHeaders=%s\n", + info->column_headers_visible ? "true" : "false"); + + if (info->zebra_visible) + g_string_append(output, "Zebra=1\n"); + + if (info->reload_interval) + g_string_append_printf(output, "ReloadInterval=%d\n", info->reload_interval); + + if (!info->normalize_percentage) + g_string_append(output, "NormalizePercentage=false\n"); + + for (i = 0; i < G_N_ELEMENTS(info_column_titles); i++) { + if (!info->column_titles[i]) + continue; + + g_string_append_printf(output, "ColumnTitle$%s=%s\n", + info_column_titles[i], info->column_titles[i]); + } +} + +gchar *info_flatten(struct Info *info) +{ + /* This is a scaffolding method: eventually the HardInfo shell should + * understand a struct Info instead of parsing these strings, which are + * brittle and unnecessarily complicates things. Being a temporary + * method, no attention is paid to improve the memory allocation + * strategy. */ + GString *values; + GString *shell_param; + guint i; + + values = g_string_new(NULL); + shell_param = g_string_new(NULL); + + if (info->groups) { + for (i = 0; i < info->groups->len; i++) { + struct InfoGroup *group = + &g_array_index(info->groups, struct InfoGroup, i); + + flatten_group(values, group, i); + flatten_shell_param(shell_param, group, i); + + free_group_fields(group); + } + + g_array_free(info->groups, TRUE); + } + + flatten_shell_param_global(shell_param, info); + g_string_append_printf(values, "[$ShellParam$]\n%s", shell_param->str); + + g_string_free(shell_param, TRUE); + g_free(info); + + return g_string_free(values, FALSE); +} + +void info_remove_group(struct Info *info, guint index) +{ + struct InfoGroup *grp; + + if (index >= info->groups->len) + return; + + grp = &g_array_index(info->groups, struct InfoGroup, index); + free_group_fields(grp); + + g_array_remove_index(info->groups, index); +} + +struct InfoField *info_find_field(struct Info *info, const gchar *tag, const gchar *name) { + struct InfoGroup *group; + struct InfoField *field; + guint gi,fi; + for (gi = 0; gi < info->groups->len; gi++) { + struct InfoGroup *group = &g_array_index(info->groups, struct InfoGroup, gi); + for (fi = 0; fi < group->fields->len; fi++) { + struct InfoField *field = &g_array_index(group->fields, struct InfoField, fi); + if (tag && SEQ(tag, field->tag) ) + return field; + else if (name && SEQ(name, field->name) ) + return field; + } + } + return NULL; +} + +#define VAL_FALSE_OR_TRUE ((!g_strcmp0(value, "true") || !g_strcmp0(value, "1")) ? TRUE : FALSE) + +struct Info *info_unflatten(const gchar *str) +{ + struct Info *info = info_new(); + GKeyFile *key_file = g_key_file_new(); + gchar **groups; + gsize ngroups; + int g, k, spg = -1; + + g_key_file_load_from_data(key_file, str, strlen(str), 0, NULL); + groups = g_key_file_get_groups(key_file, &ngroups); + for (g = 0; groups[g]; g++) { + gchar *group_name = groups[g]; + gchar **keys = g_key_file_get_keys(key_file, group_name, NULL, NULL); + + if (*group_name == '$') { + /* special group */ + if (SEQ(group_name, "$ShellParam$") ) + spg = g; /* handle after all groups are added */ + else { + /* This special group is unknown and won't be handled, so + * the name will not be linked anywhere. */ + g_free(group_name); + continue; + } + } else { + /* normal group */ + struct InfoGroup group = {}; + group.name = group_name; + group.fields = g_array_new(FALSE, FALSE, sizeof(struct InfoField)); + group.sort = INFO_GROUP_SORT_NONE; + + for (k = 0; keys[k]; k++) { + struct InfoField field = {}; + gchar *flags, *tag, *name, *label, *dis; + key_get_components(keys[k], &flags, &tag, &name, &label, &dis, TRUE); + gchar *value = g_key_file_get_value(key_file, group_name, keys[k], NULL); + + field.tag = tag; + field.name = name; + field.value = value; + if (key_label_is_escaped(flags)) + field.label_is_escaped = TRUE; + if (key_wants_details(flags)) + field.report_details = TRUE; + if (key_is_highlighted(flags)) + field.highlight = TRUE; + if (key_value_has_vendor_string(flags)) + field.value_has_vendor = TRUE; + field.free_value_on_flatten = TRUE; + field.free_name_on_flatten = TRUE; + + g_free(flags); + g_free(label); + g_free(dis); + g_array_append_val(group.fields, field); + } + g_array_append_val(info->groups, group); + } + } + + if (spg >= 0) { + gchar *group_name = groups[spg]; + gchar **keys = g_key_file_get_keys(key_file, group_name, NULL, NULL); + for (k = 0; keys[k]; k++) { + gchar *value = g_key_file_get_value(key_file, group_name, keys[k], NULL); + gchar *parm = NULL; + if (SEQ(keys[k], "ViewType")) { + info_set_view_type(info, atoi(value)); + } else if (SEQ(keys[k], "ShowColumnHeaders")) { + info_set_column_headers_visible(info, VAL_FALSE_OR_TRUE); + } else if (SEQ(keys[k], "Zebra")) { + info_set_zebra_visible(info, VAL_FALSE_OR_TRUE); + } else if (SEQ(keys[k], "ReloadInterval")) { + info_set_reload_interval(info, atoi(value)); + } else if (SEQ(keys[k], "NormalizePercentage")) { + info_set_normalize_percentage(info, VAL_FALSE_OR_TRUE); + } else if (g_str_has_prefix(keys[k], "ColumnTitle$")) { + info_set_column_title(info, strchr(keys[k], '$') + 1, value); + } else if (g_str_has_prefix(keys[k], "Icon$")) { + const gchar *chk_name = NULL; + gchar *chk_tag = NULL; + parm = strchr(keys[k], '$'); + if (key_is_flagged(parm)) + chk_tag = key_mi_tag(parm); + struct InfoField *field = info_find_field(info, chk_tag, NULL); + if (field) + field->icon = value; + g_free(chk_tag); + } else if (g_str_has_prefix(keys[k], "UpdateInterval$")) { + const gchar *chk_name = NULL; + gchar *chk_tag = NULL; + parm = strchr(keys[k], '$'); + if (key_is_flagged(parm)) { + chk_tag = key_mi_tag(parm); + chk_name = key_get_name(parm); + } else + chk_name = key_get_name(parm+1); + struct InfoField *field = info_find_field(info, chk_tag, chk_name); + if (field) + field->update_interval = atoi(value); + g_free(chk_tag); + } + } + g_free(group_name); + g_strfreev(keys); + } + + return info; +} diff --git a/hardinfo2/pci_util.c b/hardinfo2/pci_util.c new file mode 100644 index 00000000..f0c5059a --- /dev/null +++ b/hardinfo2/pci_util.c @@ -0,0 +1,463 @@ +/* + * HardInfo - Displays System Information + * Copyright (C) 2003-2017 L. A. F. Pereira <l@tia.mat.br> + * This file + * Copyright (C) 2018 Burt P. <pburt0@gmail.com> + * + * This program 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, version 2 or later. + * + * 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, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include "hardinfo.h" +#include "pci_util.h" +#include "util_ids.h" + +gchar *pci_ids_file = NULL; +GTimer *pci_ids_timer = NULL; +const gboolean nolspci = FALSE; /* true for testing */ + +/* Two pieces of info still only from lspci: + * - kernel driver in use + * - kernel modules list + * + * TODO: could use readlink() and basename() to get kernel driver from sysfs + * - /sys/bus/pci/devices/<addy>/driver is a symlink + */ + +const gchar *find_pci_ids_file() { + if (pci_ids_file) { + if (!strstr(pci_ids_file, ".min")) + return pci_ids_file; + if (g_timer_elapsed(pci_ids_timer, NULL) > 2.0) { + /* try again for the full version */ + DEBUG("find_pci_ids_file() found only a \".min\" version, trying again..."); + g_free(pci_ids_file); + pci_ids_file = NULL; + } + } + char *file_search_order[] = { + g_strdup("/usr/share/hwdata/pci.ids"), + g_strdup("/usr/share/misc/pci.ids"), + g_build_filename(g_get_user_config_dir(), "hardinfo2", "pci.ids", NULL), + g_build_filename(params.path_data, "pci.ids", NULL), + g_build_filename(g_get_user_config_dir(), "hardinfo2", "pci.ids.min", NULL), + g_build_filename(params.path_data, "pci.ids.min", NULL), + NULL + }; + int n; + for(n = 0; file_search_order[n]; n++) { + if (!pci_ids_file && !access(file_search_order[n], R_OK)) + pci_ids_file = file_search_order[n]; + else + g_free(file_search_order[n]); + } + DEBUG("find_pci_ids_file() result: %s", pci_ids_file); + if (pci_ids_file) { + if (!pci_ids_timer) + pci_ids_timer = g_timer_new(); + else + g_timer_reset(pci_ids_timer); + } + return pci_ids_file; +} + +char *pci_lookup_ids_vendor_str(uint32_t id) { + gchar *ret = NULL; + + ids_query_result result;// = {}; + gchar *qpath; + memset(&result,0,sizeof(ids_query_result)); + if (!find_pci_ids_file()) + return FALSE; + + qpath = g_strdup_printf("%04x", id); + scan_ids_file(pci_ids_file, qpath, &result, -1); + if (result.results[0]) { + ret = g_strdup(result.results[0]); + } + g_free(qpath); + + return ret; +} + +static gboolean pci_lookup_ids(pcid *d) { + gboolean ret = FALSE; + ids_query_result result;// = {}; + gchar *qpath; + memset(&result,0,sizeof(ids_query_result)); + if (!find_pci_ids_file()) + return FALSE; + + /* lookup vendor, device, sub device */ + qpath = g_strdup_printf("%04x/%04x/%04x %04x", + d->vendor_id, d->device_id, d->sub_vendor_id, d->sub_device_id); + scan_ids_file(pci_ids_file, qpath, &result, -1); + if (result.results[0]) { + if (d->vendor_id_str) g_free(d->vendor_id_str); + d->vendor_id_str = g_strdup(result.results[0]); + ret = TRUE; + } + if (result.results[1]) { + if (d->device_id_str) g_free(d->device_id_str); + d->device_id_str = g_strdup(result.results[1]); + ret = TRUE; + } + if (result.results[2]) { + if (d->sub_device_id_str) g_free(d->sub_device_id_str); + d->sub_device_id_str = g_strdup(result.results[2]); + ret = TRUE; + } + g_free(qpath); + + /* lookup sub vendor by itself */ + qpath = g_strdup_printf("%04x", d->sub_vendor_id); + scan_ids_file(pci_ids_file, qpath, &result, -1); + if (result.results[0]) { + if (d->sub_vendor_id_str) g_free(d->sub_vendor_id_str); + d->sub_vendor_id_str = g_strdup(result.results[0]); + ret = TRUE; + }; + g_free(qpath); + + /* lookup class */ + qpath = g_strdup_printf("C %02x/%02x", (d->class >> 8) & 0xff, (d->class & 0xff)); + scan_ids_file(pci_ids_file, qpath, &result, -1); + if (result.results[0]) { + if (d->class_str) g_free(d->class_str); + d->class_str = g_strdup(result.results[0]); + if (result.results[1] + && !SEQ(result.results[0], result.results[1]) ) { + /* options 1: results[0] + " :: " + results[1] */ + //d->class_str = appf(d->class_str, " :: ", "%s", result.results[1]); + + /* option 2: results[1] or results[0] */ + g_free(d->class_str); + d->class_str = g_strdup(result.results[1]); + } + ret = TRUE; + } + g_free(qpath); + + return ret; +} + +gint pcid_cmp_by_addy(gconstpointer a, gconstpointer b) +{ + const struct pcid *dev_a = a; + const struct pcid *dev_b = b; + + if (!dev_a) + return !!dev_b; + if (!dev_b) + return !!dev_a; + + return g_strcmp0(dev_a->slot_str, dev_b->slot_str); +} + +void pcid_free(pcid *s) { + if (s) { + g_free(s->slot_str); + g_free(s->class_str); + g_free(s->vendor_id_str); + g_free(s->device_id_str); + g_free(s->sub_vendor_id_str); + g_free(s->sub_device_id_str); + g_free(s->driver); + g_free(s->driver_list); + g_free(s); + } +} + +static char *lspci_line_value(char *line, const char *prefix) { + if (g_str_has_prefix(g_strstrip(line), prefix)) { + line += strlen(prefix) + 1; + return g_strstrip(line); + } else + return NULL; +} + +/* read output line of lspci -vmmnn */ +static int lspci_line_string_and_code(char *line, char *prefix, char **str, uint32_t *code) { + char *l = lspci_line_value(line, prefix); + char *e; + + if (l) { + e = strchr(l, 0); + while (e > l && *e != '[') e--; + sscanf(e, "[%x]", code); + *e = 0; /* terminate at start of code */ + if (*str) free(*str); /* free if replacing */ + *str = strdup(g_strstrip(l)); + } + return 0; +} + +static gboolean pci_fill_details(pcid *s) { + if (nolspci) return FALSE; + gboolean spawned; + gchar *out, *err, *p, *l, *next_nl; + gchar *pci_loc = pci_address_str(s->domain, s->bus, s->device, s->function); + gchar *lspci_cmd = g_strdup_printf("lspci -D -s %s -vvv", pci_loc); + + spawned = hardinfo_spawn_command_line_sync(lspci_cmd, + &out, &err, NULL, NULL); + g_free(lspci_cmd); + g_free(pci_loc); + if (spawned) { + p = out; + while(next_nl = strchr(p, '\n')) { + strend(p, '\n'); + g_strstrip(p); + if (l = lspci_line_value(p, "Kernel driver in use")) { + s->driver = g_strdup(l); + goto pci_details_next; + } + if (l = lspci_line_value(p, "Kernel modules")) { + s->driver_list = g_strdup(l); + goto pci_details_next; + } + /* TODO: more details */ + + pci_details_next: + p = next_nl + 1; + } + g_free(out); + g_free(err); + return TRUE; + } + return FALSE; +} + +char *pci_address_str(uint32_t dom, uint32_t bus, uint32_t dev, uint32_t func) { + return g_strdup_printf("%04x:%02x:%02x.%01x", dom, bus, dev, func); +} + +/* /sys/bus/pci/devices/0000:01:00.0/ */ +char *_sysfs_bus_pci(uint32_t dom, uint32_t bus, uint32_t dev, uint32_t func, const char *item) { + char *ret = NULL, *pci_loc, *sysfs_path; + pci_loc = pci_address_str(dom, bus, dev, func); + sysfs_path = g_strdup_printf("%s/%s/%s", SYSFS_PCI_ROOT, pci_loc, item); + g_file_get_contents(sysfs_path, &ret, NULL, NULL); + free(pci_loc); + free(sysfs_path); + return ret; +} + +gboolean _sysfs_bus_pci_read_hex(uint32_t dom, uint32_t bus, uint32_t dev, uint32_t func, const char *item, uint32_t *val) { + char *tmp = _sysfs_bus_pci(dom, bus, dev, func, item); + uint32_t tval; + if (tmp && val) { + int ec = sscanf(tmp, "%x", &tval); + free(tmp); + if (ec) { + *val = tval; + return TRUE; + } + } + return FALSE; +} + +/* https://www.kernel.org/doc/Documentation/ABI/testing/sysfs-bus-pci */ +static gboolean pci_get_device_sysfs(uint32_t dom, uint32_t bus, uint32_t dev, uint32_t func, pcid *s) { + char *tmp = NULL; + int ec = 0; + float tf; + s->domain = dom; + s->bus = bus; + s->device = dev; + s->function = func; + s->slot_str = s->slot_str ? s->slot_str : pci_address_str(dom, bus, dev, func); + if (! _sysfs_bus_pci_read_hex(dom, bus, dev, func, "class", &s->class) ) + return FALSE; + s->class >>= 8; /* TODO: find out why */ + _sysfs_bus_pci_read_hex(dom, bus, dev, func, "device", &s->device_id); + _sysfs_bus_pci_read_hex(dom, bus, dev, func, "vendor", &s->vendor_id); + _sysfs_bus_pci_read_hex(dom, bus, dev, func, "subsystem_device", &s->sub_device_id); + _sysfs_bus_pci_read_hex(dom, bus, dev, func, "subsystem_vendor", &s->sub_vendor_id); + _sysfs_bus_pci_read_hex(dom, bus, dev, func, "revision", &s->revision); + + tmp = _sysfs_bus_pci(dom, bus, dev, func, "max_link_speed"); + if (tmp) { + ec = sscanf(tmp, "%f GT/s", &tf); + if (ec) s->pcie_speed_max = tf; + free(tmp); + } + tmp = _sysfs_bus_pci(dom, bus, dev, func, "current_link_speed"); + if (tmp) { + ec = sscanf(tmp, "%f GT/s", &tf); + if (ec) s->pcie_speed_curr = tf; + free(tmp); + } + tmp = _sysfs_bus_pci(dom, bus, dev, func, "max_link_width"); + if (tmp) { + s->pcie_width_max = strtoul(tmp, NULL, 0); + free(tmp); + } + tmp = _sysfs_bus_pci(dom, bus, dev, func, "max_link_width"); + if (tmp) { + s->pcie_width_curr = strtoul(tmp, NULL, 0); + free(tmp); + } + return TRUE; +} + +static gboolean pci_get_device_lspci(uint32_t dom, uint32_t bus, uint32_t dev, uint32_t func, pcid *s) { + if (nolspci) return FALSE; + gboolean spawned; + gchar *out, *err, *p, *l, *next_nl; + gchar *pci_loc = pci_address_str(dom, bus, dev, func); + gchar *lspci_cmd = g_strdup_printf("lspci -D -s %s -vmmnn", pci_loc); + + s->domain = dom; + s->bus = bus; + s->device = dev; + s->function = func; + + spawned = hardinfo_spawn_command_line_sync(lspci_cmd, + &out, &err, NULL, NULL); + g_free(lspci_cmd); + if (spawned) { + p = out; + while(next_nl = strchr(p, '\n')) { + strend(p, '\n'); + g_strstrip(p); + if (l = lspci_line_value(p, "Slot")) { + s->slot_str = g_strdup(l); + if (strcmp(s->slot_str, pci_loc) != 0) { + printf("PCI: %s != %s\n", s->slot_str, pci_loc); + } + } + if (l = lspci_line_value(p, "Rev")) { + s->revision = strtol(l, NULL, 16); + } + lspci_line_string_and_code(p, "Class", &s->class_str, &s->class); + lspci_line_string_and_code(p, "Vendor", &s->vendor_id_str, &s->vendor_id); + lspci_line_string_and_code(p, "Device", &s->device_id_str, &s->device_id); + lspci_line_string_and_code(p, "SVendor", &s->sub_vendor_id_str, &s->sub_vendor_id); + lspci_line_string_and_code(p, "SDevice", &s->sub_device_id_str, &s->sub_device_id); + + p = next_nl + 1; + } + g_free(out); + g_free(err); + g_free(pci_loc); + return TRUE; + } + g_free(pci_loc); + return FALSE; +} + +pcid *pci_get_device_str(const char *addy) { + uint32_t dom, bus, dev, func; + int ec; + if (addy) { + ec = sscanf(addy, "%x:%x:%x.%x", &dom, &bus, &dev, &func); + if (ec == 4) { + return pci_get_device(dom, bus, dev, func); + } + } + return NULL; +} + +pcid *pci_get_device(uint32_t dom, uint32_t bus, uint32_t dev, uint32_t func) { + pcid *s = pcid_new(); + gboolean ok = FALSE; + if (s) { + ok = pci_get_device_sysfs(dom, bus, dev, func, s); + if (ok) { + ok |= pci_lookup_ids(s); + if (!ok) + ok |= pci_get_device_lspci(dom, bus, dev, func, s); + } + if (!ok) { + pcid_free(s); + s = NULL; + } + } + return s; +} + +static pcid_list pci_get_device_list_lspci(uint32_t class_min, uint32_t class_max) { + if (nolspci) return NULL; + gboolean spawned; + gchar *out, *err, *p, *next_nl; + pcid_list dl = NULL; + pcid *nd; + uint32_t dom, bus, dev, func, cls; + int ec; + + if (class_max == 0) class_max = 0xffff; + + spawned = hardinfo_spawn_command_line_sync("lspci -D -mn", + &out, &err, NULL, NULL); + if (spawned) { + p = out; + while(next_nl = strchr(p, '\n')) { + strend(p, '\n'); + ec = sscanf(p, "%x:%x:%x.%x \"%x\"", &dom, &bus, &dev, &func, &cls); + if (ec == 5) { + if (cls >= class_min && cls <= class_max) { + nd = pci_get_device(dom, bus, dev, func); + pci_fill_details(nd); + dl = g_slist_append(dl, nd); + } + } + p = next_nl + 1; + } + g_free(out); + g_free(err); + } + return dl; +} + +static pcid_list pci_get_device_list_sysfs(uint32_t class_min, uint32_t class_max) { + pcid_list dl = NULL; + pcid *nd; + uint32_t dom, bus, dev, func, cls; + int ec; + + if (class_max == 0) class_max = 0xffff; + + const gchar *f = NULL; + GDir *d = g_dir_open("/sys/bus/pci/devices", 0, NULL); + if (!d) return 0; + + while((f = g_dir_read_name(d))) { + ec = sscanf(f, "%x:%x:%x.%x", &dom, &bus, &dev, &func); + if (ec == 4) { + gchar *cf = g_strdup_printf("/sys/bus/pci/devices/%s/class", f); + gchar *cstr = NULL; + if (g_file_get_contents(cf, &cstr, NULL, NULL) ) { + cls = strtoul(cstr, NULL, 16) >> 8; + if (cls >= class_min && cls <= class_max) { + nd = pci_get_device(dom, bus, dev, func); + pci_fill_details(nd); + dl = g_slist_append(dl, nd); + } + } + g_free(cstr); + g_free(cf); + } + } + g_dir_close(d); + return dl; +} + +pcid_list pci_get_device_list(uint32_t class_min, uint32_t class_max) { + pcid_list dl = NULL; + dl = pci_get_device_list_sysfs(class_min, class_max); + if (!dl) + dl = pci_get_device_list_lspci(class_min, class_max); + return dl; +} + diff --git a/hardinfo2/problem_marker.c b/hardinfo2/problem_marker.c new file mode 100644 index 00000000..1e0c2aed --- /dev/null +++ b/hardinfo2/problem_marker.c @@ -0,0 +1,13 @@ + +#include "hardinfo.h" + +/* requires COMPILE_FLAGS "-std=c99" */ + +const char *problem_marker() { + static const char as_markup[] = "<big><b>\u26A0</b></big>"; + static const char as_text[] = "(!)"; + if (params.markup_ok) + return as_markup; + else + return as_text; +} diff --git a/hardinfo2/socket.c b/hardinfo2/socket.c new file mode 100644 index 00000000..b94e43c2 --- /dev/null +++ b/hardinfo2/socket.c @@ -0,0 +1,127 @@ +/* + * HardInfo - Displays System Information + * Copyright (C) 2003-2007 L. A. F. Pereira <l@tia.mat.br> + * + * This program 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, version 2 or later. + * + * 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, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include <stdio.h> +#include <string.h> +#include <arpa/inet.h> +#include <errno.h> +#include <netdb.h> +#include <netinet/in.h> +#include <sys/socket.h> +#include <sys/types.h> +#include <unistd.h> + +#include <glib.h> + +#include "socket.h" + +Socket *sock_connect(gchar * host, gint port) +{ + struct sockaddr_in server; + Socket *s; + int sock; + + sock = socket(AF_INET, SOCK_STREAM, 0); + if (sock > 0) { + memset(&server, 0, sizeof(server)); + server.sin_family = AF_INET; + server.sin_addr.s_addr = inet_addr(host); + server.sin_port = htons(port); + + if (connect(sock, (struct sockaddr *) (void *) &server, sizeof(server)) < 0) { + goto cleanup; + } + + s = g_new0(Socket, 1); + s->sock = sock; + + return s; + } + +cleanup: + close(sock); + + return NULL; +} + +/* From: http://www.erlenstar.demon.co.uk/unix/faq_3.html#SEC26 */ +static inline int __sock_is_ready(Socket * s, int mode) +{ + int rc, fd = s->sock; + fd_set fds; + struct timeval tv; + + FD_ZERO(&fds); + FD_SET(fd, &fds); + tv.tv_sec = tv.tv_usec = 0; + + if (mode == 0) { + /* read */ + rc = select(fd + 1, &fds, NULL, NULL, &tv); + } else { + /* write */ + rc = select(fd + 1, NULL, &fds, NULL, &tv); + } + + if (rc < 0) + return -1; + + return FD_ISSET(fd, &fds) ? 1 : 0; +} + +int sock_ready_to_read(Socket * s) +{ + return __sock_is_ready(s, 0); +} + +int sock_ready_to_write(Socket * s) +{ + return __sock_is_ready(s, 1); +} + +int sock_read(Socket * s, gchar * buffer, gint size) +{ + if (size > 2 && sock_ready_to_read(s)) { + gint n; + + n = read(s->sock, buffer, size - 1); + if (n > 0) { + buffer[n] = '\0'; + } else { + return 0; + } + + return n; + } + + return 0; +} + +int sock_write(Socket * s, gchar * str) +{ + while (!sock_ready_to_write(s)); + + return write(s->sock, str, strlen(str)); +} + +void sock_close(Socket * s) +{ + shutdown(s->sock, 2); + close(s->sock); + g_free(s); +} diff --git a/hardinfo2/storage_util.c b/hardinfo2/storage_util.c new file mode 100644 index 00000000..e7006069 --- /dev/null +++ b/hardinfo2/storage_util.c @@ -0,0 +1,250 @@ +#include "udisks2_util.h" +#include "storage_util.h" +#include "util_ids.h" +#include "hardinfo.h" + +gchar *sdcard_ids_file = NULL; +gchar *oui_ids_file = NULL; + +// moved from udisks2_util.h +void find_sdcard_ids_file() { + if (sdcard_ids_file) return; + char *file_search_order[] = { + g_build_filename(g_get_user_config_dir(), "hardinfo2", "sdcard.ids", NULL), + g_build_filename(params.path_data, "sdcard.ids", NULL), + NULL + }; + int n; + for(n = 0; file_search_order[n]; n++) { + if (!sdcard_ids_file && !access(file_search_order[n], R_OK)) + sdcard_ids_file = file_search_order[n]; + else + g_free(file_search_order[n]); + } +} + +void find_oui_ids_file() { + if (oui_ids_file) return; + char *file_search_order[] = { + g_build_filename(g_get_user_config_dir(), "hardinfo2", "ieee_oui.ids", NULL), + g_build_filename(params.path_data, "ieee_oui.ids", NULL), + NULL + }; + int n; + for(n = 0; file_search_order[n]; n++) { + if (!oui_ids_file && !access(file_search_order[n], R_OK)) + oui_ids_file = file_search_order[n]; + else + g_free(file_search_order[n]); + } +} + +gchar* get_oui_from_wwid(gchar* wwid){ + gchar* ret = NULL; + + if (g_str_has_prefix(wwid, "nna.")) { + if (strlen(wwid)*4 < 48) + return NULL; + + switch (wwid[4]){ + case '1': + case '2': + ret = g_strndup(wwid + 8, 6); + break; + case '5': + case '6': + ret = g_strndup(wwid + 5, 6); + break; + } + } + else if(g_str_has_prefix(wwid, "eui.")) { + if (strlen(wwid)*4 < 48) + return NULL; + ret = g_strndup(wwid+4, 6); + } + + return ret; +} + +gchar* get_oui_company(gchar* oui){ + ids_query_result result = {}; + + if (!oui_ids_file) + find_oui_ids_file(); + + scan_ids_file(oui_ids_file, oui, &result, -1); + if (result.results[0]) + return g_strdup(result.results[0]); + + return NULL; +} + +// moved from udisks2_util.h +void check_sdcard_vendor(u2driveext *e) { + if (!e || !e->d) return; + if (!e->d->media) return; + if (! (g_str_has_prefix(e->d->media, "flash_sd") + || g_str_has_prefix(e->d->media, "flash_mmc") )) return; + if (e->d->vendor && e->d->vendor[0]) return; + if (!e->d->block_dev) return; + + if (!sdcard_ids_file) + find_sdcard_ids_file(); + + gchar *qpath = NULL; + ids_query_result result = {}; + + gchar *oemid_path = g_strdup_printf("/sys/block/%s/device/oemid", e->d->block_dev); + gchar *manfid_path = g_strdup_printf("/sys/block/%s/device/manfid", e->d->block_dev); + gchar *oemid = NULL, *manfid = NULL; + g_file_get_contents(oemid_path, &oemid, NULL, NULL); + g_file_get_contents(manfid_path, &manfid, NULL, NULL); + + unsigned int id = oemid?strtol(oemid, NULL, 16):0; + char c2 = id & 0xff, c1 = (id >> 8) & 0xff; + + qpath = g_strdup_printf("OEMID %02x%02x", (unsigned int)c1, (unsigned int)c2); + scan_ids_file(sdcard_ids_file, qpath, &result, -1); + g_free(oemid); + if (result.results[0]) + oemid = g_strdup(result.results[0]); + else + oemid = g_strdup_printf("OEM %02x%02x \"%c%c\"", + (unsigned int)c1, (unsigned int)c2, + isprint(c1) ? c1 : '.', isprint(c2) ? c2 : '.'); + g_free(qpath); + + id = manfid?strtol(manfid, NULL, 16):0; + qpath = g_strdup_printf("MANFID %06x", id); + scan_ids_file(sdcard_ids_file, qpath, &result, -1); + g_free(manfid); + if (result.results[0]) + manfid = g_strdup(result.results[0]); + else + manfid = g_strdup_printf("MANF %06x", id); + g_free(qpath); + + vendor_list vl = NULL; + const Vendor *v = NULL; + v = vendor_match(oemid, NULL); + if (v) vl = vendor_list_append(vl, v); + v = vendor_match(manfid, NULL); + if (v) vl = vendor_list_append(vl, v); + vl = vendor_list_remove_duplicates_deep(vl); + e->vendors = vendor_list_concat(e->vendors, vl); + + g_free(e->d->vendor); + if (g_strcmp0(oemid, manfid) == 0) + e->d->vendor = g_strdup(oemid); + else + e->d->vendor = g_strdup_printf("%s / %s", oemid, manfid); + + g_free(oemid); + g_free(manfid); + g_free(oemid_path); + g_free(manfid_path); + + if (e->d->revision && e->d->revision[0]) return; + + /* bonus: revision */ + gchar *fwrev_path = g_strdup_printf("/sys/block/%s/device/fwrev", e->d->block_dev); + gchar *hwrev_path = g_strdup_printf("/sys/block/%s/device/hwrev", e->d->block_dev); + gchar *fwrev = NULL, *hwrev = NULL; + g_file_get_contents(fwrev_path, &fwrev, NULL, NULL); + g_file_get_contents(hwrev_path, &hwrev, NULL, NULL); + + unsigned int fw = fwrev ? strtol(fwrev, NULL, 16) : 0; + unsigned int hw = hwrev ? strtol(hwrev, NULL, 16) : 0; + g_free(e->d->revision); + e->d->revision = g_strdup_printf("%02x.%02x", hw, fw); + + g_free(fwrev); + g_free(hwrev); + g_free(fwrev_path); + g_free(hwrev_path); + +} + +void set_nvme_controller_info(u2driveext *e){ + gchar *path = g_strdup_printf("/sys/block/%s/device/device", e->d->block_dev); + gchar *systarget = g_file_read_link(path, NULL); + gchar *target = systarget ? g_filename_to_utf8(systarget, -1, NULL, NULL, NULL) : NULL; + gchar *pci_addy = target ? g_path_get_basename(target) : NULL; + e->nvme_controller = pci_addy ? pci_get_device_str(pci_addy) : NULL; + g_free(path); + g_free(systarget); + g_free(target); + g_free(pci_addy); + if (e->nvme_controller) { + e->vendors = vendor_list_append(e->vendors, + vendor_match(e->nvme_controller->vendor_id_str, NULL)); + e->vendors = vendor_list_append(e->vendors, + vendor_match(e->nvme_controller->sub_vendor_id_str, NULL)); + } +} + +GSList* get_udisks2_drives_ext(void){ + GSList *node, *list; + u2driveext* extdrive; + + list = get_udisks2_all_drives_info(); + + for (node = list; node != NULL; node = node->next) { + extdrive = u2drive_ext((udiskd *)node->data); + node->data = extdrive; + + if (!extdrive->d->vendor || !extdrive->d->vendor[0]) { + // sometimes vendors adds their name to the model field + const Vendor *v = vendor_match(extdrive->d->model, NULL); + if (v) + extdrive->d->vendor = g_strdup(v->name); + } + + check_sdcard_vendor(extdrive); + + extdrive->vendors = vendor_list_append(extdrive->vendors, vendor_match(extdrive->d->vendor, NULL)); + + // get OUI from WWID + if (extdrive->d->wwid) { + extdrive->wwid_oui.oui = get_oui_from_wwid(extdrive->d->wwid); + if (extdrive->wwid_oui.oui) { + extdrive->wwid_oui.vendor = get_oui_company(extdrive->wwid_oui.oui); + } + if (extdrive->wwid_oui.vendor){ + extdrive->vendors = vendor_list_append(extdrive->vendors, vendor_match(extdrive->wwid_oui.vendor, NULL)); + } + } + + // NVMe PCI device + if (strstr(extdrive->d->block_dev, "nvme")) { + set_nvme_controller_info(extdrive); + } + + extdrive->vendors = gg_slist_remove_null(extdrive->vendors); + extdrive->vendors = vendor_list_remove_duplicates_deep(extdrive->vendors); + + } + return list; +} + + +u2driveext* u2drive_ext(udiskd * udisks_drive_data) { + u2driveext* data = g_new0(u2driveext, 1); + data->d = udisks_drive_data; + return data; +} + +void u2driveext_free(u2driveext *u) { + if (u) { + udiskd_free(u->d); + g_free(u->wwid_oui.oui); + g_free(u->wwid_oui.vendor); + pcid_free(u->nvme_controller); + g_free(u); + } +} + +void storage_shutdown(){ + g_free(sdcard_ids_file); + g_free(oui_ids_file); +} diff --git a/hardinfo2/udisks2_util.c b/hardinfo2/udisks2_util.c new file mode 100644 index 00000000..e1567d84 --- /dev/null +++ b/hardinfo2/udisks2_util.c @@ -0,0 +1,641 @@ +#include <gio/gio.h> +#include <string.h> +#include "udisks2_util.h" + +#define UDISKS2_INTERFACE "org.freedesktop.UDisks2" +#define UDISKS2_MANAGER_INTERFACE "org.freedesktop.UDisks2.Manager" +#define UDISKS2_BLOCK_INTERFACE "org.freedesktop.UDisks2.Block" +#define UDISKS2_LOOP_INTERFACE "org.freedesktop.UDisks2.Loop" +#define UDISKS2_PARTITION_INTERFACE "org.freedesktop.UDisks2.Partition" +#define UDISKS2_PART_TABLE_INTERFACE "org.freedesktop.UDisks2.PartitionTable" +#define UDISKS2_DRIVE_INTERFACE "org.freedesktop.UDisks2.Drive" +#define UDISKS2_DRIVE_ATA_INTERFACE "org.freedesktop.UDisks2.Drive.Ata" +#define DBUS_PROPERTIES_INTERFACE "org.freedesktop.DBus.Properties" +#define UDISKS2_MANAGER_OBJ_PATH "/org/freedesktop/UDisks2/Manager" +#define UDISKS2_BLOCK_DEVICES_PATH "/org/freedesktop/UDisks2/block_devices" + +#define STRDUP_IF_NOT_EMPTY(S) (g_strcmp0(S, "") == 0) ? NULL: g_strdup(S); + +GDBusConnection* udisks2_conn = NULL; + +GVariant* get_dbus_property(GDBusProxy* proxy, const gchar *interface, + const gchar *property) { + GVariant *result, *v = NULL; + GError *error = NULL; + + result = g_dbus_proxy_call_sync(proxy, "Get", + g_variant_new ("(ss)", interface, property), + G_DBUS_CALL_FLAGS_NONE, -1, NULL, &error); + + if (error != NULL) { + g_error_free (error); + return NULL; + } + + g_variant_get(result, "(v)", &v); + g_variant_unref(result); + return v; +} + +// this function works with udisks2 version 2.7.2 or newer +GSList* get_block_dev_paths_from_udisks2(GDBusConnection* conn){ + GDBusProxy *proxy; + GVariant *options, *v; + GVariantIter *iter; + GError *error = NULL; + GSList *block_paths = NULL; + const gchar *block_path = NULL; + + proxy = g_dbus_proxy_new_sync(conn, G_DBUS_PROXY_FLAGS_NONE, NULL, + UDISKS2_INTERFACE, UDISKS2_MANAGER_OBJ_PATH, + UDISKS2_MANAGER_INTERFACE, NULL, &error); + options = g_variant_new_parsed("@a{sv} { %s: <true> }", + "auth.no_user_interaction"); + if (error != NULL){ + g_error_free (error); + g_object_unref(proxy); + return NULL; + } + + v = g_dbus_proxy_call_sync(proxy, "GetBlockDevices", + g_variant_new_tuple(&options, 1), + G_DBUS_CALL_FLAGS_NONE, -1, NULL, &error); + g_object_unref(proxy); + + if (error != NULL){ + g_error_free (error); + return NULL; + } + + g_variant_get(v, "(ao)", &iter); + while (g_variant_iter_loop (iter, "o", &block_path)){ + block_paths = g_slist_append(block_paths, g_strdup(block_path)); + } + + g_variant_iter_free (iter); + g_variant_unref(v); + return block_paths; +} + +GSList* get_block_dev_paths_from_sysfs(){ + GSList *block_paths = NULL; + GDir *dir; + gchar *path; + const gchar *entry; + + dir = g_dir_open("/sys/class/block", 0, NULL); + if (!dir) + return NULL; + + while ((entry = g_dir_read_name(dir))) { + path = g_strdup_printf("%s/%s", UDISKS2_BLOCK_DEVICES_PATH, entry); + block_paths = g_slist_append(block_paths, path); + } + + g_dir_close(dir); + return block_paths; +} + +GSList* udisks2_drives_func_caller(GDBusConnection* conn, + gpointer (*func)(const char*, GDBusProxy*, + GDBusProxy*, const char*)) { + GDBusProxy *block_proxy, *drive_proxy; + GVariant *block_v, *v; + GSList *result_list = NULL, *block_dev_list, *node; + GError *error = NULL; + gpointer output; + + gchar *block_path = NULL; + const gchar *block_dev, *drive_path = NULL; + + if (conn == NULL) + return NULL; + + // get block devices + block_dev_list = get_block_dev_paths_from_udisks2(conn); + if (block_dev_list == NULL) + block_dev_list = get_block_dev_paths_from_sysfs(); + + for (node = block_dev_list; node != NULL; node = node->next) { + block_path = (gchar *)node->data; + block_proxy = g_dbus_proxy_new_sync(conn, G_DBUS_PROXY_FLAGS_NONE, + NULL, UDISKS2_INTERFACE, block_path, + DBUS_PROPERTIES_INTERFACE, NULL, &error); + if (error){ + g_error_free(error); + error = NULL; + continue; + } + + // Skip partitions + v = get_dbus_property(block_proxy, UDISKS2_PARTITION_INTERFACE, "Size"); + if (v){ + g_variant_unref(v); + g_object_unref(block_proxy); + continue; + } + + // Skip loop devices + v = get_dbus_property(block_proxy, UDISKS2_LOOP_INTERFACE, "BackingFile"); + if (v){ + g_variant_unref(v); + g_object_unref(block_proxy); + continue; + } + + block_dev = block_path + strlen(UDISKS2_BLOCK_DEVICES_PATH) + 1; + drive_path = NULL; + + // let's find drive proxy + v = get_dbus_property(block_proxy, UDISKS2_BLOCK_INTERFACE, "Drive"); + if (v){ + drive_path = g_variant_get_string(v, NULL); + + if (g_strcmp0(drive_path, "/") != 0){ + drive_proxy = g_dbus_proxy_new_sync(conn, + G_DBUS_PROXY_FLAGS_NONE, NULL, + UDISKS2_INTERFACE, drive_path, + DBUS_PROPERTIES_INTERFACE, NULL, &error); + + if (error == NULL) { + // call requested function + output = func(block_dev, block_proxy, drive_proxy, drive_path); + + if (output != NULL){ + result_list = g_slist_append(result_list, output); + } + g_object_unref(drive_proxy); + } + else { + g_error_free(error); + error = NULL; + } + } + g_variant_unref(v); + } + g_object_unref(block_proxy); + } + g_slist_free_full(block_dev_list, g_free); + + return result_list; +} + +GDBusConnection* get_udisks2_connection(void) { + GDBusConnection *conn; + GDBusProxy *proxy; + GError *error = NULL; + GVariant *result = NULL; + + // connection to system bus + conn = g_bus_get_sync(G_BUS_TYPE_SYSTEM, NULL, &error); + if (error != NULL) { + g_error_free (error); + return NULL; + } + + // let's check if udisks2 is responding + proxy = g_dbus_proxy_new_sync(conn, G_DBUS_PROXY_FLAGS_NONE, NULL, + UDISKS2_INTERFACE, UDISKS2_MANAGER_OBJ_PATH, + DBUS_PROPERTIES_INTERFACE, NULL, &error); + if (error != NULL) { + g_error_free (error); + g_object_unref(conn); + return NULL; + } + + result = get_dbus_property(proxy, UDISKS2_MANAGER_INTERFACE, "Version"); + g_object_unref(proxy); + if (error != NULL) { + g_error_free (error); + return NULL; + } + + // OK, let's return connection to system bus + g_variant_unref(result); + return conn; +} + +udiskt* udiskt_new() { + return g_new0(udiskt, 1); +} + +udisksa* udisksa_new() { + return g_new0(udisksa, 1); +} + +udiskp* udiskp_new() { + return g_new0(udiskp, 1); +} + +udiskd* udiskd_new() { + return g_new0(udiskd, 1); +} + + +void udiskt_free(udiskt *u) { + if (u) { + g_free(u->drive); + g_free(u); + } +} + +void udisksa_free(udisksa *u) { + if (u) { + g_free(u->identifier); + udisksa_free(u->next); + g_free(u); + } +} + +void udiskp_free(udiskp *u) { + if (u) { + g_free(u->block); + g_free(u->type); + g_free(u->version); + g_free(u->label); + udiskp_free(u->next); + g_free(u); + } +} + +void udiskd_free(udiskd *u) { + if (u) { + g_free(u->model); + g_free(u->vendor); + g_free(u->revision); + g_free(u->block_dev); + g_free(u->serial); + g_free(u->wwid); + g_free(u->connection_bus); + g_free(u->partition_table); + udiskp_free(u->partitions); + udisksa_free(u->smart_attributes); + g_free(u->media); + g_strfreev(u->media_compatibility); + g_free(u); + } +} + + +udiskp* get_udisks2_partition_info(const gchar *part_path) { + GVariant *v; + GDBusProxy *proxy=NULL; + GError *error = NULL; + udiskp* partition; + const gchar *str; + + if (!g_str_has_prefix(part_path, UDISKS2_BLOCK_DEVICES_PATH)) { + return NULL; + } + + partition = udiskp_new(); + partition->block = g_strdup(part_path + strlen(UDISKS2_BLOCK_DEVICES_PATH) + 1); + + proxy = g_dbus_proxy_new_sync(udisks2_conn, G_DBUS_PROXY_FLAGS_NONE, + NULL, UDISKS2_INTERFACE, part_path, + DBUS_PROPERTIES_INTERFACE, NULL, &error); + + if ((proxy != NULL) && (error == NULL)) { + v = get_dbus_property(proxy, UDISKS2_BLOCK_INTERFACE, "IdLabel"); + if (v) { + str = g_variant_get_string(v, NULL); + partition->label = STRDUP_IF_NOT_EMPTY(str); + g_variant_unref(v); + } + v = get_dbus_property(proxy, UDISKS2_BLOCK_INTERFACE, "IdType"); + if (v) { + str = g_variant_dup_string(v, NULL); + partition->type = STRDUP_IF_NOT_EMPTY(str); + g_variant_unref(v); + } + v = get_dbus_property(proxy, UDISKS2_BLOCK_INTERFACE, "IdVersion"); + if (v) { + str = g_variant_dup_string(v, NULL); + partition->version = STRDUP_IF_NOT_EMPTY(str); + g_variant_unref(v); + } + v = get_dbus_property(proxy, UDISKS2_BLOCK_INTERFACE, "Size"); + if (v) { + partition->size = g_variant_get_uint64(v); + g_variant_unref(v); + } + } + else{ + g_error_free(error); + } + + g_object_unref(proxy); + return partition; +} + +gpointer get_udisks2_temp(const char *blockdev, GDBusProxy *block, + GDBusProxy *drive, const char *drivepath){ + GVariant *v; + gboolean smart_enabled = FALSE; + udiskt* disk_temp = NULL; + + v = get_dbus_property(drive, UDISKS2_DRIVE_ATA_INTERFACE, "SmartEnabled"); + if (v) { + smart_enabled = g_variant_get_boolean(v); + g_variant_unref(v); + } + + if (!smart_enabled) { + return NULL; + } + + v = get_dbus_property(drive, UDISKS2_DRIVE_ATA_INTERFACE, "SmartTemperature"); + if (v) { + disk_temp = udiskt_new(); + disk_temp->temperature = (gint32) (g_variant_get_double(v) - 273.15); + g_variant_unref(v); + } + + if (!disk_temp) { + return NULL; + } + + v = get_dbus_property(drive, UDISKS2_DRIVE_INTERFACE, "Model"); + if (v) { + disk_temp->drive = g_variant_dup_string(v, NULL); + g_variant_unref(v); + } + + return disk_temp; +} + +gchar* get_udisks2_smart_attributes(udiskd* dsk, const char *drivepath){ + GDBusProxy *proxy; + GVariant *options, *v, *v2; + GVariantIter *iter; + GError *error = NULL; + const char* aidenf; + guint8 aid; + gint32 avalue, aworst, athreshold, pretty_unit; + gint64 pretty; + udisksa *lastp = NULL, *p; + + proxy = g_dbus_proxy_new_sync(udisks2_conn, G_DBUS_PROXY_FLAGS_NONE, NULL, + UDISKS2_INTERFACE, drivepath, + UDISKS2_DRIVE_ATA_INTERFACE, NULL, &error); + + options = g_variant_new_parsed("@a{sv} { %s: <true> }", + "auth.no_user_interaction"); + if (error != NULL){ + g_error_free (error); + return NULL; + } + + v = g_dbus_proxy_call_sync(proxy, "SmartGetAttributes", + g_variant_new_tuple(&options, 1), + G_DBUS_CALL_FLAGS_NONE, -1, NULL, &error); + g_object_unref(proxy); + + if (error != NULL){ + g_error_free (error); + g_object_unref(proxy); + return NULL; + } + + v2 = g_variant_get_child_value(v, 0); + iter = g_variant_iter_new(v2); + + // id(y), identifier(s), flags(q), value(i), worst(i), threshold(i), + // pretty(x), pretty_unit(i), expansion(a{sv}) + // pretty_unit = 0 (unknown), 1 (dimensionless), 2 (milliseconds), 3 (sectors), 4 (millikelvin). + while (g_variant_iter_loop (iter, "(ysqiiixia{sv})", &aid, &aidenf, NULL, &avalue, + &aworst, &athreshold, &pretty, &pretty_unit, NULL)){ + p = udisksa_new(); + p->id = aid; + p->identifier = g_strdup(aidenf); + p->value = avalue; + p->worst = aworst; + p->threshold = athreshold; + switch (pretty_unit){ + case 1: + p->interpreted_unit = UDSK_INTPVAL_DIMENSIONLESS; + p->interpreted = pretty; + break; + case 2: + if (pretty > 1000*60*60){ // > 1h + p->interpreted_unit = UDSK_INTPVAL_HOURS; + p->interpreted = pretty / (1000*60*60); + } + else{ + p->interpreted_unit = UDSK_INTPVAL_MILISECONDS; + p->interpreted = pretty; + } + break; + case 3: + p->interpreted_unit = UDSK_INTPVAL_SECTORS; + p->interpreted = pretty; + break; + case 4: + p->interpreted_unit = UDSK_INTPVAL_CELSIUS; + p->interpreted = (pretty - 273150) / 1000; //mK to °C + break; + default: + p->interpreted_unit = UDSK_INTPVAL_SKIP; + p->interpreted = -1; + break; + } + p->next = NULL; + + if (lastp == NULL) + dsk->smart_attributes = p; + else + lastp->next = p; + + lastp = p; + } + g_variant_iter_free (iter); + g_variant_unref(v2); + g_variant_unref(v); + + return NULL; +} + +gpointer get_udisks2_drive_info(const char *blockdev, GDBusProxy *block, + GDBusProxy *drive, const char *drivepath) { + GVariant *v; + GVariantIter *iter; + const gchar *str, *part; + udiskd *u = NULL; + udiskp **p = NULL; + gsize n, i; + u = udiskd_new(); + u->block_dev = g_strdup(blockdev); + + v = get_dbus_property(drive, UDISKS2_DRIVE_INTERFACE, "Model"); + if (v){ + u->model = g_variant_dup_string(v, NULL); + g_variant_unref(v); + } + v = get_dbus_property(drive, UDISKS2_DRIVE_INTERFACE, "Vendor"); + if (v){ + u->vendor = g_variant_dup_string(v, NULL); + g_variant_unref(v); + } + v = get_dbus_property(drive, UDISKS2_DRIVE_INTERFACE, "Revision"); + if (v){ + u->revision = g_variant_dup_string(v, NULL); + g_variant_unref(v); + } + v = get_dbus_property(drive, UDISKS2_DRIVE_INTERFACE, "Serial"); + if (v){ + u->serial = g_variant_dup_string(v, NULL); + g_variant_unref(v); + } + v = get_dbus_property(drive, UDISKS2_DRIVE_INTERFACE, "WWN"); + if (v){ + str = g_variant_get_string(v, NULL); + if (g_str_has_prefix(str, "0x")) { + u->wwid = g_strdup_printf("nna.%s", str+2); + } + else if (g_str_has_prefix(str, "nna.") || g_str_has_prefix(str, "eui.")) { + u->wwid = g_strdup(str); + } + g_variant_unref(v); + } + v = get_dbus_property(drive, UDISKS2_DRIVE_INTERFACE, "ConnectionBus"); + if (v){ + u->connection_bus = g_variant_dup_string(v, NULL); + g_variant_unref(v); + } + v = get_dbus_property(drive, UDISKS2_DRIVE_INTERFACE, "RotationRate"); + if (v){ + u->rotation_rate = g_variant_get_int32(v); + g_variant_unref(v); + } + v = get_dbus_property(drive, UDISKS2_DRIVE_INTERFACE, "Size"); + if (v){ + u->size = g_variant_get_uint64(v); + g_variant_unref(v); + } + v = get_dbus_property(drive, UDISKS2_DRIVE_INTERFACE, "Media"); + if (v){ + str = g_variant_get_string(v, NULL); + if (strcmp(str, "") != 0) { + u->media = g_strdup(str); + } + g_variant_unref(v); + } + v = get_dbus_property(drive, UDISKS2_DRIVE_INTERFACE, "MediaCompatibility"); + if (v){ + g_variant_get(v, "as", &iter); + n = g_variant_iter_n_children(iter); + u->media_compatibility = g_malloc0_n(n + 1, sizeof(gchar*)); + + i = 0; + while (g_variant_iter_loop (iter, "s", &str) && i < n){ + u->media_compatibility[i] = g_strdup(str); + i++; + } + + g_variant_iter_free (iter); + g_variant_unref(v); + } + v = get_dbus_property(drive, UDISKS2_DRIVE_INTERFACE, "Ejectable"); + if (v){ + u->ejectable = g_variant_get_boolean(v); + g_variant_unref(v); + } + v = get_dbus_property(drive, UDISKS2_DRIVE_INTERFACE, "Removable"); + if (v){ + u->removable = g_variant_get_boolean(v); + g_variant_unref(v); + } + v = get_dbus_property(drive, UDISKS2_DRIVE_ATA_INTERFACE, "PmSupported"); + if (v){ + u->pm_supported = g_variant_get_boolean(v); + g_variant_unref(v); + } + v = get_dbus_property(drive, UDISKS2_DRIVE_ATA_INTERFACE, "ApmSupported"); + if (v){ + u->apm_supported = g_variant_get_boolean(v); + g_variant_unref(v); + } + v = get_dbus_property(drive, UDISKS2_DRIVE_ATA_INTERFACE, "AamSupported"); + if (v){ + u->aam_supported = g_variant_get_boolean(v); + g_variant_unref(v); + } + v = get_dbus_property(drive, UDISKS2_DRIVE_ATA_INTERFACE, "SmartSupported"); + if (v){ + u->smart_supported = g_variant_get_boolean(v); + g_variant_unref(v); + } + v = get_dbus_property(drive, UDISKS2_DRIVE_ATA_INTERFACE, "SmartEnabled"); + if (v){ + u->smart_enabled = g_variant_get_boolean(v); + g_variant_unref(v); + } + if (u->smart_enabled){ + v = get_dbus_property(drive, UDISKS2_DRIVE_ATA_INTERFACE, "SmartPowerOnSeconds"); + if (v){ + u->smart_poweron = g_variant_get_uint64(v); + g_variant_unref(v); + } + v = get_dbus_property(drive, UDISKS2_DRIVE_ATA_INTERFACE, "SmartNumBadSectors"); + if (v){ + u->smart_bad_sectors = g_variant_get_int64(v); + g_variant_unref(v); + } + v = get_dbus_property(drive, UDISKS2_DRIVE_ATA_INTERFACE, "SmartTemperature"); + if (v){ + u->smart_temperature = (gint) (g_variant_get_double(v) - 273.15); + g_variant_unref(v); + } + v = get_dbus_property(drive, UDISKS2_DRIVE_ATA_INTERFACE, "SmartFailing"); + if (v){ + u->smart_failing = g_variant_get_boolean(v); + g_variant_unref(v); + } + get_udisks2_smart_attributes(u, drivepath); + } + + v = get_dbus_property(block, UDISKS2_PART_TABLE_INTERFACE, "Type"); + if (v){ + u->partition_table = g_variant_dup_string(v, NULL); + g_variant_unref(v); + } + // 'Partitions' property is available in udisks2 version 2.7.2 or newer + v = get_dbus_property(block, UDISKS2_PART_TABLE_INTERFACE, "Partitions"); + if (v){ + g_variant_get(v, "ao", &iter); + + p = &(u->partitions); + while (g_variant_iter_loop (iter, "o", &str)) { + *p = get_udisks2_partition_info(str); + if (*p != NULL){ + p = &((*p)->next); + } + } + + g_variant_iter_free (iter); + g_variant_unref(v); + } + + return u; +} + +GSList* get_udisks2_temps(void){ + return udisks2_drives_func_caller(udisks2_conn, get_udisks2_temp); +} + +GSList* get_udisks2_all_drives_info(void){ + return udisks2_drives_func_caller(udisks2_conn, get_udisks2_drive_info); +} + +void udisks2_init(){ + if (udisks2_conn == NULL){ + //FIXME udisks2_conn = get_udisks2_connection(); + } +} + +void udisks2_shutdown(){ + if (udisks2_conn != NULL){ + g_object_unref(udisks2_conn); + udisks2_conn = NULL; + } +} diff --git a/hardinfo2/usb_util.c b/hardinfo2/usb_util.c new file mode 100644 index 00000000..744f084f --- /dev/null +++ b/hardinfo2/usb_util.c @@ -0,0 +1,557 @@ +/* + * HardInfo - Displays System Information + * Copyright (C) 2003-2017 L. A. F. Pereira <l@tia.mat.br> + * This file + * Copyright (C) 2017 Burt P. <pburt0@gmail.com> + * Copyright (C) 2019 Ondrej ÄŒerman + * + * This program 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, version 2 or later. + * + * 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, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include "hardinfo.h" +#include "usb_util.h" +#include "util_ids.h" + +#define SYSFS_DIR_USB_DEVICES "/sys/bus/usb/devices" + +gchar *usb_ids_file = NULL; + +usbi *usbi_new() { + return g_new0(usbi, 1); +} + +void usbi_free(usbi *s) { + if (s) { + g_free(s->if_label); + g_free(s->if_class_str); + g_free(s->if_subclass_str); + g_free(s->if_protocol_str); + g_free(s->driver); + g_free(s); + } +} + +void usbi_list_free(usbi *s) { + usbi *n; + while(s != NULL) { + n = s->next; + usbi_free(s); + s = n; + } +} + +usbd *usbd_new() { + return g_new0(usbd, 1); +} + +void usbd_free(usbd *s) { + if (s) { + usbi_list_free(s->if_list); + vendor_list_free(s->vendors); + g_free(s->vendor); + g_free(s->product); + g_free(s->manufacturer); + g_free(s->device); + g_free(s->usb_version); + g_free(s->device_version); + g_free(s->serial); + g_free(s->dev_class_str); + g_free(s->dev_subclass_str); + g_free(s->dev_protocol_str); + g_free(s); + } +} + +void usbd_list_free(usbd *s) { + usbd *n; + while(s != NULL) { + n = s->next; + usbd_free(s); + s = n; + } +} + +/* returns number of items after append */ +static int usbd_list_append(usbd *l, usbd *n) { + int c = 0; + while(l != NULL) { + c++; + if (l->next == NULL) { + if (n != NULL) { + l->next = n; + c++; + } + break; + } + l = l->next; + } + return c; +} + +/* returns new top of the list */ +static usbd *usbd_list_append_sorted(usbd *l, usbd *n) { + usbd *b = NULL; + usbd *t = l; + + if (n == NULL) + return l; + + while(l != NULL) { + if ((n->bus > l->bus) || + ((n->bus == l->bus) && (n->dev >= l->dev))) { + if (l->next == NULL) { + l->next = n; + break; + } + b = l; + l = l->next; + } + else { + if (b == NULL) { + t = n; + n->next = l; + } + else { + b->next = n; + n->next = l; + } + break; + } + } + + return t; +} + +gboolean usbd_append_interface(usbd *dev, usbi *new_if){ + usbi *curr_if; + if (dev->if_list == NULL){ + dev->if_list = new_if; + return TRUE; + } + + if (dev->if_list->if_number == new_if->if_number) + return FALSE; + + curr_if = dev->if_list; + while(curr_if->next != NULL) { + curr_if = curr_if->next; + if (curr_if->if_number == new_if->if_number) + return FALSE; + } + curr_if->next = new_if; + return TRUE; +} + +int usbd_list_count(usbd *s) { + return usbd_list_append(s, NULL); +} + +static char *lsusb_line_value(char *line, const char *prefix) { + if (g_str_has_prefix(line, prefix)) { + line += strlen(prefix) + 1; + return g_strstrip(line); + } else + return NULL; +} + +static gboolean usb_get_device_lsusb(int bus, int dev, usbd *s) { + gboolean spawned; + gchar *out, *err, *p, *l, *t, *next_nl; + gchar *lsusb_cmd = g_strdup_printf("lsusb -s %d:%d -v", bus, dev); + usbi *curr_if = NULL; // do not free + + s->bus = bus; + s->dev = dev; + + spawned = hardinfo_spawn_command_line_sync(lsusb_cmd, + &out, &err, NULL, NULL); + g_free(lsusb_cmd); + if (spawned) { + if (strstr(err, "information will be missing")) { + s->user_scan = TRUE; + } + p = out; + while(next_nl = strchr(p, '\n')) { + strend(p, '\n'); + g_strstrip(p); + + // device info + if (l = lsusb_line_value(p, "idVendor")) { + s->vendor_id = strtol(l, NULL, 0); + if (t = strchr(l, ' ')) + s->vendor = g_strdup(t + 1); + } else if (l = lsusb_line_value(p, "idProduct")) { + s->product_id = strtol(l, NULL, 0); + if (t = strchr(l, ' ')) + s->product = g_strdup(t + 1); + } else if (l = lsusb_line_value(p, "MaxPower")) { + s->max_curr_ma = atoi(l); + } else if (l = lsusb_line_value(p, "bcdUSB")) { + s->usb_version = g_strdup(l); + } else if (l = lsusb_line_value(p, "bcdDevice")) { + s->device_version = g_strdup(l); + } else if (l = lsusb_line_value(p, "bDeviceClass")) { + s->dev_class = atoi(l); + if (t = strchr(l, ' ')) + s->dev_class_str = g_strdup(t + 1); + } else if (l = lsusb_line_value(p, "bDeviceSubClass")) { + s->dev_subclass = atoi(l); + if (t = strchr(l, ' ')) + s->dev_subclass_str = g_strdup(t + 1); + } else if (l = lsusb_line_value(p, "bDeviceProtocol")) { + s->dev_protocol = atoi(l); + if (t = strchr(l, ' ')) + s->dev_protocol_str = g_strdup(t + 1); + } else if (l = lsusb_line_value(p, "iManufacturer")) { + if (t = strchr(l, ' ')) + s->manufacturer = g_strdup(t + 1); + } else if (l = lsusb_line_value(p, "iProduct")) { + if (t = strchr(l, ' ')) + s->device = g_strdup(t + 1); + } else if (l = lsusb_line_value(p, "iSerial")) { + if (t = strchr(l, ' ')) + s->serial = g_strdup(t + 1); + + // interface info + } else if (l = lsusb_line_value(p, "bInterfaceNumber")) { + curr_if = usbi_new(); + curr_if->if_number = atoi(l); + + // This may parse same interface multiple times when there + // are multiple Configuration descriptors per device + // As a workaround usbd_append_interface appends interface + // with same if_number only once + if (!usbd_append_interface(s, curr_if)){ + usbi_free(curr_if); + curr_if = NULL; + } + } else if (l = lsusb_line_value(p, "bInterfaceClass")) { + if (curr_if != NULL){ + curr_if->if_class = atoi(l); + if (t = strchr(l, ' ')) + curr_if->if_class_str = g_strdup(t + 1); + } + } else if (l = lsusb_line_value(p, "bInterfaceSubClass")) { + if (curr_if != NULL){ + curr_if->if_subclass = atoi(l); + if (t = strchr(l, ' ')) + curr_if->if_subclass_str = g_strdup(t + 1); + } + } else if (l = lsusb_line_value(p, "bInterfaceProtocol")) { + if (curr_if != NULL){ + curr_if->if_protocol = atoi(l); + if (t = strchr(l, ' ')) + curr_if->if_protocol_str = g_strdup(t + 1); + } + } else if (l = lsusb_line_value(p, "iInterface")) { + if (curr_if != NULL){ + if (t = strchr(l, ' ')) + curr_if->if_label = g_strdup(t + 1); + } + } + + p = next_nl + 1; + } + g_free(out); + g_free(err); + return TRUE; + } + return FALSE; +} + +static gboolean usb_get_interface_sysfs(int conf, int number, + const char* devpath, usbi *intf){ + gchar *ifpath, *drvpath, *tmp; + ids_query_result result;// = {}; + + memset(&result,0,sizeof(ids_query_result)); + + ifpath = g_strdup_printf("%s:%d.%d", devpath, conf, number); + if (!g_file_test(ifpath, G_FILE_TEST_EXISTS)){ + return FALSE; + } + + tmp = g_strdup_printf("%s/driver", ifpath); + drvpath = g_file_read_link(tmp, NULL); + g_free(tmp); + if (drvpath){ + intf->driver = g_path_get_basename(drvpath); + g_free(drvpath); + } + else{ + intf->driver = g_strdup(_("(None)")); + } + + intf->if_number = number; + intf->if_class = h_sysfs_read_hex(ifpath, "bInterfaceClass"); + intf->if_subclass = h_sysfs_read_hex(ifpath, "bInterfaceSubClass"); + intf->if_protocol = h_sysfs_read_hex(ifpath, "bInterfaceProtocol"); + + if (intf->if_label == NULL) + intf->if_label = h_sysfs_read_string(ifpath, "interface"); + + if (intf->if_class_str == NULL && intf->if_subclass_str == NULL + && intf->if_protocol_str == NULL) { + tmp = g_strdup_printf("C %02x/%02x/%02x", intf->if_class, + intf->if_subclass, intf->if_protocol); + scan_ids_file(usb_ids_file, tmp, &result, -1); + if (result.results[0]) + intf->if_class_str = g_strdup(result.results[0]); + if (result.results[1]) + intf->if_subclass_str = g_strdup(result.results[1]); + if (result.results[2]) + intf->if_protocol_str = g_strdup(result.results[2]); + g_free(tmp); + } + + g_free(ifpath); + return TRUE; +} + +static void find_usb_ids_file() { + if (usb_ids_file) return; + char *file_search_order[] = { + g_build_filename(g_get_user_config_dir(), "hardinfo2", "usb.ids", NULL), + g_build_filename(params.path_data, "usb.ids", NULL), + NULL + }; + int n; + for(n = 0; file_search_order[n]; n++) { + if (!usb_ids_file && !access(file_search_order[n], R_OK)) + usb_ids_file = file_search_order[n]; + else + g_free(file_search_order[n]); + } +} + +void usb_lookup_ids_vendor_product_str(gint vendor_id, gint product_id, + gchar **vendor_str, gchar **product_str) { + ids_query_result result;// = {}; + gchar *qpath; + + memset(&result,0,sizeof(ids_query_result)); + if (!usb_ids_file) + find_usb_ids_file(); + if (!usb_ids_file) + return; + + qpath = g_strdup_printf("%04x/%04x", vendor_id, product_id); + scan_ids_file(usb_ids_file, qpath, &result, -1); + if (result.results[0]) + *vendor_str = g_strdup(result.results[0]); + if (result.results[1]) + *product_str = g_strdup(result.results[1]); + g_free(qpath); +} + +static gboolean usb_get_device_sysfs(int bus, int dev, const char* sysfspath, usbd *s) { + usbi *intf; + gboolean ok; + int i, if_count = 0, conf = 0, ver; + ids_query_result result;// = {}; + gchar *qpath; + + memset(&result,0,sizeof(ids_query_result)); + if (sysfspath == NULL) + return FALSE; + + if (!usb_ids_file) + find_usb_ids_file(); + + s->bus = bus; + s->dev = dev; + s->dev_class = h_sysfs_read_hex(sysfspath, "bDeviceClass"); + s->vendor_id = h_sysfs_read_hex(sysfspath, "idVendor"); + s->product_id = h_sysfs_read_hex(sysfspath, "idProduct"); + s->manufacturer = h_sysfs_read_string(sysfspath, "manufacturer"); + s->device = h_sysfs_read_string(sysfspath, "product"); + s->max_curr_ma = h_sysfs_read_int(sysfspath, "bMaxPower"); + s->dev_class = h_sysfs_read_hex(sysfspath, "bDeviceClass"); + s->dev_subclass = h_sysfs_read_hex(sysfspath, "bDeviceSubClass"); + s->dev_protocol = h_sysfs_read_hex(sysfspath, "bDeviceProtocol"); + s->speed_mbs = h_sysfs_read_int(sysfspath, "speed"); + + if (s->product == NULL && s->vendor == NULL) { + qpath = g_strdup_printf("%04x/%04x", s->vendor_id, s->product_id); + scan_ids_file(usb_ids_file, qpath, &result, -1); + if (result.results[0]) + s->vendor = g_strdup(result.results[0]); + if (result.results[1]) + s->product = g_strdup(result.results[1]); + g_free(qpath); + } + + if (s->dev_class_str == NULL && s->dev_subclass_str == NULL + && s->dev_protocol_str == NULL) { + qpath = g_strdup_printf("C %02x/%02x/%02x", s->dev_class, + s->dev_subclass, s->dev_protocol); + scan_ids_file(usb_ids_file, qpath, &result, -1); + if (result.results[0]) + s->dev_class_str = g_strdup(result.results[0]); + if (result.results[1]) + s->dev_subclass_str = g_strdup(result.results[1]); + if (result.results[2]) + s->dev_protocol_str = g_strdup(result.results[2]); + g_free(qpath); + } + + conf = h_sysfs_read_hex(sysfspath, "bConfigurationValue"); + + if (s->usb_version == NULL) + s->usb_version = h_sysfs_read_string(sysfspath, "version"); + + if (s->serial == NULL) + s->serial = h_sysfs_read_string(sysfspath, "serial"); + + if (s->device_version == NULL) { + ver = h_sysfs_read_int(sysfspath, "bcdDevice"); + if (ver > 0){ + s->device_version = g_strdup_printf("%d.%02d", ver/100, ver%100); + } + } + + if (s->if_list == NULL){ // create interfaces list + if_count = h_sysfs_read_int(sysfspath, "bNumInterfaces"); + for (i = 0; i <= if_count; i++){ + intf = usbi_new(); + ok = usb_get_interface_sysfs(conf, i, sysfspath, intf); + if (ok){ + if (!usbd_append_interface(s, intf)){ + usbi_free(intf); + } + } + else{ + usbi_free(intf); + } + } + } + else{ // improve existing list + intf = s->if_list; + while (intf){ + usb_get_interface_sysfs(conf, intf->if_number, sysfspath, intf); + intf = intf->next; + } + } + + return TRUE; +} + +usbd *usb_get_device(int bus, int dev, const gchar* sysfspath) { + usbd *s = usbd_new(); + int ok = 0; + if (s) { + /* try sysfs */ + ok = usb_get_device_sysfs(bus, dev, sysfspath, s); + if (!ok) { + /* try lsusb */ + ok = usb_get_device_lsusb(bus, dev, s); + } + if (!ok) { + usbd_free(s); + s = NULL; + } + } + return s; +} + +static usbd *usb_get_device_list_lsusb() { + gboolean spawned; + gchar *out, *err, *p, *next_nl; + usbd *head = NULL, *nd; + int bus, dev, vend, prod, ec; + + spawned = hardinfo_spawn_command_line_sync("lsusb", + &out, &err, NULL, NULL); + if (spawned) { + p = out; + while(next_nl = strchr(p, '\n')) { + strend(p, '\n'); + ec = sscanf(p, "Bus %d Device %d: ID %x:%x", &bus, &dev, &vend, &prod); + if (ec == 4) { + nd = usb_get_device(bus, dev, NULL); + if (head == NULL) { + head = nd; + } else { + usbd_list_append(head, nd); + } + } + p = next_nl + 1; + } + g_free(out); + g_free(err); + } + return head; +} + +static usbd *usb_get_device_list_sysfs() { + GDir *dir; + GRegex *regex; + GMatchInfo *match_info; + usbd *head = NULL, *nd; + gchar *devpath; + const char *entry; + int bus, dev; + + regex = g_regex_new("^([0-9]+-[0-9]+([.][0-9]+)*)|(usb[0-9]+)$", 0, 0, NULL); + if (!regex){ + return NULL; + } + + dir = g_dir_open(SYSFS_DIR_USB_DEVICES, 0, NULL); + if (!dir){ + return NULL; + } + + while ((entry = g_dir_read_name(dir))) { + g_regex_match(regex, entry, 0, &match_info); + + if (g_match_info_matches(match_info)) { + devpath = g_build_filename(SYSFS_DIR_USB_DEVICES, entry, NULL); + bus = h_sysfs_read_int(devpath, "busnum"); + dev = h_sysfs_read_int(devpath, "devnum"); + + if (bus > 0 && dev > 0){ + nd = usb_get_device(bus, dev, devpath); + if (head == NULL) { + head = nd; + } else { + head = usbd_list_append_sorted(head, nd); + } + } + g_free(devpath); + } + g_match_info_free(match_info); + } + + g_dir_close(dir); + g_regex_unref(regex); + + return head; +} + +usbd *usb_get_device_list() { + usbd *lst, *l; + + lst = usb_get_device_list_sysfs(); + if (lst == NULL) + lst = usb_get_device_list_lsusb(); + + l = lst; + while(l) { + l->vendors = vendor_list_remove_duplicates_deep(vendors_match(l->vendor, l->manufacturer, NULL)); + l = l->next; + } + + return lst; +} diff --git a/hardinfo2/util.c b/hardinfo2/util.c new file mode 100644 index 00000000..e12963c0 --- /dev/null +++ b/hardinfo2/util.c @@ -0,0 +1,1452 @@ +/* + * HardInfo - Displays System Information + * Copyright (C) 2003-2007 L. A. F. Pereira <l@tia.mat.br> + * + * This program 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, version 2. + * + * 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, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +/* + * Functions h_strdup_cprintf and h_strconcat are based on GLib version 2.4.6 + * Copyright (C) 1995-1997 Peter Mattis, Spencer Kimball and Josh MacDonald + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + */ + +#include <config.h> + +#include <report.h> +#include <string.h> +#include <shell.h> +#include <iconcache.h> +#include <hardinfo.h> +#include <gtk/gtk.h> + +#include <binreloc.h> + +#include <sys/stat.h> +#include <sys/types.h> + +#define KiB 1024 +#define MiB 1048576 +#define GiB 1073741824 +#define TiB 1099511627776 +#define PiB 1125899906842624 + +static GSList *modules_list = NULL; + +void sync_manager_clear_entries(void); + +gchar *find_program(gchar *program_name) +{ + int i; + char *temp; + static GHashTable *cache = NULL; + const char *path[] = { "/usr/local/bin", "/usr/local/sbin", + "/usr/bin", "/usr/sbin", + "/bin", "/sbin", + NULL }; + + /* we don't need to call stat() every time: cache the results */ + if (!cache) { + cache = g_hash_table_new(g_str_hash, g_str_equal); + } else if ((temp = g_hash_table_lookup(cache, program_name))) { + return g_strdup(temp); + } + + for (i = 0; path[i]; i++) { + temp = g_build_filename(path[i], program_name, NULL); + + if (g_file_test(temp, G_FILE_TEST_IS_EXECUTABLE)) { + g_hash_table_insert(cache, program_name, g_strdup(temp)); + return temp; + } + + g_free(temp); + } + + /* our search has failed; use GLib's search (which uses $PATH env var) */ + if ((temp = g_find_program_in_path(program_name))) { + g_hash_table_insert(cache, program_name, g_strdup(temp)); + return temp; + } + + return NULL; +} + +gchar *seconds_to_string(unsigned int seconds) +{ + unsigned int hours, minutes, days; + const gchar *days_fmt, *hours_fmt, *minutes_fmt, *seconds_fmt; + gchar *full_fmt, *ret = g_strdup(""); + + minutes = seconds / 60; + seconds %= 60; + hours = minutes / 60; + minutes %= 60; + days = hours / 24; + hours %= 24; + + days_fmt = ngettext("%d day", "%d days", days); + hours_fmt = ngettext("%d hour", "%d hours", hours); + minutes_fmt = ngettext("%d minute", "%d minutes", minutes); + seconds_fmt = ngettext("%d second", "%d seconds", seconds); + + if (days) { + full_fmt = g_strdup_printf("%s %s %s %s", days_fmt, hours_fmt, minutes_fmt, seconds_fmt); + ret = g_strdup_printf(full_fmt, days, hours, minutes, seconds); + } else if (hours) { + full_fmt = g_strdup_printf("%s %s %s", hours_fmt, minutes_fmt, seconds_fmt); + ret = g_strdup_printf(full_fmt, hours, minutes, seconds); + } else if (minutes) { + full_fmt = g_strdup_printf("%s %s", minutes_fmt, seconds_fmt); + ret = g_strdup_printf(full_fmt, minutes, seconds); + } else { + ret = g_strdup_printf(seconds_fmt, seconds); + } + g_free(full_fmt); + return ret; +} + +gchar *size_human_readable(gfloat size) +{ + if (size < KiB) + return g_strdup_printf(_("%.1f B"), size); + if (size < MiB) + return g_strdup_printf(_("%.1f KiB"), size / KiB); + if (size < GiB) + return g_strdup_printf(_("%.1f MiB"), size / MiB); + if (size < TiB) + return g_strdup_printf(_("%.1f GiB"), size / GiB); + if (size < PiB) + return g_strdup_printf(_("%.1f TiB"), size / TiB); + + return g_strdup_printf(_("%.1f PiB"), size / PiB); +} + +char *strend(gchar * str, gchar chr) +{ + if (!str) + return NULL; + + char *p; + if ((p = g_utf8_strchr(str, -1, chr))) + *p = 0; + + return str; +} + +void remove_quotes(gchar * str) +{ + if (!str) + return; + + while (*str == '"') + *(str++) = ' '; + + strend(str, '"'); +} + +void remove_linefeed(gchar * str) +{ + strend(str, '\n'); +} + +void widget_set_cursor(GtkWidget * widget, GdkCursorType cursor_type) +{ + GdkCursor *cursor; + GdkDisplay *display; + GdkWindow *gdk_window; + + display = gtk_widget_get_display(widget); + cursor = gdk_cursor_new_for_display(display, cursor_type); + gdk_window = gtk_widget_get_window(widget); + if (cursor) { + gdk_window_set_cursor(gdk_window, cursor); + gdk_display_flush(display); +#if GTK_CHECK_VERSION(3, 0, 0) + g_object_unref(cursor); +#else + gdk_cursor_unref(cursor); +#endif + } + + while (gtk_events_pending()) + gtk_main_iteration(); +} + +static gboolean __nonblock_cb(gpointer data) +{ + gtk_main_quit(); + return FALSE; +} + +void nonblock_sleep(guint msec) +{ + g_timeout_add(msec, (GSourceFunc) __nonblock_cb, NULL); + gtk_main(); +} + +static void __expand_cb(GtkWidget * widget, gpointer data) +{ + if (GTK_IS_EXPANDER(widget)) { + gtk_expander_set_expanded(GTK_EXPANDER(widget), TRUE); + } else if (GTK_IS_CONTAINER(widget)) { + gtk_container_foreach(GTK_CONTAINER(widget), + (GtkCallback) __expand_cb, NULL); + } +} + +void file_chooser_open_expander(GtkWidget * chooser) +{ + gtk_container_foreach(GTK_CONTAINER(chooser), + (GtkCallback) __expand_cb, NULL); +} + +void file_chooser_add_filters(GtkWidget * chooser, FileTypes * filters) +{ + GtkFileFilter *filter; + gint i; + + for (i = 0; filters[i].name; i++) { + filter = gtk_file_filter_new(); + gtk_file_filter_add_mime_type(filter, filters[i].mime_type); + gtk_file_filter_set_name(filter, filters[i].name); + gtk_file_chooser_add_filter(GTK_FILE_CHOOSER(chooser), filter); + } +} + +gchar *file_chooser_get_extension(GtkWidget * chooser, FileTypes * filters) +{ + GtkFileFilter *filter; + const gchar *filter_name; + gint i; + + filter = gtk_file_chooser_get_filter(GTK_FILE_CHOOSER(chooser)); + filter_name = gtk_file_filter_get_name(filter); + for (i = 0; filters[i].name; i++) { + if (g_str_equal(filter_name, filters[i].name)) { + return filters[i].extension; + } + } + + return NULL; +} + +gpointer file_types_get_data_by_name(FileTypes * filters, gchar * filename) +{ + gint i; + + for (i = 0; filters[i].name; i++) { + if (g_str_has_suffix(filename, filters[i].extension)) { + return filters[i].data; + } + } + + return NULL; +} + +gchar *file_chooser_build_filename(GtkWidget * chooser, gchar * extension) +{ + gchar *filename = + gtk_file_chooser_get_filename(GTK_FILE_CHOOSER(chooser)); + gchar *retval; + + if (g_str_has_suffix(filename, extension)) { + return filename; + } + + retval = g_strconcat(filename, extension, NULL); + g_free(filename); + + return retval; +} + +gboolean binreloc_init(gboolean try_hardcoded) +{ + GError *error = NULL; + gchar *tmp; + + DEBUG("initializing binreloc (hardcoded = %d)", try_hardcoded); + + /* If the runtime data directories we previously found, don't even try + to find them again. */ + if (params.path_data && params.path_lib) { + DEBUG("data and lib path already found."); + return TRUE; + } + + if (try_hardcoded || !gbr_init(&error)) { + /* We were asked to try hardcoded paths or BinReloc failed to initialize. */ + params.path_data = g_strdup(PREFIX); + params.path_lib = g_strdup(LIBPREFIX); + + if (error) { + g_error_free(error); + } + + DEBUG("%strying hardcoded paths.", + try_hardcoded ? "" : "binreloc init failed. "); + } else { + /* If we were able to initialize BinReloc, build the default data + and library paths. */ + DEBUG("done, trying to use binreloc paths."); + + tmp = gbr_find_data_dir(PREFIX); + params.path_data = g_build_filename(tmp, "hardinfo2", NULL); + g_free(tmp); + + tmp = gbr_find_lib_dir(PREFIX); + params.path_lib = g_build_filename(tmp, "hardinfo2", NULL); + g_free(tmp); + } + + DEBUG("searching for runtime data on these locations:"); + DEBUG(" lib: %s", params.path_lib); + DEBUG(" data: %s", params.path_data); + + /* Try to see if the benchmark test data file isn't missing. This isn't the + definitive test, but it should do okay for most situations. */ + tmp = g_build_filename(params.path_data, "benchmark.data", NULL); + if (!g_file_test(tmp, G_FILE_TEST_EXISTS)) { + DEBUG("runtime data not found"); + + g_free(params.path_data); + g_free(params.path_lib); + g_free(tmp); + + params.path_data = params.path_lib = NULL; + + if (try_hardcoded) { + /* We tried the hardcoded paths, but still was unable to find the + runtime data. Give up. */ + DEBUG("giving up"); + return FALSE; + } else { + /* Even though BinReloc worked OK, the runtime data was not found. + Try the hardcoded paths. */ + DEBUG("trying to find elsewhere"); + return binreloc_init(TRUE); + } + } + g_free(tmp); + + DEBUG("runtime data found!"); + /* We found the runtime data; hope everything is fine */ + return TRUE; +} + +static void +log_handler(const gchar * log_domain, + GLogLevelFlags log_level, + const gchar * message, gpointer user_data) +{ + if (!params.gui_running) { + /* No GUI running: spit the message to the terminal */ + g_print("\n\n*** %s: %s\n\n", + (log_level & G_LOG_FLAG_FATAL) ? _("Error") : _("Warning"), + message); + } else { + /* Hooray! We have a GUI running! */ + GtkWidget *dialog; + + dialog = gtk_message_dialog_new_with_markup(NULL, GTK_DIALOG_MODAL, + (log_level & + G_LOG_FLAG_FATAL) ? + GTK_MESSAGE_ERROR : + GTK_MESSAGE_WARNING, + GTK_BUTTONS_CLOSE, + "<big><b>%s</b></big>\n\n%s", + (log_level & + G_LOG_FLAG_FATAL) ? + _("Fatal Error") : + _("Warning"), message); + + gtk_dialog_run(GTK_DIALOG(dialog)); + gtk_widget_destroy(dialog); + } +} + +void parameters_init(int *argc, char ***argv, ProgramParameters * param) +{ + static gboolean create_report = FALSE; + static gboolean force_all_details = FALSE; + static gboolean show_version = FALSE; + static gboolean list_modules = FALSE; + static gboolean autoload_deps = FALSE; + static gboolean skip_benchmarks = FALSE; + static gboolean quiet = FALSE; + static gchar *report_format = NULL; + static gchar *run_benchmark = NULL; + static gchar *result_format = NULL; + static gchar *bench_user_note = NULL; + static gchar **use_modules = NULL; + static gint max_bench_results = 50; + + static GOptionEntry options[] = { + { + .long_name = "quiet", + .short_name = 'q', + .arg = G_OPTION_ARG_NONE, + .arg_data = &quiet, + .description = N_("do not print status messages to standard output")}, + { + .long_name = "generate-report", + .short_name = 'r', + .arg = G_OPTION_ARG_NONE, + .arg_data = &create_report, + .description = N_("creates a report and prints to standard output")}, + { + .long_name = "report-format", + .short_name = 'f', + .arg = G_OPTION_ARG_STRING, + .arg_data = &report_format, + .description = N_("chooses a report format ([text], html)")}, + { + .long_name = "run-benchmark", + .short_name = 'b', + .arg = G_OPTION_ARG_STRING, + .arg_data = &run_benchmark, + .description = N_("run benchmark; requires benchmark.so to be loaded")}, + { + .long_name = "user-note", + .short_name = 'u', + .arg = G_OPTION_ARG_STRING, + .arg_data = &bench_user_note, + .description = N_("note attached to benchmark results")}, + { + .long_name = "result-format", + .short_name = 'g', + .arg = G_OPTION_ARG_STRING, + .arg_data = &result_format, + .description = N_("benchmark result format ([short], conf, shell)")}, + { + .long_name = "max-results", + .short_name = 'n', + .arg = G_OPTION_ARG_INT, + .arg_data = &max_bench_results, + .description = N_("maximum number of benchmark results to include (-1 for no limit, default is 50)")}, +//#if NOT_DEFINED + { + .long_name = "list-modules", + .short_name = 'l', + .arg = G_OPTION_ARG_NONE, + .arg_data = &list_modules, + .description = N_("lists modules")}, + { + .long_name = "load-module", + .short_name = 'm', + .arg = G_OPTION_ARG_STRING_ARRAY, + .arg_data = &use_modules, + .description = N_("specify module to load")}, + { + .long_name = "autoload-deps", + .short_name = 'a', + .arg = G_OPTION_ARG_NONE, + .arg_data = &autoload_deps, + .description = N_("automatically load module dependencies")}, +//#endif + { + .long_name = "version", + .short_name = 'v', + .arg = G_OPTION_ARG_NONE, + .arg_data = &show_version, + .description = N_("shows program version and quit")}, + { + .long_name = "skip-benchmarks", + .short_name = 's', + .arg = G_OPTION_ARG_NONE, + .arg_data = &skip_benchmarks, + .description = N_("do not run benchmarks")}, + { + .long_name = "very-verbose", + .short_name = 'w', /* like -vv */ + .arg = G_OPTION_ARG_NONE, + .arg_data = &force_all_details, + .description = N_("show all details")}, + {NULL} + }; + GOptionContext *ctx; + + ctx = g_option_context_new(_("- System Profiler and Benchmark tool")); + g_option_context_set_ignore_unknown_options(ctx, FALSE); + g_option_context_set_help_enabled(ctx, TRUE); + + g_option_context_add_main_entries(ctx, options, *(argv)[0]); + g_option_context_parse(ctx, argc, argv, NULL); + + g_option_context_free(ctx); + + if (*argc >= 2) { + g_print(_("Unrecognized arguments.\n" + "Try ``%s --help'' for more information.\n"), *(argv)[0]); + exit(1); + } + + param->create_report = create_report; + param->report_format = REPORT_FORMAT_TEXT; + param->show_version = show_version; + param->list_modules = list_modules; + param->use_modules = use_modules; + param->run_benchmark = run_benchmark; + param->result_format = result_format; + param->max_bench_results = max_bench_results; + param->autoload_deps = autoload_deps; + param->skip_benchmarks = skip_benchmarks; + param->force_all_details = force_all_details; + param->quiet = quiet; + param->argv0 = *(argv)[0]; + + if (report_format) { + if (g_str_equal(report_format, "html")) + param->report_format = REPORT_FORMAT_HTML; + if (g_str_equal(report_format, "shell")) + param->report_format = REPORT_FORMAT_SHELL; + } + + /* clean user note */ + if (bench_user_note) { + char *p = NULL; + while(p = strchr(bench_user_note, ';')) { *p = ','; } + param->bench_user_note = + gg_key_file_parse_string_as_value(bench_user_note, '|'); + } + + /* html ok? + * gui: yes + * report html: yes + * report text: no + * anything else? */ + param->markup_ok = TRUE; + if (param->create_report && param->report_format != REPORT_FORMAT_HTML) + param->markup_ok = FALSE; + + // TODO: fmt_opts: FMT_OPT_ATERM, FMT_OPT_HTML, FMT_OPT_PANGO... + param->fmt_opts = FMT_OPT_NONE; + + gchar *confdir = g_build_filename(g_get_user_config_dir(), "hardinfo2", NULL); + if (!g_file_test(confdir, G_FILE_TEST_EXISTS)) { + mkdir(confdir, 0744); + } + g_free(confdir); +} + +gboolean ui_init(int *argc, char ***argv) +{ + DEBUG("initializing gtk+ UI"); + + g_set_application_name("HardInfo2"); + g_log_set_handler(NULL, + G_LOG_LEVEL_WARNING | G_LOG_FLAG_FATAL | + G_LOG_LEVEL_ERROR, log_handler, NULL); + + return gtk_init_check(argc, argv); +} + +/* Copyright: Jens Låås, SLU 2002 */ +gchar *strreplacechr(gchar * string, gchar * replace, gchar new_char) +{ + gchar *s; + for (s = string; *s; s++) + if (strchr(replace, *s)) + *s = new_char; + + return string; +} + +gchar *strreplace(gchar *string, gchar *replace, gchar *replacement) +{ + gchar **tmp, *ret; + + tmp = g_strsplit(string, replace, 0); + ret = g_strjoinv(replacement, tmp); + g_strfreev(tmp); + + return ret; +} + +static GHashTable *__module_methods = NULL; + +static void module_register_methods(ShellModule * module) +{ + const ShellModuleMethod *(*get_methods)(void); + gchar *method_name; + + if (__module_methods == NULL) { + __module_methods = g_hash_table_new(g_str_hash, g_str_equal); + } + + if (g_module_symbol + (module->dll, "hi_exported_methods", (gpointer)&get_methods)) { + const ShellModuleMethod *methods; + + for (methods = get_methods(); methods->name; methods++) { + ShellModuleMethod method = *methods; + gchar *name = g_path_get_basename(g_module_name(module->dll)); + gchar *simple_name = strreplace(name, "lib", ""); + + strend(simple_name, '.'); + + method_name = g_strdup_printf("%s::%s", simple_name, method.name); + g_hash_table_insert(__module_methods, method_name, + method.function); + g_free(name); + g_free(simple_name); + } + } + +} + +gchar *module_call_method(gchar * method) +{ + gchar *(*function) (void); + + if (__module_methods == NULL) { + return NULL; + } + + function = g_hash_table_lookup(__module_methods, method); + return function ? g_strdup(function()) : NULL; +} + +/* FIXME: varargs? */ +gchar *module_call_method_param(gchar * method, gchar * parameter) +{ + gchar *(*function) (gchar *param); + + if (__module_methods == NULL) { + return NULL; + } + + function = g_hash_table_lookup(__module_methods, method); + return function ? g_strdup(function(parameter)) : NULL; +} + +static gboolean remove_module_methods(gpointer key, gpointer value, gpointer data) +{ + return g_str_has_prefix(key, data); +} + +static void module_unload(ShellModule * module) +{ + GSList *entry; + + if (module->dll) { + gchar *name; + + if (module->deinit) { + DEBUG("cleaning up module \"%s\"", module->name); + module->deinit(); + } else { + DEBUG("module \"%s\" does not need cleanup", module->name); + } + + name = g_path_get_basename(g_module_name(module->dll)); + g_hash_table_foreach_remove(__module_methods, remove_module_methods, name); + + g_module_close(module->dll); + g_free(name); + } + + g_free(module->name); + g_object_unref(module->icon); + + for (entry = module->entries; entry; entry = entry->next) { + ShellModuleEntry *e = (ShellModuleEntry *)entry->data; + + g_source_remove_by_user_data(e); + g_free(e); + } + + g_slist_free(module->entries); + g_free(module); +} + + +void module_unload_all(void) +{ + Shell *shell; + GSList *module, *merge_id; + + shell = shell_get_main_shell(); + + sync_manager_clear_entries(); + shell_clear_timeouts(shell); + shell_clear_tree_models(shell); + shell_clear_field_updates(); + shell_set_title(shell, NULL); + + for (module = shell->tree->modules; module; module = module->next) { + module_unload((ShellModule *)module->data); + } + + for (merge_id = shell->merge_ids; merge_id; merge_id = merge_id->next) { + gtk_ui_manager_remove_ui(shell->ui_manager, + GPOINTER_TO_INT(merge_id->data)); + } + g_slist_free(shell->tree->modules); + g_slist_free(shell->merge_ids); + + shell->merge_ids = NULL; + shell->tree->modules = NULL; + shell->selected = NULL; +} + +static ShellModule *module_load(gchar * filename) +{ + ShellModule *module; + gchar *tmp; + + module = g_new0(ShellModule, 1); + + if (params.gui_running) { + gchar *tmpicon, *dot, *simple_name; + + tmpicon = g_strdup(filename); + dot = g_strrstr(tmpicon, "." G_MODULE_SUFFIX); + + *dot = '\0'; + + simple_name = strreplace(tmpicon, "lib", ""); + + tmp = g_strdup_printf("%s.png", simple_name); + module->icon = icon_cache_get_pixbuf(tmp); + + g_free(tmp); + g_free(tmpicon); + g_free(simple_name); + } + + tmp = g_build_filename(params.path_lib, "modules", filename, NULL); + module->dll = g_module_open(tmp, G_MODULE_BIND_LAZY); + DEBUG("gmodule resource for ``%s'' is %p (%s)", tmp, module->dll, g_module_error()); + g_free(tmp); + + if (module->dll) { + void (*init) (void); + ModuleEntry *(*get_module_entries) (void); + gint(*weight_func) (void); + gchar *(*name_func) (void); + ModuleEntry *entries; + gint i = 0; + + if (!g_module_symbol(module->dll, "hi_module_get_entries", (gpointer) & get_module_entries) || + !g_module_symbol(module->dll, "hi_module_get_name", (gpointer) & name_func)) { + DEBUG("cannot find needed symbols; is ``%s'' a real module?", filename); + goto failed; + } + + if (g_module_symbol(module->dll, "hi_module_init", (gpointer) & init)) { + DEBUG("initializing module ``%s''", filename); + init(); + } + + g_module_symbol(module->dll, "hi_module_get_weight", + (gpointer) & weight_func); + + module->weight = weight_func ? weight_func() : 0; + module->name = name_func(); + + g_module_symbol(module->dll, "hi_module_get_about", + (gpointer) & (module->aboutfunc)); + g_module_symbol(module->dll, "hi_module_deinit", + (gpointer) & (module->deinit)); + g_module_symbol(module->dll, "hi_module_get_summary", + (gpointer) & (module->summaryfunc)); + + entries = get_module_entries(); + while (entries[i].name) { + if (*entries[i].name == '#') { i++; continue; } /* skip */ + + ShellModuleEntry *entry = g_new0(ShellModuleEntry, 1); + + if (params.gui_running) { + entry->icon = icon_cache_get_pixbuf(entries[i].icon); + } + entry->icon_file = entries[i].icon; + + g_module_symbol(module->dll, "hi_more_info", + (gpointer) & (entry->morefunc)); + g_module_symbol(module->dll, "hi_get_field", + (gpointer) & (entry->fieldfunc)); + g_module_symbol(module->dll, "hi_note_func", + (gpointer) & (entry->notefunc)); + + entry->name = _(entries[i].name); //gettext unname N_() in computer.c line 67 etc... + entry->scan_func = entries[i].scan_callback; + entry->func = entries[i].callback; + entry->number = i; + entry->flags = entries[i].flags; + + module->entries = g_slist_append(module->entries, entry); + + i++; + } + + DEBUG("registering methods for module ``%s''", filename); + module_register_methods(module); + } else { + DEBUG("cannot g_module_open(``%s''). permission problem?", filename); + failed: + DEBUG("loading module %s failed: %s", filename, g_module_error()); + + g_free(module->name); + g_free(module); + module = NULL; + } + + return module; +} + +static gboolean module_in_module_list(gchar * module, gchar ** module_list) +{ + int i = 0; + + if (!module_list) + return TRUE; + + for (; module_list[i]; i++) { + if (g_str_equal(module_list[i], module)) + return TRUE; + } + + return FALSE; +} + +static gint module_cmp(gconstpointer m1, gconstpointer m2) +{ + ShellModule *a = (ShellModule *) m1; + ShellModule *b = (ShellModule *) m2; + + return a->weight - b->weight; +} + +#if 0 +static void module_entry_free(gpointer data, gpointer user_data) +{ + ShellModuleEntry *entry = (ShellModuleEntry *) data; + + if (entry) { + g_free(entry->name); + g_object_unref(entry->icon); + + g_free(entry); + } +} +#endif + +const ModuleAbout *module_get_about(ShellModule * module) +{ + if (module->aboutfunc) { + return module->aboutfunc(); + } + + return NULL; +} + +static GSList *modules_check_deps(GSList * modules) +{ + GSList *mm; + ShellModule *module; + + for (mm = modules; mm; mm = mm->next) { + gchar **(*get_deps) (void); + gchar **deps; + gint i; + + module = (ShellModule *) mm->data; + + if (g_module_symbol(module->dll, "hi_module_get_dependencies", + (gpointer) & get_deps)) { + for (i = 0, deps = get_deps(); deps[i]; i++) { + GSList *l; + ShellModule *m; + gboolean found = FALSE; + + for (l = modules; l && !found; l = l->next) { + m = (ShellModule *) l->data; + gchar *name = g_path_get_basename(g_module_name(m->dll)); + + found = g_str_equal(name, deps[i]); + g_free(name); + } + + if (!found) { + if (params.autoload_deps) { + ShellModule *mod = module_load(deps[i]); + + if (mod) + modules = g_slist_append(modules, mod); + modules = modules_check_deps(modules); /* re-check dependencies */ + + break; + } + + if (params.gui_running) { + GtkWidget *dialog; + + dialog = gtk_message_dialog_new(NULL, + GTK_DIALOG_DESTROY_WITH_PARENT, + GTK_MESSAGE_QUESTION, + GTK_BUTTONS_NONE, + _("Module \"%s\" depends on module \"%s\", load it?"), + module->name, + deps[i]); + gtk_dialog_add_buttons(GTK_DIALOG(dialog), + "_No", + GTK_RESPONSE_REJECT, + "_Open", + GTK_RESPONSE_ACCEPT, NULL); + + if (gtk_dialog_run(GTK_DIALOG(dialog)) == + GTK_RESPONSE_ACCEPT) { + ShellModule *mod = module_load(deps[i]); + + if (mod) + modules = g_slist_prepend(modules, mod); + modules = modules_check_deps(modules); /* re-check dependencies */ + } else { + g_error("HardInfo2 cannot run without loading the additional module."); + exit(1); + } + + gtk_widget_destroy(dialog); + } else { + g_error(_("Module \"%s\" depends on module \"%s\"."), + module->name, deps[i]); + } + } + } + } + } + + return modules; +} + + +GSList *modules_get_list() +{ + return modules_list; +} + +static GSList *modules_load(gchar ** module_list) +{ + GDir *dir; + GSList *modules = NULL; + ShellModule *module; + gchar *filename; + + filename = g_build_filename(params.path_lib, "modules", NULL); + dir = g_dir_open(filename, 0, NULL); + g_free(filename); + + if (dir) { + GList *filenames = NULL; + while ((filename = (gchar *)g_dir_read_name(dir))) { + if (g_strrstr(filename, "." G_MODULE_SUFFIX) && + module_in_module_list(filename, module_list)) { + if (g_strrstr(filename, "devices." G_MODULE_SUFFIX)) { + filenames = g_list_prepend(filenames, filename); + } + else { + filenames = g_list_append(filenames, filename); + } + } + } + GList* item = NULL; + while (item = g_list_first(filenames)) { + if (module = module_load((gchar *)item->data)) { + modules = g_slist_prepend(modules, module); + } + filenames = g_list_delete_link(filenames, item); + } +#if GLIB_CHECK_VERSION(2,44,0) + //FIXME change this to not use g_steal_pointer + g_list_free_full (g_steal_pointer (&filenames), g_object_unref); +#endif + g_dir_close(dir); + } + + modules = modules_check_deps(modules); + + if (g_slist_length(modules) == 0) { + if (params.use_modules == NULL) { + g_error + (_("No module could be loaded. Check permissions on \"%s\" and try again."), + params.path_lib); + } else { + g_error + (_("No module could be loaded. Please use hardinfo2 -l to list all avai" + "lable modules and try again with a valid module list.")); + + } + } + + modules_list = g_slist_sort(modules, module_cmp); + return modules_list; +} + +GSList *modules_load_selected(void) +{ + return modules_load(params.use_modules); +} + +GSList *modules_load_all(void) +{ + return modules_load(NULL); +} + +gint tree_view_get_visible_height(GtkTreeView * tv) +{ + GtkTreePath *path; + GdkRectangle rect; + GtkTreeIter iter; + GtkTreeModel *model = gtk_tree_view_get_model(tv); + gint nrows = 1; + + path = gtk_tree_path_new_first(); + gtk_tree_view_get_cell_area(GTK_TREE_VIEW(tv), path, NULL, &rect); + + /* FIXME: isn't there any easier way to tell the number of rows? */ + gtk_tree_model_get_iter_first(model, &iter); + do { + nrows++; + } while (gtk_tree_model_iter_next(model, &iter)); + + gtk_tree_path_free(path); + + return nrows * rect.height; +} + +void module_entry_scan_all_except(ModuleEntry * entries, gint except_entry) +{ + ModuleEntry entry; + gint i = 0; + void (*scan_callback) (gboolean reload); + gchar *text; + + shell_view_set_enabled(FALSE); + + for (entry = entries[0]; entry.name; entry = entries[++i]) { + if (i == except_entry) + continue; + + text = g_strdup_printf(_("Scanning: %s..."), _(entry.name)); + shell_status_update(text); + g_free(text); + + if ((scan_callback = entry.scan_callback)) { + scan_callback(FALSE); + } + } + + shell_view_set_enabled(TRUE); + shell_status_update(_("Done.")); +} + +void module_entry_scan_all(ModuleEntry * entries) +{ + module_entry_scan_all_except(entries, -1); +} + +void module_entry_reload(ShellModuleEntry * module_entry) +{ + + if (module_entry->scan_func) { + module_entry->scan_func(TRUE); + } +} + +void module_entry_scan(ShellModuleEntry * module_entry) +{ + if (module_entry->scan_func) { + module_entry->scan_func(FALSE); + } +} + +gchar *module_entry_get_field(ShellModuleEntry * module_entry, gchar * field) +{ + if (module_entry->fieldfunc) { + return module_entry->fieldfunc(field); + } + + return NULL; +} + +gchar *module_entry_function(ShellModuleEntry * module_entry) +{ + if (module_entry->func) { + return module_entry->func(); + } + + return NULL; +} + +gchar *module_entry_get_moreinfo(ShellModuleEntry * module_entry, gchar * field) +{ + if (module_entry->morefunc) { + return module_entry->morefunc(field); + } + + return NULL; +} + +const gchar *module_entry_get_note(ShellModuleEntry * module_entry) +{ + if (module_entry->notefunc) { + return module_entry->notefunc(module_entry->number); + } + + return NULL; +} + +gchar *h_strdup_cprintf(const gchar * format, gchar * source, ...) +{ + gchar *buffer, *retn; + va_list args; + + va_start(args, source); + buffer = g_strdup_vprintf(format, args); + va_end(args); + + if (source) { + retn = g_strconcat(source, buffer, NULL); + g_free(buffer); + g_free(source); + } else { + retn = buffer; + } + + return retn; +} + +gchar *h_strconcat(gchar * string1, ...) +{ + gsize l; + va_list args; + gchar *s; + gchar *concat; + gchar *ptr; + + if (!string1) + return NULL; + + l = 1 + strlen(string1); + va_start(args, string1); + s = va_arg(args, gchar *); + while (s) { + l += strlen(s); + s = va_arg(args, gchar *); + } + va_end(args); + + concat = g_new(gchar, l); + ptr = concat; + + ptr = g_stpcpy(ptr, string1); + va_start(args, string1); + s = va_arg(args, gchar *); + while (s) { + ptr = g_stpcpy(ptr, s); + s = va_arg(args, gchar *); + } + va_end(args); + + g_free(string1); + + return concat; +} + +static gboolean h_hash_table_remove_all_true(gpointer key, gpointer data, gpointer user_data) +{ + return TRUE; +} + +void +h_hash_table_remove_all(GHashTable *hash_table) +{ + g_hash_table_foreach_remove(hash_table, + h_hash_table_remove_all_true, + NULL); +} + +gfloat +h_sysfs_read_float(const gchar *endpoint, const gchar *entry) +{ + gchar *tmp, *buffer; + gfloat return_value = 0.0f; + + tmp = g_build_filename(endpoint, entry, NULL); + if (g_file_get_contents(tmp, &buffer, NULL, NULL)) + return_value = atof(buffer); + + g_free(tmp); + g_free(buffer); + + return return_value; +} + +gint +h_sysfs_read_int(const gchar *endpoint, const gchar *entry) +{ + gchar *tmp, *buffer; + gint return_value = 0; + + tmp = g_build_filename(endpoint, entry, NULL); + if (g_file_get_contents(tmp, &buffer, NULL, NULL)) + return_value = atoi(buffer); + + g_free(tmp); + g_free(buffer); + + return return_value; +} + +gint +h_sysfs_read_hex(const gchar *endpoint, const gchar *entry) +{ + gchar *tmp, *buffer; + gint return_value = 0; + + tmp = g_build_filename(endpoint, entry, NULL); + if (g_file_get_contents(tmp, &buffer, NULL, NULL)) + return_value = (gint) strtoll(buffer, NULL, 16); + + g_free(tmp); + g_free(buffer); + + return return_value; +} + +gchar * +h_sysfs_read_string(const gchar *endpoint, const gchar *entry) +{ + gchar *tmp, *return_value; + + tmp = g_build_filename(endpoint, entry, NULL); + if (!g_file_get_contents(tmp, &return_value, NULL, NULL)) { + g_free(return_value); + + return_value = NULL; + } else { + return_value = g_strstrip(return_value); + } + + g_free(tmp); + + return return_value; +} + +static GHashTable *_moreinfo = NULL; + +void +moreinfo_init(void) +{ + if (G_UNLIKELY(_moreinfo)) { + DEBUG("moreinfo already initialized"); + return; + } + DEBUG("initializing moreinfo"); + _moreinfo = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, g_free); +} + +void +moreinfo_shutdown(void) +{ + if (G_UNLIKELY(!_moreinfo)) { + DEBUG("moreinfo not initialized"); + return; + } + DEBUG("shutting down moreinfo"); + g_hash_table_destroy(_moreinfo); + _moreinfo = NULL; +} + +void +moreinfo_add_with_prefix(gchar *prefix, gchar *key, gchar *value) +{ + if (G_UNLIKELY(!_moreinfo)) { + DEBUG("moreinfo not initialized"); + return; + } + + if (prefix) { + gchar *hashkey = g_strconcat(prefix, ":", key, NULL); + g_hash_table_insert(_moreinfo, hashkey, value); + return; + } + + g_hash_table_insert(_moreinfo, g_strdup(key), value); +} + +void +moreinfo_add(gchar *key, gchar *value) +{ + moreinfo_add_with_prefix(NULL, key, value); +} + +static gboolean +_moreinfo_del_cb(gpointer key, gpointer value, gpointer data) +{ + return g_str_has_prefix(key, data); +} + +void +moreinfo_del_with_prefix(gchar *prefix) +{ + if (G_UNLIKELY(!_moreinfo)) { + DEBUG("moreinfo not initialized"); + return; + } + + g_hash_table_foreach_remove(_moreinfo, _moreinfo_del_cb, prefix); +} + +void +moreinfo_clear(void) +{ + if (G_UNLIKELY(!_moreinfo)) { + DEBUG("moreinfo not initialized"); + return; + } + h_hash_table_remove_all(_moreinfo); +} + +gchar * +moreinfo_lookup_with_prefix(gchar *prefix, gchar *key) +{ + if (G_UNLIKELY(!_moreinfo)) { + DEBUG("moreinfo not initialized"); + return 0; + } + + if (prefix) { + gchar *lookup_key = g_strconcat(prefix, ":", key, NULL); + gchar *result = g_hash_table_lookup(_moreinfo, lookup_key); + g_free(lookup_key); + return result; + } + + return g_hash_table_lookup(_moreinfo, key); +} + +gchar * +moreinfo_lookup(gchar *key) +{ + return moreinfo_lookup_with_prefix(NULL, key); +} + +#if !GLIB_CHECK_VERSION(2,44,0) +gboolean g_strv_contains(const gchar * const * strv, const gchar *str) { + /* g_strv_contains() requires glib>2.44 + * fallback for older versions */ + gint i = 0; + while(strv[i] != NULL) { + if (g_strcmp0(strv[i], str) == 0) + return 1; + i++; + } + return 0; +} +#endif + +gchar *hardinfo_clean_grpname(const gchar *v, int replacing) { + gchar *clean, *p; + + p = clean = g_strdup(v); + while (*p != 0) { + switch(*p) { + case '[': + *p = '('; break; + case ']': + *p = ')'; break; + default: + break; + } + p++; + } + if (replacing) + g_free((gpointer)v); + return clean; +} + +/* Hardinfo labels that have # are truncated and/or hidden. + * Labels can't have $ because that is the delimiter in + * moreinfo. */ +gchar *hardinfo_clean_label(const gchar *v, int replacing) { + gchar *clean, *p; + + p = clean = g_strdup(v); + while (*p != 0) { + switch(*p) { + case '#': case '$': + *p = '_'; + break; + default: + break; + } + p++; + } + if (replacing) + g_free((gpointer)v); + return clean; +} + +/* hardinfo uses the values as {ht,x}ml, apparently */ +gchar *hardinfo_clean_value(const gchar *v, int replacing) { + gchar *clean, *tmp; + gchar **vl; + if (v == NULL) return NULL; + + vl = g_strsplit(v, "&", -1); + if (g_strv_length(vl) > 1) + clean = g_strjoinv("&", vl); + else + clean = g_strdup(v); + g_strfreev(vl); + + vl = g_strsplit(clean, "<", -1); + if (g_strv_length(vl) > 1) { + tmp = g_strjoinv("<", vl); + g_free(clean); + clean = tmp; + } + g_strfreev(vl); + + vl = g_strsplit(clean, ">", -1); + if (g_strv_length(vl) > 1) { + tmp = g_strjoinv(">", vl); + g_free(clean); + clean = tmp; + } + g_strfreev(vl); + + if (replacing) + g_free((gpointer)v); + return clean; +} + +gboolean hardinfo_spawn_command_line_sync(const gchar *command_line, + gchar **standard_output, + gchar **standard_error, + gint *exit_status, + GError **error) +{ + shell_status_pulse(); + return g_spawn_command_line_sync(command_line, standard_output, + standard_error, exit_status, error); +} diff --git a/hardinfo2/vendor.c b/hardinfo2/vendor.c new file mode 100644 index 00000000..9230c696 --- /dev/null +++ b/hardinfo2/vendor.c @@ -0,0 +1,655 @@ +/* + * HardInfo - Displays System Information + * Copyright (C) 2003-2009 L. A. F. Pereira <l@tia.mat.br> + * + * List of vendors based on GtkSysInfo (c) Pissens Sebastien. + * + * This program 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, version 2 or later. + * + * 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, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include <string.h> +#include <gtk/gtk.h> + +#include "vendor.h" +#include "syncmanager.h" +#include "config.h" +#include "hardinfo.h" +#include "strstr_word.h" +#include "util_sysobj.h" /* for appfsp() and SEQ() */ + +/* { match_string, match_rule, name, url } */ +static Vendor vendors_builtin[] = { + /* BIOS manufacturers */ + { "American Megatrends", 0, "American Megatrends", "www.ami.com"}, + { "Award", 0, "Award Software International", "www.award-bios.com"}, + { "Phoenix", 0, "Phoenix Technologies", "www.phoenix.com"}, + /* x86 vendor strings */ + { "AMDisbetter!", 0, "Advanced Micro Devices", "www.amd.com" }, + { "AuthenticAMD", 0, "Advanced Micro Devices", "www.amd.com" }, + { "CentaurHauls", 0, "VIA (formerly Centaur Technology)", "www.via.tw" }, + { "CyrixInstead", 0, "Cyrix", "" }, + { "GenuineIntel", 0, "Intel", "www.intel.com" }, + { "TransmetaCPU", 0, "Transmeta", "" }, + { "GenuineTMx86", 0, "Transmeta", "" }, + { "Geode by NSC", 0, "National Semiconductor", "" }, + { "NexGenDriven", 0, "NexGen", "" }, + { "RiseRiseRise", 0, "Rise Technology", "" }, + { "SiS SiS SiS", 0, "Silicon Integrated Systems", "" }, + { "UMC UMC UMC", 0, "United Microelectronics Corporation", "" }, + { "VIA VIA VIA", 0, "VIA", "www.via.tw" }, + { "Vortex86 SoC", 0, "DMP Electronics", "" }, + /* x86 VM vendor strings */ + { "KVMKVMKVM", 0, "KVM", "" }, + { "Microsoft Hv", 0, "Microsoft Hyper-V", "www.microsoft.com" }, + { "lrpepyh vr", 0, "Parallels", "" }, + { "VMwareVMware", 0, "VMware", "" }, + { "XenVMMXenVMM", 0, "Xen HVM", "" }, +}; + +#define ven_msg(msg, ...) fprintf (stderr, "[%s] " msg "\n", __FUNCTION__, ##__VA_ARGS__) /**/ +#define ven_file_err(msg, ...) { \ + ven_msg(msg, ##__VA_ARGS__); \ + fflush(stderr); \ + if (vendor_die_on_error) exit(-1); } + +static vendor_list vendors = NULL; +vendor_list get_vendors_list() { return vendors; } +gboolean vendor_die_on_error = FALSE; + +/* sort the vendor list by length of match_string, + * LONGEST first */ +int vendor_sort (const Vendor *ap, const Vendor *bp) { + int la = 0, lb = 0; + if (ap) la = ap->weight; + if (bp) lb = bp->weight; + return lb-la; +} + +static int read_from_vendor_conf(const char *path) { + GKeyFile *vendors_file; + gchar *tmp; + gint num_vendors, i, count = 0; /* num_vendors is file-reported, count is actual */ + + DEBUG("using vendor.conf format loader for %s", path); + + vendors_file = g_key_file_new(); + if (g_key_file_load_from_file(vendors_file, path, 0, NULL)) { + num_vendors = g_key_file_get_integer(vendors_file, "vendors", "number", NULL); + + for (i = num_vendors - 1; i >= 0; i--) { + Vendor *v = g_new0(Vendor, 1); + + tmp = g_strdup_printf("vendor%d", i); + + v->match_string = g_key_file_get_string(vendors_file, tmp, "match_string", NULL); + if (v->match_string == NULL) { + /* try old name */ + v->match_string = g_key_file_get_string(vendors_file, tmp, "id", NULL); + } + if (v->match_string) { + v->match_rule = g_key_file_get_integer(vendors_file, tmp, "match_case", NULL); + v->name = g_key_file_get_string(vendors_file, tmp, "name", NULL); + v->name_short = g_key_file_get_string(vendors_file, tmp, "name_short", NULL); + v->url = g_key_file_get_string(vendors_file, tmp, "url", NULL); + v->url_support = g_key_file_get_string(vendors_file, tmp, "url_support", NULL); + + vendors = g_slist_prepend(vendors, v); + count++; + } else { + /* don't add if match_string is null */ + g_free(v); + } + + g_free(tmp); + } + g_key_file_free(vendors_file); + DEBUG("... found %d match strings", count); + return count; + } + g_key_file_free(vendors_file); + return 0; +} + +static int read_from_vendor_ids(const char *path) { +#define VEN_BUFF_SIZE 128 +#define VEN_FFWD() while(isspace((unsigned char)*p)) p++; +#define VEN_CHK(TOK) (strncmp(p, TOK, tl = strlen(TOK)) == 0 && (ok = 1)) + char buff[VEN_BUFF_SIZE] = ""; + + char vars[7][VEN_BUFF_SIZE]; + char *name = vars[0]; + char *name_short = vars[1]; + char *url = vars[2]; + char *url_support = vars[3]; + char *wikipedia = vars[4]; + char *note = vars[5]; + char *ansi_color = vars[6]; + int name_rule_count = -1; + + int count = 0; + FILE *fd; + char *p, *b; + int tl, line = -1, ok = 0; + + DEBUG("using vendor.ids format loader for %s", path); + + fd = fopen(path, "r"); + if (!fd) return 0; + + while (fgets(buff, VEN_BUFF_SIZE, fd)) { + ok = 0; + line++; + + b = strchr(buff, '\n'); + if (b) + *b = 0; + else + ven_file_err("%s:%d: line longer than VEN_BUFF_SIZE (%lu)", path, line, (unsigned long)VEN_BUFF_SIZE); + + b = strchr(buff, '#'); + if (b) *b = 0; /* line ends at comment */ + + p = buff; + VEN_FFWD(); + if (VEN_CHK("name ")) { + if (name_rule_count == 0) + ven_file_err("%s:%d: name \"%s\" had no match rules", path, line, name); + strncpy(name, p + tl, VEN_BUFF_SIZE - 1); + strcpy(name_short, ""); + strcpy(url, ""); + strcpy(url_support, ""); + strcpy(wikipedia, ""); + strcpy(note, ""); + strcpy(ansi_color, ""); + name_rule_count = 0; + } + if (VEN_CHK("name_short ")) + strncpy(name_short, p + tl, VEN_BUFF_SIZE - 1); + if (VEN_CHK("url ")) + strncpy(url, p + tl, VEN_BUFF_SIZE - 1); + if (VEN_CHK("url_support ")) + strncpy(url_support, p + tl, VEN_BUFF_SIZE - 1); + if (VEN_CHK("wikipedia ")) + strncpy(wikipedia, p + tl, VEN_BUFF_SIZE - 1); + if (VEN_CHK("note ")) + strncpy(note, p + tl, VEN_BUFF_SIZE - 1); + if (VEN_CHK("ansi_color ")) + strncpy(ansi_color, p + tl, VEN_BUFF_SIZE - 1); + +#define dup_if_not_empty(s) (strlen(s) ? g_strdup(s) : NULL) + + int mrule = -1; + if (VEN_CHK("match_string ")) + mrule = VENDOR_MATCH_RULE_WORD_IGNORE_CASE; + else if (VEN_CHK("match_string_case ")) + mrule = VENDOR_MATCH_RULE_WORD_MATCH_CASE; + else if (VEN_CHK("match_string_exact ")) + mrule = VENDOR_MATCH_RULE_EXACT; + else if (VEN_CHK("match_string_prefix ")) + mrule = VENDOR_MATCH_RULE_WORD_PREFIX_IGNORE_CASE; + else if (VEN_CHK("match_string_prefix_case ")) + mrule = VENDOR_MATCH_RULE_WORD_PREFIX_MATCH_CASE; + else if (VEN_CHK("match_string_suffix ")) + mrule = VENDOR_MATCH_RULE_WORD_PREFIX_IGNORE_CASE; + else if (VEN_CHK("match_string_suffix_case ")) + mrule = VENDOR_MATCH_RULE_WORD_PREFIX_MATCH_CASE; + else if (VEN_CHK("match_string_num_prefix ")) + mrule = VENDOR_MATCH_RULE_NUM_PREFIX_IGNORE_CASE; + else if (VEN_CHK("match_string_num_prefix_case ")) + mrule = VENDOR_MATCH_RULE_NUM_PREFIX_MATCH_CASE; + + if (mrule >= 0) { + Vendor *v = g_new0(Vendor, 1); + v->file_line = line; + v->match_string = g_strdup(p+tl); + v->ms_length = strlen(v->match_string); + v->match_rule = mrule; + v->name = g_strdup(name); + v->name_short = dup_if_not_empty(name_short); + v->url = dup_if_not_empty(url); + v->url_support = dup_if_not_empty(url_support); + v->wikipedia = dup_if_not_empty(wikipedia); + v->note = dup_if_not_empty(note); + v->ansi_color = dup_if_not_empty(ansi_color); + + v->weight = v->ms_length; + /* NUM_PREFIX rules consider +1 characters */ + if (v->match_rule == VENDOR_MATCH_RULE_NUM_PREFIX_MATCH_CASE + || v->match_rule == VENDOR_MATCH_RULE_NUM_PREFIX_IGNORE_CASE) + v->weight++; + + v->has_parens = g_utf8_strchr(v->match_string, -1, '(') ? TRUE : FALSE; + + vendors = g_slist_prepend(vendors, v); + name_rule_count++; + count++; + } + + g_strstrip(buff); + if (!ok && *buff != 0) + ven_file_err("unrecognised item at %s:%d, %s", path, line, buff); + } + + fclose(fd); + + DEBUG("... found %d match strings", count); + + return count; +} + +void vendor_init(void) +{ + gchar *path; + static SyncEntry se = { + .name = N_("Update vendor list"), + .file_name = "vendor.ids", + }; + + /* already initialized */ + if (vendors) return; + + DEBUG("initializing vendor list"); + sync_manager_add_entry(&se); + + char *file_search_order[] = { + /* new format */ + g_build_filename(g_get_user_config_dir(), "hardinfo2", "vendor.ids", NULL), + g_build_filename(params.path_data, "vendor.ids", NULL), + /* old format */ + g_build_filename(g_get_user_config_dir(), "hardinfo2", "vendor.conf", NULL), + g_build_filename(g_get_home_dir(), ".hardinfo2", "vendor.conf", NULL), /* old place */ + g_build_filename(params.path_data, "vendor.conf", NULL), + NULL + }; + + int n = 0; + while (file_search_order[n]) { + DEBUG("searching for vendor data at %s ...", file_search_order[n]); + if (!access(file_search_order[n], R_OK)) { + path = file_search_order[n]; + DEBUG("... file exists."); + break; + } + n++; + } + + int fail = 1; + + /* new format */ + if (path && strstr(path, "vendor.ids")) { + fail = !read_from_vendor_ids(path); + } + + /* old format */ + if (path && strstr(path, "vendor.conf")) { + fail = !read_from_vendor_conf(path); + } + + if (fail) { + gint i; + + DEBUG("vendor data not found, using internal database"); + + for (i = G_N_ELEMENTS(vendors_builtin) - 1; i >= 0; i--) { + vendors = g_slist_prepend(vendors, (gpointer) &vendors_builtin[i]); + } + } + + /* sort the vendor list by length of match string so that short strings are + * less likely to incorrectly match. + * example: ST matches ASUSTeK but SEAGATE is not ASUS */ + vendors = g_slist_sort(vendors, (GCompareFunc)vendor_sort); + + /* free search location strings */ + n = 0; + while (file_search_order[n]) { + free(file_search_order[n]); + n++; + } +} + +void vendor_cleanup() { + DEBUG("cleanup vendor list"); + //FIXME CRASH g_slist_free_full(vendors, (GDestroyNotify)vendor_free); +} + +void vendor_free(Vendor *v) { + if (v) { + g_free(v->name); + g_free(v->name_short); + g_free(v->url); + g_free(v->url_support); + g_free(v->ansi_color); + g_free(v->match_string); + g_free(v); + } +} + +const Vendor *vendor_match(const gchar *id_str, ...) { + Vendor *ret = NULL; + va_list ap, ap2; + gchar *tmp = NULL, *p = NULL; + int tl = 0, c = 0; + + if (id_str) { + c++; + tl += strlen(id_str); + tmp = appfsp(tmp, "%s", id_str); + + va_start(ap, id_str); + p = va_arg(ap, gchar*); + while(p) { + c++; + tl += strlen(p); + tmp = appfsp(tmp, "%s", p); + p = va_arg(ap, gchar*); + } + va_end(ap); + } + if (!c || tl == 0) + return NULL; + + vendor_list vl = vendors_match_core(tmp, 1); + if (vl) { + ret = (Vendor*)vl->data; + vendor_list_free(vl); + } + return ret; +} + +static const gchar *vendor_get_name_ex(const gchar * id_str, short shortest) { + const Vendor *v = vendor_match(id_str, NULL); + + if (v) { + if (shortest) { + int sl = strlen(id_str); + int nl = (v->name) ? strlen(v->name) : 0; + int snl = (v->name_short) ? strlen(v->name_short) : 0; + if (!nl) nl = 9999; + if (!snl) snl = 9999; + /* if id_str is shortest, then return as if + * not found (see below). + * if all equal then prefer + * name_short > name > id_str. */ + if (nl < snl) + return (sl < nl) ? id_str : v->name; + else + return (sl < snl) ? id_str : v->name_short; + } else + return v->name; + } + + return id_str; /* Preserve an old behavior, but what about const? */ +} + +const gchar *vendor_get_name(const gchar * id_str) { + return vendor_get_name_ex(id_str, 0); +} + +const gchar *vendor_get_shortest_name(const gchar * id_str) { + return vendor_get_name_ex(id_str, 1); +} + +const gchar *vendor_get_url(const gchar * id_str) { + const Vendor *v = vendor_match(id_str, NULL); + + if (v) + return v->url; + + return NULL; +} + +gchar *vendor_get_link(const gchar *id_str) +{ + const Vendor *v = vendor_match(id_str, NULL); + + if (!v) { + return g_strdup(id_str); + } + + return vendor_get_link_from_vendor(v); +} + +gchar *vendor_get_link_from_vendor(const Vendor *v) +{ +#if GTK_CHECK_VERSION(2, 18, 0) + gboolean gtk_label_link_ok = TRUE; +#else + gboolean gtk_label_link_ok = FALSE; +#endif + gboolean link_ok = params.markup_ok && gtk_label_link_ok; + /* If using a real link, and wikipedia is available, + * target that instead of url. There's usually much more + * information there, plus easily click through to company url. */ + gboolean link_wikipedia = TRUE; + + if (!v) + return g_strdup(_("Unknown")); + + gchar *url = NULL; + + if (link_ok && link_wikipedia && v->wikipedia + || v->wikipedia && !v->url) + url = g_strdup_printf("http://wikipedia.com/wiki/%s", v->wikipedia); + else if (v->url) + url = g_strdup(v->url); + + if (!url) + return g_strdup(v->name); + + auto_free(url); + + if (link_ok) { + const gchar *prefix; + + if (!strncmp(url, "http://", sizeof("http://") - 1) || + !strncmp(url, "https://", sizeof("https://") - 1)) { + prefix = ""; + } else { + prefix = "http://"; + } + + return g_strdup_printf("<a href=\"%s%s\">%s</a>", prefix, url, v->name); + } + + return g_strdup_printf("%s (%s)", v->name, url); +} + +vendor_list vendor_list_concat_va(int count, vendor_list vl, ...) { + vendor_list ret = vl, p = NULL; + va_list ap; + va_start(ap, vl); + if (count > 0) { + count--; /* includes vl */ + while (count) { + p = va_arg(ap, vendor_list); + ret = g_slist_concat(ret, p); + count--; + } + } else { + p = va_arg(ap, vendor_list); + while (p) { + ret = g_slist_concat(ret, p); + p = va_arg(ap, vendor_list); + } + } + va_end(ap); + return ret; +} + +int vendor_cmp_deep(const Vendor *a, const Vendor *b) { + int r; + if (a && !b) return 1; + if (!a && b) return -1; + if (!a && !b) return 0; + r = g_strcmp0(a->name, b->name); + if (!!r) return r; + r = g_strcmp0(a->name_short, b->name_short); + if (!!r) return r; + r = g_strcmp0(a->ansi_color, b->ansi_color); + if (!!r) return r; + r = g_strcmp0(a->url, b->url); + if (!!r) return r; + r = g_strcmp0(a->url_support, b->url_support); + if (!!r) return r; + r = g_strcmp0(a->wikipedia, b->wikipedia); + if (!!r) return r; + return 0; +} + +vendor_list vendor_list_remove_duplicates_deep(vendor_list vl) { + /* vendor_list is GSList* */ + GSList *tvl = vl; + GSList *evl = NULL; + while(tvl) { + const Vendor *tv = tvl->data; + evl = tvl->next; + while(evl) { + const Vendor *ev = evl->data; + if (vendor_cmp_deep(ev, tv) == 0) { + GSList *next = evl->next; + vl = g_slist_delete_link(vl, evl); + evl = next; + } else + evl = evl->next; + } + tvl = tvl->next; + } + return vl; +} + +vendor_list vendors_match(const gchar *id_str, ...) { + va_list ap, ap2; + gchar *tmp = NULL, *p = NULL; + int tl = 0, c = 0; + + if (id_str) { + c++; + tl += strlen(id_str); + tmp = appfsp(tmp, "%s", id_str); + + va_start(ap, id_str); + p = va_arg(ap, gchar*); + while(p) { + c++; + tl += strlen(p); + tmp = appfsp(tmp, "%s", p); + p = va_arg(ap, gchar*); + } + va_end(ap); + } + if (!c || tl == 0) + return NULL; + + return vendors_match_core(tmp, -1); +} + +vendor_list vendors_match_core(const gchar *str, int limit) { + gchar *p = NULL; + GSList *vlp; + int found = 0; + vendor_list ret = NULL; + + /* pass [array_index]: function + * 1st [3]: only check match strings that have () in them + * 2nd [2]: ignore text in (), like (formerly ...) or (nee ...), + * but unfortunately also (now ...) + * 3rd [1]: (passes[0]): full text */ + gchar *passes[3] = { g_strdup(str), g_strdup(str), g_strdup(str) }; + int pass = 1; p = passes[1]; + while((p = strchr(p, '('))) { + pass = 3; p++; + while(*p && *p != ')') { *p = ' '; p++; } + } + + for (; pass > 0; pass--) { + for (vlp = vendors; vlp; vlp = vlp->next) { + Vendor *v = (Vendor *)vlp->data; + char *m = NULL; + + if (!v) continue; + if (!v->match_string) continue; + + if (v->has_parens) + if (pass != 3) continue; + + //ven_msg("pass:%d <<%s>> EXAMINE: \"%s\"", pass, v->match_string, passes[pass-1]); + int epass; + +#define standard_match_work_inner(fn) { \ + /* clear so it doesn't match again */ \ + for(epass = pass; epass > 0; epass--) \ + { char *s = passes[epass-1] + (m - passes[pass-1]); \ + char *e = s + v->ms_length; \ + for(; s < e; s++) *s = ' '; \ + g_strstrip(passes[epass-1]); } \ + /* add to return list */ \ + ret = vendor_list_append(ret, v); \ + found++; \ + if (*passes[0] == 0) \ + goto vendors_match_core_finish; \ + if (limit > 0 && found >= limit) \ + goto vendors_match_core_finish; } +#define standard_match_work(fn) \ + if ((m = fn(passes[pass-1], v->match_string))) \ + standard_match_work_inner(); + + switch(v->match_rule) { + case VENDOR_MATCH_RULE_EXACT: + if (SEQ(passes[pass-1], v->match_string) ) { + /* add to return list */ + ret = vendor_list_append(ret, v); + found++; + goto vendors_match_core_finish; /* no way for any other match to happen */ + } + break; + case VENDOR_MATCH_RULE_WORD_IGNORE_CASE: + standard_match_work(strcasestr_word); + break; + case VENDOR_MATCH_RULE_WORD_MATCH_CASE: + standard_match_work(strstr_word); + break; + case VENDOR_MATCH_RULE_WORD_PREFIX_MATCH_CASE: + standard_match_work(strstr_word_prefix); + break; + case VENDOR_MATCH_RULE_WORD_PREFIX_IGNORE_CASE: + standard_match_work(strcasestr_word_prefix); + break; + case VENDOR_MATCH_RULE_WORD_SUFFIX_MATCH_CASE: + standard_match_work(strstr_word_suffix); + break; + case VENDOR_MATCH_RULE_WORD_SUFFIX_IGNORE_CASE: + standard_match_work(strcasestr_word_suffix); + break; + case VENDOR_MATCH_RULE_NUM_PREFIX_IGNORE_CASE: + if ((m = strstr_word_prefix(passes[pass-1], v->match_string))) + if (isdigit(m[v->ms_length])) + standard_match_work_inner(); + break; + case VENDOR_MATCH_RULE_NUM_PREFIX_MATCH_CASE: + if ((m = strcasestr_word_prefix(passes[pass-1], v->match_string))) + if (isdigit(m[v->ms_length])) + standard_match_work_inner(); + break; + } + } + } + +vendors_match_core_finish: + + g_free(passes[0]); + g_free(passes[1]); + g_free(passes[2]); + return ret; +} diff --git a/hardinfo2/x_util.c b/hardinfo2/x_util.c new file mode 100644 index 00000000..2a7febb4 --- /dev/null +++ b/hardinfo2/x_util.c @@ -0,0 +1,342 @@ +/* + * HardInfo - Displays System Information + * Copyright (C) 2003-2017 L. A. F. Pereira <l@tia.mat.br> + * This file + * Copyright (C) 2018 Burt P. <pburt0@gmail.com> + * + * This program 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, version 2 or later. + * + * 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, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include "hardinfo.h" +#include "x_util.h" +#include <X11/Xlib.h> + +/* wayland stuff lives here for now */ + +wl_info *get_walyand_info() { + wl_info *s = malloc(sizeof(wl_info)); + memset(s, 0, sizeof(wl_info)); + s->xdg_session_type = g_strdup( getenv("XDG_SESSION_TYPE") ); + if (s->xdg_session_type == NULL) + s->xdg_session_type = strdup("x11"); + s->display_name = g_strdup( getenv("WAYLAND_DISPLAY") ); + return s; +} + +void wl_free(wl_info *s) { + if (s) { + free(s->xdg_session_type); + free(s->display_name); + free(s); + } +} + +/* get x information from xrandr, xdpyinfo, and glxinfo */ + +static char *simple_line_value(char *line, const char *prefix) { + if (g_str_has_prefix(g_strstrip(line), prefix)) { + line += strlen(prefix) + 1; + return g_strstrip(line); + } else + return NULL; +} + +gboolean fill_glx_info(glx_info *glx) { + gboolean spawned; + gchar *out, *err, *p, *l, *next_nl; + gchar *glx_cmd = g_strdup("glxinfo"); + +#define GLX_MATCH_LINE(prefix_str, struct_member) \ + if (l = simple_line_value(p, prefix_str)) { glx->struct_member = g_strdup(l); goto glx_next_line; } + + spawned = hardinfo_spawn_command_line_sync(glx_cmd, + &out, &err, NULL, NULL); + g_free(glx_cmd); + if (spawned) { + p = out; + while(next_nl = strchr(p, '\n')) { + strend(p, '\n'); + g_strstrip(p); + GLX_MATCH_LINE("GLX version", glx_version); + GLX_MATCH_LINE("OpenGL vendor string", ogl_vendor); + GLX_MATCH_LINE("OpenGL renderer string", ogl_renderer); + GLX_MATCH_LINE("OpenGL renderer string", ogl_renderer); + GLX_MATCH_LINE("OpenGL core profile version string", ogl_core_version); + GLX_MATCH_LINE("OpenGL core profile shading language version string", ogl_core_sl_version); + GLX_MATCH_LINE("OpenGL version string", ogl_version); + GLX_MATCH_LINE("OpenGL shading language version string", ogl_sl_version); + GLX_MATCH_LINE("OpenGL ES profile version string", ogles_version); + GLX_MATCH_LINE("OpenGL ES profile shading language version string", ogles_sl_version); + + if (l = simple_line_value(p, "direct rendering")) { + if (strstr(p, "Yes")) + glx->direct_rendering = 1; + goto glx_next_line; + } + + glx_next_line: + p = next_nl + 1; + } + g_free(out); + g_free(err); + return TRUE; + } + return FALSE; +} + +glx_info *glx_create() { + glx_info *s = malloc(sizeof(glx_info)); + memset(s, 0, sizeof(glx_info)); + return s; +} + +void glx_free(glx_info *s) { + if (s) { + free(s->glx_version); + free(s->ogl_vendor); + free(s->ogl_renderer); + free(s->ogl_core_version); + free(s->ogl_core_sl_version); + free(s->ogl_version); + free(s->ogl_sl_version); + free(s->ogles_version); + free(s->ogles_sl_version); + free(s); + } +} + +gboolean fill_xinfo(xinfo *xi) { + gboolean spawned; + gchar *out, *err, *p, *l, *next_nl; + gchar *xi_cmd = g_strdup("xdpyinfo"); + +#define XI_MATCH_LINE(prefix_str, struct_member) \ + if (l = simple_line_value(p, prefix_str)) { xi->struct_member = g_strdup(l); goto xi_next_line; } + + spawned = hardinfo_spawn_command_line_sync(xi_cmd, + &out, &err, NULL, NULL); + g_free(xi_cmd); + if (spawned) { + p = out; + while(next_nl = strchr(p, '\n')) { + strend(p, '\n'); + g_strstrip(p); + XI_MATCH_LINE("name of display", display_name); + XI_MATCH_LINE("vendor string", vendor); + XI_MATCH_LINE("vendor release number", release_number); + XI_MATCH_LINE("XFree86 version", version); + XI_MATCH_LINE("X.Org version", version); + + xi_next_line: + p = next_nl + 1; + } + g_free(out); + g_free(err); + return TRUE; + } + return FALSE; +} + +gboolean fill_xrr_info(xrr_info *xrr) { + gboolean spawned; + gchar *out, *err, *p, *next_nl; + gchar *xrr_cmd = g_strdup("xrandr --prop"); + int ec; + + x_screen ts; + x_output to; + char output_id[128]; + char status[128]; + char alist[512]; + + memset(&ts, 0, sizeof(x_screen)); + memset(&to, 0, sizeof(x_output)); + memset(output_id, 0, sizeof(output_id)); + memset(status, 0, sizeof(status)); + memset(alist, 0, sizeof(alist)); + + spawned = hardinfo_spawn_command_line_sync(xrr_cmd, + &out, &err, NULL, NULL); + g_free(xrr_cmd); + if (spawned) { + p = out; + while(next_nl = strchr(p, '\n')) { + strend(p, '\n'); + g_strstrip(p); + + ec = sscanf(p, "Screen %d: minimum %d x %d, current %d x %d, maximum %d x %d", + &ts.number, + &ts.min_px_width, &ts.min_px_height, + &ts.px_width, &ts.px_height, + &ts.max_px_width, &ts.max_px_height); + if (ec == 7) { + /* new screen */ + xrr->screen_count++; + if (xrr->screens == NULL) + xrr->screens = malloc(xrr->screen_count * sizeof(x_screen)); + else + xrr->screens = realloc(xrr->screens, xrr->screen_count * sizeof(x_screen)); + memcpy(&xrr->screens[xrr->screen_count-1], &ts, sizeof(x_screen)); + memset(&ts, 0, sizeof(x_screen)); /* clear the temp */ + goto xrr_next_line; + } + + /* looking for: + * <output_id> (connected|disconnected|unknown connection) (primary|?) <%dx%d+%d+%d> (attribute_list) mm x mm + */ + ec = sscanf(p, "%127s %127[^(](%511[^)]", output_id, status, alist); + if (ec == 3) { + int is_output = 0, found_rect = 0, n = 0; + gchar **ot = g_strsplit(status, " ", 0); + + /* find rect */ + while (ot[n] != NULL) { + ec = sscanf(ot[n], "%dx%d+%d+%d", &to.px_width, &to.px_height, &to.px_offset_x, &to.px_offset_y); + if (ec == 4) { + found_rect = 1; + break; + } + n++; + } + if (found_rect) + to.screen = xrr->screen_count - 1; + else + to.screen = -1; + + if (strcmp("disconnected", ot[0]) == 0) { + is_output = 1; + to.connected = 0; + } else + if (strcmp("unknown", ot[0]) == 0 && strcmp("connection", ot[1]) == 0) { + is_output = 1; + to.connected = -1; + } else + if (strcmp("connected", ot[0]) == 0) { + is_output = 1; + to.connected = 1; + } + g_strfreev(ot); + if (is_output) { + strncpy(to.name, output_id, 63); + xrr->output_count++; + if (xrr->outputs == NULL) + xrr->outputs = malloc(xrr->output_count * sizeof(x_output)); + else + xrr->outputs = realloc(xrr->outputs, xrr->output_count * sizeof(x_output)); + memcpy(&xrr->outputs[xrr->output_count-1], &to, sizeof(x_output)); + memset(&to, 0, sizeof(x_output)); /* clear the temp */ + goto xrr_next_line; + } + } + + xrr_next_line: + p = next_nl + 1; + } + g_free(out); + g_free(err); + return TRUE; + } + return FALSE; +} + +gboolean fill_basic_xlib(xinfo *xi) { + Display *display; + int s, w, h, rn; + + display = XOpenDisplay(NULL); + if (display) { + if (!xi->display_name) + xi->display_name = XDisplayName(NULL); + + if (!xi->vendor) + xi->vendor = XServerVendor(display); + + if (!xi->release_number) { + rn = XVendorRelease(display); + xi->release_number = g_strdup_printf("%d", rn); + } + + if (!xi->xrr->screen_count) { + s = DefaultScreen(display); + w = XDisplayWidth(display, s); + h = XDisplayHeight(display, s); + + /* create at least one screen */ + x_screen ts; + memset(&ts, 0, sizeof(x_screen)); + ts.number = s; + ts.px_width = w; + ts.px_height = h; + + xi->xrr->screen_count++; + if (xi->xrr->screens == NULL) + xi->xrr->screens = malloc(xi->xrr->screen_count * sizeof(x_screen)); + else + xi->xrr->screens = realloc(xi->xrr->screens, xi->xrr->screen_count * sizeof(x_screen)); + memcpy(&xi->xrr->screens[xi->xrr->screen_count-1], &ts, sizeof(x_screen)); + } + return TRUE; + } + return FALSE; +} + +xrr_info *xrr_create() { + xrr_info *xrr = malloc(sizeof(xrr_info)); + memset(xrr, 0, sizeof(xrr_info)); + return xrr; +} + +void xrr_free(xrr_info *xrr) { + if (xrr) { + free(xrr->screens); + free(xrr->outputs); + free(xrr->providers); + free(xrr->monitors); + free(xrr); + } +} + +xinfo *xinfo_get_info() { + int fail = 0; + xinfo *xi = malloc(sizeof(xinfo)); + memset(xi, 0, sizeof(xinfo)); + xi->glx = glx_create(); + xi->xrr = xrr_create(); + + if ( !fill_xinfo(xi) ) + fail++; + if ( !fill_xrr_info(xi->xrr) ) + fail++; + if ( !fill_glx_info(xi->glx) ) + fail++; + + if (fail) { + /* as fallback, try xlib directly */ + if ( !fill_basic_xlib(xi) ) + xi->nox = 1; + } + return xi; +} + +void xinfo_free(xinfo *xi) { + if (xi) { + free(xi->display_name); + free(xi->vendor); + free(xi->version); + free(xi->release_number); + xrr_free(xi->xrr); + glx_free(xi->glx); + free(xi); + } +} |