diff options
| author | bigbear <ns@bigbear.dk> | 2024-02-08 19:52:52 +0100 | 
|---|---|---|
| committer | bigbear <ns@bigbear.dk> | 2024-02-08 19:52:52 +0100 | 
| commit | 9a05f56e1bf36abab41d12f92f2c75266c44bd4a (patch) | |
| tree | f9ac600d137939dd484732515363a2337bdc6535 /hardinfo2 | |
| parent | 0f5dea06fa62062c7e98b36945a63aae7835a80a (diff) | |
Clean - HARDINFO2 BASE
Diffstat (limited to 'hardinfo2')
| -rw-r--r-- | hardinfo2/binreloc.c | 668 | ||||
| -rw-r--r-- | hardinfo2/cpu_util.c | 251 | ||||
| -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 | 111 | ||||
| -rw-r--r-- | hardinfo2/gg_strescape.c | 138 | ||||
| -rw-r--r-- | hardinfo2/gpu_util.c | 477 | ||||
| -rw-r--r-- | hardinfo2/hardinfo.c | 169 | ||||
| -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 | 639 | ||||
| -rw-r--r-- | hardinfo2/usb_util.c | 553 | ||||
| -rw-r--r-- | hardinfo2/util.c | 1445 | ||||
| -rw-r--r-- | hardinfo2/vendor.c | 655 | ||||
| -rw-r--r-- | hardinfo2/x_util.c | 342 | 
20 files changed, 8799 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..8db5f61a --- /dev/null +++ b/hardinfo2/cpu_util.c @@ -0,0 +1,251 @@ +/* + *    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. + * + *    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); + +    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..94351ad7 --- /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. + * + *    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; +    chk = g_strdup(*pstr); + +    if (pstr == NULL || *pstr == NULL) +        return -1; +#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..1f65d9c6 --- /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. + * + *    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..83e1e02c --- /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. + * + *    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..7e871bc1 --- /dev/null +++ b/hardinfo2/gg_key_file_parse_string_as_value.c @@ -0,0 +1,111 @@ +/* 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> + +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..ada9cf89 --- /dev/null +++ b/hardinfo2/gg_strescape.c @@ -0,0 +1,138 @@ +/* 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> + +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..29f6c77e --- /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. + * + *    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..1488e0a1 --- /dev/null +++ b/hardinfo2/hardinfo.c @@ -0,0 +1,169 @@ +/* + *    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. + * + *    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> + +ProgramParameters params = { 0 }; + +int main(int argc, char **argv) +{ +    int exit_code = 0; +    GSList *modules; + +    setlocale(LC_ALL, ""); +    bindtextdomain("hardinfo", LOCALEDIR); +    textdomain("hardinfo"); + +    DEBUG("HardInfo version " VERSION ". Debug version."); + +    /* parse all command line parameters */ +    parameters_init(&argc, &argv, ¶ms); + +    /* show version information and quit */ +    if (params.show_version) { +        g_print("HardInfo version " VERSION "\n"); +        g_print +            (_(/*/ %d will be latest year of copyright*/ "Copyright (C) 2003-%d L. A. F. Pereira. See COPYING for details.\n\n"), HARDINFO_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 HardInfo 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..5e6db7e4 --- /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. + * + *    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) +{ +    int 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) +{ +    int 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; +    int 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..ccf4f7c4 --- /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. + * + *    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(), "hardinfo", "pci.ids", NULL), +        g_build_filename(params.path_data, "pci.ids", NULL), +        g_build_filename(g_get_user_config_dir(), "hardinfo", "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; + +    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; + +    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..6e3a3f9e --- /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. + * + *    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..7e61d336 --- /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(), "hardinfo", "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(), "hardinfo", "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..8e6ebd94 --- /dev/null +++ b/hardinfo2/udisks2_util.c @@ -0,0 +1,639 @@ +#include <gio/gio.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; +    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 (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){ +        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..6f9f9ef8 --- /dev/null +++ b/hardinfo2/usb_util.c @@ -0,0 +1,553 @@ +/* + *    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. + * + *    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 = {}; + +    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(), "hardinfo", "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; + +    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; + +    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..38c7bce1 --- /dev/null +++ b/hardinfo2/util.c @@ -0,0 +1,1445 @@ +/* + *    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, "hardinfo", NULL); +	g_free(tmp); + +	tmp = gbr_find_lib_dir(PREFIX); +	params.path_lib = g_build_filename(tmp, "hardinfo", 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 uidefs.xml 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 run_xmlrpc_server = 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 = 10; + +    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 10)")}, +	{ +	 .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")}, +#ifdef HAS_LIBSOUP +	{ +	 .long_name = "xmlrpc-server", +	 .short_name = 'x', +	 .arg = G_OPTION_ARG_NONE, +	 .arg_data = &run_xmlrpc_server, +	 .description = N_("run in XML-RPC server mode")}, +#endif	/* HAS_LIBSOUP */ +	{ +	 .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->run_xmlrpc_server = run_xmlrpc_server; +    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(), "hardinfo", 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("HardInfo"); +    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("HardInfo 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) { +	while ((filename = (gchar *) g_dir_read_name(dir))) { +	    if (g_strrstr(filename, "." G_MODULE_SUFFIX) && +		module_in_module_list(filename, module_list) && +		((module = module_load(filename)))) { +		modules = g_slist_prepend(modules, module); +	    } +	} + +	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 hardinfo -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..cb9cc9ba --- /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. + * + *    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; +const 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(), "hardinfo", "vendor.ids", NULL), +        g_build_filename(params.path_data, "vendor.ids", NULL), +        /* old format */ +        g_build_filename(g_get_user_config_dir(), "hardinfo", "vendor.conf", NULL), +        g_build_filename(g_get_home_dir(), ".hardinfo", "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"); +    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..2187b224 --- /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. + * + *    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); +    } +} | 
