aboutsummaryrefslogtreecommitdiff
path: root/modules
diff options
context:
space:
mode:
authorLucas de Castro Borges <lucas@gnuabordo.com.br>2024-04-22 00:35:56 -0300
committerLucas de Castro Borges <lucas@gnuabordo.com.br>2024-04-22 00:35:56 -0300
commit754b5d1114f096778e483f8a6f3a5dc333225e26 (patch)
tree30911ec9da4cfd2f5572c27f7288fcbfa4cd212d /modules
parent35c2857da302ab8b3c308052f2cd1674fb4141a6 (diff)
parent5f01c706267c595de92406a32e7f31ef5056c2d0 (diff)
Update upstream source from tag 'upstream/2.0.3pre'
Update to upstream version '2.0.3pre' with Debian dir 6683980bf6b5c02f6847fd56765833301f75f4f3
Diffstat (limited to 'modules')
-rw-r--r--modules/benchmark.c946
-rw-r--r--modules/benchmark/bench_results.c605
-rw-r--r--modules/benchmark/bench_util.c58
-rw-r--r--modules/benchmark/benches.c254
-rw-r--r--modules/benchmark/blowfish.c493
-rw-r--r--modules/benchmark/blowfish2.c83
-rw-r--r--modules/benchmark/cryptohash.c89
-rw-r--r--modules/benchmark/drawing.c33
-rw-r--r--modules/benchmark/fbench.c747
-rw-r--r--modules/benchmark/fft.c70
-rw-r--r--modules/benchmark/fftbench.c212
-rw-r--r--modules/benchmark/fib.c61
-rw-r--r--modules/benchmark/guibench.c353
-rw-r--r--modules/benchmark/md5.c317
-rw-r--r--modules/benchmark/nqueens.c68
-rw-r--r--modules/benchmark/raytrace.c56
-rw-r--r--modules/benchmark/sha1.c329
-rw-r--r--modules/benchmark/sysbench.c270
-rw-r--r--modules/benchmark/zlib.c88
-rw-r--r--modules/computer.c1124
-rw-r--r--modules/computer/alsa.c71
-rw-r--r--modules/computer/boots.c68
-rw-r--r--modules/computer/display.c74
-rw-r--r--modules/computer/environment.c45
-rw-r--r--modules/computer/filesystem.c108
-rw-r--r--modules/computer/groups.c45
-rw-r--r--modules/computer/languages.c182
-rw-r--r--modules/computer/loadavg.c68
-rw-r--r--modules/computer/memory.c62
-rw-r--r--modules/computer/memory_usage.c125
-rw-r--r--modules/computer/modules.c400
-rw-r--r--modules/computer/os.c582
-rw-r--r--modules/computer/ubuntu_flavors.c90
-rw-r--r--modules/computer/uptime.c72
-rw-r--r--modules/computer/users.c61
-rw-r--r--modules/devices.c946
-rw-r--r--modules/devices/alpha/processor.c94
-rw-r--r--modules/devices/arm/arm_data.c235
-rw-r--r--modules/devices/arm/arm_data.h39
-rw-r--r--modules/devices/arm/processor.c515
-rw-r--r--modules/devices/battery.c385
-rw-r--r--modules/devices/devicetree.c307
-rw-r--r--modules/devices/devicetree/pmac_data.c98
-rw-r--r--modules/devices/devicetree/rpi_data.c179
-rw-r--r--modules/devices/dmi.c168
-rw-r--r--modules/devices/dmi_memory.c1050
-rw-r--r--modules/devices/e2k/processor.c420
-rw-r--r--modules/devices/firmware.c261
-rw-r--r--modules/devices/gpu.c291
-rw-r--r--modules/devices/ia64/processor.c215
-rw-r--r--modules/devices/inputdevices.c171
-rw-r--r--modules/devices/loongarch64/processor.c90
-rw-r--r--modules/devices/m68k/processor.c92
-rw-r--r--modules/devices/mips/processor.c81
-rw-r--r--modules/devices/monitors.c515
-rw-r--r--modules/devices/parisc/processor.c210
-rw-r--r--modules/devices/pci.c200
-rw-r--r--modules/devices/ppc/processor.c206
-rw-r--r--modules/devices/printers.c272
-rw-r--r--modules/devices/resources.c129
-rw-r--r--modules/devices/riscv/processor.c233
-rw-r--r--modules/devices/riscv/riscv_data.c212
-rw-r--r--modules/devices/riscv/riscv_data.h33
-rw-r--r--modules/devices/s390/processor.c180
-rw-r--r--modules/devices/sensors.c770
-rw-r--r--modules/devices/sh/processor.c93
-rw-r--r--modules/devices/sparc/processor.c80
-rw-r--r--modules/devices/spd-decode.c1272
-rw-r--r--modules/devices/spd-vendors.c292
-rw-r--r--modules/devices/storage.c820
-rw-r--r--modules/devices/usb.c224
-rw-r--r--modules/devices/x86/processor.c823
-rw-r--r--modules/devices/x86/x86_data.c400
-rw-r--r--modules/devices/x86/x86_data.h28
-rw-r--r--modules/network.c447
-rw-r--r--modules/network/net.c494
-rw-r--r--modules/network/nfs.c59
-rw-r--r--modules/network/samba.c123
78 files changed, 22061 insertions, 0 deletions
diff --git a/modules/benchmark.c b/modules/benchmark.c
new file mode 100644
index 00000000..018e30fe
--- /dev/null
+++ b/modules/benchmark.c
@@ -0,0 +1,946 @@
+/*
+ * HardInfo - Displays System Information
+ * Copyright (C) 2003-2009 L. A. F. Pereira <l@tia.mat.br>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, version 2 or later.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#define _GNU_SOURCE
+#include <config.h>
+#include <hardinfo.h>
+#include <iconcache.h>
+#include <shell.h>
+#include <syncmanager.h>
+
+#include <sys/resource.h>
+#include <sys/time.h>
+
+#include <signal.h>
+#include <sys/types.h>
+
+#include "appf.h"
+#include "benchmark.h"
+#include "cpu_util.h"
+
+#include "benchmark/bench_results.c"
+
+bench_value bench_results[BENCHMARK_N_ENTRIES];
+
+static void do_benchmark(void (*benchmark_function)(void), int entry);
+static gchar *benchmark_include_results_reverse(bench_value result,
+ const gchar *benchmark);
+static gchar *benchmark_include_results(bench_value result,
+ const gchar *benchmark);
+
+/* ModuleEntry entries, scan_*(), callback_*(), etc. */
+#include "benchmark/benches.c"
+
+char *bench_value_to_str(bench_value r)
+{
+ gboolean has_rev = r.revision >= 0;
+ gboolean has_extra = r.extra && *r.extra != 0;
+ gboolean has_user_note = r.user_note && *r.user_note != 0;
+ char *ret = g_strdup_printf("%lf; %lf; %d", r.result, r.elapsed_time,
+ r.threads_used);
+ if (has_rev || has_extra || has_user_note)
+ ret = appf(ret, "; ", "%d", r.revision);
+ if (has_extra || has_user_note)
+ ret = appf(ret, "; ", "%s", r.extra);
+ if (has_user_note)
+ ret = appf(ret, "; ", "%s", r.user_note);
+ return ret;
+}
+
+bench_value bench_value_from_str(const char *str)
+{
+ bench_value ret = EMPTY_BENCH_VALUE;
+ char rstr[32] = "", estr[32] = "", *p;
+ int t, c, v;
+ char extra[256], user_note[256];
+ if (str) {
+ /* try to handle floats from locales that use ',' or '.' as decimal sep
+ */
+ c = sscanf(
+ str, "%[-+0-9.,]; %[-+0-9.,]; %d; %d; %255[^\r\n;|]; %255[^\r\n;|]",
+ rstr, estr, &t, &v, extra, user_note);
+ if (c >= 3) {
+ if ((p = strchr(rstr, ','))) {
+ *p = '.';
+ }
+ if ((p = strchr(estr, ','))) {
+ *p = '.';
+ }
+ ret.result = g_ascii_strtod(rstr, NULL);
+ ret.elapsed_time = g_ascii_strtod(estr, NULL);
+ ret.threads_used = t;
+ }
+ if (c >= 4) {
+ ret.revision = v;
+ }
+ if (c >= 5) {
+ strcpy(ret.extra, extra);
+ }
+ if (c >= 6) {
+ strcpy(ret.user_note, user_note);
+ }
+ }
+ return ret;
+}
+
+typedef struct _ParallelBenchTask ParallelBenchTask;
+
+struct _ParallelBenchTask {
+ gint thread_number;
+ guint start, end;
+ gpointer data, callback;
+ int *stop;
+};
+
+static gpointer benchmark_crunch_for_dispatcher(gpointer data)
+{
+ ParallelBenchTask *pbt = (ParallelBenchTask *)data;
+ gpointer (*callback)(void *data, gint thread_number);
+ gpointer return_value = g_malloc(sizeof(double));
+ int count = 0;
+
+ if ((callback = pbt->callback)) {
+ while (!*pbt->stop) {
+ callback(pbt->data, pbt->thread_number);
+ /* don't count if didn't finish in time */
+ if (!*pbt->stop)
+ count++;
+ }
+ } else {
+ DEBUG("this is thread %p; callback is NULL and it should't be!",
+ g_thread_self());
+ }
+
+ g_free(pbt);
+
+ *(double *)return_value = (double)count;
+ return return_value;
+}
+
+bench_value benchmark_crunch_for(float seconds,
+ gint n_threads,
+ gpointer callback,
+ gpointer callback_data)
+{
+ int cpu_procs, cpu_cores, cpu_threads, cpu_nodes;
+ int thread_number, stop = 0;
+ GSList *threads = NULL, *t;
+ GTimer *timer;
+ bench_value ret = EMPTY_BENCH_VALUE;
+
+ timer = g_timer_new();
+
+ cpu_procs_cores_threads_nodes(&cpu_procs, &cpu_cores, &cpu_threads, &cpu_nodes);
+ if (n_threads > 0)
+ ret.threads_used = n_threads;
+ else if (n_threads < 0)
+ ret.threads_used = cpu_cores;
+ else
+ ret.threads_used = cpu_threads;
+
+ g_timer_start(timer);
+ for (thread_number = 0; thread_number < ret.threads_used; thread_number++) {
+ ParallelBenchTask *pbt = g_new0(ParallelBenchTask, 1);
+ GThread *thread;
+
+ DEBUG("launching thread %d", thread_number);
+
+ pbt->thread_number = thread_number;
+ pbt->data = callback_data;
+ pbt->callback = callback;
+ pbt->stop = &stop;
+
+#if GLIB_CHECK_VERSION(2,32,0)
+ thread = g_thread_new("dispatcher", (GThreadFunc)benchmark_crunch_for_dispatcher, pbt);
+#else
+ thread = g_thread_create((GThreadFunc)benchmark_crunch_for_dispatcher, pbt,TRUE,NULL);
+#endif
+ threads = g_slist_prepend(threads, thread);
+
+ DEBUG("thread %d launched as context %p", thread_number, thread);
+ }
+
+ /* wait for time */
+ // while ( g_timer_elapsed(timer, NULL) < seconds ) { }
+ g_usleep(seconds * 1000000);
+
+ /* signal all threads to stop */
+ stop = 1;
+ g_timer_stop(timer);
+
+ ret.result = 0;
+ DEBUG("waiting for all threads to finish");
+ for (t = threads; t; t = t->next) {
+ DEBUG("waiting for thread with context %p", t->data);
+ gpointer *rv = g_thread_join((GThread *)t->data);
+ ret.result += *(double *)rv;
+ g_free(rv);
+ }
+
+ ret.elapsed_time = g_timer_elapsed(timer, NULL);
+
+ g_slist_free(threads);
+ g_timer_destroy(timer);
+
+ return ret;
+}
+
+static gpointer benchmark_parallel_for_dispatcher(gpointer data)
+{
+ ParallelBenchTask *pbt = (ParallelBenchTask *)data;
+ gpointer (*callback)(unsigned int start, unsigned int end, void *data,
+ gint thread_number);
+ gpointer return_value = NULL;
+
+ if ((callback = pbt->callback)) {
+ DEBUG("this is thread %p; items %d -> %d, data %p", g_thread_self(),
+ pbt->start, pbt->end, pbt->data);
+ return_value =
+ callback(pbt->start, pbt->end, pbt->data, pbt->thread_number);
+ DEBUG("this is thread %p; return value is %p", g_thread_self(),
+ return_value);
+ } else {
+ DEBUG("this is thread %p; callback is NULL and it should't be!",
+ g_thread_self());
+ }
+
+ g_free(pbt);
+
+ return return_value;
+}
+
+/* one call for each thread to be used */
+bench_value
+benchmark_parallel(gint n_threads, gpointer callback, gpointer callback_data)
+{
+ int cpu_procs, cpu_cores, cpu_threads, cpu_nodes;
+ cpu_procs_cores_threads_nodes(&cpu_procs, &cpu_cores, &cpu_threads, &cpu_nodes);
+
+ if (n_threads == 0)
+ n_threads = cpu_threads;
+ else if (n_threads == -1)
+ n_threads = cpu_cores;
+
+ return benchmark_parallel_for(n_threads, 0, n_threads, callback,
+ callback_data);
+}
+
+/* Note:
+ * benchmark_parallel_for(): element [start] included, but [end] is excluded.
+ * callback(): expected to processes elements [start] through [end]
+ * inclusive.
+ */
+bench_value benchmark_parallel_for(gint n_threads,
+ guint start,
+ guint end,
+ gpointer callback,
+ gpointer callback_data)
+{
+ gchar *temp;
+ int cpu_procs, cpu_cores, cpu_threads, cpu_nodes;
+ guint iter_per_thread, iter, thread_number = 0;
+ GSList *threads = NULL, *t;
+ GTimer *timer;
+
+ bench_value ret = EMPTY_BENCH_VALUE;
+
+ timer = g_timer_new();
+
+ cpu_procs_cores_threads_nodes(&cpu_procs, &cpu_cores, &cpu_threads, &cpu_nodes);
+
+ if (n_threads > 0)
+ ret.threads_used = n_threads;
+ else if (n_threads < 0)
+ ret.threads_used = cpu_cores;
+ else
+ ret.threads_used = cpu_threads;
+
+ while (ret.threads_used > 0) {
+ iter_per_thread = (end - start) / ret.threads_used;
+
+ if (iter_per_thread == 0) {
+ DEBUG("not enough items per thread; disabling one thread");
+ ret.threads_used--;
+ } else {
+ break;
+ }
+ }
+
+ DEBUG("Using %d threads across %d logical processors; processing %d "
+ "elements (%d per thread)",
+ ret.threads_used, cpu_threads, (end - start), iter_per_thread);
+
+ g_timer_start(timer);
+ for (iter = start; iter < end;) {
+ ParallelBenchTask *pbt = g_new0(ParallelBenchTask, 1);
+ GThread *thread;
+
+ guint ts = iter, te = iter + iter_per_thread;
+ /* add the remainder of items/iter_per_thread to the last thread */
+ if (end - te < iter_per_thread)
+ te = end;
+ iter = te;
+
+ DEBUG("launching thread %d", 1 + thread_number);
+
+ pbt->thread_number = thread_number++;
+ pbt->start = ts;
+ pbt->end = te - 1;
+ pbt->data = callback_data;
+ pbt->callback = callback;
+
+#if GLIB_CHECK_VERSION(2,32,0)
+ thread = g_thread_new("dispatcher", (GThreadFunc)benchmark_parallel_for_dispatcher, pbt);
+#else
+ thread = g_thread_create((GThreadFunc)benchmark_parallel_for_dispatcher, pbt,TRUE,NULL);
+#endif
+
+ threads = g_slist_prepend(threads, thread);
+
+ DEBUG("thread %d launched as context %p", thread_number, thread);
+ }
+
+ DEBUG("waiting for all threads to finish");
+ for (t = threads; t; t = t->next) {
+ DEBUG("waiting for thread with context %p", t->data);
+ gpointer *rv = g_thread_join((GThread *)t->data);
+ if (rv) {
+ if (ret.result == -1.0)
+ ret.result = 0;
+ ret.result += *(double *)rv;
+ }
+ g_free(rv);
+ }
+
+ g_timer_stop(timer);
+ ret.elapsed_time = g_timer_elapsed(timer, NULL);
+
+ g_slist_free(threads);
+ g_timer_destroy(timer);
+
+ DEBUG("finishing; all threads took %f seconds to finish", ret.elapsed_time);
+
+ return ret;
+}
+
+gchar *hi_more_info(gchar *entry)
+{
+ const gchar *info = moreinfo_lookup_with_prefix("BENCH", entry);
+ return g_strdup(info ? info : "?");
+}
+
+gchar *hi_get_field(gchar *field)
+{
+ const gchar *info = moreinfo_lookup_with_prefix("BENCH", field);
+ return g_strdup(info ? info : field);
+}
+
+static void br_mi_add(char **results_list, bench_result *b, gboolean select)
+{
+ static unsigned int ri = 0; /* to ensure key is unique */
+ gchar *rkey, *lbl, *elbl, *this_marker;
+
+ if (select) {
+ this_marker = format_with_ansi_color(_("This Machine"), "0;30;43",
+ params.fmt_opts);
+ } else {
+ this_marker = "";
+ }
+
+ rkey = g_strdup_printf("%s__%d", b->machine->mid, ri++);
+
+ lbl = g_strdup_printf("%s%s%s%s", this_marker, select ? " " : "",
+ b->machine->cpu_name,
+ b->legacy ? problem_marker() : "");
+ elbl = key_label_escape(lbl);
+
+ *results_list = h_strdup_cprintf("$@%s%s$%s=%.2f|%s\n", *results_list,
+ select ? "*" : "", rkey, elbl,
+ b->bvalue.result, b->machine->cpu_config);
+
+ moreinfo_add_with_prefix("BENCH", rkey, bench_result_more_info(b));
+
+ g_free(lbl);
+ g_free(elbl);
+ g_free(rkey);
+ if (*this_marker)
+ g_free(this_marker);
+}
+gint bench_result_sort(gconstpointer a, gconstpointer b)
+{
+ bench_result *A = (bench_result *)a, *B = (bench_result *)b;
+ if (A->bvalue.result < B->bvalue.result)
+ return -1;
+ if (A->bvalue.result > B->bvalue.result)
+ return 1;
+ return 0;
+}
+
+struct append_machine_result_json_data {
+ GSList **result_list;
+ const gchar *benchmark_name;
+};
+
+static void append_machine_result_json(JsonArray *array,
+ guint index,
+ JsonNode *element_node,
+ gpointer user_data)
+{
+ struct append_machine_result_json_data *data = user_data;
+ bench_result *result;
+
+ result = bench_result_benchmarkjson(data->benchmark_name, element_node);
+ *data->result_list = g_slist_append(*data->result_list, result);
+}
+
+static GSList *benchmark_include_results_json(const gchar *path,
+ bench_value r,
+ const gchar *benchmark)
+{
+ JsonParser *parser;
+ JsonNode *root;
+ bench_result *this_machine = NULL;
+ GSList *result_list = NULL;
+ GError *error=NULL;
+
+ DEBUG("Loading benchmark results from JSON file %s", path);
+
+ parser = json_parser_new();
+ json_parser_load_from_file(parser, path, &error);
+ if(error){
+ DEBUG ("Unable to parse JSON %s %s", path, error->message);
+ g_error_free(error);
+ g_object_unref(parser);
+ return result_list;
+ }
+
+ root = json_parser_get_root(parser);
+ if (json_node_get_node_type(root) != JSON_NODE_OBJECT) goto out;
+
+ JsonObject *results = json_node_get_object(root);
+ if (results) {
+ JsonArray *machines = json_object_get_array_member(results, benchmark);
+
+ if (machines) {
+ struct append_machine_result_json_data data = {
+ .result_list = &result_list,
+ .benchmark_name = benchmark,
+ };
+ json_array_foreach_element(machines, append_machine_result_json,
+ &data);
+ }
+ }
+
+out:
+ g_object_unref(parser);
+
+ return result_list;
+}
+
+static gchar *find_benchmark_conf(void)
+{
+ const gchar *config_dir = g_get_user_config_dir();
+ gchar *path;
+
+ path = g_build_filename(config_dir, "hardinfo2", "benchmark.json", NULL);
+ if (g_file_test(path, G_FILE_TEST_EXISTS))
+ return path;
+ g_free(path);
+
+ path = g_build_filename(params.path_data, "benchmark.json", NULL);
+ if (g_file_test(path, G_FILE_TEST_EXISTS))
+ return path;
+ g_free(path);
+
+ return NULL;
+}
+
+struct bench_window {
+ int min, max;
+};
+
+static struct bench_window get_bench_window(GSList *result_list,
+ const bench_result *this_machine)
+{
+ struct bench_window window = {};
+ int size = params.max_bench_results;
+ int len = g_slist_length(result_list);
+
+ if (size == 0)
+ size = 1;
+ else if (size < 0)
+ size = len;
+
+ int loc = g_slist_index(result_list, this_machine); /* -1 if not found */
+ if (loc >= 0) {
+ window.min = loc - size / 2;
+ window.max = window.min + size;
+ if (window.min < 0) {
+ window.min = 0;
+ window.max = MIN(size, len);
+ } else if (window.max > len) {
+ window.max = len;
+ window.min = MAX(len - size, 0);
+ }
+ } else {
+ window.min = 0;
+ window.max = len;
+ }
+
+ DEBUG("...len: %d, loc: %d, win_size: %d, win: [%d..%d]\n", len, loc, size,
+ window.min, window.max - 1);
+
+ return window;
+}
+
+static gboolean is_in_bench_window(const struct bench_window *window, int i)
+{
+ return i >= window->min && i < window->max;
+}
+
+static gchar *benchmark_include_results_internal(bench_value this_machine_value,
+ const gchar *benchmark,
+ ShellOrderType order_type)
+{
+ bench_result *this_machine;
+ GSList *result_list, *li;
+ gchar *results = g_strdup("");
+ gchar *output;
+ gchar *path;
+ gint i;
+
+ path = find_benchmark_conf();
+ if (path) {
+ result_list = benchmark_include_results_json(
+ path, this_machine_value, benchmark);
+ }
+
+ /* this result */
+ if (this_machine_value.result > 0.0) {
+ this_machine = bench_result_this_machine(benchmark, this_machine_value);
+ result_list = g_slist_prepend(result_list, this_machine);
+ } else {
+ this_machine = NULL;
+ }
+
+ /* sort */
+ result_list = g_slist_sort(result_list, bench_result_sort);
+ if (order_type == SHELL_ORDER_DESCENDING)
+ result_list = g_slist_reverse(result_list);
+
+ /* prepare for shell */
+ moreinfo_del_with_prefix("BENCH");
+
+ const struct bench_window window =
+ get_bench_window(result_list, this_machine);
+ for (i = 0, li = result_list; li; li = g_slist_next(li), i++) {
+ bench_result *br = li->data;
+
+ if (is_in_bench_window(&window, i))
+ br_mi_add(&results, br, br == this_machine);
+
+ bench_result_free(br); /* no longer needed */
+ }
+ g_slist_free(result_list);
+
+ output = g_strdup_printf("[$ShellParam$]\n"
+ "Zebra=1\n"
+ "OrderType=%d\n"
+ "ViewType=4\n"
+ "ColumnTitle$Extra1=%s\n" /* CPU Clock */
+ "ColumnTitle$Progress=%s\n" /* Results */
+ "ColumnTitle$TextValue=%s\n" /* CPU */
+ "ShowColumnHeaders=true\n"
+ "[%s]\n%s",
+ order_type, _("CPU Config"), _("Results"),
+ _("CPU"), benchmark, results);
+
+ g_free(path);
+ g_free(results);
+
+ return output;
+}
+
+static gchar *benchmark_include_results_reverse(bench_value result,
+ const gchar *benchmark)
+{
+ return benchmark_include_results_internal(result, benchmark,
+ SHELL_ORDER_DESCENDING);
+}
+
+static gchar *benchmark_include_results(bench_value result,
+ const gchar *benchmark)
+{
+ return benchmark_include_results_internal(result, benchmark,
+ SHELL_ORDER_ASCENDING);
+}
+
+typedef struct _BenchmarkDialog BenchmarkDialog;
+struct _BenchmarkDialog {
+ GtkWidget *dialog;
+ bench_value r;
+};
+
+static gboolean
+do_benchmark_handler(GIOChannel *source, GIOCondition condition, gpointer data)
+{
+ BenchmarkDialog *bench_dialog = (BenchmarkDialog *)data;
+ GIOStatus status;
+ gchar *result;
+ bench_value r = EMPTY_BENCH_VALUE;
+
+ status = g_io_channel_read_line(source, &result, NULL, NULL, NULL);
+ if (status != G_IO_STATUS_NORMAL) {
+ DEBUG("error while reading benchmark result");
+ r.result = -1.0f;
+ bench_dialog->r = r;
+ gtk_widget_destroy(bench_dialog->dialog);
+ return FALSE;
+ }
+
+ r = bench_value_from_str(result);
+ /* attach a user note */
+ if (params.bench_user_note)
+ strncpy(r.user_note, params.bench_user_note, 255);
+ bench_dialog->r = r;
+
+ gtk_widget_destroy(bench_dialog->dialog);
+ g_free(result);
+
+ return FALSE;
+}
+
+static void do_benchmark(void (*benchmark_function)(void), int entry)
+{
+ int old_priority = 0;
+
+ if (params.skip_benchmarks)
+ return;
+
+ if (params.gui_running) {
+ gchar *argv[] = {params.argv0, "-b", entries[entry].name,
+ "-m", "benchmark.so", "-a",
+ NULL};
+ GPid bench_pid;
+ gint bench_stdout;
+ GtkWidget *bench_dialog;
+ GtkWidget *bench_image;
+ BenchmarkDialog *benchmark_dialog;
+ GSpawnFlags spawn_flags = G_SPAWN_STDERR_TO_DEV_NULL;
+ gchar *bench_status;
+ GtkWidget *content_area, *box, *label;
+ bench_value r = EMPTY_BENCH_VALUE;
+ bench_results[entry] = r;
+
+ bench_status =
+ g_strdup_printf(_("Benchmarking: <b>%s</b>."), entries[entry].name);
+
+ shell_view_set_enabled(FALSE);
+ shell_status_update(bench_status);
+
+ g_free(bench_status);
+
+ bench_image = icon_cache_get_image("benchmark.png");
+
+ bench_dialog = gtk_dialog_new_with_buttons ("Benchmarking...",
+ GTK_WINDOW(shell_get_main_shell()->transient_dialog),
+ GTK_DIALOG_DESTROY_WITH_PARENT | GTK_DIALOG_MODAL,
+ "Stop",
+ GTK_BUTTONS_CLOSE,
+ NULL);
+
+ gtk_widget_set_sensitive(GTK_WIDGET(shell_get_main_shell()->transient_dialog), FALSE);
+
+ content_area = gtk_dialog_get_content_area (GTK_DIALOG(bench_dialog));
+
+#if GTK_CHECK_VERSION(3,0,0)
+ box = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 1);
+#else
+ box = gtk_hbox_new(FALSE, 1);
+#endif
+ label = gtk_label_new ("Please do not move your mouse\n"
+ "or press any keys.");
+
+ gtk_widget_show (bench_image);
+
+#if GTK_CHECK_VERSION(3,0,0)
+ gtk_widget_set_halign (bench_image, GTK_ALIGN_START);
+#else
+ gtk_misc_set_alignment(GTK_MISC(bench_image), 0.0, 0.0);
+#endif
+
+ g_signal_connect_swapped (bench_dialog,
+ "response",
+ G_CALLBACK (gtk_widget_destroy),
+ bench_dialog);
+
+ gtk_box_pack_start (GTK_BOX(box), bench_image, TRUE, TRUE, 10);
+ gtk_box_pack_start (GTK_BOX(box), label, TRUE, TRUE, 10);
+ gtk_container_add (GTK_CONTAINER(content_area), box);
+
+ gtk_window_set_deletable(GTK_WINDOW(bench_dialog), FALSE);
+ gtk_widget_show_all (bench_dialog);
+
+ while (gtk_events_pending()) {
+ gtk_main_iteration();
+ }
+
+ benchmark_dialog = g_new0(BenchmarkDialog, 1);
+ benchmark_dialog->dialog = bench_dialog;
+ benchmark_dialog->r = r;
+
+ if (!g_path_is_absolute(params.argv0)) {
+ spawn_flags |= G_SPAWN_SEARCH_PATH;
+ }
+
+ if (g_spawn_async_with_pipes(NULL, argv, NULL, spawn_flags, NULL, NULL,
+ &bench_pid, NULL, &bench_stdout, NULL,
+ NULL)) {
+ GIOChannel *channel;
+ guint watch_id;
+
+ DEBUG("spawning benchmark; pid=%d", bench_pid);
+
+ channel = g_io_channel_unix_new(bench_stdout);
+ watch_id = g_io_add_watch(channel, G_IO_IN, do_benchmark_handler,
+ benchmark_dialog);
+
+ switch (gtk_dialog_run(GTK_DIALOG(bench_dialog))) {
+ case GTK_RESPONSE_NONE:
+ DEBUG("benchmark finished");
+ break;
+ case GTK_RESPONSE_ACCEPT:
+ DEBUG("cancelling benchmark");
+
+ gtk_widget_destroy(bench_dialog);
+ g_source_remove(watch_id);
+ kill(bench_pid, SIGINT);
+ }
+
+ bench_results[entry] = benchmark_dialog->r;
+
+ g_io_channel_unref(channel);
+ shell_view_set_enabled(TRUE);
+ shell_status_set_enabled(TRUE);
+ gtk_widget_set_sensitive(
+ GTK_WIDGET(shell_get_main_shell()->transient_dialog), TRUE);
+ g_free(benchmark_dialog);
+
+ shell_status_update(_("Done."));
+
+ return;
+ }
+
+ gtk_widget_destroy(bench_dialog);
+ g_free(benchmark_dialog);
+ shell_status_set_enabled(TRUE);
+ shell_status_update(_("Done."));
+ }
+
+ setpriority(PRIO_PROCESS, 0, -20);
+ benchmark_function();
+ setpriority(PRIO_PROCESS, 0, old_priority);
+}
+
+gchar *hi_module_get_name(void) { return g_strdup(_("Benchmarks")); }
+
+guchar hi_module_get_weight(void) { return 240; }
+
+ModuleEntry *hi_module_get_entries(void) { return entries; }
+
+const ModuleAbout *hi_module_get_about(void)
+{
+ static const ModuleAbout ma = {
+ .author = "L. A. F. Pereira",
+ .description = N_("Perform tasks and compare with other systems"),
+ .version = VERSION,
+ .license = "GNU GPL version 2 or later.",
+ };
+
+ return &ma;
+}
+
+static gchar *get_benchmark_results(gsize *len)
+{
+ void (*scan_callback)(gboolean);
+ JsonBuilder *builder;
+ JsonGenerator *generator;
+ JsonNode *root;
+ bench_machine *this_machine;
+ gchar *out;
+ guint i;
+
+ for (i = 0; i < G_N_ELEMENTS(entries); i++) {
+ if (!entries[i].name || !entries[i].scan_callback)
+ continue;
+ if (entries[i].flags & MODULE_FLAG_HIDE)
+ continue;
+
+ scan_callback = entries[i].scan_callback;
+ if (scan_callback)
+ scan_callback(bench_results[i].result < 0.0);
+ }
+
+ this_machine = bench_machine_this();
+ builder = json_builder_new();
+ json_builder_begin_object(builder);
+ for (i = 0; i < G_N_ELEMENTS(entries); i++) {
+ if (!entries[i].name || entries[i].flags & MODULE_FLAG_HIDE)
+ continue;
+ if (bench_results[i].result < 0.0) {
+ /* Benchmark failed? */
+ continue;
+ }
+
+ json_builder_set_member_name(builder, entries[i].name);
+
+ json_builder_begin_object(builder);
+
+#define ADD_JSON_VALUE(type, name, value) \
+ do { \
+ json_builder_set_member_name(builder, (name)); \
+ json_builder_add_##type##_value(builder, (value)); \
+ } while (0)
+
+ ADD_JSON_VALUE(string, "Board", this_machine->board);
+ ADD_JSON_VALUE(int, "MemoryInKiB", this_machine->memory_kiB);
+ ADD_JSON_VALUE(string, "CpuName", this_machine->cpu_name);
+ ADD_JSON_VALUE(string, "CpuDesc", this_machine->cpu_desc);
+ ADD_JSON_VALUE(string, "CpuConfig", this_machine->cpu_config);
+ ADD_JSON_VALUE(string, "CpuConfig", this_machine->cpu_config);
+ ADD_JSON_VALUE(string, "OpenGlRenderer", this_machine->ogl_renderer);
+ ADD_JSON_VALUE(string, "GpuDesc", this_machine->gpu_desc);
+ ADD_JSON_VALUE(int, "NumCpus", this_machine->processors);
+ ADD_JSON_VALUE(int, "NumCores", this_machine->cores);
+ ADD_JSON_VALUE(int, "NumNodes", this_machine->nodes);
+ ADD_JSON_VALUE(int, "NumThreads", this_machine->threads);
+ ADD_JSON_VALUE(string, "MachineId", this_machine->mid);
+ ADD_JSON_VALUE(int, "PointerBits", this_machine->ptr_bits);
+ ADD_JSON_VALUE(boolean, "DataFromSuperUser", this_machine->is_su_data);
+ ADD_JSON_VALUE(int, "PhysicalMemoryInMiB",
+ this_machine->memory_phys_MiB);
+ ADD_JSON_VALUE(string, "MemoryTypes", this_machine->ram_types);
+ ADD_JSON_VALUE(int, "MachineDataVersion",
+ this_machine->machine_data_version);
+ ADD_JSON_VALUE(string, "MachineType", this_machine->machine_type);
+
+ ADD_JSON_VALUE(boolean, "Legacy", FALSE);
+ ADD_JSON_VALUE(string, "ExtraInfo", bench_results[i].extra);
+ ADD_JSON_VALUE(string, "UserNote", bench_results[i].user_note);
+ ADD_JSON_VALUE(double, "BenchmarkResult", bench_results[i].result);
+ ADD_JSON_VALUE(double, "ElapsedTime", bench_results[i].elapsed_time);
+ ADD_JSON_VALUE(int, "UsedThreads", bench_results[i].threads_used);
+ ADD_JSON_VALUE(int, "BenchmarkVersion", bench_results[i].revision);
+
+#undef ADD_JSON_VALUE
+
+ json_builder_end_object(builder);
+ }
+ json_builder_end_object(builder);
+
+ generator = json_generator_new();
+ json_generator_set_root(generator, json_builder_get_root(builder));
+ json_generator_set_pretty(generator, TRUE);
+
+ out = json_generator_to_data(generator, len);
+
+ g_object_unref(generator);
+ g_object_unref(builder);
+ bench_machine_free(this_machine);
+
+ return out;
+}
+
+static gchar *run_benchmark(gchar *name)
+{
+ int i;
+
+ DEBUG("name = %s", name);
+
+ for (i = 0; entries[i].name; i++) {
+ if (g_str_equal(entries[i].name, name)) {
+ void (*scan_callback)(gboolean rescan);
+
+ if ((scan_callback = entries[i].scan_callback)) {
+ scan_callback(FALSE);
+
+#define CHK_RESULT_FORMAT(F) \
+ (params.result_format && strcmp(params.result_format, F) == 0)
+
+ if (params.run_benchmark) {
+ /* attach the user note */
+ if (params.bench_user_note)
+ strncpy(bench_results[i].user_note,
+ params.bench_user_note, 255);
+
+ if (CHK_RESULT_FORMAT("shell")) {
+ bench_result *b =
+ bench_result_this_machine(name, bench_results[i]);
+ char *temp = bench_result_more_info_complete(b);
+ bench_result_free(b);
+ return temp;
+ }
+ /* defaults to "short" which is below */
+ }
+
+ return bench_value_to_str(bench_results[i]);
+ }
+ }
+ }
+
+ return NULL;
+}
+
+const ShellModuleMethod *hi_exported_methods(void)
+{
+ static const ShellModuleMethod m[] = {
+ {"runBenchmark", run_benchmark},
+ {NULL},
+ };
+
+ return m;
+}
+
+void hi_module_init(void)
+{
+ static SyncEntry se[] = {
+ {
+ .name = N_("Send benchmark results"),
+ .file_name = "benchmark.json",
+ .generate_contents_for_upload = get_benchmark_results,
+ },
+ {
+ .name = N_("Receive benchmark results"),
+ .file_name = "benchmark.json",
+ },
+ };
+
+ sync_manager_add_entry(&se[0]);
+ sync_manager_add_entry(&se[1]);
+
+ guint i;
+ for (i = 0; i < G_N_ELEMENTS(entries) - 1 /* account for NULL */; i++)
+ bench_results[i] = (bench_value)EMPTY_BENCH_VALUE;
+}
+
+gchar **hi_module_get_dependencies(void)
+{
+ static gchar *deps[] = {"devices.so", NULL};
+
+ return deps;
+}
diff --git a/modules/benchmark/bench_results.c b/modules/benchmark/bench_results.c
new file mode 100644
index 00000000..18ed0739
--- /dev/null
+++ b/modules/benchmark/bench_results.c
@@ -0,0 +1,605 @@
+/*
+ * HardInfo - Displays System Information
+ * Copyright (C) 2020 L. A. F. Pereira <l@tia.mat.br>
+ * This file:
+ * Copyright (C) 2017 Burt P. <pburt0@gmail.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, version 2 or later.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#include <stdlib.h>
+#include <locale.h>
+#include <inttypes.h>
+#include <json-glib/json-glib.h>
+#include "nice_name.h"
+
+/* in dmi_memory.c */
+uint64_t memory_devices_get_system_memory_MiB();
+gchar *memory_devices_get_system_memory_types_str();
+
+/*/ Used for an unknown value. Having it in only one place cleans up the .po
+ * line references */
+static const char *unk = N_("(Unknown)");
+
+typedef struct {
+ char *board;
+ uint64_t memory_kiB; /* from /proc/meminfo -> MemTotal */
+ char *cpu_name;
+ char *cpu_desc;
+ char *cpu_config;
+ char *ogl_renderer;
+ char *gpu_desc;
+ int processors;
+ int cores;
+ int threads;
+ int nodes;
+ char *mid;
+ int ptr_bits; /* 32, 64... BENCH_PTR_BITS; 0 for unspecified */
+ int is_su_data; /* 1 = data collected as root */
+ uint64_t memory_phys_MiB; /* from DMI/SPD/DTree/Table/Blocks, etc. */
+ char *ram_types;
+ int machine_data_version;
+ char *machine_type;
+} bench_machine;
+
+typedef struct {
+ char *name;
+ bench_value bvalue;
+ bench_machine *machine;
+ int legacy; /* an old benchmark.conf result */
+} bench_result;
+
+static char *cpu_config_retranslate(char *str, int force_en, int replacing)
+{
+ char *new_str = NULL;
+ char *mhz = (force_en) ? "MHz" : _("MHz");
+ char *c = str, *tmp;
+ int t;
+ float f;
+
+ if (str != NULL) {
+ new_str = strdup("");
+ if (strchr(str, 'x')) {
+ while (c != NULL && (sscanf(c, "%dx %f", &t, &f)==2)) {
+ tmp = g_strdup_printf("%s%s%dx %.2f %s", new_str,
+ strlen(new_str) ? " + " : "", t, f, mhz);
+ free(new_str);
+ new_str = tmp;
+ c = strchr(c + 1, '+');
+ if (c)
+ c++; /* move past the + */
+ }
+ } else {
+ sscanf(c, "%f", &f);
+ tmp = g_strdup_printf("%s%s%dx %.2f %s", new_str,
+ strlen(new_str) ? " + " : "", 1, f, mhz);
+ free(new_str);
+ new_str = tmp;
+ }
+
+ if (replacing)
+ free(str);
+ }
+
+ return new_str;
+}
+
+/* "2x 1400.00 MHz + 2x 800.00 MHz" -> 4400.0 */
+static float cpu_config_val(char *str)
+{
+ char *c = str;
+ int t;
+ float f, r = 0.0;
+ if (str != NULL) {
+ if (strchr(str, 'x')) {
+ while (c != NULL && (sscanf(c, "%dx %f", &t, &f)==2)) {
+ r += f * t;
+ c = strchr(c + 1, '+');
+ if (c)
+ c++; /* move past the + */
+ }
+ } else {
+ sscanf(c, "%f", &r);
+ }
+ }
+ return r;
+}
+
+static int cpu_config_cmp(char *str0, char *str1)
+{
+ float r0, r1;
+ r0 = cpu_config_val(str0);
+ r1 = cpu_config_val(str1);
+ if (r0 == r1)
+ return 0;
+ if (r0 < r1)
+ return -1;
+ return 1;
+}
+
+static int cpu_config_is_close(char *str0, char *str1)
+{
+ float r0, r1, r1n;
+ r0 = cpu_config_val(str0);
+ r1 = cpu_config_val(str1);
+ r1n = r1 * .9;
+ if (r0 > r1n && r0 < r1)
+ return 1;
+ return 0;
+}
+
+static void gen_machine_id(bench_machine *m)
+{
+ char *s;
+
+ if (m) {
+ if (m->mid != NULL)
+ free(m->mid);
+
+ /* Don't try and translate unknown. The mid string needs to be made of
+ * all untranslated elements.*/
+ m->mid = g_strdup_printf("%s;%s;%.2f",
+ (m->board != NULL) ? m->board : "(Unknown)",
+ m->cpu_name, cpu_config_val(m->cpu_config));
+ for (s = m->mid; *s; s++) {
+ if (!isalnum(*s) && *s != '(' && *s != ')' && *s != ';')
+ *s = '_';
+ }
+ }
+}
+
+bench_machine *bench_machine_new()
+{
+ return calloc(1, sizeof(bench_machine));
+}
+
+bench_machine *bench_machine_this()
+{
+ bench_machine *m = NULL;
+ char *tmp;
+
+ m = bench_machine_new();
+ if (m) {
+ m->ptr_bits = BENCH_PTR_BITS;
+ m->is_su_data = (getuid() == 0);
+ m->board = module_call_method("devices::getMotherboard");
+ m->cpu_name = module_call_method("devices::getProcessorName");
+ m->cpu_desc = module_call_method("devices::getProcessorDesc");
+ m->cpu_config =
+ module_call_method("devices::getProcessorFrequencyDesc");
+ m->gpu_desc = module_call_method("devices::getGPUList");
+ m->ogl_renderer = module_call_method("computer::getOGLRenderer");
+ tmp = module_call_method("computer::getMemoryTotal");
+ m->memory_kiB = strtoull(tmp, NULL, 10);
+ m->memory_phys_MiB = memory_devices_get_system_memory_MiB();
+ m->ram_types = memory_devices_get_system_memory_types_str();
+ m->machine_type = module_call_method("computer::getMachineType");
+ free(tmp);
+
+ cpu_procs_cores_threads_nodes(&m->processors, &m->cores, &m->threads, &m->nodes);
+ gen_machine_id(m);
+ }
+ return m;
+}
+
+void bench_machine_free(bench_machine *s)
+{
+ if (s) {
+ free(s->board);
+ free(s->cpu_name);
+ free(s->cpu_desc);
+ free(s->cpu_config);
+ free(s->mid);
+ free(s->ram_types);
+ free(s->machine_type);
+ free(s);
+ }
+}
+
+void bench_result_free(bench_result *s)
+{
+ if (s) {
+ free(s->name);
+ bench_machine_free(s->machine);
+ g_free(s);
+ }
+}
+
+bench_result *bench_result_this_machine(const char *bench_name, bench_value r)
+{
+ bench_result *b = NULL;
+
+ b = malloc(sizeof(bench_result));
+ if (b) {
+ memset(b, 0, sizeof(bench_result));
+ b->machine = bench_machine_this();
+ b->name = strdup(bench_name);
+ b->bvalue = r;
+ b->legacy = 0;
+ }
+ return b;
+}
+
+/* -1 for none */
+static int nx_prefix(const char *str)
+{
+ char *s, *x;
+ if (str != NULL) {
+ s = (char *)str;
+ x = strchr(str, 'x');
+ if (x && x - s >= 1) {
+ while (s != x) {
+ if (!isdigit(*s))
+ return -1;
+ s++;
+ }
+ *x = 0;
+ return atoi(str);
+ }
+ }
+ return -1;
+}
+
+/* old results didn't store the actual number of threads used */
+static int guess_threads_old_result(const char *bench_name,
+ int threads_available)
+{
+#define CHKBNAME(BN) (strcmp(bench_name, BN) == 0)
+ if (CHKBNAME("CPU Fibonacci"))
+ return 1;
+ if (CHKBNAME("FPU FFT")) {
+ if (threads_available >= 4)
+ return 4;
+ else if (threads_available >= 2)
+ return 2;
+ else
+ return 1;
+ }
+ if (CHKBNAME("CPU N-Queens")) {
+ if (threads_available >= 10)
+ return 10;
+ else if (threads_available >= 5)
+ return 5;
+ else if (threads_available >= 2)
+ return 2;
+ else
+ return 1;
+ }
+ return threads_available;
+}
+
+static gboolean cpu_name_needs_cleanup(const char *cpu_name)
+{
+ return strstr(cpu_name, "Intel") || strstr(cpu_name, "AMD") ||
+ strstr(cpu_name, "VIA") || strstr(cpu_name, "Cyrix");
+}
+
+static void filter_invalid_chars(gchar *str)
+{
+ gchar *p;
+
+ for (p = str; *p; p++) {
+ if (*p == '\n' || *p == ';' || *p == '|')
+ *p = '_';
+ }
+}
+
+static gboolean json_get_boolean(JsonObject *obj, const gchar *key)
+{
+ if (!json_object_has_member(obj, key))
+ return FALSE;
+ return json_object_get_boolean_member(obj, key);
+}
+
+static double json_get_double(JsonObject *obj, const gchar *key)
+{
+ if (!json_object_has_member(obj, key))
+ return 0;
+ return json_object_get_double_member(obj, key);
+}
+
+static int json_get_int(JsonObject *obj, const gchar *key)
+{
+ if (!json_object_has_member(obj, key))
+ return 0;
+ return json_object_get_int_member(obj, key);
+}
+
+static const gchar *json_get_string(JsonObject *obj, const gchar *key)
+{
+ if (!json_object_has_member(obj, key))
+ return "";
+ return json_object_get_string_member(obj, key);
+}
+
+static gchar *json_get_string_dup(JsonObject *obj, const gchar *key)
+{
+ return g_strdup(json_get_string(obj, key));
+}
+
+static double parse_frequency(const char *freq)
+{
+ static locale_t locale;
+
+ if (!locale)
+ locale = newlocale(LC_NUMERIC_MASK, "C", NULL);
+
+ return strtod_l(freq, NULL, locale);
+}
+
+static void append_cpu_config(JsonObject *object,
+ const gchar *member_name,
+ JsonNode *member_node,
+ gpointer user_data)
+{
+ GString *output = user_data;
+
+ if (output->len)
+ g_string_append(output, ", ");
+
+ g_string_append_printf(output, "%ldx %.2f %s", (long int)json_node_get_int(member_node),
+ parse_frequency(member_name), _("MHz"));
+}
+
+static gchar *get_cpu_config(JsonObject *machine)
+{
+ JsonObject *cpu_config_map =
+ json_object_get_object_member(machine, "CpuConfigMap");
+
+ if (!cpu_config_map)
+ return json_get_string_dup(machine, "CpuConfig");
+
+ GString *output = g_string_new(NULL);
+ json_object_foreach_member(cpu_config_map, append_cpu_config, output);
+ return g_string_free(output, FALSE);
+}
+
+static gchar *get_cpu_desc(JsonObject *machine)
+{
+ int num_cpus = json_get_int(machine, "NumCpus");
+
+ if (!num_cpus)
+ return json_get_string_dup(machine, "CpuDesc");
+
+ /* FIXME: This is adapted from processor_describe_default() in
+ * devices.c! */
+
+ int num_cores = json_get_int(machine, "NumCores");
+ int num_threads = json_get_int(machine, "NumThreads");
+ int num_nodes = json_get_int(machine, "NumNodes");
+ const char *packs_fmt =
+ ngettext("%d physical processor", "%d physical processors", num_cpus);
+ const char *cores_fmt = ngettext("%d core", "%d cores", num_cores);
+ const char *threads_fmt = ngettext("%d thread", "%d threads", num_threads);
+ char *full_fmt, *ret;
+
+ if (num_nodes > 1) {
+ const char *nodes_fmt =
+ ngettext("%d NUMA node", "%d NUMA nodes", num_nodes);
+
+ full_fmt = g_strdup_printf(
+ _(/*/NP procs; NC cores across NN nodes; NT threads*/
+ "%s; %s across %s; %s"),
+ packs_fmt, cores_fmt, nodes_fmt, threads_fmt);
+ ret = g_strdup_printf(full_fmt, num_cpus, num_cores * num_nodes, num_nodes, num_threads);
+ } else {
+ full_fmt =
+ g_strdup_printf(_(/*/NP procs; NC cores; NT threads*/ "%s; %s; %s"),
+ packs_fmt, cores_fmt, threads_fmt);
+ ret = g_strdup_printf(full_fmt, num_cpus, num_cores, num_threads);
+ }
+
+ free(full_fmt);
+ return ret;
+}
+
+bench_result *bench_result_benchmarkjson(const gchar *bench_name,
+ JsonNode *node)
+{
+ JsonObject *machine;
+ bench_result *b;
+ gchar *p;
+
+ if (json_node_get_node_type(node) != JSON_NODE_OBJECT)
+ return NULL;
+
+ machine = json_node_get_object(node);
+
+ b = g_new0(bench_result, 1);
+ b->name = g_strdup(bench_name);
+ b->legacy = json_get_boolean(machine, "Legacy");
+
+ b->bvalue = (bench_value){
+ .result = json_get_double(machine, "BenchmarkResult"),
+ .elapsed_time = json_get_double(machine, "ElapsedTime"),
+ .threads_used = json_get_int(machine, "UsedThreads"),
+ .revision = json_get_int(machine, "BenchmarkRevision"),
+ };
+
+ snprintf(b->bvalue.extra, sizeof(b->bvalue.extra), "%s",
+ json_get_string(machine, "ExtraInfo"));
+ filter_invalid_chars(b->bvalue.extra);
+
+ snprintf(b->bvalue.user_note, sizeof(b->bvalue.user_note), "%s",
+ json_get_string(machine, "UserNote"));
+ filter_invalid_chars(b->bvalue.user_note);
+
+ int nodes = json_get_int(machine, "NumNodes");
+
+ if (nodes == 0)
+ nodes = 1;
+
+ b->machine = bench_machine_new();
+ *b->machine = (bench_machine){
+ .board = json_get_string_dup(machine, "Board"),
+ .memory_kiB = json_get_int(machine, "MemoryInKiB"),
+ .cpu_name = json_get_string_dup(machine, "CpuName"),
+ .cpu_desc = get_cpu_desc(machine),
+ .cpu_config = get_cpu_config(machine),
+ .ogl_renderer = json_get_string_dup(machine, "OpenGlRenderer"),
+ .gpu_desc = json_get_string_dup(machine, "GpuDesc"),
+ .processors = json_get_int(machine, "NumCpus"),
+ .cores = json_get_int(machine, "NumCores"),
+ .threads = json_get_int(machine, "NumThreads"),
+ .nodes = nodes,
+ .mid = json_get_string_dup(machine, "MachineId"),
+ .ptr_bits = json_get_int(machine, "PointerBits"),
+ .is_su_data = json_get_boolean(machine, "DataFromSuperUser"),
+ .memory_phys_MiB = json_get_int(machine, "PhysicalMemoryInMiB"),
+ .ram_types = json_get_string_dup(machine, "MemoryTypes"),
+ .machine_data_version = json_get_int(machine, "MachineDataVersion"),
+ .machine_type = json_get_string_dup(machine, "MachineType"),
+ };
+
+ return b;
+}
+
+static char *bench_result_more_info_less(bench_result *b)
+{
+ char *memory = NULL;
+ if (b->machine->memory_phys_MiB) {
+ memory =
+ g_strdup_printf("%" PRId64 " %s %s", b->machine->memory_phys_MiB,
+ _("MiB"), b->machine->ram_types);
+ } else {
+ memory =
+ (b->machine->memory_kiB > 0)
+ ? g_strdup_printf("%" PRId64 " %s %s", b->machine->memory_kiB,
+ _("kiB"), problem_marker())
+ : g_strdup(_(unk));
+ }
+ char bench_str[256] = "";
+ if (b->bvalue.revision >= 0)
+ snprintf(bench_str, 127, "%d", b->bvalue.revision);
+ char bits[24] = "";
+ if (b->machine->ptr_bits)
+ snprintf(bits, 23, _("%d-bit"), b->machine->ptr_bits);
+
+ char *ret = g_strdup_printf(
+ "[%s]\n"
+ /* threads */ "%s=%d\n"
+ /* elapsed */ "%s=%0.4f %s\n"
+ "%s=%s\n"
+ "%s=%s\n"
+ "%s=%s\n"
+ /* legacy */ "%s%s=%s\n"
+ "[%s]\n"
+ /* board */ "%s=%s\n"
+ /* machine_type */ "%s=%s\n"
+ /* cpu */ "%s=%s\n"
+ /* cpudesc */ "%s=%s\n"
+ /* cpucfg */ "%s=%s\n"
+ /* threads */ "%s=%d\n"
+ /* gpu desc */ "%s=%s\n"
+ /* ogl rend */ "%s=%s\n"
+ /* mem */ "%s=%s\n"
+ /* bits */ "%s=%s\n",
+ _("Benchmark Result"), _("Threads"), b->bvalue.threads_used,
+ _("Elapsed Time"), b->bvalue.elapsed_time, _("seconds"),
+ *bench_str ? _("Revision") : "#Revision", bench_str,
+ *b->bvalue.extra ? _("Extra Information") : "#Extra", b->bvalue.extra,
+ *b->bvalue.user_note ? _("User Note") : "#User Note",
+ b->bvalue.user_note, b->legacy ? problem_marker() : "",
+ b->legacy ? _("Note") : "#Note",
+ b->legacy ? _("This result is from an old version of HardInfo. Results "
+ "might not be comparable to current version. Some "
+ "details are missing.")
+ : "",
+ _("Machine"),
+ _("Board"), (b->machine->board != NULL) ? b->machine->board : _(unk),
+ _("Machine Type"), (b->machine->machine_type != NULL) ? b->machine->machine_type : _(unk),
+ _("CPU Name"), b->machine->cpu_name,
+ _("CPU Description"), (b->machine->cpu_desc != NULL) ? b->machine->cpu_desc : _(unk),
+ _("CPU Config"), b->machine->cpu_config,
+ _("Threads Available"), b->machine->threads,
+ _("GPU"), (b->machine->gpu_desc != NULL) ? b->machine->gpu_desc : _(unk),
+ _("OpenGL Renderer"), (b->machine->ogl_renderer != NULL) ? b->machine->ogl_renderer : _(unk),
+ _("Memory"), memory,
+ b->machine->ptr_bits ? _("Pointer Size") : "#AddySize", bits);
+ free(memory);
+ return ret;
+}
+
+static char *bench_result_more_info_complete(bench_result *b)
+{
+ char bench_str[256] = "";
+ strncpy(bench_str, b->name, 127);
+ if (b->bvalue.revision >= 0)
+ snprintf(bench_str + strlen(bench_str), 127, " (r%d)",
+ b->bvalue.revision);
+ char bits[24] = "";
+ if (b->machine->ptr_bits)
+ snprintf(bits, 23, _("%d-bit"), b->machine->ptr_bits);
+
+ return g_strdup_printf(
+ "[%s]\n"
+ /* bench name */ "%s=%s\n"
+ /* threads */ "%s=%d\n"
+ /* result */ "%s=%0.2f\n"
+ /* elapsed */ "%s=%0.4f %s\n"
+ "%s=%s\n"
+ "%s=%s\n"
+ /* legacy */ "%s%s=%s\n"
+ "[%s]\n"
+ /* board */ "%s=%s\n"
+ /* machine_type */ "%s=%s\n"
+ /* cpu */ "%s=%s\n"
+ /* cpudesc */ "%s=%s\n"
+ /* cpucfg */ "%s=%s\n"
+ /* threads */ "%s=%d\n"
+ /* gpu desc */ "%s=%s\n"
+ /* ogl rend */ "%s=%s\n"
+ /* mem */ "%s=%" PRId64 " %s\n"
+ /* mem phys */ "%s=%" PRId64 " %s %s\n"
+ /* bits */ "%s=%s\n"
+ "%s=%d\n"
+ "%s=%d\n"
+ "[%s]\n"
+ /* mid */ "%s=%s\n"
+ /* cfg_val */ "%s=%.2f\n",
+ _("Benchmark Result"), _("Benchmark"), bench_str, _("Threads"),
+ b->bvalue.threads_used, _("Result"), b->bvalue.result,
+ _("Elapsed Time"), b->bvalue.elapsed_time, _("seconds"),
+ *b->bvalue.extra ? _("Extra Information") : "#Extra", b->bvalue.extra,
+ *b->bvalue.user_note ? _("User Note") : "#User Note",
+ b->bvalue.user_note, b->legacy ? problem_marker() : "",
+ b->legacy ? _("Note") : "#Note",
+ b->legacy ? _("This result is from an old version of HardInfo. Results "
+ "might not be comparable to current version. Some "
+ "details are missing.")
+ : "",
+ _("Machine"), _("Board"),
+ (b->machine->board != NULL) ? b->machine->board : _(unk),
+ _("Machine Type"), (b->machine->machine_type != NULL) ? b->machine->machine_type : _(unk),
+ _("CPU Name"),
+ b->machine->cpu_name, _("CPU Description"),
+ (b->machine->cpu_desc != NULL) ? b->machine->cpu_desc : _(unk),
+ _("CPU Config"), b->machine->cpu_config, _("Threads Available"),
+ b->machine->threads, _("GPU"),
+ (b->machine->gpu_desc != NULL) ? b->machine->gpu_desc : _(unk),
+ _("OpenGL Renderer"),
+ (b->machine->ogl_renderer != NULL) ? b->machine->ogl_renderer : _(unk),
+ _("Memory"), b->machine->memory_kiB, _("kiB"), _("Physical Memory"),
+ b->machine->memory_phys_MiB, _("MiB"), b->machine->ram_types,
+ b->machine->ptr_bits ? _("Pointer Size") : "#AddySize", bits,
+ ".machine_data_version", b->machine->machine_data_version,
+ ".is_su_data", b->machine->is_su_data, _("Handles"), _("mid"),
+ b->machine->mid, _("cfg_val"), cpu_config_val(b->machine->cpu_config));
+}
+
+char *bench_result_more_info(bench_result *b)
+{
+ // return bench_result_more_info_complete(b);
+ return bench_result_more_info_less(b);
+}
diff --git a/modules/benchmark/bench_util.c b/modules/benchmark/bench_util.c
new file mode 100644
index 00000000..d9e5bc55
--- /dev/null
+++ b/modules/benchmark/bench_util.c
@@ -0,0 +1,58 @@
+
+#include "benchmark.h"
+#include "md5.h"
+
+gchar *get_test_data(gsize min_size) {
+ gchar *bdata_path, *data;
+ gsize data_size;
+
+ gchar *exp_data, *p;
+ gsize sz;
+
+ bdata_path = g_build_filename(params.path_data, "benchmark.data", NULL);
+ if (!g_file_get_contents(bdata_path, &data, &data_size, NULL)) {
+ g_free(bdata_path);
+ return NULL;
+ }
+
+ if (data_size < min_size) {
+ DEBUG("expanding %lu bytes of test data to %lu bytes", data_size, min_size);
+ exp_data = g_malloc(min_size + 1);
+ memcpy(exp_data, data, data_size);
+ p = exp_data + data_size;
+ sz = data_size;
+ while (sz < (min_size - data_size) ) {
+ memcpy(p, data, data_size);
+ p += data_size;
+ sz += data_size;
+ }
+ strncpy(p, data, min_size - sz);
+ g_free(data);
+ data = exp_data;
+ }
+ g_free(bdata_path);
+
+ return data;
+}
+
+char *digest_to_str(const char *digest, int len) {
+ int max = len * 2;
+ char *ret = malloc(max+1);
+ char *p = ret;
+ memset(ret, 0, max+1);
+ int i = 0;
+ for(; i < len; i++) {
+ unsigned char byte = digest[i];
+ p += sprintf(p, "%02x", byte);
+ }
+ return ret;
+}
+
+char *md5_digest_str(const char *data, unsigned int len) {
+ struct MD5Context ctx;
+ guchar digest[16];
+ MD5Init(&ctx);
+ MD5Update(&ctx, (guchar *)data, len);
+ MD5Final(digest, &ctx);
+ return digest_to_str(digest, 16);
+}
diff --git a/modules/benchmark/benches.c b/modules/benchmark/benches.c
new file mode 100644
index 00000000..c621d695
--- /dev/null
+++ b/modules/benchmark/benches.c
@@ -0,0 +1,254 @@
+/*
+ * HardInfo - System Information and Benchmark
+ * Copyright (C) 2003-2017 L. A. F. Pereira <l@tia.mat.br>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, version 2 or later.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+/* These are parts of modules/benchmark.c where specific benchmarks are defined. */
+
+#define BENCH_CALLBACK(CN, BN, BID, R) \
+gchar *CN() { \
+ if (R) \
+ return benchmark_include_results_reverse(bench_results[BID], BN); \
+ else \
+ return benchmark_include_results(bench_results[BID], BN); \
+}
+
+#define BENCH_SCAN_SIMPLE(SN, BF, BID) \
+void SN(gboolean reload) { \
+ SCAN_START(); \
+ do_benchmark(BF, BID); \
+ SCAN_END(); \
+}
+
+#define BENCH_SIMPLE(BID, BN, BF, R) \
+ BENCH_CALLBACK(callback_##BF, BN, BID, R); \
+ BENCH_SCAN_SIMPLE(scan_##BF, BF, BID);
+
+// ID, NAME, FUNCTION, R (0 = lower is better, 1 = higher is better)
+BENCH_SIMPLE(BENCHMARK_FIB, "CPU Fibonacci", benchmark_fib, 1);
+BENCH_SIMPLE(BENCHMARK_NQUEENS, "CPU N-Queens", benchmark_nqueens, 1);
+BENCH_SIMPLE(BENCHMARK_FFT, "FPU FFT", benchmark_fft, 1);
+BENCH_SIMPLE(BENCHMARK_RAYTRACE, "FPU Raytracing (Single-thread)", benchmark_raytrace, 1);
+BENCH_SIMPLE(BENCHMARK_BLOWFISH_SINGLE, "CPU Blowfish (Single-thread)", benchmark_bfish_single, 1);
+BENCH_SIMPLE(BENCHMARK_BLOWFISH_THREADS, "CPU Blowfish (Multi-thread)", benchmark_bfish_threads, 1);
+BENCH_SIMPLE(BENCHMARK_BLOWFISH_CORES, "CPU Blowfish (Multi-core)", benchmark_bfish_cores, 1);
+BENCH_SIMPLE(BENCHMARK_ZLIB, "CPU Zlib", benchmark_zlib, 1);
+BENCH_SIMPLE(BENCHMARK_CRYPTOHASH, "CPU CryptoHash", benchmark_cryptohash, 1);
+BENCH_SIMPLE(BENCHMARK_SBCPU_SINGLE, "SysBench CPU (Single-thread)", benchmark_sbcpu_single, 1);
+BENCH_SIMPLE(BENCHMARK_SBCPU_ALL, "SysBench CPU (Multi-thread)", benchmark_sbcpu_all, 1);
+BENCH_SIMPLE(BENCHMARK_SBCPU_QUAD, "SysBench CPU (Four threads)", benchmark_sbcpu_quad, 1);
+BENCH_SIMPLE(BENCHMARK_MEMORY_SINGLE, "SysBench Memory (Single-thread)", benchmark_memory_single, 1);
+BENCH_SIMPLE(BENCHMARK_MEMORY_DUAL, "SysBench Memory (Two threads)", benchmark_memory_dual, 1);
+BENCH_SIMPLE(BENCHMARK_MEMORY_QUAD, "SysBench Memory (Quad threads)", benchmark_memory_quad, 1);
+BENCH_SIMPLE(BENCHMARK_MEMORY_ALL, "SysBench Memory (Multi-thread)", benchmark_memory_all, 1);
+
+#if !GTK_CHECK_VERSION(3,0,0)
+BENCH_CALLBACK(callback_gui, "GPU Drawing", BENCHMARK_GUI, 1);
+void scan_gui(gboolean reload)
+{
+ SCAN_START();
+
+ bench_value er = EMPTY_BENCH_VALUE;
+
+ if (params.run_benchmark) {
+ int argc = 0;
+
+ ui_init(&argc, NULL);
+ }
+
+ if (params.gui_running || params.run_benchmark) {
+ do_benchmark(benchmark_gui, BENCHMARK_GUI);
+ } else {
+ bench_results[BENCHMARK_GUI] = er;
+ }
+ SCAN_END();
+}
+#endif
+
+static ModuleEntry entries[] = {
+ [BENCHMARK_BLOWFISH_SINGLE] =
+ {
+ N_("CPU Blowfish (Single-thread)"),
+ "blowfish.png",
+ callback_benchmark_bfish_single,
+ scan_benchmark_bfish_single,
+ MODULE_FLAG_NONE,
+ },
+ [BENCHMARK_BLOWFISH_THREADS] =
+ {
+ N_("CPU Blowfish (Multi-thread)"),
+ "blowfish.png",
+ callback_benchmark_bfish_threads,
+ scan_benchmark_bfish_threads,
+ MODULE_FLAG_NONE,
+ },
+ [BENCHMARK_BLOWFISH_CORES] =
+ {
+ N_("CPU Blowfish (Multi-core)"),
+ "blowfish.png",
+ callback_benchmark_bfish_cores,
+ scan_benchmark_bfish_cores,
+ MODULE_FLAG_NONE,
+ },
+ [BENCHMARK_ZLIB] =
+ {
+ N_("CPU Zlib"),
+ "file-roller.png",
+ callback_benchmark_zlib,
+ scan_benchmark_zlib,
+ MODULE_FLAG_NONE,
+ },
+ [BENCHMARK_CRYPTOHASH] =
+ {
+ N_("CPU CryptoHash"),
+ "cryptohash.png",
+ callback_benchmark_cryptohash,
+ scan_benchmark_cryptohash,
+ MODULE_FLAG_NONE,
+ },
+ [BENCHMARK_FIB] =
+ {
+ N_("CPU Fibonacci"),
+ "nautilus.png",
+ callback_benchmark_fib,
+ scan_benchmark_fib,
+ MODULE_FLAG_NONE,
+ },
+ [BENCHMARK_NQUEENS] =
+ {
+ N_("CPU N-Queens"),
+ "nqueens.png",
+ callback_benchmark_nqueens,
+ scan_benchmark_nqueens,
+ MODULE_FLAG_NONE,
+ },
+ [BENCHMARK_FFT] =
+ {
+ N_("FPU FFT"),
+ "fft.png",
+ callback_benchmark_fft,
+ scan_benchmark_fft,
+ MODULE_FLAG_NONE,
+ },
+ [BENCHMARK_RAYTRACE] =
+ {
+ N_("FPU Raytracing (Single-thread)"),
+ "raytrace.png",
+ callback_benchmark_raytrace,
+ scan_benchmark_raytrace,
+ MODULE_FLAG_NONE,
+ },
+ [BENCHMARK_SBCPU_SINGLE] =
+ {
+ N_("SysBench CPU (Single-thread)"),
+ "processor.png",
+ callback_benchmark_sbcpu_single,
+ scan_benchmark_sbcpu_single,
+ MODULE_FLAG_NONE,
+ },
+ [BENCHMARK_SBCPU_ALL] =
+ {
+ N_("SysBench CPU (Multi-thread)"),
+ "processor.png",
+ callback_benchmark_sbcpu_all,
+ scan_benchmark_sbcpu_all,
+ MODULE_FLAG_NONE,
+ },
+ [BENCHMARK_SBCPU_QUAD] =
+ {
+ N_("SysBench CPU (Four threads)"),
+ "processor.png",
+ callback_benchmark_sbcpu_quad,
+ scan_benchmark_sbcpu_quad,
+ MODULE_FLAG_HIDE,
+ },
+ [BENCHMARK_MEMORY_SINGLE] =
+ {
+ N_("SysBench Memory (Single-thread)"),
+ "memory.png",
+ callback_benchmark_memory_single,
+ scan_benchmark_memory_single,
+ MODULE_FLAG_NONE,
+ },
+ [BENCHMARK_MEMORY_DUAL] =
+ {
+ N_("SysBench Memory (Two threads)"),
+ "memory.png",
+ callback_benchmark_memory_dual,
+ scan_benchmark_memory_dual,
+ MODULE_FLAG_HIDE,
+ },
+ [BENCHMARK_MEMORY_QUAD] =
+ {
+ N_("SysBench Memory (Quad threads)"),
+ "memory.png",
+ callback_benchmark_memory_quad,
+ scan_benchmark_memory_quad,
+ MODULE_FLAG_HIDE,
+ },
+ [BENCHMARK_MEMORY_ALL] =
+ {
+ N_("SysBench Memory (Multi-thread)"),
+ "memory.png",
+ callback_benchmark_memory_all,
+ scan_benchmark_memory_all,
+ MODULE_FLAG_NONE,
+ },
+#if !GTK_CHECK_VERSION(3, 0, 0)
+ [BENCHMARK_GUI] =
+ {
+ N_("GPU Drawing"),
+ "module.png",
+ callback_gui,
+ scan_gui,
+ MODULE_FLAG_NO_REMOTE | MODULE_FLAG_HIDE,
+ },
+#else
+ [BENCHMARK_GUI] = {"#"},
+#endif
+ {NULL}};
+
+const gchar *hi_note_func(gint entry)
+{
+ switch (entry) {
+ case BENCHMARK_SBCPU_SINGLE:
+ case BENCHMARK_SBCPU_QUAD:
+ case BENCHMARK_SBCPU_ALL:
+ return _("Alexey Kopytov's <i><b>sysbench</b></i> is required.\n"
+ "Results in events/second. Higher is better.");
+
+ case BENCHMARK_MEMORY_SINGLE:
+ case BENCHMARK_MEMORY_DUAL:
+ case BENCHMARK_MEMORY_QUAD:
+ case BENCHMARK_MEMORY_ALL:
+ return _("Alexey Kopytov's <i><b>sysbench</b></i> is required.\n"
+ "Results in MiB/second. Higher is better.");
+
+ case BENCHMARK_CRYPTOHASH:
+ case BENCHMARK_BLOWFISH_SINGLE:
+ case BENCHMARK_BLOWFISH_THREADS:
+ case BENCHMARK_BLOWFISH_CORES:
+ case BENCHMARK_ZLIB:
+ case BENCHMARK_GUI:
+ case BENCHMARK_FFT:
+ case BENCHMARK_RAYTRACE:
+ case BENCHMARK_FIB:
+ case BENCHMARK_NQUEENS:
+ return _("Results in HIMarks. Higher is better.");
+ }
+
+ return NULL;
+}
diff --git a/modules/benchmark/blowfish.c b/modules/benchmark/blowfish.c
new file mode 100644
index 00000000..f8d2e14a
--- /dev/null
+++ b/modules/benchmark/blowfish.c
@@ -0,0 +1,493 @@
+/*
+blowfish.c: C implementation of the Blowfish algorithm.
+
+Copyright (C) 1997 by Paul Kocher
+
+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, write to the Free Software
+Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+
+
+
+COMMENTS ON USING THIS CODE:
+
+Normal usage is as follows:
+ [1] Allocate a BLOWFISH_CTX. (It may be too big for the stack.)
+ [2] Call Blowfish_Init with a pointer to your BLOWFISH_CTX, a pointer to
+ the key, and the number of bytes in the key.
+ [3] To encrypt a 64-bit block, call Blowfish_Encrypt with a pointer to
+ BLOWFISH_CTX, a pointer to the 32-bit left half of the plaintext
+ and a pointer to the 32-bit right half. The plaintext will be
+ overwritten with the ciphertext.
+ [4] Decryption is the same as encryption except that the plaintext and
+ ciphertext are reversed.
+
+Warning #1: The code does not check key lengths. (Caveat encryptor.)
+Warning #2: Beware that Blowfish keys repeat such that "ab" = "abab".
+Warning #3: It is normally a good idea to zeroize the BLOWFISH_CTX before
+ freeing it.
+Warning #4: Endianness conversions are the responsibility of the caller.
+ (To encrypt bytes on a little-endian platforms, you'll probably want
+ to swap bytes around instead of just casting.)
+Warning #5: Make sure to use a reasonable mode of operation for your
+ application. (If you don't know what CBC mode is, see Warning #7.)
+Warning #6: This code is susceptible to timing attacks.
+Warning #7: Security engineering is risky and non-intuitive. Have someone
+ check your work. If you don't know what you are doing, get help.
+
+
+This is code is fast enough for most applications, but is not optimized for
+speed.
+
+If you require this code under a license other than LGPL, please ask. (I
+can be located using your favorite search engine.) Unfortunately, I do not
+have time to provide unpaid support for everyone who uses this code.
+
+ -- Paul Kocher
+*/
+
+#include "hardinfo.h"
+#include "benchmark.h"
+#include "blowfish.h"
+
+#define N 16
+static const unsigned long ORIG_P[16 + 2] =
+ { 0x243F6A88L, 0x85A308D3L, 0x13198A2EL, 0x03707344L, 0xA4093822L,
+ 0x299F31D0L, 0x082EFA98L, 0xEC4E6C89L, 0x452821E6L, 0x38D01377L,
+ 0xBE5466CFL, 0x34E90C6CL, 0xC0AC29B7L,
+ 0xC97C50DDL, 0x3F84D5B5L, 0xB5470917L, 0x9216D5D9L, 0x8979FB1BL
+};
+
+static const unsigned long ORIG_S[4][256] = {
+ {0xD1310BA6L, 0x98DFB5ACL, 0x2FFD72DBL, 0xD01ADFB7L, 0xB8E1AFEDL,
+ 0x6A267E96L, 0xBA7C9045L, 0xF12C7F99L, 0x24A19947L, 0xB3916CF7L,
+ 0x0801F2E2L, 0x858EFC16L, 0x636920D8L, 0x71574E69L, 0xA458FEA3L,
+ 0xF4933D7EL, 0x0D95748FL, 0x728EB658L, 0x718BCD58L, 0x82154AEEL,
+ 0x7B54A41DL, 0xC25A59B5L, 0x9C30D539L, 0x2AF26013L, 0xC5D1B023L,
+ 0x286085F0L, 0xCA417918L, 0xB8DB38EFL, 0x8E79DCB0L, 0x603A180EL,
+ 0x6C9E0E8BL, 0xB01E8A3EL, 0xD71577C1L, 0xBD314B27L, 0x78AF2FDAL,
+ 0x55605C60L, 0xE65525F3L, 0xAA55AB94L, 0x57489862L, 0x63E81440L,
+ 0x55CA396AL, 0x2AAB10B6L, 0xB4CC5C34L, 0x1141E8CEL, 0xA15486AFL,
+ 0x7C72E993L, 0xB3EE1411L, 0x636FBC2AL, 0x2BA9C55DL, 0x741831F6L,
+ 0xCE5C3E16L, 0x9B87931EL, 0xAFD6BA33L, 0x6C24CF5CL, 0x7A325381L,
+ 0x28958677L, 0x3B8F4898L, 0x6B4BB9AFL, 0xC4BFE81BL, 0x66282193L,
+ 0x61D809CCL, 0xFB21A991L, 0x487CAC60L, 0x5DEC8032L, 0xEF845D5DL,
+ 0xE98575B1L, 0xDC262302L, 0xEB651B88L, 0x23893E81L, 0xD396ACC5L,
+ 0x0F6D6FF3L, 0x83F44239L, 0x2E0B4482L, 0xA4842004L, 0x69C8F04AL,
+ 0x9E1F9B5EL, 0x21C66842L, 0xF6E96C9AL, 0x670C9C61L, 0xABD388F0L,
+ 0x6A51A0D2L, 0xD8542F68L, 0x960FA728L, 0xAB5133A3L, 0x6EEF0B6CL,
+ 0x137A3BE4L, 0xBA3BF050L, 0x7EFB2A98L, 0xA1F1651DL, 0x39AF0176L,
+ 0x66CA593EL, 0x82430E88L, 0x8CEE8619L, 0x456F9FB4L, 0x7D84A5C3L,
+ 0x3B8B5EBEL, 0xE06F75D8L, 0x85C12073L, 0x401A449FL, 0x56C16AA6L,
+ 0x4ED3AA62L, 0x363F7706L, 0x1BFEDF72L, 0x429B023DL, 0x37D0D724L,
+ 0xD00A1248L, 0xDB0FEAD3L, 0x49F1C09BL, 0x075372C9L, 0x80991B7BL,
+ 0x25D479D8L, 0xF6E8DEF7L, 0xE3FE501AL, 0xB6794C3BL, 0x976CE0BDL,
+ 0x04C006BAL, 0xC1A94FB6L, 0x409F60C4L, 0x5E5C9EC2L, 0x196A2463L,
+ 0x68FB6FAFL, 0x3E6C53B5L, 0x1339B2EBL, 0x3B52EC6FL, 0x6DFC511FL,
+ 0x9B30952CL, 0xCC814544L, 0xAF5EBD09L, 0xBEE3D004L, 0xDE334AFDL,
+ 0x660F2807L, 0x192E4BB3L, 0xC0CBA857L, 0x45C8740FL, 0xD20B5F39L,
+ 0xB9D3FBDBL, 0x5579C0BDL, 0x1A60320AL, 0xD6A100C6L, 0x402C7279L,
+ 0x679F25FEL, 0xFB1FA3CCL, 0x8EA5E9F8L, 0xDB3222F8L, 0x3C7516DFL,
+ 0xFD616B15L, 0x2F501EC8L, 0xAD0552ABL, 0x323DB5FAL, 0xFD238760L,
+ 0x53317B48L, 0x3E00DF82L, 0x9E5C57BBL, 0xCA6F8CA0L, 0x1A87562EL,
+ 0xDF1769DBL, 0xD542A8F6L, 0x287EFFC3L, 0xAC6732C6L, 0x8C4F5573L,
+ 0x695B27B0L, 0xBBCA58C8L, 0xE1FFA35DL, 0xB8F011A0L, 0x10FA3D98L,
+ 0xFD2183B8L, 0x4AFCB56CL, 0x2DD1D35BL, 0x9A53E479L, 0xB6F84565L,
+ 0xD28E49BCL, 0x4BFB9790L, 0xE1DDF2DAL, 0xA4CB7E33L, 0x62FB1341L,
+ 0xCEE4C6E8L, 0xEF20CADAL, 0x36774C01L, 0xD07E9EFEL, 0x2BF11FB4L,
+ 0x95DBDA4DL, 0xAE909198L, 0xEAAD8E71L, 0x6B93D5A0L, 0xD08ED1D0L,
+ 0xAFC725E0L, 0x8E3C5B2FL, 0x8E7594B7L, 0x8FF6E2FBL, 0xF2122B64L,
+ 0x8888B812L, 0x900DF01CL, 0x4FAD5EA0L, 0x688FC31CL, 0xD1CFF191L,
+ 0xB3A8C1ADL, 0x2F2F2218L, 0xBE0E1777L, 0xEA752DFEL, 0x8B021FA1L,
+ 0xE5A0CC0FL, 0xB56F74E8L, 0x18ACF3D6L, 0xCE89E299L, 0xB4A84FE0L,
+ 0xFD13E0B7L, 0x7CC43B81L, 0xD2ADA8D9L, 0x165FA266L, 0x80957705L,
+ 0x93CC7314L, 0x211A1477L, 0xE6AD2065L, 0x77B5FA86L, 0xC75442F5L,
+ 0xFB9D35CFL, 0xEBCDAF0CL, 0x7B3E89A0L, 0xD6411BD3L, 0xAE1E7E49L,
+ 0x00250E2DL, 0x2071B35EL, 0x226800BBL, 0x57B8E0AFL, 0x2464369BL,
+ 0xF009B91EL, 0x5563911DL, 0x59DFA6AAL, 0x78C14389L, 0xD95A537FL,
+ 0x207D5BA2L, 0x02E5B9C5L, 0x83260376L, 0x6295CFA9L, 0x11C81968L,
+ 0x4E734A41L, 0xB3472DCAL, 0x7B14A94AL, 0x1B510052L, 0x9A532915L,
+ 0xD60F573FL, 0xBC9BC6E4L, 0x2B60A476L, 0x81E67400L, 0x08BA6FB5L,
+ 0x571BE91FL, 0xF296EC6BL, 0x2A0DD915L, 0xB6636521L, 0xE7B9F9B6L,
+ 0xFF34052EL, 0xC5855664L, 0x53B02D5DL, 0xA99F8FA1L, 0x08BA4799L,
+ 0x6E85076AL}, {0x4B7A70E9L, 0xB5B32944L, 0xDB75092EL,
+ 0xC4192623L, 0xAD6EA6B0L, 0x49A7DF7DL,
+ 0x9CEE60B8L, 0x8FEDB266L, 0xECAA8C71L,
+ 0x699A17FFL, 0x5664526CL, 0xC2B19EE1L,
+ 0x193602A5L, 0x75094C29L, 0xA0591340L,
+ 0xE4183A3EL, 0x3F54989AL, 0x5B429D65L,
+ 0x6B8FE4D6L, 0x99F73FD6L, 0xA1D29C07L,
+ 0xEFE830F5L, 0x4D2D38E6L, 0xF0255DC1L,
+ 0x4CDD2086L, 0x8470EB26L, 0x6382E9C6L,
+ 0x021ECC5EL, 0x09686B3FL, 0x3EBAEFC9L,
+ 0x3C971814L, 0x6B6A70A1L, 0x687F3584L,
+ 0x52A0E286L, 0xB79C5305L, 0xAA500737L,
+ 0x3E07841CL, 0x7FDEAE5CL, 0x8E7D44ECL,
+ 0x5716F2B8L, 0xB03ADA37L, 0xF0500C0DL,
+ 0xF01C1F04L, 0x0200B3FFL, 0xAE0CF51AL,
+ 0x3CB574B2L, 0x25837A58L, 0xDC0921BDL,
+ 0xD19113F9L, 0x7CA92FF6L, 0x94324773L,
+ 0x22F54701L, 0x3AE5E581L, 0x37C2DADCL,
+ 0xC8B57634L, 0x9AF3DDA7L, 0xA9446146L,
+ 0x0FD0030EL, 0xECC8C73EL, 0xA4751E41L,
+ 0xE238CD99L, 0x3BEA0E2FL, 0x3280BBA1L,
+ 0x183EB331L, 0x4E548B38L, 0x4F6DB908L,
+ 0x6F420D03L, 0xF60A04BFL, 0x2CB81290L,
+ 0x24977C79L, 0x5679B072L, 0xBCAF89AFL,
+ 0xDE9A771FL, 0xD9930810L, 0xB38BAE12L,
+ 0xDCCF3F2EL, 0x5512721FL, 0x2E6B7124L,
+ 0x501ADDE6L, 0x9F84CD87L, 0x7A584718L,
+ 0x7408DA17L, 0xBC9F9ABCL, 0xE94B7D8CL,
+ 0xEC7AEC3AL, 0xDB851DFAL, 0x63094366L,
+ 0xC464C3D2L, 0xEF1C1847L, 0x3215D908L,
+ 0xDD433B37L, 0x24C2BA16L, 0x12A14D43L,
+ 0x2A65C451L, 0x50940002L, 0x133AE4DDL,
+ 0x71DFF89EL, 0x10314E55L, 0x81AC77D6L,
+ 0x5F11199BL, 0x043556F1L, 0xD7A3C76BL,
+ 0x3C11183BL, 0x5924A509L, 0xF28FE6EDL,
+ 0x97F1FBFAL, 0x9EBABF2CL, 0x1E153C6EL,
+ 0x86E34570L, 0xEAE96FB1L, 0x860E5E0AL,
+ 0x5A3E2AB3L, 0x771FE71CL, 0x4E3D06FAL,
+ 0x2965DCB9L, 0x99E71D0FL, 0x803E89D6L,
+ 0x5266C825L, 0x2E4CC978L, 0x9C10B36AL,
+ 0xC6150EBAL, 0x94E2EA78L, 0xA5FC3C53L,
+ 0x1E0A2DF4L, 0xF2F74EA7L, 0x361D2B3DL,
+ 0x1939260FL, 0x19C27960L, 0x5223A708L,
+ 0xF71312B6L, 0xEBADFE6EL, 0xEAC31F66L,
+ 0xE3BC4595L, 0xA67BC883L, 0xB17F37D1L,
+ 0x018CFF28L, 0xC332DDEFL, 0xBE6C5AA5L,
+ 0x65582185L, 0x68AB9802L, 0xEECEA50FL,
+ 0xDB2F953BL, 0x2AEF7DADL, 0x5B6E2F84L,
+ 0x1521B628L, 0x29076170L, 0xECDD4775L,
+ 0x619F1510L, 0x13CCA830L, 0xEB61BD96L,
+ 0x0334FE1EL, 0xAA0363CFL, 0xB5735C90L,
+ 0x4C70A239L, 0xD59E9E0BL, 0xCBAADE14L,
+ 0xEECC86BCL, 0x60622CA7L, 0x9CAB5CABL,
+ 0xB2F3846EL, 0x648B1EAFL, 0x19BDF0CAL,
+ 0xA02369B9L, 0x655ABB50L, 0x40685A32L,
+ 0x3C2AB4B3L, 0x319EE9D5L, 0xC021B8F7L,
+ 0x9B540B19L, 0x875FA099L, 0x95F7997EL,
+ 0x623D7DA8L, 0xF837889AL, 0x97E32D77L,
+ 0x11ED935FL, 0x16681281L, 0x0E358829L,
+ 0xC7E61FD6L, 0x96DEDFA1L, 0x7858BA99L,
+ 0x57F584A5L, 0x1B227263L, 0x9B83C3FFL,
+ 0x1AC24696L, 0xCDB30AEBL, 0x532E3054L,
+ 0x8FD948E4L, 0x6DBC3128L, 0x58EBF2EFL,
+ 0x34C6FFEAL, 0xFE28ED61L, 0xEE7C3C73L,
+ 0x5D4A14D9L, 0xE864B7E3L, 0x42105D14L,
+ 0x203E13E0L, 0x45EEE2B6L, 0xA3AAABEAL,
+ 0xDB6C4F15L, 0xFACB4FD0L, 0xC742F442L,
+ 0xEF6ABBB5L, 0x654F3B1DL, 0x41CD2105L,
+ 0xD81E799EL, 0x86854DC7L, 0xE44B476AL,
+ 0x3D816250L, 0xCF62A1F2L, 0x5B8D2646L,
+ 0xFC8883A0L, 0xC1C7B6A3L, 0x7F1524C3L,
+ 0x69CB7492L, 0x47848A0BL, 0x5692B285L,
+ 0x095BBF00L, 0xAD19489DL, 0x1462B174L,
+ 0x23820E00L, 0x58428D2AL, 0x0C55F5EAL,
+ 0x1DADF43EL, 0x233F7061L, 0x3372F092L,
+ 0x8D937E41L, 0xD65FECF1L, 0x6C223BDBL,
+ 0x7CDE3759L, 0xCBEE7460L, 0x4085F2A7L,
+ 0xCE77326EL, 0xA6078084L, 0x19F8509EL,
+ 0xE8EFD855L, 0x61D99735L, 0xA969A7AAL,
+ 0xC50C06C2L, 0x5A04ABFCL, 0x800BCADCL,
+ 0x9E447A2EL, 0xC3453484L, 0xFDD56705L,
+ 0x0E1E9EC9L, 0xDB73DBD3L, 0x105588CDL,
+ 0x675FDA79L, 0xE3674340L, 0xC5C43465L,
+ 0x713E38D8L, 0x3D28F89EL, 0xF16DFF20L,
+ 0x153E21E7L, 0x8FB03D4AL, 0xE6E39F2BL,
+ 0xDB83ADF7L}, {0xE93D5A68L, 0x948140F7L,
+ 0xF64C261CL, 0x94692934L,
+ 0x411520F7L, 0x7602D4F7L,
+ 0xBCF46B2EL, 0xD4A20068L,
+ 0xD4082471L, 0x3320F46AL,
+ 0x43B7D4B7L, 0x500061AFL,
+ 0x1E39F62EL, 0x97244546L,
+ 0x14214F74L, 0xBF8B8840L,
+ 0x4D95FC1DL, 0x96B591AFL,
+ 0x70F4DDD3L, 0x66A02F45L,
+ 0xBFBC09ECL, 0x03BD9785L,
+ 0x7FAC6DD0L, 0x31CB8504L,
+ 0x96EB27B3L, 0x55FD3941L,
+ 0xDA2547E6L, 0xABCA0A9AL,
+ 0x28507825L, 0x530429F4L,
+ 0x0A2C86DAL, 0xE9B66DFBL,
+ 0x68DC1462L, 0xD7486900L,
+ 0x680EC0A4L, 0x27A18DEEL,
+ 0x4F3FFEA2L, 0xE887AD8CL,
+ 0xB58CE006L, 0x7AF4D6B6L,
+ 0xAACE1E7CL, 0xD3375FECL,
+ 0xCE78A399L, 0x406B2A42L,
+ 0x20FE9E35L, 0xD9F385B9L,
+ 0xEE39D7ABL, 0x3B124E8BL,
+ 0x1DC9FAF7L, 0x4B6D1856L,
+ 0x26A36631L, 0xEAE397B2L,
+ 0x3A6EFA74L, 0xDD5B4332L,
+ 0x6841E7F7L, 0xCA7820FBL,
+ 0xFB0AF54EL, 0xD8FEB397L,
+ 0x454056ACL, 0xBA489527L,
+ 0x55533A3AL, 0x20838D87L,
+ 0xFE6BA9B7L, 0xD096954BL,
+ 0x55A867BCL, 0xA1159A58L,
+ 0xCCA92963L, 0x99E1DB33L,
+ 0xA62A4A56L, 0x3F3125F9L,
+ 0x5EF47E1CL, 0x9029317CL,
+ 0xFDF8E802L, 0x04272F70L,
+ 0x80BB155CL, 0x05282CE3L,
+ 0x95C11548L, 0xE4C66D22L,
+ 0x48C1133FL, 0xC70F86DCL,
+ 0x07F9C9EEL, 0x41041F0FL,
+ 0x404779A4L, 0x5D886E17L,
+ 0x325F51EBL, 0xD59BC0D1L,
+ 0xF2BCC18FL, 0x41113564L,
+ 0x257B7834L, 0x602A9C60L,
+ 0xDFF8E8A3L, 0x1F636C1BL,
+ 0x0E12B4C2L, 0x02E1329EL,
+ 0xAF664FD1L, 0xCAD18115L,
+ 0x6B2395E0L, 0x333E92E1L,
+ 0x3B240B62L, 0xEEBEB922L,
+ 0x85B2A20EL, 0xE6BA0D99L,
+ 0xDE720C8CL, 0x2DA2F728L,
+ 0xD0127845L, 0x95B794FDL,
+ 0x647D0862L, 0xE7CCF5F0L,
+ 0x5449A36FL, 0x877D48FAL,
+ 0xC39DFD27L, 0xF33E8D1EL,
+ 0x0A476341L, 0x992EFF74L,
+ 0x3A6F6EABL, 0xF4F8FD37L,
+ 0xA812DC60L, 0xA1EBDDF8L,
+ 0x991BE14CL, 0xDB6E6B0DL,
+ 0xC67B5510L, 0x6D672C37L,
+ 0x2765D43BL, 0xDCD0E804L,
+ 0xF1290DC7L, 0xCC00FFA3L,
+ 0xB5390F92L, 0x690FED0BL,
+ 0x667B9FFBL, 0xCEDB7D9CL,
+ 0xA091CF0BL, 0xD9155EA3L,
+ 0xBB132F88L, 0x515BAD24L,
+ 0x7B9479BFL, 0x763BD6EBL,
+ 0x37392EB3L, 0xCC115979L,
+ 0x8026E297L, 0xF42E312DL,
+ 0x6842ADA7L, 0xC66A2B3BL,
+ 0x12754CCCL, 0x782EF11CL,
+ 0x6A124237L, 0xB79251E7L,
+ 0x06A1BBE6L, 0x4BFB6350L,
+ 0x1A6B1018L, 0x11CAEDFAL,
+ 0x3D25BDD8L, 0xE2E1C3C9L,
+ 0x44421659L, 0x0A121386L,
+ 0xD90CEC6EL, 0xD5ABEA2AL,
+ 0x64AF674EL, 0xDA86A85FL,
+ 0xBEBFE988L, 0x64E4C3FEL,
+ 0x9DBC8057L, 0xF0F7C086L,
+ 0x60787BF8L, 0x6003604DL,
+ 0xD1FD8346L, 0xF6381FB0L,
+ 0x7745AE04L, 0xD736FCCCL,
+ 0x83426B33L, 0xF01EAB71L,
+ 0xB0804187L, 0x3C005E5FL,
+ 0x77A057BEL, 0xBDE8AE24L,
+ 0x55464299L, 0xBF582E61L,
+ 0x4E58F48FL, 0xF2DDFDA2L,
+ 0xF474EF38L, 0x8789BDC2L,
+ 0x5366F9C3L, 0xC8B38E74L,
+ 0xB475F255L, 0x46FCD9B9L,
+ 0x7AEB2661L, 0x8B1DDF84L,
+ 0x846A0E79L, 0x915F95E2L,
+ 0x466E598EL, 0x20B45770L,
+ 0x8CD55591L, 0xC902DE4CL,
+ 0xB90BACE1L, 0xBB8205D0L,
+ 0x11A86248L, 0x7574A99EL,
+ 0xB77F19B6L, 0xE0A9DC09L,
+ 0x662D09A1L, 0xC4324633L,
+ 0xE85A1F02L, 0x09F0BE8CL,
+ 0x4A99A025L, 0x1D6EFE10L,
+ 0x1AB93D1DL, 0x0BA5A4DFL,
+ 0xA186F20FL, 0x2868F169L,
+ 0xDCB7DA83L, 0x573906FEL,
+ 0xA1E2CE9BL, 0x4FCD7F52L,
+ 0x50115E01L, 0xA70683FAL,
+ 0xA002B5C4L, 0x0DE6D027L,
+ 0x9AF88C27L, 0x773F8641L,
+ 0xC3604C06L, 0x61A806B5L,
+ 0xF0177A28L, 0xC0F586E0L,
+ 0x006058AAL, 0x30DC7D62L,
+ 0x11E69ED7L, 0x2338EA63L,
+ 0x53C2DD94L, 0xC2C21634L,
+ 0xBBCBEE56L, 0x90BCB6DEL,
+ 0xEBFC7DA1L, 0xCE591D76L,
+ 0x6F05E409L, 0x4B7C0188L,
+ 0x39720A3DL, 0x7C927C24L,
+ 0x86E3725FL, 0x724D9DB9L,
+ 0x1AC15BB4L, 0xD39EB8FCL,
+ 0xED545578L, 0x08FCA5B5L,
+ 0xD83D7CD3L, 0x4DAD0FC4L,
+ 0x1E50EF5EL, 0xB161E6F8L,
+ 0xA28514D9L, 0x6C51133CL,
+ 0x6FD5C7E7L, 0x56E14EC4L,
+ 0x362ABFCEL, 0xDDC6C837L,
+ 0xD79A3234L, 0x92638212L,
+ 0x670EFA8EL, 0x406000E0L},
+{0x3A39CE37L, 0xD3FAF5CFL, 0xABC27737L, 0x5AC52D1BL, 0x5CB0679EL,
+ 0x4FA33742L, 0xD3822740L, 0x99BC9BBEL, 0xD5118E9DL, 0xBF0F7315L,
+ 0xD62D1C7EL, 0xC700C47BL, 0xB78C1B6BL, 0x21A19045L, 0xB26EB1BEL,
+ 0x6A366EB4L, 0x5748AB2FL, 0xBC946E79L, 0xC6A376D2L, 0x6549C2C8L,
+ 0x530FF8EEL, 0x468DDE7DL, 0xD5730A1DL, 0x4CD04DC6L, 0x2939BBDBL,
+ 0xA9BA4650L, 0xAC9526E8L, 0xBE5EE304L, 0xA1FAD5F0L, 0x6A2D519AL,
+ 0x63EF8CE2L, 0x9A86EE22L, 0xC089C2B8L, 0x43242EF6L, 0xA51E03AAL,
+ 0x9CF2D0A4L, 0x83C061BAL, 0x9BE96A4DL, 0x8FE51550L, 0xBA645BD6L,
+ 0x2826A2F9L, 0xA73A3AE1L, 0x4BA99586L, 0xEF5562E9L, 0xC72FEFD3L,
+ 0xF752F7DAL, 0x3F046F69L, 0x77FA0A59L, 0x80E4A915L, 0x87B08601L,
+ 0x9B09E6ADL, 0x3B3EE593L, 0xE990FD5AL, 0x9E34D797L, 0x2CF0B7D9L,
+ 0x022B8B51L, 0x96D5AC3AL, 0x017DA67DL, 0xD1CF3ED6L, 0x7C7D2D28L,
+ 0x1F9F25CFL, 0xADF2B89BL, 0x5AD6B472L, 0x5A88F54CL, 0xE029AC71L,
+ 0xE019A5E6L, 0x47B0ACFDL, 0xED93FA9BL, 0xE8D3C48DL, 0x283B57CCL,
+ 0xF8D56629L, 0x79132E28L, 0x785F0191L, 0xED756055L, 0xF7960E44L,
+ 0xE3D35E8CL, 0x15056DD4L, 0x88F46DBAL, 0x03A16125L, 0x0564F0BDL,
+ 0xC3EB9E15L, 0x3C9057A2L, 0x97271AECL, 0xA93A072AL, 0x1B3F6D9BL,
+ 0x1E6321F5L, 0xF59C66FBL, 0x26DCF319L, 0x7533D928L, 0xB155FDF5L,
+ 0x03563482L, 0x8ABA3CBBL, 0x28517711L, 0xC20AD9F8L, 0xABCC5167L,
+ 0xCCAD925FL, 0x4DE81751L, 0x3830DC8EL, 0x379D5862L, 0x9320F991L,
+ 0xEA7A90C2L, 0xFB3E7BCEL, 0x5121CE64L, 0x774FBE32L, 0xA8B6E37EL,
+ 0xC3293D46L, 0x48DE5369L, 0x6413E680L, 0xA2AE0810L, 0xDD6DB224L,
+ 0x69852DFDL, 0x09072166L, 0xB39A460AL, 0x6445C0DDL, 0x586CDECFL,
+ 0x1C20C8AEL, 0x5BBEF7DDL, 0x1B588D40L, 0xCCD2017FL, 0x6BB4E3BBL,
+ 0xDDA26A7EL, 0x3A59FF45L, 0x3E350A44L, 0xBCB4CDD5L, 0x72EACEA8L,
+ 0xFA6484BBL, 0x8D6612AEL, 0xBF3C6F47L, 0xD29BE463L, 0x542F5D9EL,
+ 0xAEC2771BL, 0xF64E6370L, 0x740E0D8DL, 0xE75B1357L, 0xF8721671L,
+ 0xAF537D5DL, 0x4040CB08L, 0x4EB4E2CCL, 0x34D2466AL, 0x0115AF84L,
+ 0xE1B00428L, 0x95983A1DL, 0x06B89FB4L, 0xCE6EA048L, 0x6F3F3B82L,
+ 0x3520AB82L, 0x011A1D4BL, 0x277227F8L, 0x611560B1L, 0xE7933FDCL,
+ 0xBB3A792BL, 0x344525BDL, 0xA08839E1L, 0x51CE794BL, 0x2F32C9B7L,
+ 0xA01FBAC9L, 0xE01CC87EL, 0xBCC7D1F6L, 0xCF0111C3L, 0xA1E8AAC7L,
+ 0x1A908749L, 0xD44FBD9AL, 0xD0DADECBL, 0xD50ADA38L, 0x0339C32AL,
+ 0xC6913667L, 0x8DF9317CL, 0xE0B12B4FL, 0xF79E59B7L, 0x43F5BB3AL,
+ 0xF2D519FFL, 0x27D9459CL, 0xBF97222CL, 0x15E6FC2AL, 0x0F91FC71L,
+ 0x9B941525L, 0xFAE59361L, 0xCEB69CEBL, 0xC2A86459L, 0x12BAA8D1L,
+ 0xB6C1075EL, 0xE3056A0CL, 0x10D25065L, 0xCB03A442L, 0xE0EC6E0EL,
+ 0x1698DB3BL, 0x4C98A0BEL, 0x3278E964L, 0x9F1F9532L, 0xE0D392DFL,
+ 0xD3A0342BL, 0x8971F21EL, 0x1B0A7441L, 0x4BA3348CL, 0xC5BE7120L,
+ 0xC37632D8L, 0xDF359F8DL, 0x9B992F2EL, 0xE60B6F47L, 0x0FE3F11DL,
+ 0xE54CDA54L, 0x1EDAD891L, 0xCE6279CFL, 0xCD3E7E6FL, 0x1618B166L,
+ 0xFD2C1D05L, 0x848FD2C5L, 0xF6FB2299L, 0xF523F357L, 0xA6327623L,
+ 0x93A83531L, 0x56CCCD02L, 0xACF08162L, 0x5A75EBB5L, 0x6E163697L,
+ 0x88D273CCL, 0xDE966292L, 0x81B949D0L, 0x4C50901BL, 0x71C65614L,
+ 0xE6C6C7BDL, 0x327A140AL, 0x45E1D006L, 0xC3F27B9AL, 0xC9AA53FDL,
+ 0x62A80F00L, 0xBB25BFE2L, 0x35BDD2F6L, 0x71126905L, 0xB2040222L,
+ 0xB6CBCF7CL, 0xCD769C2BL, 0x53113EC0L, 0x1640E3D3L, 0x38ABBD60L,
+ 0x2547ADF0L, 0xBA38209CL, 0xF746CE76L, 0x77AFA1C5L, 0x20756060L,
+ 0x85CBFE4EL, 0x8AE88DD8L, 0x7AAAF9B0L, 0x4CF9AA7EL, 0x1948C25CL,
+ 0x02FB8A8CL, 0x01C36AE4L, 0xD6EBE1F9L, 0x90D4F869L, 0xA65CDEA0L,
+ 0x3F09252DL, 0xC208E69FL, 0xB74E6132L, 0xCE77E25BL, 0x578FDFE3L,
+ 0x3AC372E6L}
+};
+
+static unsigned long F(BLOWFISH_CTX * ctx, unsigned long x)
+{
+ unsigned short a, b, c, d;
+ unsigned long y;
+ d = (unsigned short) (x & 0xFF);
+ x >>= 8;
+ c = (unsigned short) (x & 0xFF);
+ x >>= 8;
+ b = (unsigned short) (x & 0xFF);
+ x >>= 8;
+ a = (unsigned short) (x & 0xFF);
+ y = ctx->S[0][a] + ctx->S[1][b];
+ y = y ^ ctx->S[2][c];
+ y = y + ctx->S[3][d];
+ return y;
+}
+
+void Blowfish_Encrypt(BLOWFISH_CTX * ctx, unsigned long *xl,
+ unsigned long *xr)
+{
+ unsigned long Xl;
+ unsigned long Xr;
+ unsigned long temp;
+ short i;
+ Xl = *xl;
+ Xr = *xr;
+ for (i = 0; i < N; ++i) {
+ Xl = Xl ^ ctx->P[i];
+ Xr = F(ctx, Xl) ^ Xr;
+ temp = Xl;
+ Xl = Xr;
+ Xr = temp;
+ }
+ temp = Xl;
+ Xl = Xr;
+ Xr = temp;
+ Xr = Xr ^ ctx->P[N];
+ Xl = Xl ^ ctx->P[N + 1];
+ *xl = Xl;
+ *xr = Xr;
+}
+
+void Blowfish_Decrypt(BLOWFISH_CTX * ctx, unsigned long *xl,
+ unsigned long *xr)
+{
+ unsigned long Xl;
+ unsigned long Xr;
+ unsigned long temp;
+ short i;
+ Xl = *xl;
+ Xr = *xr;
+ for (i = N + 1; i > 1; --i) {
+ Xl = Xl ^ ctx->P[i];
+ Xr = F(ctx, Xl) ^ Xr;
+
+ /* Exchange Xl and Xr */
+ temp = Xl;
+ Xl = Xr;
+ Xr = temp;
+ }
+
+ /* Exchange Xl and Xr */
+ temp = Xl;
+ Xl = Xr;
+ Xr = temp;
+ Xr = Xr ^ ctx->P[1];
+ Xl = Xl ^ ctx->P[0];
+ *xl = Xl;
+ *xr = Xr;
+}
+
+void Blowfish_Init(BLOWFISH_CTX * ctx, unsigned char *key, int keyLen)
+{
+ int i, j, k;
+ unsigned long data, datal, datar;
+ for (i = 0; i < 4; i++) {
+ for (j = 0; j < 256; j++)
+ ctx->S[i][j] = ORIG_S[i][j];
+ }
+ j = 0;
+ for (i = 0; i < N + 2; ++i) {
+ data = 0x00000000;
+ for (k = 0; k < 4; ++k) {
+ data = (data << 8) | key[j];
+ j = j + 1;
+ if (j >= keyLen)
+ j = 0;
+ }
+ ctx->P[i] = ORIG_P[i] ^ data;
+ }
+ datal = 0x00000000;
+ datar = 0x00000000;
+ for (i = 0; i < N + 2; i += 2) {
+ Blowfish_Encrypt(ctx, &datal, &datar);
+ ctx->P[i] = datal;
+ ctx->P[i + 1] = datar;
+ }
+ for (i = 0; i < 4; ++i) {
+ for (j = 0; j < 256; j += 2) {
+ Blowfish_Encrypt(ctx, &datal, &datar);
+ ctx->S[i][j] = datal;
+ ctx->S[i][j + 1] = datar;
+ }
+ }
+}
diff --git a/modules/benchmark/blowfish2.c b/modules/benchmark/blowfish2.c
new file mode 100644
index 00000000..7426bef9
--- /dev/null
+++ b/modules/benchmark/blowfish2.c
@@ -0,0 +1,83 @@
+/*
+ * HardInfo - Displays System Information
+ * Copyright (C) 2003-2017 L. A. F. Pereira <l@tia.mat.br>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, version 2 or later.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#include "hardinfo.h"
+#include "benchmark.h"
+#include "blowfish.h"
+
+/* if anything changes in this block, increment revision */
+#define BENCH_REVISION 1
+#define CRUNCH_TIME 7
+#define BENCH_DATA_SIZE 65536
+#define BENCH_DATA_MD5 "c25cf5c889f7bead2ff39788eedae37b"
+#define BLOW_KEY "Has my shampoo arrived?"
+#define BLOW_KEY_MD5 "6eac709cca51a228bfa70150c9c5a7c4"
+
+static gpointer bfish_exec(const void *in_data, gint thread_number)
+{
+ unsigned char key[] = BLOW_KEY;
+ unsigned char *data = NULL;
+ unsigned long data_len = BENCH_DATA_SIZE, i = 0;
+ BLOWFISH_CTX ctx;
+
+ data = malloc(BENCH_DATA_SIZE);
+ memcpy(data, in_data, BENCH_DATA_SIZE);
+
+ Blowfish_Init(&ctx, key, strlen(key));
+ for(i = 0; i < data_len; i += 8) {
+ Blowfish_Encrypt(&ctx, (unsigned long*)&data[i], (unsigned long*)&data[i+4]);
+ }
+ for(i = 0; i < data_len; i += 8) {
+ Blowfish_Decrypt(&ctx, (unsigned long*)&data[i], (unsigned long*)&data[i+4]);
+ }
+
+ free(data);
+ return NULL;
+}
+
+void benchmark_bfish_do(int threads, int entry, const char *status)
+{
+ bench_value r = EMPTY_BENCH_VALUE;
+ gchar *test_data = get_test_data(BENCH_DATA_SIZE);
+ if (!test_data) return;
+
+ shell_view_set_enabled(FALSE);
+ shell_status_update(status);
+
+ gchar *k = md5_digest_str(BLOW_KEY, strlen(BLOW_KEY));
+ if (!SEQ(k, BLOW_KEY_MD5))
+ bench_msg("test key has different md5sum: expected %s, actual %s", BLOW_KEY_MD5, k);
+ gchar *d = md5_digest_str(test_data, BENCH_DATA_SIZE);
+ if (!SEQ(d, BENCH_DATA_MD5))
+ bench_msg("test data has different md5sum: expected %s, actual %s", BENCH_DATA_MD5, d);
+
+ r = benchmark_crunch_for(CRUNCH_TIME, threads, bfish_exec, test_data);
+ r.result /= 100;
+ r.revision = BENCH_REVISION;
+ snprintf(r.extra, 255, "%0.1fs, k:%s, d:%s", (double)CRUNCH_TIME, k, d);
+
+ g_free(test_data);
+ g_free(k);
+ g_free(d);
+
+ bench_results[entry] = r;
+}
+
+void benchmark_bfish_threads(void) { benchmark_bfish_do(0, BENCHMARK_BLOWFISH_THREADS, "Performing Blowfish benchmark (multi-thread)..."); }
+void benchmark_bfish_single(void) { benchmark_bfish_do(1, BENCHMARK_BLOWFISH_SINGLE, "Performing Blowfish benchmark (single-thread)..."); }
+void benchmark_bfish_cores(void) { benchmark_bfish_do(-1, BENCHMARK_BLOWFISH_CORES, "Performing Blowfish benchmark (multi-core)..."); }
diff --git a/modules/benchmark/cryptohash.c b/modules/benchmark/cryptohash.c
new file mode 100644
index 00000000..6daef545
--- /dev/null
+++ b/modules/benchmark/cryptohash.c
@@ -0,0 +1,89 @@
+/*
+ * HardInfo - System Information and Benchmark
+ * Copyright (C) 2003-2007 L. A. F. Pereira <l@tia.mat.br>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, version 2 or later.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#include "md5.h"
+#include "sha1.h"
+#include "benchmark.h"
+
+/* if anything changes in this block, increment revision */
+#define BENCH_REVISION 2
+#define BENCH_DATA_SIZE 65536
+#define CRUNCH_TIME 5
+#define BENCH_DATA_MD5 "c25cf5c889f7bead2ff39788eedae37b"
+#define STEPS 250
+
+inline void md5_step(char *data, glong srclen)
+{
+ struct MD5Context ctx;
+ guchar checksum[16];
+
+ MD5Init(&ctx);
+ MD5Update(&ctx, (guchar *)data, srclen);
+ MD5Final(checksum, &ctx);
+}
+
+inline void sha1_step(char *data, glong srclen)
+{
+ SHA1_CTX ctx;
+ guchar checksum[20];
+
+ SHA1Init(&ctx);
+ SHA1Update(&ctx, (guchar*)data, srclen);
+ SHA1Final(checksum, &ctx);
+}
+
+static gpointer cryptohash_for(void *in_data, gint thread_number)
+{
+ unsigned int i;
+
+ for (i = 0;i <= STEPS; i++) {
+ if (i & 1) {
+ md5_step(in_data, BENCH_DATA_SIZE);
+ } else {
+ sha1_step(in_data, BENCH_DATA_SIZE);
+ }
+ }
+
+ return NULL;
+}
+
+void
+benchmark_cryptohash(void)
+{
+ bench_value r = EMPTY_BENCH_VALUE;
+ gchar *test_data = get_test_data(BENCH_DATA_SIZE);
+ if (!test_data) return;
+
+ shell_view_set_enabled(FALSE);
+ shell_status_update("Running CryptoHash benchmark...");
+
+ gchar *d = md5_digest_str(test_data, BENCH_DATA_SIZE);
+ if (!SEQ(d, BENCH_DATA_MD5))
+ bench_msg("test data has different md5sum: expected %s, actual %s", BENCH_DATA_MD5, d);
+
+ r = benchmark_crunch_for(CRUNCH_TIME, 0, cryptohash_for, test_data);
+ r.revision = BENCH_REVISION;
+ snprintf(r.extra, 255, "r:%d, d:%s", STEPS, d);
+
+ g_free(test_data);
+ g_free(d);
+
+ r.result /= 10;
+
+ bench_results[BENCHMARK_CRYPTOHASH] = r;
+}
diff --git a/modules/benchmark/drawing.c b/modules/benchmark/drawing.c
new file mode 100644
index 00000000..e92b9d62
--- /dev/null
+++ b/modules/benchmark/drawing.c
@@ -0,0 +1,33 @@
+/*
+ * HardInfo - Displays System Information
+ * Copyright (C) 2003-2007 L. A. F. Pereira <l@tia.mat.br>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, version 2 or later.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#include "benchmark.h"
+#include "guibench.h"
+
+void
+benchmark_gui(void)
+{
+ bench_value r = EMPTY_BENCH_VALUE;
+
+ shell_view_set_enabled(FALSE);
+ shell_status_update("Running drawing benchmark...");
+
+ r.result = guibench(); //TODO: explain in code comments
+
+ bench_results[BENCHMARK_GUI] = r;
+}
diff --git a/modules/benchmark/fbench.c b/modules/benchmark/fbench.c
new file mode 100644
index 00000000..cf1ff934
--- /dev/null
+++ b/modules/benchmark/fbench.c
@@ -0,0 +1,747 @@
+/*
+
+ John Walker's Floating Point Benchmark, derived from...
+
+ Marinchip Interactive Lens Design System
+
+ John Walker December 1980
+
+ By John Walker
+ https://www.fourmilab.ch/
+
+ This program may be used, distributed, and modified freely as
+ long as the origin information is preserved.
+
+ This is a complete optical design raytracing algorithm,
+ stripped of its user interface and recast into portable C. It
+ not only determines execution speed on an extremely floating
+ point (including trig function) intensive real-world
+ application, it checks accuracy on an algorithm that is
+ exquisitely sensitive to errors. The performance of this
+ program is typically far more sensitive to changes in the
+ efficiency of the trigonometric library routines than the
+ average floating point program.
+
+ The benchmark may be compiled in two modes. If the symbol
+ INTRIG is defined, built-in trigonometric and square root
+ routines will be used for all calculations. Timings made with
+ INTRIG defined reflect the machine's basic floating point
+ performance for the arithmetic operators. If INTRIG is not
+ defined, the system library <math.h> functions are used.
+ Results with INTRIG not defined reflect the system's library
+ performance and/or floating point hardware support for trig
+ functions and square root. Results with INTRIG defined are a
+ good guide to general floating point performance, while
+ results with INTRIG undefined indicate the performance of an
+ application which is math function intensive.
+
+ Special note regarding errors in accuracy: this program has
+ generated numbers identical to the last digit it formats and
+ checks on the following machines, floating point
+ architectures, and languages:
+
+ Marinchip 9900 QBASIC IBM 370 double-precision (REAL * 8) format
+
+ IBM PC / XT / AT Lattice C IEEE 64 bit, 80 bit temporaries
+ High C same, in line 80x87 code
+ BASICA "Double precision"
+ Quick BASIC IEEE double precision, software routines
+
+ Sun 3 C IEEE 64 bit, 80 bit temporaries,
+ in-line 68881 code, in-line FPA code.
+
+ MicroVAX II C Vax "G" format floating point
+
+ Macintosh Plus MPW C SANE floating point, IEEE 64 bit format
+ implemented in ROM.
+
+ Inaccuracies reported by this program should be taken VERY
+ SERIOUSLY INDEED, as the program has been demonstrated to be
+ invariant under changes in floating point format, as long as
+ the format is a recognised double precision format. If you
+ encounter errors, please remember that they are just as likely
+ to be in the floating point editing library or the
+ trigonometric libraries as in the low level operator code.
+
+ The benchmark assumes that results are basically reliable, and
+ only tests the last result computed against the reference. If
+ you're running on a suspect system you can compile this
+ program with ACCURACY defined. This will generate a version
+ which executes as an infinite loop, performing the ray trace
+ and checking the results on every pass. All incorrect results
+ will be reported.
+
+ Representative timings are given below. All have been
+ normalised as if run for 1000 iterations.
+
+ Time in seconds Computer, Compiler, and notes
+ Normal INTRIG
+
+ 3466.00 4031.00 Commodore 128, 2 Mhz 8510 with software floating
+ point. Abacus Software/Data-Becker Super-C 128,
+ version 3.00, run in fast (2 Mhz) mode. Note:
+ the results generated by this system differed
+ from the reference results in the 8th to 10th
+ decimal place.
+
+ 3290.00 IBM PC/AT 6 Mhz, Microsoft/IBM BASICA version A3.00.
+ Run with the "/d" switch, software floating point.
+
+ 2131.50 IBM PC/AT 6 Mhz, Lattice C version 2.14, small model.
+ This version of Lattice compiles subroutine
+ calls which either do software floating point
+ or use the 80x87. The machine on which I ran
+ this had an 80287, but the results were so bad
+ I wonder if it was being used.
+
+ 1598.00 Macintosh Plus, MPW C, SANE Software floating point.
+
+ 1582.13 Marinchip 9900 2 Mhz, QBASIC compiler with software
+ floating point. This was a QBASIC version of the
+ program which contained the identical algorithm.
+
+ 404.00 IBM PC/AT 6 Mhz, Microsoft QuickBASIC version 2.0.
+ Software floating point.
+
+ 165.15 IBM PC/AT 6 Mhz, Metaware High C version 1.3, small
+ model. This was compiled to call subroutines for
+ floating point, and the machine contained an 80287
+ which was used by the subroutines.
+
+ 143.20 Macintosh II, MPW C, SANE calls. I was unable to
+ determine whether SANE was using the 68881 chip or
+ not.
+
+ 121.80 Sun 3/160 16 Mhz, Sun C. Compiled with -fsoft switch
+ which executes floating point in software.
+
+ 78.78 110.11 IBM RT PC (Model 6150). IBM AIX 1.0 C compiler
+ with -O switch.
+
+ 75.2 254.0 Microsoft Quick C 1.0, in-line 8087 instructions,
+ compiled with 80286 optimisation on. (Switches
+ were -Ol -FPi87-G2 -AS). Small memory model.
+
+ 69.50 IBM PC/AT 6Mhz, Borland Turbo BASIC 1.0. Compiled
+ in "8087 required" mode to generate in-line
+ code for the math coprocessor.
+
+ 66.96 IBM PC/AT 6Mhz, Microsoft QuickBASIC 4.0. This
+ release of QuickBASIC compiles code for the
+ 80287 math coprocessor.
+
+ 66.36 206.35 IBM PC/AT 6Mhz, Metaware High C version 1.3, small
+ model. This was compiled with in-line code for the
+ 80287 math coprocessor. Trig functions still call
+ library routines.
+
+ 63.07 220.43 IBM PC/AT, 6Mhz, Borland Turbo C, in-line 8087 code,
+ small model, word alignment, no stack checking,
+ 8086 code mode.
+
+ 17.18 Apollo DN-3000, 12 Mhz 68020 with 68881, compiled
+ with in-line code for the 68881 coprocessor.
+ According to Apollo, the library routines are chosen
+ at runtime based on coprocessor presence. Since the
+ coprocessor was present, the library is supposed to
+ use in-line floating point code.
+
+ 15.55 27.56 VAXstation II GPX. Compiled and executed under
+ VAX/VMS C.
+
+ 15.14 37.93 Macintosh II, Unix system V. Green Hills 68020
+ Unix compiler with in-line code for the 68881
+ coprocessor (-O -ZI switches).
+
+ 12.69 Sun 3/160 16 Mhz, Sun C. Compiled with -fswitch,
+ which calls a subroutine to select the fastest
+ floating point processor. This was using the 68881.
+
+ 11.74 26.73 Compaq Deskpro 386, 16 Mhz 80386 with 16 Mhz 80387.
+ Metaware High C version 1.3, compiled with in-line
+ for the math coprocessor (but not optimised for the
+ 80386/80387). Trig functions still call library
+ routines.
+
+ 8.43 30.49 Sun 3/160 16 Mhz, Sun C. Compiled with -f68881,
+ generating in-line MC68881 instructions. Trig
+ functions still call library routines.
+
+ 6.29 25.17 Sun 3/260 25 Mhz, Sun C. Compiled with -f68881,
+ generating in-line MC68881 instructions. Trig
+ functions still call library routines.
+
+ 4.57 Sun 3/260 25 Mhz, Sun FORTRAN 77. Compiled with
+ -O -f68881, generating in-line MC68881 instructions.
+ Trig functions are compiled in-line. This used
+ the FORTRAN 77 version of the program, FBFORT77.F.
+
+ 4.00 14.20 Sun386i/25 Mhz model 250, Sun C compiler.
+
+ 4.00 14.00 Sun386i/25 Mhz model 250, Metaware C.
+
+ 3.10 12.00 Compaq 386/387 25 Mhz running SCO Xenix 2.
+ Compiled with Metaware HighC 386, optimized
+ for 386.
+
+ 3.00 12.00 Compaq 386/387 25MHZ optimized for 386/387.
+
+ 2.96 5.17 Sun 4/260, Sparc RISC processor. Sun C,
+ compiled with the -O2 switch for global
+ optimisation.
+
+ 2.47 COMPAQ 486/25, secondary cache disabled, High C,
+ 486/387, inline f.p., small memory model.
+
+ 2.20 3.40 Data General Motorola 88000, 16 Mhz, Gnu C.
+
+ 1.56 COMPAQ 486/25, 128K secondary cache, High C, 486/387,
+ inline f.p., small memory model.
+
+ 0.66 1.50 DEC Pmax, Mips processor.
+
+ 0.63 0.91 Sun SparcStation 2, Sun C (SunOS 4.1.1) with
+ -O4 optimisation and "/usr/lib/libm.il" inline
+ floating point.
+
+ 0.60 1.07 Intel 860 RISC processor, 33 Mhz, Greenhills
+ C compiler.
+
+ 0.40 0.90 Dec 3MAX, MIPS 3000 processor, -O4.
+
+ 0.31 0.90 IBM RS/6000, -O.
+
+ 0.1129 0.2119 Dell Dimension XPS P133c, Pentium 133 MHz,
+ Windows 95, Microsoft Visual C 5.0.
+
+ 0.0883 0.2166 Silicon Graphics Indigo², MIPS R4400,
+ 175 Mhz, "-O3".
+
+ 0.0351 0.0561 Dell Dimension XPS R100, Pentium II 400 MHz,
+ Windows 98, Microsoft Visual C 5.0.
+
+ 0.0312 0.0542 Sun Ultra 2, UltraSPARC V9, 300 MHz, Solaris
+ 2.5.1.
+
+ 0.00862 0.01074 Dell Inspiron 9100, Pentium 4, 3.4 GHz, gcc -O3.
+
+*/
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#ifndef INTRIG
+#include <math.h>
+#endif
+
+#define cot(x) (1.0 / tan(x))
+
+#define TRUE 1
+#define FALSE 0
+
+#define max_surfaces 10
+
+/* Local variables */
+
+/*static char tbfr[132];*/
+
+static short current_surfaces;
+static short paraxial;
+
+static double clear_aperture;
+
+static double aberr_lspher;
+static double aberr_osc;
+static double aberr_lchrom;
+
+static double max_lspher;
+static double max_osc;
+static double max_lchrom;
+
+static double radius_of_curvature;
+static double object_distance;
+static double ray_height;
+static double axis_slope_angle;
+static double from_index;
+static double to_index;
+
+static double spectral_line[9];
+static double s[max_surfaces][5];
+static double od_sa[2][2];
+
+ /*static char outarr[8][80];*//* Computed output of program goes here */
+
+static int itercount; /* The iteration counter for the main loop
+ in the program is made global so that
+ the compiler should not be allowed to
+ optimise out the loop over the ray
+ tracing code. */
+
+#define ITERATIONS 300
+static int niter = ITERATIONS; /* Iteration counter */
+
+#if 0
+static char *refarr[] = { /* Reference results. These happen to
+ be derived from a run on Microsoft
+ Quick BASIC on the IBM PC/AT. */
+
+ " Marginal ray 47.09479120920 0.04178472683",
+ " Paraxial ray 47.08372160249 0.04177864821",
+ "Longitudinal spherical aberration: -0.01106960671",
+ " (Maximum permissible): 0.05306749907",
+ "Offense against sine condition (coma): 0.00008954761",
+ " (Maximum permissible): 0.00250000000",
+ "Axial chromatic aberration: 0.00448229032",
+ " (Maximum permissible): 0.05306749907"
+};
+#endif
+
+/* The test case used in this program is the design for a 4 inch
+ achromatic telescope objective used as the example in Wyld's
+ classic work on ray tracing by hand, given in Amateur Telescope
+ Making, Volume 3. */
+
+static double testcase[4][4] = {
+ {27.05, 1.5137, 63.6, 0.52},
+ {-16.68, 1, 0, 0.138},
+ {-16.68, 1.6164, 36.7, 0.38},
+ {-78.1, 1, 0, 0}
+};
+
+/* Internal trig functions (used only if INTRIG is defined). These
+ standard functions may be enabled to obtain timings that reflect
+ the machine's floating point performance rather than the speed of
+ its trig function evaluation. */
+
+#ifdef INTRIG
+
+/* The following definitions should keep you from getting intro trouble
+ with compilers which don't let you redefine intrinsic functions. */
+
+#define sin I_sin
+#define cos I_cos
+#define tan I_tan
+#define sqrt I_sqrt
+#define atan I_atan
+#define atan2 I_atan2
+#define asin I_asin
+
+#define fabs(x) ((x < 0.0) ? -x : x)
+
+#define pic 3.1415926535897932
+
+/* Commonly used constants */
+
+static double pi = pic,
+ twopi = pic * 2.0,
+ piover4 = pic / 4.0, fouroverpi = 4.0 / pic, piover2 = pic / 2.0;
+
+/* Coefficients for ATAN evaluation */
+
+static double atanc[] = {
+ 0.0,
+ 0.4636476090008061165,
+ 0.7853981633974483094,
+ 0.98279372324732906714,
+ 1.1071487177940905022,
+ 1.1902899496825317322,
+ 1.2490457723982544262,
+ 1.2924966677897852673,
+ 1.3258176636680324644
+};
+
+/* aint(x) Return integer part of number. Truncates towards 0 */
+
+double aint(x)
+double x;
+{
+ long l;
+
+ /* Note that this routine cannot handle the full floating point
+ number range. This function should be in the machine-dependent
+ floating point library! */
+
+ l = x;
+ if ((int) (-0.5) != 0 && l < 0)
+ l++;
+ x = l;
+ return x;
+}
+
+/* sin(x) Return sine, x in radians */
+
+static double sin(x)
+double x;
+{
+ int sign;
+ double y, r, z;
+
+ x = (((sign = (x < 0.0)) != 0) ? -x : x);
+
+ if (x > twopi)
+ x -= (aint(x / twopi) * twopi);
+
+ if (x > pi) {
+ x -= pi;
+ sign = !sign;
+ }
+
+ if (x > piover2)
+ x = pi - x;
+
+ if (x < piover4) {
+ y = x * fouroverpi;
+ z = y * y;
+ r = y *
+ (((((((-0.202253129293E-13 * z + 0.69481520350522E-11) * z -
+ 0.17572474176170806E-8) * z +
+ 0.313361688917325348E-6) * z -
+ 0.365762041821464001E-4) * z +
+ 0.249039457019271628E-2) * z - 0.0807455121882807815) * z +
+ 0.785398163397448310);
+ } else {
+ y = (piover2 - x) * fouroverpi;
+ z = y * y;
+ r = ((((((-0.38577620372E-12 * z + 0.11500497024263E-9) * z -
+ 0.2461136382637005E-7) * z +
+ 0.359086044588581953E-5) * z -
+ 0.325991886926687550E-3) * z + 0.0158543442438154109) * z -
+ 0.308425137534042452) * z + 1.0;
+ }
+ return sign ? -r : r;
+}
+
+/* cos(x) Return cosine, x in radians, by identity */
+
+static double cos(x)
+double x;
+{
+ x = (x < 0.0) ? -x : x;
+ if (x > twopi) /* Do range reduction here to limit */
+ x = x - (aint(x / twopi) * twopi); /* roundoff on add of PI/2 */
+ return sin(x + piover2);
+}
+
+/* tan(x) Return tangent, x in radians, by identity */
+
+static double tan(x)
+double x;
+{
+ return sin(x) / cos(x);
+}
+
+/* sqrt(x) Return square root. Initial guess, then Newton-
+ Raphson refinement */
+
+double sqrt(x)
+double x;
+{
+ double c, cl, y;
+ int n;
+
+ if (x == 0.0)
+ return 0.0;
+
+ if (x < 0.0) {
+ fprintf(stderr,
+ "\nGood work! You tried to take the square root of %g",
+ x);
+ fprintf(stderr,
+ "\nunfortunately, that is too complex for me to handle.\n");
+ exit(1);
+ }
+
+ y = (0.154116 + 1.893872 * x) / (1.0 + 1.047988 * x);
+
+ c = (y - x / y) / 2.0;
+ cl = 0.0;
+ for (n = 50; c != cl && n--;) {
+ y = y - c;
+ cl = c;
+ c = (y - x / y) / 2.0;
+ }
+ return y;
+}
+
+/* atan(x) Return arctangent in radians,
+ range -pi/2 to pi/2 */
+
+static double atan(x)
+double x;
+{
+ int sign, l, y;
+ double a, b, z;
+
+ x = (((sign = (x < 0.0)) != 0) ? -x : x);
+ l = 0;
+
+ if (x >= 4.0) {
+ l = -1;
+ x = 1.0 / x;
+ y = 0;
+ goto atl;
+ } else {
+ if (x < 0.25) {
+ y = 0;
+ goto atl;
+ }
+ }
+
+ y = aint(x / 0.5);
+ z = y * 0.5;
+ x = (x - z) / (x * z + 1);
+
+ atl:
+ z = x * x;
+ b = ((((893025.0 * z + 49116375.0) * z + 425675250.0) * z +
+ 1277025750.0) * z + 1550674125.0) * z + 654729075.0;
+ a = (((13852575.0 * z + 216602100.0) * z + 891080190.0) * z +
+ 1332431100.0) * z + 654729075.0;
+ a = (a / b) * x + atanc[y];
+ if (l)
+ a = piover2 - a;
+ return sign ? -a : a;
+}
+
+/* atan2(y,x) Return arctangent in radians of y/x,
+ range -pi to pi */
+
+static double atan2(y, x)
+double y, x;
+{
+ double temp;
+
+ if (x == 0.0) {
+ if (y == 0.0) /* Special case: atan2(0,0) = 0 */
+ return 0.0;
+ else if (y > 0)
+ return piover2;
+ else
+ return -piover2;
+ }
+ temp = atan(y / x);
+ if (x < 0.0) {
+ if (y >= 0.0)
+ temp += pic;
+ else
+ temp -= pic;
+ }
+ return temp;
+}
+
+/* asin(x) Return arcsine in radians of x */
+
+static double asin(x)
+double x;
+{
+ if (fabs(x) > 1.0) {
+ fprintf(stderr,
+ "\nInverse trig functions lose much of their gloss when");
+ fprintf(stderr,
+ "\ntheir arguments are greater than 1, such as the");
+ fprintf(stderr, "\nvalue %g you passed.\n", x);
+ exit(1);
+ }
+ return atan2(x, sqrt(1 - x * x));
+}
+#endif
+
+/* Calculate passage through surface
+
+ If the variable PARAXIAL is true, the trace through the
+ surface will be done using the paraxial approximations.
+ Otherwise, the normal trigonometric trace will be done.
+
+ This routine takes the following inputs:
+
+ RADIUS_OF_CURVATURE Radius of curvature of surface
+ being crossed. If 0, surface is
+ plane.
+
+ OBJECT_DISTANCE Distance of object focus from
+ lens vertex. If 0, incoming
+ rays are parallel and
+ the following must be specified:
+
+ RAY_HEIGHT Height of ray from axis. Only
+ relevant if OBJECT.DISTANCE == 0
+
+ AXIS_SLOPE_ANGLE Angle incoming ray makes with axis
+ at intercept
+
+ FROM_INDEX Refractive index of medium being left
+
+ TO_INDEX Refractive index of medium being
+ entered.
+
+ The outputs are the following variables:
+
+ OBJECT_DISTANCE Distance from vertex to object focus
+ after refraction.
+
+ AXIS_SLOPE_ANGLE Angle incoming ray makes with axis
+ at intercept after refraction.
+
+*/
+
+static void transit_surface()
+{
+ double iang, /* Incidence angle */
+ rang, /* Refraction angle */
+ iang_sin, /* Incidence angle sin */
+ rang_sin, /* Refraction angle sin */
+ old_axis_slope_angle, sagitta;
+
+ if (paraxial) {
+ if (radius_of_curvature != 0.0) {
+ if (object_distance == 0.0) {
+ axis_slope_angle = 0.0;
+ iang_sin = ray_height / radius_of_curvature;
+ } else
+ iang_sin = ((object_distance -
+ radius_of_curvature) / radius_of_curvature) *
+ axis_slope_angle;
+
+ rang_sin = (from_index / to_index) * iang_sin;
+ old_axis_slope_angle = axis_slope_angle;
+ axis_slope_angle = axis_slope_angle + iang_sin - rang_sin;
+ if (object_distance != 0.0)
+ ray_height = object_distance * old_axis_slope_angle;
+ object_distance = ray_height / axis_slope_angle;
+ return;
+ }
+ object_distance = object_distance * (to_index / from_index);
+ axis_slope_angle = axis_slope_angle * (from_index / to_index);
+ return;
+ }
+
+ if (radius_of_curvature != 0.0) {
+ if (object_distance == 0.0) {
+ axis_slope_angle = 0.0;
+ iang_sin = ray_height / radius_of_curvature;
+ } else {
+ iang_sin = ((object_distance -
+ radius_of_curvature) / radius_of_curvature) *
+ sin(axis_slope_angle);
+ }
+ iang = asin(iang_sin);
+ rang_sin = (from_index / to_index) * iang_sin;
+ old_axis_slope_angle = axis_slope_angle;
+ axis_slope_angle = axis_slope_angle + iang - asin(rang_sin);
+ sagitta = sin((old_axis_slope_angle + iang) / 2.0);
+ sagitta = 2.0 * radius_of_curvature * sagitta * sagitta;
+ object_distance =
+ ((radius_of_curvature * sin(old_axis_slope_angle + iang)) *
+ cot(axis_slope_angle)) + sagitta;
+ return;
+ }
+
+ rang = -asin((from_index / to_index) * sin(axis_slope_angle));
+ object_distance = object_distance * ((to_index *
+ cos(-rang)) / (from_index *
+ cos
+ (axis_slope_angle)));
+ axis_slope_angle = -rang;
+}
+
+/* Perform ray trace in specific spectral line */
+
+static void trace_line(line, ray_h)
+int line;
+double ray_h;
+{
+ int i;
+
+ object_distance = 0.0;
+ ray_height = ray_h;
+ from_index = 1.0;
+
+ for (i = 1; i <= current_surfaces; i++) {
+ radius_of_curvature = s[i][1];
+ to_index = s[i][2];
+ if (to_index > 1.0)
+ to_index = to_index + ((spectral_line[4] -
+ spectral_line[line]) /
+ (spectral_line[3] -
+ spectral_line[6])) * ((s[i][2] -
+ 1.0) / s[i][3]);
+ transit_surface();
+ from_index = to_index;
+ if (i < current_surfaces)
+ object_distance = object_distance - s[i][4];
+ }
+}
+
+/* Initialise when called the first time */
+
+void fbench()
+{
+ int i, j;
+ double od_fline, od_cline;
+
+ spectral_line[1] = 7621.0; /* A */
+ spectral_line[2] = 6869.955; /* B */
+ spectral_line[3] = 6562.816; /* C */
+ spectral_line[4] = 5895.944; /* D */
+ spectral_line[5] = 5269.557; /* E */
+ spectral_line[6] = 4861.344; /* F */
+ spectral_line[7] = 4340.477; /* G' */
+ spectral_line[8] = 3968.494; /* H */
+
+ /* Load test case into working array */
+
+ clear_aperture = 4.0;
+ current_surfaces = 4;
+ for (i = 0; i < current_surfaces; i++)
+ for (j = 0; j < 4; j++)
+ s[i + 1][j + 1] = testcase[i][j];
+
+ for (itercount = 0; itercount < niter; itercount++) {
+ /* Do main trace in D light */
+
+ paraxial = FALSE;
+
+ trace_line(4, clear_aperture / 2.0);
+ od_sa[0][0] = object_distance;
+ od_sa[0][1] = axis_slope_angle;
+
+ paraxial = TRUE;
+
+ trace_line(4, clear_aperture / 2.0);
+ od_sa[1][0] = object_distance;
+ od_sa[1][1] = axis_slope_angle;
+
+ paraxial = FALSE;
+
+ /* Trace marginal ray in C */
+
+ trace_line(3, clear_aperture / 2.0);
+ od_cline = object_distance;
+
+ /* Trace marginal ray in F */
+
+ trace_line(6, clear_aperture / 2.0);
+ od_fline = object_distance;
+
+ aberr_lspher = od_sa[1][0] - od_sa[0][0];
+ aberr_osc = 1.0 - (od_sa[1][0] * od_sa[1][1]) /
+ (sin(od_sa[0][1]) * od_sa[0][0]);
+ aberr_lchrom = od_fline - od_cline;
+ max_lspher = sin(od_sa[0][1]);
+
+ /* D light */
+
+ max_lspher = 0.0000926 / (max_lspher * max_lspher);
+ max_osc = 0.0025;
+ max_lchrom = max_lspher;
+ }
+}
+
+#ifdef __FBENCH_TEST__
+int main(void)
+{
+ fbench();
+
+ return 0;
+}
+#endif
diff --git a/modules/benchmark/fft.c b/modules/benchmark/fft.c
new file mode 100644
index 00000000..503a7aaf
--- /dev/null
+++ b/modules/benchmark/fft.c
@@ -0,0 +1,70 @@
+/*
+ * HardInfo - System Information and Benchmark
+ * Copyright (C) 2003-2007 L. A. F. Pereira <l@tia.mat.br>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, version 2 or later.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#include "hardinfo.h"
+#include "benchmark.h"
+#include "fftbench.h"
+
+/* if anything changes in this block, increment revision */
+#define BENCH_REVISION 2
+#define CRUNCH_TIME 5
+
+static gpointer fft_for(void *in_data, gint thread_number)
+{
+ unsigned int i;
+ FFTBench **benches = (FFTBench **)in_data;
+ FFTBench *fftbench = (FFTBench *)(benches[thread_number]);
+
+ fft_bench_run(benches[thread_number]);
+
+ return NULL;
+}
+
+void
+benchmark_fft(void)
+{
+ int cpu_procs, cpu_cores, cpu_threads, cpu_nodes;
+ bench_value r = EMPTY_BENCH_VALUE;
+
+ int n_cores, i;
+ gchar *temp;
+ FFTBench **benches=NULL;
+
+ shell_view_set_enabled(FALSE);
+ shell_status_update("Running FFT benchmark...");
+
+ cpu_procs_cores_threads_nodes(&cpu_procs, &cpu_cores, &cpu_threads, &cpu_nodes);
+
+ /* Pre-allocate all benchmarks */
+ benches = g_new0(FFTBench *, cpu_threads);
+ for (i = 0; i < cpu_threads; i++) {benches[i] = fft_bench_new();}
+
+ /* Run the benchmark */
+ r = benchmark_crunch_for(CRUNCH_TIME, 0, fft_for, benches);
+
+ /* Free up the memory */
+ for (i = 0; i < cpu_threads; i++) {
+ fft_bench_free(benches[i]);
+ }
+ g_free(benches);
+
+ r.result /= 100;
+
+ r.revision = BENCH_REVISION;
+ bench_results[BENCHMARK_FFT] = r;
+}
diff --git a/modules/benchmark/fftbench.c b/modules/benchmark/fftbench.c
new file mode 100644
index 00000000..9449cffd
--- /dev/null
+++ b/modules/benchmark/fftbench.c
@@ -0,0 +1,212 @@
+/*
+ fftbench.c
+
+ Written by Scott Robert Ladd (scott@coyotegulch.com)
+ No rights reserved. This is public domain software, for use by anyone.
+
+ A number-crunching benchmark using LUP-decomposition to solve a large
+ linear equation.
+
+ The code herein is design for the purpose of testing computational
+ performance; error handling is minimal.
+
+ In fact, this is a weak implementation of the FFT; unfortunately, all
+ of my really nifty FFTs are in commercial code, and I haven't had time
+ to write a new FFT routine for this benchmark. I may add a Hartley
+ transform to the seat, too.
+
+ Actual benchmark results can be found at:
+ http://scottrobertladd.net/coyotegulch/
+
+ Please do not use this information or algorithm in any way that might
+ upset the balance of the universe or otherwise cause a disturbance in
+ the space-time continuum.
+*/
+
+#include <time.h>
+#include <string.h>
+#include <stdlib.h>
+#include <math.h>
+#include <stdbool.h>
+#include <stdio.h>
+
+#include "fftbench.h"
+
+// embedded random number generator; ala Park and Miller
+static long seed = 1325;
+static const long IA = 16807;
+static const long IM = 2147483647;
+static const double AM = 4.65661287525E-10;
+static const long IQ = 127773;
+static const long IR = 2836;
+static const long MASK = 123459876;
+
+static double random_double()
+{
+ long k;
+ double result;
+
+ seed ^= MASK;
+ k = seed / IQ;
+ seed = IA * (seed - k * IQ) - IR * k;
+
+ if (seed < 0)
+ seed += IM;
+
+ result = AM * seed;
+ seed ^= MASK;
+
+ return result;
+}
+
+static const int N = 100;
+static const int NM1 = 99; // N - 1
+static const int NP1 = 101; // N + 1
+
+static void lup_decompose(FFTBench *fftbench)
+{
+ int i, j, k, k2, t;
+ double p, temp, **a;
+
+ int *perm = (int *) malloc(sizeof(double) * N);
+
+ fftbench->p = perm;
+ a = fftbench->a;
+
+ for (i = 0; i < N; ++i)
+ perm[i] = i;
+
+ for (k = 0; k < NM1; ++k) {
+ p = 0.0;
+
+ for (i = k; i < N; ++i) {
+ temp = fabs(a[i][k]);
+
+ if (temp > p) {
+ p = temp;
+ k2 = i;
+ }
+ }
+
+ // check for invalid a
+ if (p == 0.0)
+ return;
+
+ // exchange rows
+ t = perm[k];
+ perm[k] = perm[k2];
+ perm[k2] = t;
+
+ for (i = 0; i < N; ++i) {
+ temp = a[k][i];
+ a[k][i] = a[k2][i];
+ a[k2][i] = temp;
+ }
+
+ for (i = k + 1; i < N; ++i) {
+ a[i][k] /= a[k][k];
+
+ for (j = k + 1; j < N; ++j)
+ a[i][j] -= a[i][k] * a[k][j];
+ }
+ }
+}
+
+static double *lup_solve(FFTBench *fftbench)
+{
+ int i, j, j2;
+ double sum, u;
+
+ double *y = (double *) malloc(sizeof(double) * N);
+ double *x = (double *) malloc(sizeof(double) * N);
+
+ double **a = fftbench->a;
+ double *b = fftbench->b;
+ int *perm = fftbench->p;
+
+ for (i = 0; i < N; ++i) {
+ y[i] = 0.0;
+ x[i] = 0.0;
+ }
+
+ for (i = 0; i < N; ++i) {
+ sum = 0.0;
+ j2 = 0;
+
+ for (j = 1; j <= i; ++j) {
+ sum += a[i][j2] * y[j2];
+ ++j2;
+ }
+
+ y[i] = b[perm[i]] - sum;
+ }
+
+ i = NM1;
+
+ while (1) {
+ sum = 0.0;
+ u = a[i][i];
+
+ for (j = i + 1; j < N; ++j)
+ sum += a[i][j] * x[j];
+
+ x[i] = (y[i] - sum) / u;
+
+ if (i == 0)
+ break;
+
+ --i;
+ }
+
+ free(y);
+
+ return x;
+}
+
+FFTBench *fft_bench_new(void)
+{
+ FFTBench *fftbench;
+ int i, j;
+
+ fftbench = g_new0(FFTBench, 1);
+
+ // generate test data
+ fftbench->a = (double **) malloc(sizeof(double *) * N);
+
+ for (i = 0; i < N; ++i) {
+ fftbench->a[i] = (double *) malloc(sizeof(double) * N);
+
+ for (j = 0; j < N; ++j)
+ fftbench->a[i][j] = random_double();
+ }
+
+ fftbench->b = (double *) malloc(sizeof(double) * N);
+
+ for (i = 0; i < N; ++i)
+ fftbench->b[i] = random_double();
+
+ return fftbench;
+}
+
+void fft_bench_run(FFTBench *fftbench)
+{
+ lup_decompose(fftbench);
+ double *x = lup_solve(fftbench);
+ free(x);
+}
+
+void fft_bench_free(FFTBench *fftbench)
+{
+ int i;
+
+ // clean up
+ for (i = 0; i < N; ++i)
+ free(fftbench->a[i]);
+
+ free(fftbench->a);
+ free(fftbench->b);
+ free(fftbench->p);
+ free(fftbench->r);
+
+ g_free(fftbench);
+}
diff --git a/modules/benchmark/fib.c b/modules/benchmark/fib.c
new file mode 100644
index 00000000..2bec8bed
--- /dev/null
+++ b/modules/benchmark/fib.c
@@ -0,0 +1,61 @@
+/*
+ * HardInfo - System Information and Benchmark
+ * Copyright (C) 2003-2007 L. A. F. Pereira <l@tia.mat.br>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, version 2 or later.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#include "benchmark.h"
+
+/* if anything changes in this block, increment revision */
+#define BENCH_REVISION 2
+#define ANSWER 25
+#define CRUNCH_TIME 5
+
+gulong fib(gulong n)
+{
+ if (n == 0)
+ return 0;
+ else if (n <= 2)
+ return 1;
+ return fib(n - 1) + fib(n - 2);
+}
+
+static gpointer fib_for(void *in_data, gint thread_number)
+{
+ fib(ANSWER);
+
+ return NULL;
+}
+
+
+void
+benchmark_fib(void)
+{
+ GTimer *timer = g_timer_new();
+ bench_value r = EMPTY_BENCH_VALUE;
+
+ shell_view_set_enabled(FALSE);
+ shell_status_update("Calculating Fibonacci number...");
+
+ r = benchmark_crunch_for(CRUNCH_TIME, 0, fib_for, NULL);
+ //r.threads_used = 1;
+
+ r.result /= 100;
+
+ r.revision = BENCH_REVISION;
+ snprintf(r.extra, 255, "a:%d", ANSWER);
+
+ bench_results[BENCHMARK_FIB] = r;
+}
diff --git a/modules/benchmark/guibench.c b/modules/benchmark/guibench.c
new file mode 100644
index 00000000..0faf6b69
--- /dev/null
+++ b/modules/benchmark/guibench.c
@@ -0,0 +1,353 @@
+/*
+ * HardInfo - Displays System Information
+ * Copyright (C) 2003-2009 L. A. F. Pereira <l@tia.mat.br>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, version 2 or later.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#include <gtk/gtk.h>
+
+#include "iconcache.h"
+#include "config.h"
+
+#define N_ITERATIONS 100000
+#define PHRASE "I \342\231\245 HardInfo"
+
+typedef double (*BenchCallback)(GtkWindow *window);
+
+static double test_lines(GtkWindow *window);
+static double test_shapes(GtkWindow *window);
+static double test_filled_shapes(GtkWindow *window);
+static double test_text(GtkWindow *window);
+static double test_icons(GtkWindow *window);
+
+/*
+Results on a AMD Athlon 3200+ (Barton), 1GB RAM,
+nVidia Geforce 6200 with nvidia Xorg driver,
+running Linux 2.6.28, Xorg 1.6.0, Ubuntu 9.04
+desktop, GNOME 2.26.1, composite enabled.
+
+Test Time Iter/Sec
+Line Drawing 3.9570 25271.7663
+Shape Drawing 22.2499 4494.4065
+Filled Shape Drawing 4.0377 24766.2806
+Text Drawing 59.1565 1690.4309
+Icon Blitting 51.720941 1933.4528
+
+Results are normalized according to these values.
+A guibench() result of 1000.0 is roughly equivalent
+to this same setup.
+*/
+
+static struct {
+ BenchCallback callback;
+ gchar *title;
+ gdouble weight;
+} tests[] = {
+ { test_lines, "Line Drawing", 25271.77 },
+ { test_shapes, "Shape Drawing", 4494.49 },
+ { test_filled_shapes, "Filled Shape Drawing", 24766.28 },
+ { test_text, "Text Drawing", 1690.43 },
+ { test_icons, "Icon Blitting", 1933.45 },
+ { NULL, NULL }
+};
+
+static gchar *phrase = NULL;
+
+static gboolean keypress_event(GtkWidget *widget, GdkEventKey *event, gpointer user_data)
+{
+ const int magic[] = { 0x1b, 0x33, 0x3a, 0x35, 0x51 };
+ const int states[] = { 0xff52, 0xff52, 0xff54, 0xff54,
+ 0xff51, 0xff53, 0xff51, 0xff53,
+ 0x62, 0x61 };
+ static int state = 0;
+
+ if (event->keyval == states[state]) {
+ state++;
+ } else {
+ state = 0;
+ }
+
+ if (state == G_N_ELEMENTS(states)) {
+ int i;
+
+ for (i = 0; i < G_N_ELEMENTS(magic); i++) {
+ phrase[i + 6] = magic[i] ^ (states[i] & (states[i] >> 8));
+ }
+
+ state = 0;
+ }
+
+ return FALSE;
+}
+
+static double test_icons(GtkWindow *window)
+{
+ GdkPixbuf *pixbufs[3];
+ GdkGC *gc;
+ GRand *rand;
+ GTimer *timer;
+ double time;
+ GdkWindow *gdk_window = GTK_WIDGET(window)->window;
+ int icons;
+
+ gdk_window_clear(gdk_window);
+
+ rand = g_rand_new();
+ gc = gdk_gc_new(GDK_DRAWABLE(gdk_window));
+ timer = g_timer_new();
+
+ pixbufs[0] = icon_cache_get_pixbuf("hardinfo2.png");
+ pixbufs[1] = icon_cache_get_pixbuf("syncmanager.png");
+ pixbufs[2] = icon_cache_get_pixbuf("report-large.png");
+
+ g_timer_start(timer);
+ for (icons = N_ITERATIONS; icons >= 0; icons--) {
+ int x, y;
+
+ x = g_rand_int_range(rand, 0, 800);
+ y = g_rand_int_range(rand, 0, 600);
+
+ gdk_draw_pixbuf(GDK_DRAWABLE(gdk_window), gc,
+ pixbufs[icons % G_N_ELEMENTS(pixbufs)],
+ 0, 0, x, y, 48, 48,
+ GDK_RGB_DITHER_NONE, 0, 0);
+
+ while (gtk_events_pending()) {
+ gtk_main_iteration();
+ }
+ }
+ g_timer_stop(timer);
+
+ time = g_timer_elapsed(timer, NULL);
+
+ g_rand_free(rand);
+ gdk_gc_destroy(gc);
+ g_timer_destroy(timer);
+
+ return time;
+}
+
+static double test_text(GtkWindow *window)
+{
+ GRand *rand;
+ GTimer *timer;
+ GdkGC *gc;
+ double time;
+ PangoLayout *layout;
+ PangoFontDescription *font;
+ GdkWindow *gdk_window = GTK_WIDGET(window)->window;
+ int strings;
+
+ gdk_window_clear(gdk_window);
+
+ rand = g_rand_new();
+ gc = gdk_gc_new(GDK_DRAWABLE(gdk_window));
+ timer = g_timer_new();
+
+ font = pango_font_description_new();
+ layout = pango_layout_new(gtk_widget_get_pango_context(GTK_WIDGET(window)));
+ pango_layout_set_text(layout, phrase, -1);
+
+ g_timer_start(timer);
+ for (strings = N_ITERATIONS; strings >= 0; strings--) {
+ int x, y, size;
+
+ x = g_rand_int_range(rand, 0, 800);
+ y = g_rand_int_range(rand, 0, 600);
+ size = g_rand_int_range(rand, 1, 96) * PANGO_SCALE;
+
+ pango_font_description_set_size(font, size);
+ pango_layout_set_font_description(layout, font);
+ gdk_draw_layout(GDK_DRAWABLE(gdk_window), gc, x, y, layout);
+
+ gdk_rgb_gc_set_foreground(gc, strings << 8);
+
+ while (gtk_events_pending()) {
+ gtk_main_iteration();
+ }
+
+ }
+ g_timer_stop(timer);
+
+ time = g_timer_elapsed(timer, NULL);
+
+ g_rand_free(rand);
+ gdk_gc_destroy(gc);
+ g_timer_destroy(timer);
+ g_object_unref(layout);
+ pango_font_description_free(font);
+
+ return time;
+}
+
+static double test_filled_shapes(GtkWindow *window)
+{
+ GRand *rand;
+ GTimer *timer;
+ GdkGC *gc;
+ double time;
+ GdkWindow *gdk_window = GTK_WIDGET(window)->window;
+ int lines;
+
+ gdk_window_clear(gdk_window);
+
+ rand = g_rand_new();
+ gc = gdk_gc_new(GDK_DRAWABLE(gdk_window));
+ timer = g_timer_new();
+
+ g_timer_start(timer);
+ for (lines = N_ITERATIONS; lines >= 0; lines--) {
+ int x1, y1;
+
+ x1 = g_rand_int_range(rand, 0, 800);
+ y1 = g_rand_int_range(rand, 0, 600);
+
+ gdk_rgb_gc_set_foreground(gc, lines << 8);
+
+ gdk_draw_rectangle(GDK_DRAWABLE(gdk_window), gc, TRUE,
+ x1, y1,
+ g_rand_int_range(rand, 0, 400),
+ g_rand_int_range(rand, 0, 300));
+
+ while (gtk_events_pending()) {
+ gtk_main_iteration();
+ }
+ }
+ g_timer_stop(timer);
+
+ time = g_timer_elapsed(timer, NULL);
+
+ g_rand_free(rand);
+ gdk_gc_destroy(gc);
+ g_timer_destroy(timer);
+
+ return time;
+}
+
+static double test_shapes(GtkWindow *window)
+{
+ GRand *rand;
+ GTimer *timer;
+ GdkGC *gc;
+ double time;
+ GdkWindow *gdk_window = GTK_WIDGET(window)->window;
+ int lines;
+
+ gdk_window_clear(gdk_window);
+
+ rand = g_rand_new();
+ gc = gdk_gc_new(GDK_DRAWABLE(gdk_window));
+ timer = g_timer_new();
+
+ g_timer_start(timer);
+ for (lines = N_ITERATIONS; lines >= 0; lines--) {
+ int x1, y1;
+
+ x1 = g_rand_int_range(rand, 0, 800);
+ y1 = g_rand_int_range(rand, 0, 600);
+
+ gdk_rgb_gc_set_foreground(gc, lines << 8);
+
+ gdk_draw_rectangle(GDK_DRAWABLE(gdk_window), gc, FALSE,
+ x1, y1,
+ g_rand_int_range(rand, 0, 400),
+ g_rand_int_range(rand, 0, 300));
+ while (gtk_events_pending()) {
+ gtk_main_iteration();
+ }
+ }
+ g_timer_stop(timer);
+
+ time = g_timer_elapsed(timer, NULL);
+
+ g_rand_free(rand);
+ gdk_gc_destroy(gc);
+ g_timer_destroy(timer);
+
+ return time;
+}
+
+static double test_lines(GtkWindow *window)
+{
+ GRand *rand;
+ GTimer *timer;
+ GdkGC *gc;
+ double time;
+ GdkWindow *gdk_window = GTK_WIDGET(window)->window;
+ int lines;
+
+ gdk_window_clear(gdk_window);
+
+ rand = g_rand_new();
+ gc = gdk_gc_new(GDK_DRAWABLE(gdk_window));
+ timer = g_timer_new();
+
+ g_timer_start(timer);
+ for (lines = N_ITERATIONS; lines >= 0; lines--) {
+ int x1, y1, x2, y2;
+
+ x1 = g_rand_int_range(rand, 0, 800);
+ y1 = g_rand_int_range(rand, 0, 600);
+ x2 = g_rand_int_range(rand, 0, 800);
+ y2 = g_rand_int_range(rand, 0, 600);
+
+ gdk_draw_line(GDK_DRAWABLE(gdk_window), gc, x1, y1, x2, y2);
+ gdk_rgb_gc_set_foreground(gc, lines << 8);
+
+ while (gtk_events_pending()) {
+ gtk_main_iteration();
+ }
+ }
+ g_timer_stop(timer);
+
+ time = g_timer_elapsed(timer, NULL);
+
+ g_rand_free(rand);
+ gdk_gc_destroy(gc);
+ g_timer_destroy(timer);
+
+ return time;
+}
+
+double guibench(void)
+{
+ GtkWidget *window;
+ gdouble score = 0.0f;
+ gint i;
+
+ phrase = g_strdup(PHRASE);
+
+ window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
+ gtk_widget_set_size_request(window, 800, 600);
+ gtk_window_set_title(GTK_WINDOW(window), "guibench");
+
+ gtk_window_set_position(GTK_WINDOW(window), GTK_WIN_POS_CENTER_ALWAYS);
+ gtk_widget_show(window);
+
+ g_signal_connect(window, "key-press-event", G_CALLBACK(keypress_event), NULL);
+
+ for (i = 0; tests[i].title; i++) {
+ double time;
+
+ gtk_window_set_title(GTK_WINDOW(window), tests[i].title);
+ time = tests[i].callback(GTK_WINDOW(window));
+ score += (N_ITERATIONS / time) / tests[i].weight;
+ }
+
+ gtk_widget_destroy(window);
+ g_free(phrase);
+
+ return (score / i) * 1000.0f;
+}
diff --git a/modules/benchmark/md5.c b/modules/benchmark/md5.c
new file mode 100644
index 00000000..f4032ddd
--- /dev/null
+++ b/modules/benchmark/md5.c
@@ -0,0 +1,317 @@
+/*
+ * This code implements the MD5 message-digest algorithm.
+ * The algorithm is due to Ron Rivest. This code was
+ * written by Colin Plumb in 1993, no copyright is claimed.
+ * This code is in the public domain; do with it what you wish.
+ *
+ * Equivalent code is available from RSA Data Security, Inc.
+ * This code has been tested against that, and is equivalent,
+ * except that you don't need to include two pages of legalese
+ * with every copy.
+ *
+ * To compute the message digest of a chunk of bytes, declare an
+ * MD5Context structure, pass it to MD5Init, call MD5Update as
+ * needed on buffers full of bytes, and then call MD5Final, which
+ * will fill a supplied 16-byte array with the digest.
+ */
+
+/* This code was modified in 1997 by Jim Kingdon of Cyclic Software to
+ not require an integer type which is exactly 32 bits. This work
+ draws on the changes for the same purpose by Tatu Ylonen
+ <ylo@cs.hut.fi> as part of SSH, but since I didn't actually use
+ that code, there is no copyright issue. I hereby disclaim
+ copyright in any changes I have made; this code remains in the
+ public domain. */
+
+#include <string.h> /* for memcpy() and memset() */
+
+/* Add prototype support. */
+#ifndef PROTO
+#if defined (USE_PROTOTYPES) ? USE_PROTOTYPES : defined (__STDC__)
+#define PROTO(ARGS) ARGS
+#else
+#define PROTO(ARGS) ()
+#endif
+#endif
+
+#include "md5.h"
+
+#if defined(__OPTIMIZE__)
+#error You must compile this program without "-O". (Or else the benchmark results may be different!)
+#endif
+
+/* Little-endian byte-swapping routines. Note that these do not
+ depend on the size of datatypes such as uint32, nor do they require
+ us to detect the endianness of the machine we are running on. It
+ is possible they should be macros for speed, but I would be
+ surprised if they were a performance bottleneck for MD5. */
+
+static uint32 getu32(addr)
+const unsigned char *addr;
+{
+ return (((((unsigned long) addr[3] << 8) | addr[2]) << 8)
+ | addr[1]) << 8 | addr[0];
+}
+
+static void putu32(data, addr)
+uint32 data;
+unsigned char *addr;
+{
+ addr[0] = (unsigned char) data;
+ addr[1] = (unsigned char) (data >> 8);
+ addr[2] = (unsigned char) (data >> 16);
+ addr[3] = (unsigned char) (data >> 24);
+}
+
+/*
+ * Start MD5 accumulation. Set bit count to 0 and buffer to mysterious
+ * initialization constants.
+ */
+void MD5Init(ctx)
+struct MD5Context *ctx;
+{
+ ctx->buf[0] = 0x67452301;
+ ctx->buf[1] = 0xefcdab89;
+ ctx->buf[2] = 0x98badcfe;
+ ctx->buf[3] = 0x10325476;
+
+ ctx->bits[0] = 0;
+ ctx->bits[1] = 0;
+}
+
+/*
+ * Update context to reflect the concatenation of another buffer full
+ * of bytes.
+ */
+void MD5Update(ctx, buf, len)
+struct MD5Context *ctx;
+unsigned char const *buf;
+unsigned len;
+{
+ uint32 t;
+
+ /* Update bitcount */
+
+ t = ctx->bits[0];
+ if ((ctx->bits[0] = (t + ((uint32) len << 3)) & 0xffffffff) < t)
+ ctx->bits[1]++; /* Carry from low to high */
+ ctx->bits[1] += len >> 29;
+
+ t = (t >> 3) & 0x3f; /* Bytes already in shsInfo->data */
+
+ /* Handle any leading odd-sized chunks */
+
+ if (t) {
+ unsigned char *p = ctx->in + t;
+
+ t = 64 - t;
+ if (len < t) {
+ memcpy(p, buf, len);
+ return;
+ }
+ memcpy(p, buf, t);
+ MD5Transform(ctx->buf, ctx->in);
+ buf += t;
+ len -= t;
+ }
+
+ /* Process data in 64-byte chunks */
+
+ while (len >= 64) {
+ memcpy(ctx->in, buf, 64);
+ MD5Transform(ctx->buf, ctx->in);
+ buf += 64;
+ len -= 64;
+ }
+
+ /* Handle any remaining bytes of data. */
+
+ memcpy(ctx->in, buf, len);
+}
+
+/*
+ * Final wrapup - pad to 64-byte boundary with the bit pattern
+ * 1 0* (64-bit count of bits processed, MSB-first)
+ */
+void MD5Final(digest, ctx)
+unsigned char digest[16];
+struct MD5Context *ctx;
+{
+ unsigned count;
+ unsigned char *p;
+
+ /* Compute number of bytes mod 64 */
+ count = (ctx->bits[0] >> 3) & 0x3F;
+
+ /* Set the first char of padding to 0x80. This is safe since there is
+ always at least one byte free */
+ p = ctx->in + count;
+ *p++ = 0x80;
+
+ /* Bytes of padding needed to make 64 bytes */
+ count = 64 - 1 - count;
+
+ /* Pad out to 56 mod 64 */
+ if (count < 8) {
+ /* Two lots of padding: Pad the first block to 64 bytes */
+ memset(p, 0, count);
+ MD5Transform(ctx->buf, ctx->in);
+
+ /* Now fill the next block with 56 bytes */
+ memset(ctx->in, 0, 56);
+ } else {
+ /* Pad block to 56 bytes */
+ memset(p, 0, count - 8);
+ }
+
+ /* Append length in bits and transform */
+ putu32(ctx->bits[0], ctx->in + 56);
+ putu32(ctx->bits[1], ctx->in + 60);
+
+ MD5Transform(ctx->buf, ctx->in);
+ putu32(ctx->buf[0], digest);
+ putu32(ctx->buf[1], digest + 4);
+ putu32(ctx->buf[2], digest + 8);
+ putu32(ctx->buf[3], digest + 12);
+ memset(ctx, 0, sizeof(*ctx)); /* In case it's sensitive */
+}
+
+#ifndef ASM_MD5
+
+/* The four core functions - F1 is optimized somewhat */
+
+/* #define F1(x, y, z) (x & y | ~x & z) */
+#define F1(x, y, z) (z ^ (x & (y ^ z)))
+#define F2(x, y, z) F1(z, x, y)
+#define F3(x, y, z) (x ^ y ^ z)
+#define F4(x, y, z) (y ^ (x | ~z))
+
+/* This is the central step in the MD5 algorithm. */
+#define MD5STEP(f, w, x, y, z, data, s) \
+ ( w += f(x, y, z) + data, w &= 0xffffffff, w = w<<s | w>>(32-s), w += x )
+
+/*
+ * The core of the MD5 algorithm, this alters an existing MD5 hash to
+ * reflect the addition of 16 longwords of new data. MD5Update blocks
+ * the data and converts bytes into longwords for this routine.
+ */
+void MD5Transform(buf, inraw)
+uint32 buf[4];
+const unsigned char inraw[64];
+{
+ register uint32 a, b, c, d;
+ uint32 in[16];
+ int i;
+
+ for (i = 0; i < 16; ++i)
+ in[i] = getu32(inraw + 4 * i);
+
+ a = buf[0];
+ b = buf[1];
+ c = buf[2];
+ d = buf[3];
+
+ MD5STEP(F1, a, b, c, d, in[0] + 0xd76aa478, 7);
+ MD5STEP(F1, d, a, b, c, in[1] + 0xe8c7b756, 12);
+ MD5STEP(F1, c, d, a, b, in[2] + 0x242070db, 17);
+ MD5STEP(F1, b, c, d, a, in[3] + 0xc1bdceee, 22);
+ MD5STEP(F1, a, b, c, d, in[4] + 0xf57c0faf, 7);
+ MD5STEP(F1, d, a, b, c, in[5] + 0x4787c62a, 12);
+ MD5STEP(F1, c, d, a, b, in[6] + 0xa8304613, 17);
+ MD5STEP(F1, b, c, d, a, in[7] + 0xfd469501, 22);
+ MD5STEP(F1, a, b, c, d, in[8] + 0x698098d8, 7);
+ MD5STEP(F1, d, a, b, c, in[9] + 0x8b44f7af, 12);
+ MD5STEP(F1, c, d, a, b, in[10] + 0xffff5bb1, 17);
+ MD5STEP(F1, b, c, d, a, in[11] + 0x895cd7be, 22);
+ MD5STEP(F1, a, b, c, d, in[12] + 0x6b901122, 7);
+ MD5STEP(F1, d, a, b, c, in[13] + 0xfd987193, 12);
+ MD5STEP(F1, c, d, a, b, in[14] + 0xa679438e, 17);
+ MD5STEP(F1, b, c, d, a, in[15] + 0x49b40821, 22);
+
+ MD5STEP(F2, a, b, c, d, in[1] + 0xf61e2562, 5);
+ MD5STEP(F2, d, a, b, c, in[6] + 0xc040b340, 9);
+ MD5STEP(F2, c, d, a, b, in[11] + 0x265e5a51, 14);
+ MD5STEP(F2, b, c, d, a, in[0] + 0xe9b6c7aa, 20);
+ MD5STEP(F2, a, b, c, d, in[5] + 0xd62f105d, 5);
+ MD5STEP(F2, d, a, b, c, in[10] + 0x02441453, 9);
+ MD5STEP(F2, c, d, a, b, in[15] + 0xd8a1e681, 14);
+ MD5STEP(F2, b, c, d, a, in[4] + 0xe7d3fbc8, 20);
+ MD5STEP(F2, a, b, c, d, in[9] + 0x21e1cde6, 5);
+ MD5STEP(F2, d, a, b, c, in[14] + 0xc33707d6, 9);
+ MD5STEP(F2, c, d, a, b, in[3] + 0xf4d50d87, 14);
+ MD5STEP(F2, b, c, d, a, in[8] + 0x455a14ed, 20);
+ MD5STEP(F2, a, b, c, d, in[13] + 0xa9e3e905, 5);
+ MD5STEP(F2, d, a, b, c, in[2] + 0xfcefa3f8, 9);
+ MD5STEP(F2, c, d, a, b, in[7] + 0x676f02d9, 14);
+ MD5STEP(F2, b, c, d, a, in[12] + 0x8d2a4c8a, 20);
+
+ MD5STEP(F3, a, b, c, d, in[5] + 0xfffa3942, 4);
+ MD5STEP(F3, d, a, b, c, in[8] + 0x8771f681, 11);
+ MD5STEP(F3, c, d, a, b, in[11] + 0x6d9d6122, 16);
+ MD5STEP(F3, b, c, d, a, in[14] + 0xfde5380c, 23);
+ MD5STEP(F3, a, b, c, d, in[1] + 0xa4beea44, 4);
+ MD5STEP(F3, d, a, b, c, in[4] + 0x4bdecfa9, 11);
+ MD5STEP(F3, c, d, a, b, in[7] + 0xf6bb4b60, 16);
+ MD5STEP(F3, b, c, d, a, in[10] + 0xbebfbc70, 23);
+ MD5STEP(F3, a, b, c, d, in[13] + 0x289b7ec6, 4);
+ MD5STEP(F3, d, a, b, c, in[0] + 0xeaa127fa, 11);
+ MD5STEP(F3, c, d, a, b, in[3] + 0xd4ef3085, 16);
+ MD5STEP(F3, b, c, d, a, in[6] + 0x04881d05, 23);
+ MD5STEP(F3, a, b, c, d, in[9] + 0xd9d4d039, 4);
+ MD5STEP(F3, d, a, b, c, in[12] + 0xe6db99e5, 11);
+ MD5STEP(F3, c, d, a, b, in[15] + 0x1fa27cf8, 16);
+ MD5STEP(F3, b, c, d, a, in[2] + 0xc4ac5665, 23);
+
+ MD5STEP(F4, a, b, c, d, in[0] + 0xf4292244, 6);
+ MD5STEP(F4, d, a, b, c, in[7] + 0x432aff97, 10);
+ MD5STEP(F4, c, d, a, b, in[14] + 0xab9423a7, 15);
+ MD5STEP(F4, b, c, d, a, in[5] + 0xfc93a039, 21);
+ MD5STEP(F4, a, b, c, d, in[12] + 0x655b59c3, 6);
+ MD5STEP(F4, d, a, b, c, in[3] + 0x8f0ccc92, 10);
+ MD5STEP(F4, c, d, a, b, in[10] + 0xffeff47d, 15);
+ MD5STEP(F4, b, c, d, a, in[1] + 0x85845dd1, 21);
+ MD5STEP(F4, a, b, c, d, in[8] + 0x6fa87e4f, 6);
+ MD5STEP(F4, d, a, b, c, in[15] + 0xfe2ce6e0, 10);
+ MD5STEP(F4, c, d, a, b, in[6] + 0xa3014314, 15);
+ MD5STEP(F4, b, c, d, a, in[13] + 0x4e0811a1, 21);
+ MD5STEP(F4, a, b, c, d, in[4] + 0xf7537e82, 6);
+ MD5STEP(F4, d, a, b, c, in[11] + 0xbd3af235, 10);
+ MD5STEP(F4, c, d, a, b, in[2] + 0x2ad7d2bb, 15);
+ MD5STEP(F4, b, c, d, a, in[9] + 0xeb86d391, 21);
+
+ buf[0] += a;
+ buf[1] += b;
+ buf[2] += c;
+ buf[3] += d;
+}
+#endif
+
+#ifdef TEST
+/* Simple test program. Can use it to manually run the tests from
+ RFC1321 for example. */
+#include <stdio.h>
+
+int main(int argc, char **argv)
+{
+ struct MD5Context context;
+ unsigned char checksum[16];
+ int i;
+ int j;
+
+ if (argc < 2) {
+ fprintf(stderr, "usage: %s string-to-hash\n", argv[0]);
+ exit(1);
+ }
+ for (j = 1; j < argc; ++j) {
+ printf("MD5 (\"%s\") = ", argv[j]);
+ MD5Init(&context);
+ MD5Update(&context, argv[j], strlen(argv[j]));
+ MD5Final(checksum, &context);
+ for (i = 0; i < 16; i++) {
+ printf("%02x", (unsigned int) checksum[i]);
+ }
+ printf("\n");
+ }
+ return 0;
+}
+#endif /* TEST */
diff --git a/modules/benchmark/nqueens.c b/modules/benchmark/nqueens.c
new file mode 100644
index 00000000..6aad7638
--- /dev/null
+++ b/modules/benchmark/nqueens.c
@@ -0,0 +1,68 @@
+/*
+ * N-Queens Problem Solver
+ * Found somewhere on the Internet; can't remember where. Possibly Wikipedia.
+ */
+#include <stdio.h>
+#include <stdbool.h>
+#include <stdlib.h>
+
+#include "hardinfo.h"
+#include "benchmark.h"
+
+/* if anything changes in this block, increment revision */
+#define BENCH_REVISION 2
+#define QUEENS 6
+#define CRUNCH_TIME 5
+
+int row[QUEENS];
+
+bool safe(int x, int y) {
+ int i;
+ for (i = 1; i <= y; i++)
+ if (row[y - i] == x || row[y - i] == x - i || row[y - i] == x + i)
+ return false;
+ return true;
+}
+
+int nqueens(int y) {
+ int x;
+
+ for (x = 0; x < QUEENS; x++) {
+ if (safe((row[y - 1] = x), y - 1)) {
+ if (y < QUEENS) {
+ nqueens(y + 1);
+ } else {
+ break;
+ }
+ }
+ }
+
+ return 0;
+}
+
+static gpointer nqueens_for(void *data, gint thread_number)
+{
+ nqueens(0);
+
+ return NULL;
+}
+
+void
+benchmark_nqueens(void)
+{
+ bench_value r = EMPTY_BENCH_VALUE;
+
+ shell_view_set_enabled(FALSE);
+ shell_status_update("Running N-Queens benchmark...");
+
+ r = benchmark_crunch_for(CRUNCH_TIME, 0, nqueens_for, NULL);
+
+ r.revision = BENCH_REVISION;
+ snprintf(r.extra, 255, "q:%d", QUEENS);
+
+ r.result /= 25;
+
+ bench_results[BENCHMARK_NQUEENS] = r;
+}
+
+
diff --git a/modules/benchmark/raytrace.c b/modules/benchmark/raytrace.c
new file mode 100644
index 00000000..bddc3232
--- /dev/null
+++ b/modules/benchmark/raytrace.c
@@ -0,0 +1,56 @@
+/*
+ * HardInfo - System Information and Benchmark
+ * Copyright (C) 2003-2007 L. A. F. Pereira <l@tia.mat.br>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, version 2 or later.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#include "benchmark.h"
+
+/* if anything changes in this block, increment revision */
+#define BENCH_REVISION 2
+#define CRUNCH_TIME 5
+
+void fbench(); /* fbench.c */
+
+static gpointer parallel_raytrace(void *in_data, gint thread_number)
+{
+ unsigned int i;
+
+ fbench();
+
+ return NULL;
+}
+
+void
+benchmark_raytrace(void)
+{
+ bench_value r = EMPTY_BENCH_VALUE;
+ gchar *test_data = get_test_data(1000);
+
+ shell_view_set_enabled(FALSE);
+ shell_status_update("Performing John Walker's FBENCH...");
+
+ r = benchmark_crunch_for(CRUNCH_TIME, 1, parallel_raytrace, test_data);
+
+ r.revision = BENCH_REVISION;
+ snprintf(r.extra, 255, "r:%d", 500);//niter from fbench
+
+ g_free(test_data);
+
+ r.result /= 10;
+
+ bench_results[BENCHMARK_RAYTRACE] = r;
+}
+
diff --git a/modules/benchmark/sha1.c b/modules/benchmark/sha1.c
new file mode 100644
index 00000000..3b213218
--- /dev/null
+++ b/modules/benchmark/sha1.c
@@ -0,0 +1,329 @@
+/*
+SHA-1 in C
+By Steve Reid <steve@edmweb.com>
+100% Public Domain
+
+Test Vectors (from FIPS PUB 180-1)
+"abc"
+ A9993E36 4706816A BA3E2571 7850C26C 9CD0D89D
+"abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq"
+ 84983E44 1C3BD26E BAAE4AA1 F95129E5 E54670F1
+A million repetitions of "a"
+ 34AA973C D4C4DAA4 F61EEB2B DBAD2731 6534016F
+*/
+
+
+/* #define SHA1HANDSOFF * Copies data before messing with it. */
+
+#include <stdio.h>
+#include <string.h>
+#include <sha1.h>
+
+#if defined(__OPTIMIZE__)
+#error You must compile this program without "-O".
+#endif
+
+
+#define rol(value, bits) (((value) << (bits)) | ((value) >> (32 - (bits))))
+
+/* blk0() and blk() perform the initial expand. */
+/* I got the idea of expanding during the round function from SSLeay */
+#ifdef LITTLE_ENDIAN
+#define blk0(i) (block->l[i] = (rol(block->l[i],24)&0xFF00FF00) \
+ |(rol(block->l[i],8)&0x00FF00FF))
+#else
+#define blk0(i) block->l[i]
+#endif
+#define blk(i) (block->l[i&15] = rol(block->l[(i+13)&15]^block->l[(i+8)&15] \
+ ^block->l[(i+2)&15]^block->l[i&15],1))
+
+/* (R0+R1), R2, R3, R4 are the different operations used in SHA1 */
+#define R0(v,w,x,y,z,i) z+=((w&(x^y))^y)+blk0(i)+0x5A827999+rol(v,5);w=rol(w,30);
+#define R1(v,w,x,y,z,i) z+=((w&(x^y))^y)+blk(i)+0x5A827999+rol(v,5);w=rol(w,30);
+#define R2(v,w,x,y,z,i) z+=(w^x^y)+blk(i)+0x6ED9EBA1+rol(v,5);w=rol(w,30);
+#define R3(v,w,x,y,z,i) z+=(((w|x)&y)|(w&x))+blk(i)+0x8F1BBCDC+rol(v,5);w=rol(w,30);
+#define R4(v,w,x,y,z,i) z+=(w^x^y)+blk(i)+0xCA62C1D6+rol(v,5);w=rol(w,30);
+
+
+/* Hash a single 512-bit block. This is the core of the algorithm. */
+
+void SHA1Transform(guint32 state[20], guchar buffer[64])
+{
+ guint32 a, b, c, d, e;
+ typedef union {
+ guchar c[64];
+ guint32 l[16];
+ } CHAR64LONG16;
+ CHAR64LONG16 *block;
+#ifdef SHA1HANDSOFF
+ static guchar workspace[64];
+ block = (CHAR64LONG16 *) workspace;
+ memcpy(block, buffer, 64);
+#else
+ block = (CHAR64LONG16 *) buffer;
+#endif
+ /* Copy context->state[] to working vars */
+ a = state[0];
+ b = state[1];
+ c = state[2];
+ d = state[3];
+ e = state[4];
+ /* 4 rounds of 20 operations each. Loop unrolled. */
+ R0(a, b, c, d, e, 0);
+ R0(e, a, b, c, d, 1);
+ R0(d, e, a, b, c, 2);
+ R0(c, d, e, a, b, 3);
+ R0(b, c, d, e, a, 4);
+ R0(a, b, c, d, e, 5);
+ R0(e, a, b, c, d, 6);
+ R0(d, e, a, b, c, 7);
+ R0(c, d, e, a, b, 8);
+ R0(b, c, d, e, a, 9);
+ R0(a, b, c, d, e, 10);
+ R0(e, a, b, c, d, 11);
+ R0(d, e, a, b, c, 12);
+ R0(c, d, e, a, b, 13);
+ R0(b, c, d, e, a, 14);
+ R0(a, b, c, d, e, 15);
+ R1(e, a, b, c, d, 16);
+ R1(d, e, a, b, c, 17);
+ R1(c, d, e, a, b, 18);
+ R1(b, c, d, e, a, 19);
+ R2(a, b, c, d, e, 20);
+ R2(e, a, b, c, d, 21);
+ R2(d, e, a, b, c, 22);
+ R2(c, d, e, a, b, 23);
+ R2(b, c, d, e, a, 24);
+ R2(a, b, c, d, e, 25);
+ R2(e, a, b, c, d, 26);
+ R2(d, e, a, b, c, 27);
+ R2(c, d, e, a, b, 28);
+ R2(b, c, d, e, a, 29);
+ R2(a, b, c, d, e, 30);
+ R2(e, a, b, c, d, 31);
+ R2(d, e, a, b, c, 32);
+ R2(c, d, e, a, b, 33);
+ R2(b, c, d, e, a, 34);
+ R2(a, b, c, d, e, 35);
+ R2(e, a, b, c, d, 36);
+ R2(d, e, a, b, c, 37);
+ R2(c, d, e, a, b, 38);
+ R2(b, c, d, e, a, 39);
+ R3(a, b, c, d, e, 40);
+ R3(e, a, b, c, d, 41);
+ R3(d, e, a, b, c, 42);
+ R3(c, d, e, a, b, 43);
+ R3(b, c, d, e, a, 44);
+ R3(a, b, c, d, e, 45);
+ R3(e, a, b, c, d, 46);
+ R3(d, e, a, b, c, 47);
+ R3(c, d, e, a, b, 48);
+ R3(b, c, d, e, a, 49);
+ R3(a, b, c, d, e, 50);
+ R3(e, a, b, c, d, 51);
+ R3(d, e, a, b, c, 52);
+ R3(c, d, e, a, b, 53);
+ R3(b, c, d, e, a, 54);
+ R3(a, b, c, d, e, 55);
+ R3(e, a, b, c, d, 56);
+ R3(d, e, a, b, c, 57);
+ R3(c, d, e, a, b, 58);
+ R3(b, c, d, e, a, 59);
+ R4(a, b, c, d, e, 60);
+ R4(e, a, b, c, d, 61);
+ R4(d, e, a, b, c, 62);
+ R4(c, d, e, a, b, 63);
+ R4(b, c, d, e, a, 64);
+ R4(a, b, c, d, e, 65);
+ R4(e, a, b, c, d, 66);
+ R4(d, e, a, b, c, 67);
+ R4(c, d, e, a, b, 68);
+ R4(b, c, d, e, a, 69);
+ R4(a, b, c, d, e, 70);
+ R4(e, a, b, c, d, 71);
+ R4(d, e, a, b, c, 72);
+ R4(c, d, e, a, b, 73);
+ R4(b, c, d, e, a, 74);
+ R4(a, b, c, d, e, 75);
+ R4(e, a, b, c, d, 76);
+ R4(d, e, a, b, c, 77);
+ R4(c, d, e, a, b, 78);
+ R4(b, c, d, e, a, 79);
+ /* Add the working vars back into context.state[] */
+ state[0] += a;
+ state[1] += b;
+ state[2] += c;
+ state[3] += d;
+ state[4] += e;
+ /* Wipe variables */
+ a = b = c = d = e = 0;
+}
+
+
+/* SHA1Init - Initialize new context */
+
+void SHA1Init(SHA1_CTX * context)
+{
+ /* SHA1 initialization constants */
+ context->state[0] = 0x67452301;
+ context->state[1] = 0xEFCDAB89;
+ context->state[2] = 0x98BADCFE;
+ context->state[3] = 0x10325476;
+ context->state[4] = 0xC3D2E1F0;
+ context->count[0] = context->count[1] = 0;
+}
+
+
+/* Run your data through this. */
+
+void SHA1Update(SHA1_CTX * context, guchar * data, guint32 len)
+{
+ guint32 i, j;
+
+ j = (context->count[0] >> 3) & 63;
+ if ((context->count[0] += len << 3) < (len << 3))
+ context->count[1]++;
+ context->count[1] += (len >> 29);
+ if ((j + len) > 63) {
+ memcpy(&context->buffer[j], data, (i = 64 - j));
+ SHA1Transform(context->state, context->buffer);
+ for (; i + 63 < len; i += 64) {
+ SHA1Transform(context->state, &data[i]);
+ }
+ j = 0;
+ } else
+ i = 0;
+ memcpy(&context->buffer[j], &data[i], len - i);
+}
+
+
+/* Add padding and return the message digest. */
+
+void SHA1Final(guchar digest[20], SHA1_CTX * context)
+{
+ guint32 i, j;
+ guchar finalcount[8];
+
+ for (i = 0; i < 8; i++) {
+ finalcount[i] = (guchar) ((context->count[(i >= 4 ? 0 : 1)]
+ >> ((3 - (i & 3)) * 8)) & 255); /* Endian independent */
+ }
+ SHA1Update(context, (guchar *) "\200", 1);
+ while ((context->count[0] & 504) != 448) {
+ SHA1Update(context, (guchar *) "\0", 1);
+ }
+ SHA1Update(context, finalcount, 8); /* Should cause a SHA1Transform() */
+ for (i = 0; i < 20; i++) {
+ digest[i] = (guchar)
+ ((context->state[i >> 2] >> ((3 - (i & 3)) * 8)) & 255);
+ }
+ /* Wipe variables */
+ i = j = 0;
+ memset(context->buffer, 0, 64);
+ memset(context->state, 0, 20);
+ memset(context->count, 0, 8);
+ memset(&finalcount, 0, 8);
+#ifdef SHA1HANDSOFF /* make SHA1Transform overwrite it's own static vars */
+ SHA1Transform(context->state, context->buffer);
+#endif
+}
+
+#ifdef SHA1_TEST
+static char *b32_alphabet = "ABCDEFGHIJKLMNOPQRSTUVWXYZ234567";
+
+static void base32_encode_exactly(guchar * buf, gint len,
+ guchar * encbuf, gint enclen)
+{
+ gint i = 0;
+ guchar *ip = buf + len;
+ guchar *op = encbuf + enclen;
+
+ switch (len % 5) {
+ case 0:
+ do {
+ g_assert(op - encbuf >= 8);
+ i = *--ip; /* Input #4 */
+ *--op = b32_alphabet[i & 0x1f]; /* Ouput #7 */
+ i >>= 5; /* upper <234>, input #4 */
+ /* FALLTHROUGH */
+ case 4:
+ i |= ((guint32) * --ip) << 3; /* had 3 bits in `i' */
+ *--op = b32_alphabet[i & 0x1f]; /* Output #6 */
+ i >>= 5; /* upper <401234>, input #3 */
+ *--op = b32_alphabet[i & 0x1f]; /* Output #5 */
+ i >>= 5; /* upper <4>, input #3 */
+ /* FALLTHROUGH */
+ case 3:
+ i |= ((guint32) * --ip) << 1; /* had 1 bits in `i' */
+ *--op = b32_alphabet[i & 0x1f]; /* Output #4 */
+ i >>= 5; /* upper <1234>, input #2 */
+ /* FALLTHROUGH */
+ case 2:
+ i |= ((guint32) * --ip) << 4; /* had 4 bits in `i' */
+ *--op = b32_alphabet[i & 0x1f]; /* Output #3 */
+ i >>= 5; /* upper <3401234>, input #1 */
+ *--op = b32_alphabet[i & 0x1f]; /* Output #2 */
+ i >>= 5; /* upper <34>, input #1 */
+ /* FALLTHROUGH */
+ case 1:
+ i |= ((guint32) * --ip) << 2; /* had 2 bits in `i' */
+ *--op = b32_alphabet[i & 0x1f]; /* Output #1 */
+ i >>= 5; /* upper <01234>, input #0 */
+ *--op = b32_alphabet[i & 0x1f]; /* Output #0 */
+ i >>= 5; /* Holds nothing, MBZ */
+ g_assert(i == 0);
+ g_assert(op >= encbuf);
+ } while (op > encbuf);
+ }
+}
+
+
+
+/*************************************************************/
+
+int main(int argc, char **argv)
+{
+ gint i, j;
+ SHA1_CTX context;
+ guchar digest[20], buffer[16384];
+ FILE *file;
+
+ if (argc > 2) {
+ puts("Public domain SHA-1 implementation - by Steve Reid <steve@edmweb.com>");
+ puts("Produces the SHA-1 hash of a file, or stdin if no file is specified.");
+ exit(0);
+ }
+ if (argc < 2) {
+ file = stdin;
+ } else {
+ if (!(file = fopen(argv[1], "rb"))) {
+ fputs("Unable to open file.", stderr);
+ exit(-1);
+ }
+ }
+ SHA1Init(&context);
+ while (!feof(file)) { /* note: what if ferror(file) */
+ i = fread(buffer, 1, 16384, file);
+ SHA1Update(&context, buffer, i);
+ }
+ SHA1Final(digest, &context);
+ fclose(file);
+
+ for (i = 0; i < 5; i++) {
+ for (j = 0; j < 4; j++) {
+ printf("%02X", digest[i*4+j]);
+ }
+ putchar(' ');
+ }
+ putchar('\n');
+
+ {
+ guchar tmp[33];
+ tmp[32] = '\0';
+ base32_encode_exactly(digest, 20, tmp, 32);
+ printf("%s\n", tmp);
+ }
+
+ exit(0);
+}
+#endif /* SHA1_TEST */
diff --git a/modules/benchmark/sysbench.c b/modules/benchmark/sysbench.c
new file mode 100644
index 00000000..5c45831d
--- /dev/null
+++ b/modules/benchmark/sysbench.c
@@ -0,0 +1,270 @@
+/*
+ * HardInfo - Displays System Information
+ * Copyright (C) 2003-2017 L. A. F. Pereira <l@tia.mat.br>
+ * Copyright (C) 2019 Burt P. <pburt0@gmail.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, version 2 or later.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#include "hardinfo.h"
+#include "benchmark.h"
+#include "cpu_util.h"
+
+#define STATMSG "Performing Alexey Kopytov's sysbench memory benchmark"
+
+/* known to work with:
+ * sysbench 0.4.12 --> r:4012
+ * sysbench 1.0.11 --> r:1000011
+ * sysbench 1.0.15 --> r:1000015
+ */
+
+struct sysbench_ctx {
+ char *test;
+ int threads;
+ int max_time;
+ char *parms_test;
+ bench_value r;
+};
+
+int sysbench_version() {
+ int ret = -1;
+ int v1 = 0, v2 = 0, v3 = 0, mc = 0;
+ gboolean spawned;
+ gchar *out, *err, *p, *next_nl;
+
+ spawned = g_spawn_command_line_sync("sysbench --version",
+ &out, &err, NULL, NULL);
+ if (spawned) {
+ ret = 0;
+ p = out;
+ while(next_nl = strchr(p, '\n')) {
+ *next_nl = 0;
+ /* version */
+ mc = sscanf(p, "sysbench %d.%d.%d", &v1, &v2, &v3);
+ if (mc >= 1) {
+ ret += v1 * 1000000;
+ ret += v2 * 1000;
+ ret += v3;
+ break;
+ }
+ p = next_nl + 1;
+ }
+ g_free(out);
+ g_free(err);
+ }
+ return ret;
+}
+
+static gboolean sysbench_run(struct sysbench_ctx *ctx, int expecting_version) {
+ gboolean spawned;
+ gchar *out, *err, *p, *next_nl;
+
+ int v1 = 0, v2 = 0, v3 = 0, mc = 0;
+ char *pp = NULL;
+
+ if (!ctx) return FALSE;
+ if (!ctx->test) return FALSE;
+ if (!ctx->parms_test) return FALSE;
+ if (!ctx->threads) ctx->threads = 1;
+ ctx->r.threads_used = ctx->threads;
+ if (!ctx->max_time) ctx->max_time = 7;
+
+ gchar *cmd_line = NULL;
+ snprintf(ctx->r.extra, 255, "--time=%d %s", ctx->max_time, ctx->parms_test);
+ util_compress_space(ctx->r.extra);
+
+ if (!expecting_version)
+ expecting_version = sysbench_version();
+ if (expecting_version < 1000000) {
+ /* v0.x.x: sysbench [general-options]... --test=<test-name> [test-options]... command */
+ cmd_line = g_strdup_printf("sysbench --num-threads=%d --max-time=%d --test=%s %s run", ctx->threads, ctx->max_time, ctx->test, ctx->parms_test);
+ } else {
+ /* v1.x.x: sysbench [options]... [testname] [command] */
+ cmd_line = g_strdup_printf("sysbench --threads=%d --time=%d %s %s run", ctx->threads, ctx->max_time, ctx->parms_test, ctx->test);
+ }
+ //bench_msg("\ncmd_line: %s", cmd_line);
+
+ spawned = g_spawn_command_line_sync(cmd_line,
+ &out, &err, NULL, NULL);
+ g_free(cmd_line);
+ if (spawned) {
+ p = out;
+ while(next_nl = strchr(p, '\n')) {
+ *next_nl = 0;
+
+ if (strstr(p, "Usage:")) {
+ /* We're hosed */
+ goto sysbench_failed;
+ }
+
+ /* version */
+ mc = sscanf(p, "sysbench %d.%d.%d", &v1, &v2, &v3);
+ if (mc >= 1) {
+ ctx->r.revision = 0;
+ ctx->r.revision += v1 * 1000000;
+ ctx->r.revision += v2 * 1000;
+ ctx->r.revision += v3;
+ }
+
+ /* total_time */
+ if (pp = strstr(p, "total time:")) {
+ pp = strchr(pp, ':') + 1;
+ ctx->r.elapsed_time = strtof(pp, NULL);
+ }
+
+ /* result */
+ if (SEQ(ctx->test, "memory") ) {
+ // 57894.30 MiB transferred (5787.59 MiB/sec)
+ if (pp = strstr(p, " transferred (")) {
+ pp = strchr(pp, '(') + 1;
+ ctx->r.result = strtof(pp, NULL);
+ }
+ }
+ if (SEQ(ctx->test, "cpu") ) {
+ if (ctx->r.revision < 1000000) {
+ // there is not a nice result line
+ // to grab in version 0.x...
+ // total time: 7.0016s
+ // total number of events: 873
+
+ /* should already have "total time:" */
+ if (pp = strstr(p, " total number of events:")) {
+ pp = strchr(pp, ':') + 1;
+ ctx->r.result = strtof(pp, NULL);
+ ctx->r.result /= ctx->r.elapsed_time;
+ }
+ }
+ if (ctx->r.revision >= 1000000) {
+ // events per second: 1674.97
+ if (pp = strstr(p, " events per second:")) {
+ pp = strchr(pp, ':') + 1;
+ ctx->r.result = strtof(pp, NULL);
+ }
+ }
+ }
+
+ p = next_nl + 1;
+ }
+ g_free(out);
+ g_free(err);
+ } else {
+ bench_msg("\nfailed to spawn sysbench");
+ sleep(5);
+ }
+
+ if (ctx->r.result == -1)
+ goto sysbench_failed;
+
+ return spawned;
+
+sysbench_failed:
+ bench_msg("\nfailed to configure sysbench");
+ g_free(out);
+ g_free(err);
+ return 0;
+}
+
+void benchmark_memory_run(int threads, int result_index) {
+ int cpu_procs, cpu_cores, cpu_threads, cpu_nodes;
+
+ cpu_procs_cores_threads_nodes(&cpu_procs, &cpu_cores, &cpu_threads, &cpu_nodes);
+
+ struct sysbench_ctx ctx = {
+ .test = "memory",
+ .threads = threads>0 ? threads : cpu_threads,
+ .parms_test = "",
+ .r = EMPTY_BENCH_VALUE};
+
+ int sbv = sysbench_version();
+ if (BENCH_PTR_BITS > 32 && sbv >= 1000011) {
+ ctx.parms_test =
+ " --memory-block-size=1K"
+ " --memory-total-size=100G"
+ " --memory-scope=global"
+ " --memory-hugetlb=off"
+ " --memory-oper=write"
+ " --memory-access-mode=seq";
+ } else {
+ /* safer set */
+ ctx.parms_test =
+ " --memory-block-size=1K"
+ " --memory-total-size=3056M"
+ " --memory-scope=global"
+ " --memory-hugetlb=off"
+ " --memory-oper=write"
+ " --memory-access-mode=seq";
+ }
+
+ shell_view_set_enabled(FALSE);
+ char msg[128] = "";
+ snprintf(msg, 128, "%s (threads: %d)", STATMSG, threads);
+ shell_status_update(msg);
+
+ sysbench_run(&ctx, sbv);
+ bench_results[result_index] = ctx.r;
+}
+
+void benchmark_memory_single(void) { benchmark_memory_run(1, BENCHMARK_MEMORY_SINGLE); }
+void benchmark_memory_dual(void) { benchmark_memory_run(2, BENCHMARK_MEMORY_DUAL); }
+void benchmark_memory_quad(void) { benchmark_memory_run(4, BENCHMARK_MEMORY_QUAD); }
+void benchmark_memory_all(void) { benchmark_memory_run(0, BENCHMARK_MEMORY_ALL); }
+
+void benchmark_sbcpu_single(void) {
+ struct sysbench_ctx ctx = {
+ .test = "cpu",
+ .threads = 1,
+ .parms_test =
+ "--cpu-max-prime=10000",
+ .r = EMPTY_BENCH_VALUE};
+
+ shell_view_set_enabled(FALSE);
+ shell_status_update(STATMSG " (single thread)...");
+
+ sysbench_run(&ctx, 0);
+ bench_results[BENCHMARK_SBCPU_SINGLE] = ctx.r;
+}
+
+void benchmark_sbcpu_all(void) {
+ int cpu_procs, cpu_cores, cpu_threads, cpu_nodes;
+
+ cpu_procs_cores_threads_nodes(&cpu_procs, &cpu_cores, &cpu_threads, &cpu_nodes);
+
+ struct sysbench_ctx ctx = {
+ .test = "cpu",
+ .threads = cpu_threads,
+ .parms_test =
+ "--cpu-max-prime=10000",
+ .r = EMPTY_BENCH_VALUE};
+
+ shell_view_set_enabled(FALSE);
+ shell_status_update(STATMSG " (Multi-thread)...");
+
+ sysbench_run(&ctx, 0);
+ bench_results[BENCHMARK_SBCPU_ALL] = ctx.r;
+}
+
+void benchmark_sbcpu_quad(void) {
+ struct sysbench_ctx ctx = {
+ .test = "cpu",
+ .threads = 4,
+ .parms_test =
+ "--cpu-max-prime=10000",
+ .r = EMPTY_BENCH_VALUE};
+
+ shell_view_set_enabled(FALSE);
+ shell_status_update(STATMSG " (Four thread)...");
+
+ sysbench_run(&ctx, 0);
+ bench_results[BENCHMARK_SBCPU_QUAD] = ctx.r;
+}
diff --git a/modules/benchmark/zlib.c b/modules/benchmark/zlib.c
new file mode 100644
index 00000000..2045969f
--- /dev/null
+++ b/modules/benchmark/zlib.c
@@ -0,0 +1,88 @@
+/*
+ * HardInfo - Displays System Information
+ * Copyright (C) 2017 L. A. F. Pereira <l@tia.mat.br>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, version 2 or later.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#include <glib.h>
+#include <stdlib.h>
+#include <zlib.h>
+
+#include "benchmark.h"
+
+/* zip/unzip 256KB blocks for 7 seconds
+ * result is number of full completions / 100 */
+
+/* if anything changes in this block, increment revision */
+#define BENCH_REVISION 3
+#define BENCH_DATA_SIZE 262144
+#define BENCH_DATA_MD5 "3753b649c4fa9ea4576fc8f89a773de2"
+#define CRUNCH_TIME 7
+#define VERIFY_RESULT 1
+
+static unsigned int zlib_errors = 0;
+
+static gpointer zlib_for(void *in_data, gint thread_number) {
+ char *compressed;
+ uLong bound = compressBound(BENCH_DATA_SIZE);
+ unsigned int i;
+
+ compressed = malloc(bound);
+ if (!compressed)
+ return NULL;
+
+ char uncompressed[BENCH_DATA_SIZE];
+ uLong compressedBound = bound;
+ uLong destBound = sizeof(uncompressed);
+
+ compress(compressed, &compressedBound, in_data, BENCH_DATA_SIZE);
+ uncompress(uncompressed, &destBound, compressed, compressedBound);
+ if (VERIFY_RESULT) {
+ int cr = memcmp(in_data, uncompressed, BENCH_DATA_SIZE);
+ if (!!cr) {
+ zlib_errors++;
+ bench_msg("zlib error: uncompressed != original");
+ }
+ }
+
+ free(compressed);
+
+ return NULL;
+}
+
+void
+benchmark_zlib(void)
+{
+ bench_value r = EMPTY_BENCH_VALUE;
+ gchar *test_data = get_test_data(BENCH_DATA_SIZE);
+ if (!test_data)
+ return;
+
+ shell_view_set_enabled(FALSE);
+ shell_status_update("Running Zlib benchmark...");
+
+ gchar *d = md5_digest_str(test_data, BENCH_DATA_SIZE);
+ if (!SEQ(d, BENCH_DATA_MD5))
+ bench_msg("test data has different md5sum: expected %s, actual %s", BENCH_DATA_MD5, d);
+
+ r = benchmark_crunch_for(CRUNCH_TIME, 0, zlib_for, test_data);
+ r.result /= 100;
+ r.revision = BENCH_REVISION;
+ snprintf(r.extra, 255, "zlib %s (built against: %s), d:%s, e:%d", zlib_version, ZLIB_VERSION, d, zlib_errors);
+ bench_results[BENCHMARK_ZLIB] = r;
+
+ g_free(test_data);
+ g_free(d);
+}
diff --git a/modules/computer.c b/modules/computer.c
new file mode 100644
index 00000000..fff8ba36
--- /dev/null
+++ b/modules/computer.c
@@ -0,0 +1,1124 @@
+/*
+ * HardInfo - Displays System Information
+ * Copyright (C) 2003-2008 L. A. F. Pereira <l@tia.mat.br>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, version 2 or later.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#include <config.h>
+#include <gtk/gtk.h>
+#include <stdarg.h>
+#include <stdlib.h>
+#include <string.h>
+#include <string.h>
+#include <sys/stat.h>
+#include <time.h>
+
+#include <hardinfo.h>
+#include <iconcache.h>
+#include <shell.h>
+
+#include <vendor.h>
+
+#include "computer.h"
+
+#include "dmi_util.h" /* for dmi_get_str() */
+#include "dt_util.h" /* for dtr_get_string() */
+
+#include "info.h"
+
+#define THISORUNK(t) ( (t) ? t : _("(Unknown)") )
+
+/* Callbacks */
+gchar *callback_summary(void);
+gchar *callback_os(void);
+gchar *callback_security(void);
+gchar *callback_modules(void);
+gchar *callback_boots(void);
+gchar *callback_locales(void);
+gchar *callback_memory_usage();
+gchar *callback_fs(void);
+gchar *callback_display(void);
+gchar *callback_network(void);
+gchar *callback_users(void);
+gchar *callback_groups(void);
+gchar *callback_env_var(void);
+#if GLIB_CHECK_VERSION(2,14,0)
+gchar *callback_dev(void);
+#endif /* GLIB_CHECK_VERSION(2,14,0) */
+
+/* Scan callbacks */
+void scan_summary(gboolean reload);
+void scan_os(gboolean reload);
+void scan_security(gboolean reload);
+void scan_modules(gboolean reload);
+void scan_boots(gboolean reload);
+void scan_locales(gboolean reload);
+void scan_fs(gboolean reload);
+void scan_memory_usage(gboolean reload);
+void scan_display(gboolean reload);
+void scan_network(gboolean reload);
+void scan_users(gboolean reload);
+void scan_groups(gboolean reload);
+void scan_env_var(gboolean reload);
+#if GLIB_CHECK_VERSION(2,14,0)
+void scan_dev(gboolean reload);
+#endif /* GLIB_CHECK_VERSION(2,14,0) */
+
+enum {
+ ENTRY_SUMMARY,
+ ENTRY_OS,
+ ENTRY_SECURITY,
+ ENTRY_KMOD,
+ ENTRY_BOOTS,
+ ENTRY_LANGUAGES,
+ ENTRY_MEMORY_USAGE,
+ ENTRY_FS,
+ ENTRY_DISPLAY,
+ ENTRY_ENV,
+ ENTRY_DEVEL,
+ ENTRY_USERS,
+ ENTRY_GROUPS
+};
+
+static ModuleEntry entries[] = {
+ [ENTRY_SUMMARY] = {N_("Summary"), "summary.png", callback_summary, scan_summary, MODULE_FLAG_NONE},
+ [ENTRY_OS] = {N_("Operating System"), "os.png", callback_os, scan_os, MODULE_FLAG_NONE},
+ [ENTRY_SECURITY] = {N_("Security"), "security.png", callback_security, scan_security, MODULE_FLAG_NONE},
+ [ENTRY_KMOD] = {N_("Kernel Modules"), "module.png", callback_modules, scan_modules, MODULE_FLAG_NONE},
+ [ENTRY_BOOTS] = {N_("Boots"), "boot.png", callback_boots, scan_boots, MODULE_FLAG_NONE},
+ [ENTRY_LANGUAGES] = {N_("Languages"), "language.png", callback_locales, scan_locales, MODULE_FLAG_NONE},
+ [ENTRY_MEMORY_USAGE] = {N_("Memory Usage"), "memory.png", callback_memory_usage, scan_memory_usage, MODULE_FLAG_NONE},
+ [ENTRY_FS] = {N_("Filesystems"), "dev_removable.png", callback_fs, scan_fs, MODULE_FLAG_NONE},
+ [ENTRY_DISPLAY] = {N_("Display"), "monitor.png", callback_display, scan_display, MODULE_FLAG_NONE},
+ [ENTRY_ENV] = {N_("Environment Variables"), "environment.png", callback_env_var, scan_env_var, MODULE_FLAG_NONE},
+#if GLIB_CHECK_VERSION(2,14,0)
+ [ENTRY_DEVEL] = {N_("Development"), "devel.png", callback_dev, scan_dev, MODULE_FLAG_NONE},
+#else
+ [ENTRY_DEVEL] = {N_("Development"), "devel.png", callback_dev, scan_dev, MODULE_FLAG_HIDE},
+#endif /* GLIB_CHECK_VERSION(2,14,0) */
+ [ENTRY_USERS] = {N_("Users"), "users.png", callback_users, scan_users, MODULE_FLAG_NONE},
+ [ENTRY_GROUPS] = {N_("Groups"), "users.png", callback_groups, scan_groups, MODULE_FLAG_NONE},
+ {NULL},
+};
+
+gchar *module_list = NULL;
+Computer *computer = NULL;
+gchar *meminfo = NULL;
+
+gchar *hi_more_info(gchar * entry)
+{
+ gchar *info = moreinfo_lookup_with_prefix("COMP", entry);
+
+ if (info)
+ return g_strdup(info);
+
+ return g_strdup_printf("[%s]", entry);
+}
+
+/* a g_str_equal() where either may be null */
+#define g_str_equal0(a,b) (g_strcmp0(a,b) == 0)
+
+gchar *hi_get_field(gchar * field)
+{
+ gchar *tag, *label;
+ key_get_components(field, NULL, &tag, NULL, &label, NULL, TRUE);
+
+ gchar *tmp;
+
+ if (g_str_equal0(label, _("Memory"))) {
+ MemoryInfo *mi = computer_get_memory();
+ tmp = g_strdup_printf(_("%dMB (%dMB used)"), mi->total, mi->used);
+ g_free(mi);
+ } else if (g_str_equal0(label, _("Uptime"))) {
+ tmp = computer_get_formatted_uptime();
+ } else if (g_str_equal0(label, _("Date/Time"))) {
+ time_t t = time(NULL);
+
+ tmp = g_new0(gchar, 64);
+ strftime(tmp, 64, "%c", localtime(&t));
+ } else if (g_str_equal0(label, _("Load Average"))) {
+ tmp = computer_get_formatted_loadavg();
+ } else if (g_str_equal0(tag, "entropy")) {
+ tmp = computer_get_entropy_avail();
+ } else {
+ gchar *info = NULL;
+ if (tag)
+ info = moreinfo_lookup_with_prefix("DEV", tag);
+ else if (label)
+ info = moreinfo_lookup_with_prefix("DEV", label);
+
+ if (info)
+ tmp = g_strdup(info);
+ else
+ tmp = g_strdup_printf("Unknown field: [tag: %s] label: %s", tag ? tag : "(none)", label ? label : "(empty)");
+ }
+ return tmp;
+}
+
+void scan_summary(gboolean reload)
+{
+ SCAN_START();
+ module_entry_scan_all_except(entries, 0);
+ computer->alsa = computer_get_alsainfo();
+ SCAN_END();
+}
+
+void scan_os(gboolean reload)
+{
+ SCAN_START();
+ computer->os = computer_get_os();
+ SCAN_END();
+}
+
+void scan_security(gboolean reload)
+{
+ SCAN_START();
+ //nothing to do here yet
+ SCAN_END();
+}
+
+void scan_modules(gboolean reload)
+{
+ SCAN_START();
+ scan_modules_do();
+ SCAN_END();
+}
+
+void scan_boots(gboolean reload)
+{
+ SCAN_START();
+ scan_boots_real();
+ SCAN_END();
+}
+
+void scan_locales(gboolean reload)
+{
+ SCAN_START();
+ scan_os(FALSE);
+ scan_languages(computer->os);
+ SCAN_END();
+}
+
+void scan_fs(gboolean reload)
+{
+ SCAN_START();
+ scan_filesystems();
+ SCAN_END();
+}
+
+void scan_memory_usage(gboolean reload)
+{
+ SCAN_START();
+ scan_memory_do();
+ SCAN_END();
+}
+
+void scan_display(gboolean reload)
+{
+ SCAN_START();
+ if (computer->display)
+ computer_free_display(computer->display);
+ computer->display = computer_get_display();
+ SCAN_END();
+}
+
+void scan_users(gboolean reload)
+{
+ SCAN_START();
+ scan_users_do();
+ SCAN_END();
+}
+
+void scan_groups(gboolean reload)
+{
+ SCAN_START();
+ scan_groups_do();
+ SCAN_END();
+}
+
+#if GLIB_CHECK_VERSION(2,14,0)
+static gchar *dev_list = NULL;
+void scan_dev(gboolean reload)
+{
+ SCAN_START();
+
+ guint i;
+ struct {
+ gchar *compiler_name;
+ gchar *version_command;
+ gchar *regex;
+ gboolean read_stdout;
+ } detect_lang[] = {
+ { N_("Scripting Languages"), NULL, FALSE },
+ { N_("Gambas3 (gbr3)"), "gbr3 --version", "\\d+\\.\\d+\\.\\d+", TRUE },
+ { N_("Python (default)"), "python -V", "\\d+\\.\\d+\\.\\d+", FALSE },
+ { N_("Python2"), "python2 -V", "\\d+\\.\\d+\\.\\d+", FALSE },
+ { N_("Python3"), "python3 -V", "\\d+\\.\\d+\\.\\d+", TRUE },
+ { N_("Perl"), "perl -v", "\\d+\\.\\d+\\.\\d+", TRUE },
+ { N_("Perl6 (VM)"), "perl6 -v", "(?<=This is ).*", TRUE },
+ { N_("Perl6"), "perl6 -v", "(?<=implementing Perl )\\w*\\.\\w*", TRUE },
+ { N_("PHP"), "php --version", "\\d+\\.\\d+\\.\\S+", TRUE},
+ { N_("Ruby"), "ruby --version", "\\d+\\.\\d+\\.\\d+", TRUE },
+ { N_("Bash"), "bash --version", "\\d+\\.\\d+\\.\\S+", TRUE},
+ { N_("JavaScript (Node.js)"), "node --version", "(?<=v)(\\d\\.?)+", TRUE },
+ { N_("awk"), "awk --version", "GNU Awk \\d+\\.\\d+\\.\\d+", TRUE },
+ { N_("Compilers"), NULL, FALSE },
+ { N_("C (GCC)"), "gcc -v", "\\d+\\.\\d+\\.\\d+", FALSE },
+ { N_("C (Clang)"), "clang -v", "\\d+\\.\\d+", FALSE },
+ { N_("D (dmd)"), "dmd --help", "\\d+\\.\\d+", TRUE },
+ { N_("Gambas3 (gbc3)"), "gbc3 --version", "\\d+\\.\\d+\\.\\d+", TRUE },
+ { N_("Java"), "javac -version", "\\d+\\.\\d+\\.\\d+", FALSE },
+ { N_("C♯ (mcs)"), "mcs --version", "\\d+\\.\\d+\\.\\d+\\.\\d+", TRUE },
+ { N_("Vala"), "valac --version", "\\d+\\.\\d+\\.\\d+", TRUE },
+ { N_("Haskell (GHC)"), "ghc -v", "\\d+\\.\\d+\\.\\d+", FALSE },
+ { N_("FreePascal"), "fpc -iV", "\\d+\\.\\d+\\.?\\d*", TRUE },
+ { N_("Go"), "go version", "\\d+\\.\\d+\\.?\\d* ", TRUE },
+ { N_("Rust"), "rustc --version", "(?<=rustc )(\\d\\.?)+", TRUE },
+ { N_("Tools"), NULL, FALSE },
+ { N_("make"), "make --version", "\\d+\\.\\d+", TRUE },
+ { N_("ninja"), "ninja --version", "\\d+\\.\\d+\\.\\d+", TRUE },
+ { N_("GDB"), "gdb --version", "(?<=^GNU gdb ).*", TRUE },
+ { N_("LLDB"), "lldb --version", "(?<=lldb version )(\\d\\.?)+", TRUE },
+ { N_("strace"), "strace -V", "\\d+\\.\\d+\\.?\\d*", TRUE },
+ { N_("valgrind"), "valgrind --version", "\\d+\\.\\d+\\.\\S+", TRUE },
+ { N_("QMake"), "qmake --version", "\\d+\\.\\S+", TRUE},
+ { N_("CMake"), "cmake --version", "\\d+\\.\\d+\\.?\\d*", TRUE},
+ { N_("Gambas3 IDE"), "gambas3 --version", "\\d+\\.\\d+\\.\\d+", TRUE },
+ { N_("Radare2"), "radare2 -v", "(?<=radare2 )(\\d+\\.?)+(-git)?", TRUE },
+ { N_("ltrace"), "ltrace --version", "(?<=ltrace version )\\d+\\.\\d+\\.\\d+", TRUE },
+ { N_("Powershell"), "pwsh --version", "\\d+\\.\\d+\\.\\d+", TRUE },
+ };
+
+ g_free(dev_list);
+
+ dev_list = g_strdup("");
+
+ for (i = 0; i < G_N_ELEMENTS(detect_lang); i++) {
+ gchar *version = NULL;
+ gchar *output, *ignored;
+ gchar *temp;
+ GRegex *regex;
+ GMatchInfo *match_info;
+ gboolean found;
+
+ if (!detect_lang[i].regex) {
+ dev_list = h_strdup_cprintf("[%s]\n", dev_list, _(detect_lang[i].compiler_name));
+ continue;
+ }
+
+ if (detect_lang[i].read_stdout) {
+ found = hardinfo_spawn_command_line_sync(detect_lang[i].version_command, &output, &ignored, NULL, NULL);
+ } else {
+ found = hardinfo_spawn_command_line_sync(detect_lang[i].version_command, &ignored, &output, NULL, NULL);
+ }
+ g_free(ignored);
+
+ if (found) {
+ regex = g_regex_new(detect_lang[i].regex, 0, 0, NULL);
+
+ g_regex_match(regex, output, 0, &match_info);
+ if (g_match_info_matches(match_info)) {
+ version = g_match_info_fetch(match_info, 0);
+ }
+
+ g_match_info_free(match_info);
+ g_regex_unref(regex);
+ g_free(output);
+ }
+
+ if (version == NULL)
+ version = strdup(_("Not found"));
+
+ dev_list = h_strdup_cprintf("%s=%s\n", dev_list, detect_lang[i].compiler_name, version);
+ g_free(version);
+
+ temp = g_strdup_printf(_("Detecting version: %s"),
+ detect_lang[i].compiler_name);
+ shell_status_update(temp);
+ g_free(temp);
+ }
+
+ SCAN_END();
+}
+
+gchar *callback_dev(void)
+{
+ return g_strdup_printf(
+ "[$ShellParam$]\n"
+ "ViewType=5\n"
+ "ColumnTitle$TextValue=%s\n" /* Program */
+ "ColumnTitle$Value=%s\n" /* Version */
+ "ShowColumnHeaders=true\n"
+ "%s",
+ _("Program"), _("Version"),
+ dev_list);
+}
+#endif /* GLIB_CHECK_VERSION(2,14,0) */
+
+gchar *callback_memory_usage()
+{
+ extern gchar *lginterval;
+ return g_strdup_printf("[Memory]\n"
+ "%s\n"
+ "[$ShellParam$]\n"
+ "ViewType=2\n"
+ "LoadGraphSuffix= kB\n"
+ "RescanInterval=2000\n"
+ "ColumnTitle$TextValue=%s\n"
+ "ColumnTitle$Extra1=%s\n"
+ "ColumnTitle$Value=%s\n"
+ "ShowColumnHeaders=true\n"
+ "%s\n", meminfo,
+ _("Field"), _("Description"), _("Value"), /* column labels */
+ lginterval);
+}
+
+static gchar *detect_machine_type(void)
+{
+ GDir *dir;
+ gchar *chassis;
+
+ chassis = dmi_chassis_type_str(-1, 0);
+ if (chassis)
+ return chassis;
+
+ chassis = dtr_get_string("/model", 0);
+ if (chassis) {
+ if (strstr(chassis, "Raspberry Pi") != NULL
+ || strstr(chassis, "ODROID") != NULL
+ || strstr(chassis, "Firefly ROC") != NULL
+ /* FIXME: consider making a table when adding more models */ ) {
+ g_free(chassis);
+ return g_strdup(_("Single-board computer"));
+ }
+ g_free(chassis);
+ }
+
+ if (g_file_test("/proc/pmu/info", G_FILE_TEST_EXISTS))
+ return g_strdup(_("Laptop"));
+
+ dir = g_dir_open("/proc/acpi/battery", 0, NULL);
+ if (dir) {
+ const gchar *name = g_dir_read_name(dir);
+
+ g_dir_close(dir);
+
+ if (name)
+ return g_strdup(_("Laptop"));
+ }
+
+ dir = g_dir_open("/sys/class/power_supply", 0, NULL);
+ if (dir) {
+ const gchar *name;
+
+ while ((name = g_dir_read_name(dir))) {
+ gchar *contents;
+ gchar type[PATH_MAX];
+ int r;
+
+ r = snprintf(type, sizeof(type), "%s/%s/type",
+ "/sys/class/power_supply", name);
+ if (r < 0 || r > PATH_MAX)
+ continue;
+
+ if (g_file_get_contents(type, &contents, NULL, NULL)) {
+ if (g_str_has_prefix(contents, "Battery")) {
+ g_free(contents);
+ g_dir_close(dir);
+
+ return g_strdup(_("Laptop"));
+ }
+
+ g_free(contents);
+ }
+ }
+
+ g_dir_close(dir);
+ }
+
+ /* FIXME: check if batteries are found using /proc/apm */
+
+ return g_strdup(_("Unknown physical machine type"));
+}
+
+/* Table based off imvirt by Thomas Liske <liske@ibh.de>
+ Copyright (c) 2008 IBH IT-Service GmbH under GPLv2. */
+gchar *computer_get_virtualization(void)
+{
+ gboolean found = FALSE;
+ gint i, j;
+ gchar *files[] = {
+ "/proc/scsi/scsi",
+ "/proc/cpuinfo",
+ "/var/log/dmesg",
+ NULL
+ };
+ static const struct {
+ gchar *str;
+ gchar *vmtype;
+ } vm_types[] = {
+ /* VMware */
+ { "VMware", N_("Virtual (VMware)") },
+ { ": VMware Virtual IDE CDROM Drive", N_("Virtual (VMware)") },
+ /* QEMU */
+ { "QEMU", N_("Virtual (QEMU)") },
+ { "QEMU Virtual CPU", N_("Virtual (QEMU)") },
+ { ": QEMU HARDDISK", N_("Virtual (QEMU)") },
+ { ": QEMU CD-ROM", N_("Virtual (QEMU)") },
+ /* Generic Virtual Machine */
+ { ": Virtual HD,", N_("Virtual (Unknown)") },
+ { ": Virtual CD,", N_("Virtual (Unknown)") },
+ /* Virtual Box */
+ { "VBOX", N_("Virtual (VirtualBox)") },
+ { ": VBOX HARDDISK", N_("Virtual (VirtualBox)") },
+ { ": VBOX CD-ROM", N_("Virtual (VirtualBox)") },
+ /* Xen */
+ { "Xen virtual console", N_("Virtual (Xen)") },
+ { "Xen reported: ", N_("Virtual (Xen)") },
+ { "xen-vbd: registered block device", N_("Virtual (Xen)") },
+ /* Generic */
+ { " hypervisor", N_("Virtual (hypervisor present)") } ,
+ { NULL }
+ };
+ gchar *tmp;
+
+ DEBUG("Detecting virtual machine");
+
+ if (g_file_test("/proc/xen", G_FILE_TEST_EXISTS)) {
+ DEBUG("/proc/xen found; assuming Xen");
+ return g_strdup(_("Virtual (Xen)"));
+ }
+
+ tmp = module_call_method("devices::getMotherboard");
+ if (strstr(tmp, "VirtualBox") != NULL) {
+ g_free(tmp);
+ return g_strdup(_("Virtual (VirtualBox)"));
+ }
+ g_free(tmp);
+
+ for (i = 0; files[i+1]; i++) {
+ gchar buffer[512];
+ FILE *file;
+
+ if ((file = fopen(files[i], "r"))) {
+ while (!found && fgets(buffer, 512, file)) {
+ for (j = 0; vm_types[j+1].str; j++) {
+ if (strstr(buffer, vm_types[j].str)) {
+ found = TRUE;
+ break;
+ }
+ }
+ }
+
+ fclose(file);
+
+ if (found) {
+ DEBUG("%s found (by reading file %s)",
+ vm_types[j].vmtype, files[i]);
+ return g_strdup(_(vm_types[j].vmtype));
+ }
+ }
+
+ }
+
+ DEBUG("no virtual machine detected; assuming physical machine");
+
+ return detect_machine_type();
+}
+
+gchar *callback_summary(void)
+{
+ struct Info *info = info_new();
+
+ info_set_view_type(info, SHELL_VIEW_DETAIL);
+
+ info_add_group(info, _("Computer"),
+ info_field(_("Processor"),
+ idle_free(module_call_method("devices::getProcessorNameAndDesc"))),
+ info_field_update(_("Memory"), 1000),
+ info_field_printf(_("Machine Type"), "%s",
+ computer_get_virtualization()),
+ info_field(_("Operating System"), computer->os->distro),
+ info_field(_("User Name"), computer->os->username),
+ info_field_update(_("Date/Time"), 1000),
+ info_field_last());
+
+ info_add_group(info, _("Display"),
+ info_field_printf(_("Resolution"), _(/* label for resolution */ "%dx%d pixels"),
+ computer->display->width, computer->display->height),
+ info_field(_("Display Adapter"),
+ idle_free(module_call_method("devices::getGPUList"))),
+ info_field(_("OpenGL Renderer"), THISORUNK(computer->display->xi->glx->ogl_renderer)),
+ info_field(_("Session Display Server"), THISORUNK(computer->display->display_server)),
+ info_field_last());
+
+ info_add_computed_group(info, _("Audio Devices"),
+ idle_free(computer_get_alsacards(computer)));
+ info_add_computed_group_wo_extra(info, _("Input Devices"),
+ idle_free(module_call_method("devices::getInputDevices")));
+ info_add_computed_group(info, NULL, /* getPrinters provides group headers */
+ idle_free(module_call_method("devices::getPrinters")));
+ info_add_computed_group_wo_extra(info, NULL, /* getStorageDevices provides group headers */
+ idle_free(module_call_method("devices::getStorageDevices")));
+
+ return info_flatten(info);
+}
+
+gchar *callback_os(void)
+{
+ struct Info *info = info_new();
+ gchar *distro_icon;
+ gchar *distro;
+
+ info_set_view_type(info, SHELL_VIEW_DETAIL);
+
+ distro_icon = computer->os->distroid
+ ? idle_free(g_strdup_printf("distros/%s.svg",
+ computer->os->distroid))
+ : NULL;
+ distro = computer->os->distrocode
+ ? idle_free(g_strdup_printf("%s (%s)",
+ computer->os->distro, computer->os->distrocode))
+ : computer->os->distro;
+
+ struct InfoGroup *version_group =
+ info_add_group(
+ info, _("Version"), info_field(_("Kernel"), computer->os->kernel),
+ info_field(_("Command Line"), computer->os->kcmdline ?: _("Unknown")),
+ info_field(_("Version"), computer->os->kernel_version),
+ info_field(_("C Library"), computer->os->libc),
+ info_field(_("Distribution"), distro,
+ .value_has_vendor = TRUE,
+ .icon = distro_icon),
+ info_field_last());
+
+ if (computer->os->distro_flavor) {
+ info_group_add_field(version_group,
+ info_field(_("Spin/Flavor"), computer->os->distro_flavor->name,
+ .value_has_vendor = TRUE,
+ .icon = computer->os->distro_flavor->icon) );
+ }
+
+ info_add_group(info, _("Current Session"),
+ info_field(_("Computer Name"), computer->os->hostname),
+ info_field(_("User Name"), computer->os->username),
+ info_field(_("Language"), computer->os->language),
+ info_field(_("Home Directory"), computer->os->homedir),
+ info_field(_("Desktop Environment"), computer->os->desktop),
+ info_field_last());
+
+ info_add_group(info, _("Misc"), info_field_update(_("Uptime"), 1000),
+ info_field_update(_("Load Average"), 10000),
+ info_field_last());
+
+ return info_flatten(info);
+}
+
+gchar *callback_security(void)
+{
+ struct Info *info = info_new();
+
+ info_set_view_type(info, SHELL_VIEW_DETAIL);
+
+ info_add_group(info, _("HardInfo"),
+ info_field(_("HardInfo running as"),
+ (getuid() == 0) ? _("Superuser") : _("User")),
+ info_field_last());
+
+ info_add_group(
+ info, _("Health"),
+ info_field_update(_("Available entropy in /dev/random"), 1000, .tag = g_strdup("entropy") ),
+ info_field_last());
+
+ info_add_group(
+ info, _("Hardening Features"),
+ info_field(_("ASLR"), idle_free(computer_get_aslr())),
+ info_field(_("dmesg"), idle_free(computer_get_dmesg_status())),
+ info_field_last());
+
+ info_add_group(
+ info, _("Linux Security Modules"),
+ info_field(_("Modules available"), idle_free(computer_get_lsm())),
+ info_field(_("SELinux status"), computer_get_selinux()),
+ info_field_last());
+
+ GDir *dir = g_dir_open("/sys/devices/system/cpu/vulnerabilities", 0, NULL);
+ if (dir) {
+ struct InfoGroup *vulns =
+ info_add_group(info, _("CPU Vulnerabilities"), info_field_last());
+ vulns->sort = INFO_GROUP_SORT_NAME_ASCENDING;
+ const gchar *vuln;
+
+ while ((vuln = g_dir_read_name(dir))) {
+ gchar *contents = h_sysfs_read_string(
+ "/sys/devices/system/cpu/vulnerabilities", vuln);
+ if (!contents)
+ continue;
+
+ const gchar *icon = NULL;
+ if (g_strstr_len(contents, -1, "Not affected") )
+ icon = "circle_green_check.svg";
+
+ if (g_str_has_prefix(contents, "Mitigation:") ||
+ g_str_has_prefix(contents, "mitigation:"))
+ icon = "circle_yellow_exclaim.svg";
+
+ if (g_strstr_len(contents, -1, "Vulnerable") ||
+ g_strstr_len(contents, -1, "vulnerable"))
+ icon = "circle_red_x.svg";
+
+ info_group_add_fields(vulns,
+ info_field(g_strdup(vuln),
+ idle_free(contents), .icon = icon,
+ .free_name_on_flatten = TRUE),
+ info_field_last());
+ }
+
+ g_dir_close(dir);
+ }
+
+ return info_flatten(info);
+}
+
+gchar *callback_modules(void)
+{
+ struct Info *info = info_new();
+
+ info_add_computed_group(info, _("Loaded Modules"), module_list);
+
+ info_set_column_title(info, "TextValue", _("Name"));
+ info_set_column_title(info, "Value", _("Description"));
+ info_set_column_headers_visible(info, TRUE);
+ info_set_view_type(info, SHELL_VIEW_DUAL);
+
+ return info_flatten(info);
+}
+
+gchar *callback_boots(void)
+{
+ struct Info *info = info_new();
+
+ info_add_computed_group(info, _("Boots"), computer->os->boots);
+
+ info_set_column_title(info, "TextValue", _("Date & Time"));
+ info_set_column_title(info, "Value", _("Kernel Version"));
+ info_set_column_headers_visible(info, TRUE);
+
+ return info_flatten(info);
+}
+
+gchar *callback_locales(void)
+{
+ struct Info *info = info_new();
+
+ info_add_computed_group(info, _("Available Languages"), computer->os->languages);
+
+ info_set_column_title(info, "TextValue", _("Language Code"));
+ info_set_column_title(info, "Value", _("Name"));
+ info_set_view_type(info, SHELL_VIEW_DUAL);
+ info_set_column_headers_visible(info, TRUE);
+
+ return info_flatten(info);
+}
+
+gchar *callback_fs(void)
+{
+ struct Info *info = info_new();
+
+ info_add_computed_group(info, _("Mounted File Systems"), fs_list);
+
+ info_set_column_title(info, "Extra1", _("Mount Point"));
+ info_set_column_title(info, "Progress", _("Usage"));
+ info_set_column_title(info, "TextValue", _("Device"));
+ info_set_column_headers_visible(info, TRUE);
+ info_set_view_type(info, SHELL_VIEW_PROGRESS_DUAL);
+ info_set_zebra_visible(info, TRUE);
+ info_set_normalize_percentage(info, FALSE);
+
+ return info_flatten(info);
+}
+
+gchar *callback_display(void)
+{
+ int n = 0;
+ gchar *screens_str = strdup(""), *outputs_str = strdup("");
+ xinfo *xi = computer->display->xi;
+ xrr_info *xrr = xi->xrr;
+ glx_info *glx = xi->glx;
+ wl_info *wl = computer->display->wl;
+
+ struct Info *info = info_new();
+
+ info_set_view_type(info, SHELL_VIEW_DETAIL);
+
+ info_add_group(info, _("Session"),
+ info_field(_("Type"), THISORUNK(computer->display->session_type)),
+ info_field_last());
+
+ info_add_group(info, _("Wayland"),
+ info_field(_("Current Display Name"),
+ (wl->display_name) ? (wl->display_name) : _("(Not Available)")),
+ info_field_last());
+
+ info_add_group(info, _("X Server"),
+ info_field(_("Current Display Name"), THISORUNK(xi->display_name) ),
+ info_field(_("Vendor"), THISORUNK(xi->vendor), .value_has_vendor = TRUE ),
+ info_field(_("Version"), THISORUNK(xi->version) ),
+ info_field(_("Release Number"), THISORUNK(xi->release_number) ),
+ info_field_last());
+
+ for (n = 0; n < xrr->screen_count; n++) {
+ gchar *dims = g_strdup_printf(_(/* resolution WxH unit */ "%dx%d pixels"), xrr->screens[n].px_width, xrr->screens[n].px_height);
+ screens_str = h_strdup_cprintf("Screen %d=%s\n", screens_str, xrr->screens[n].number, dims);
+ g_free(dims);
+ }
+ info_add_computed_group(info, _("Screens"), screens_str);
+
+ for (n = 0; n < xrr->output_count; n++) {
+ gchar *connection = NULL;
+ switch (xrr->outputs[n].connected) {
+ case 0:
+ connection = _("Disconnected");
+ break;
+ case 1:
+ connection = _("Connected");
+ break;
+ case -1:
+ default:
+ connection = _("Unknown");
+ break;
+ }
+ gchar *dims = (xrr->outputs[n].screen == -1)
+ ? g_strdup(_("Unused"))
+ : g_strdup_printf(_("%dx%d pixels, offset (%d, %d)"),
+ xrr->outputs[n].px_width, xrr->outputs[n].px_height,
+ xrr->outputs[n].px_offset_x, xrr->outputs[n].px_offset_y);
+
+ outputs_str = h_strdup_cprintf("%s=%s; %s\n", outputs_str,
+ xrr->outputs[n].name, connection, dims);
+
+ g_free(dims);
+ }
+ info_add_computed_group(info, _("Outputs (XRandR)"), outputs_str);
+
+ info_add_group(info, _("OpenGL (GLX)"),
+ info_field(_("Vendor"), THISORUNK(glx->ogl_vendor), .value_has_vendor = TRUE ),
+ info_field(_("Renderer"), THISORUNK(glx->ogl_renderer) ),
+ info_field(_("Direct Rendering"),
+ glx->direct_rendering ? _("Yes") : _("No")),
+ info_field(_("Version (Compatibility)"), THISORUNK(glx->ogl_version) ),
+ info_field(_("Shading Language Version (Compatibility)"), THISORUNK(glx->ogl_sl_version) ),
+ info_field(_("Version (Core)"), THISORUNK(glx->ogl_core_version) ),
+ info_field(_("Shading Language Version (Core)"), THISORUNK(glx->ogl_core_sl_version) ),
+ info_field(_("Version (ES)"), THISORUNK(glx->ogles_version) ),
+ info_field(_("Shading Language Version (ES)"), THISORUNK(glx->ogles_sl_version) ),
+ info_field(_("GLX Version"), THISORUNK(glx->glx_version) ),
+ info_field_last());
+
+ return info_flatten(info);
+}
+
+gchar *callback_users(void)
+{
+ struct Info *info = info_new();
+
+ info_add_computed_group(info, _("Users"), users);
+ info_set_view_type(info, SHELL_VIEW_DUAL);
+ info_set_reload_interval(info, 10000);
+
+ return info_flatten(info);
+}
+
+gchar *callback_groups(void)
+{
+ struct Info *info = info_new();
+
+ info_add_computed_group(info, _("Group"), groups);
+
+ info_set_column_title(info, "TextValue", _("Name"));
+ info_set_column_title(info, "Value", _("Group ID"));
+ info_set_column_headers_visible(info, TRUE);
+ info_set_reload_interval(info, 10000);
+
+ return info_flatten(info);
+}
+
+gchar *get_os_kernel(void)
+{
+ scan_os(FALSE);
+ return g_strdup(computer->os->kernel);
+}
+
+gchar *get_os(void)
+{
+ scan_os(FALSE);
+ return g_strdup(computer->os->distro);
+}
+
+gchar *get_ogl_renderer(void)
+{
+ scan_display(FALSE);
+
+ return g_strdup(computer->display->xi->glx->ogl_renderer);
+}
+
+gchar *get_display_summary(void)
+{
+ scan_display(FALSE);
+
+ gchar *gpu_list = module_call_method("devices::getGPUList");
+
+ gchar *ret = g_strdup_printf(
+ "%s\n"
+ "%dx%d\n"
+ "%s\n"
+ "%s",
+ gpu_list,
+ computer->display->width, computer->display->height,
+ computer->display->display_server,
+ (computer->display->xi->glx->ogl_renderer)
+ ? computer->display->xi->glx->ogl_renderer
+ : "" );
+ g_free(gpu_list);
+ return ret;
+}
+
+gchar *get_kernel_module_description(gchar *module)
+{
+ gchar *description;
+
+ if (!_module_hash_table) {
+ scan_modules(FALSE);
+ }
+
+ description = g_hash_table_lookup(_module_hash_table, module);
+ if (!description) {
+ return NULL;
+ }
+
+ return g_strdup(description);
+}
+
+gchar *get_audio_cards(void)
+{
+ if (!computer->alsa) {
+ computer->alsa = computer_get_alsainfo();
+ }
+
+ return computer_get_alsacards(computer);
+}
+
+/* the returned string must stay in kB as it is used
+ * elsewhere with that expectation */
+gchar *get_memory_total(void)
+{
+ scan_memory_usage(FALSE);
+ return moreinfo_lookup ("DEV:MemTotal");
+}
+
+gchar *memory_devices_get_system_memory_str(); /* in dmi_memory.c */
+gchar *memory_devices_get_system_memory_types_str();
+/* Note 1: moreinfo_lookup() results should not be freed because
+ * they are pointers into a GHash.
+ * module_call_method() g_strdup()s it's return value. */
+const gchar *get_memory_desc(void) // [1] const (as to say "don't free")
+{
+ scan_memory_usage(FALSE);
+ gchar *avail = g_strdup(moreinfo_lookup("DEV:MemTotal")); // [1] g_strdup()
+ double k = avail ? (double)strtol(avail, NULL, 10) : 0;
+ if (k) {
+ g_free(avail);
+ avail = NULL;
+ const char *fmt = _(/*/ <value> <unit> "usable memory" */ "%0.1f %s available to Linux");
+ if (k > (2048 * 1024))
+ avail = g_strdup_printf(fmt, k / (1024*1024), _("GiB") );
+ else if (k > 2048)
+ avail = g_strdup_printf(fmt, k / 1024, _("MiB") );
+ else
+ avail = g_strdup_printf(fmt, k, _("KiB") );
+ }
+ gchar *mem = memory_devices_get_system_memory_str();
+ if (mem) {
+ gchar *types = memory_devices_get_system_memory_types_str();
+ gchar *ret = g_strdup_printf("%s %s\n%s", mem, types, avail ? avail : "");
+ g_free(avail);
+ g_free(mem);
+ g_free(types);
+ return (gchar*)idle_free(ret); // [1] idle_free()
+ }
+ return (gchar*)idle_free(avail); // [1] idle_free()
+}
+
+static gchar *get_machine_type(void)
+{
+ return computer_get_virtualization();
+}
+
+const ShellModuleMethod *hi_exported_methods(void)
+{
+ static const ShellModuleMethod m[] = {
+ {"getOSKernel", get_os_kernel},
+ {"getOS", get_os},
+ {"getDisplaySummary", get_display_summary},
+ {"getOGLRenderer", get_ogl_renderer},
+ {"getAudioCards", get_audio_cards},
+ {"getKernelModuleDescription", get_kernel_module_description},
+ {"getMemoryTotal", get_memory_total},
+ {"getMemoryDesc", get_memory_desc},
+ {"getMachineType", get_machine_type},
+ {NULL},
+ };
+
+ return m;
+}
+
+ModuleEntry *hi_module_get_entries(void)
+{
+ return entries;
+}
+
+gchar *hi_module_get_name(void)
+{
+ return g_strdup(_("Computer"));
+}
+
+guchar hi_module_get_weight(void)
+{
+ return 80;
+}
+
+gchar **hi_module_get_dependencies(void)
+{
+ static gchar *deps[] = { "devices.so", NULL };
+
+ return deps;
+}
+
+gchar *hi_module_get_summary(void)
+{
+ gchar *virt = computer_get_virtualization();
+ gchar *machine_type = g_strdup_printf("%s (%s)",
+ _("Motherboard"),
+ (char*)idle_free(virt));
+
+ return g_strdup_printf("[%s]\n"
+ "Icon=os.png\n"
+ "Method=computer::getOS\n"
+ "[%s]\n"
+ "Icon=processor.png\n"
+ "Method=devices::getProcessorNameAndDesc\n"
+ "[%s]\n"
+ "Icon=memory.png\n"
+ "Method=computer::getMemoryDesc\n"
+ "[%s]\n"
+ "Icon=module.png\n"
+ "Method=devices::getMotherboard\n"
+ "[%s]\n"
+ "Icon=monitor.png\n"
+ "Method=computer::getDisplaySummary\n"
+ "[%s]\n"
+ "Icon=hdd.png\n"
+ "Method=devices::getStorageDevicesSimple\n"
+ "[%s]\n"
+ "Icon=printer.png\n"
+ "Method=devices::getPrinters\n"
+ "[%s]\n"
+ "Icon=audio.png\n"
+ "Method=computer::getAudioCards\n",
+ _("Operating System"),
+ _("Processor"), _("Memory"), (char*)idle_free(machine_type), _("Graphics"),
+ _("Storage"), _("Printers"), _("Audio")
+ );
+}
+
+void hi_module_deinit(void)
+{
+ g_hash_table_destroy(memlabels);
+
+ if (computer->os) {
+ g_free(computer->os->kernel);
+ g_free(computer->os->kcmdline);
+ g_free(computer->os->libc);
+ g_free(computer->os->distrocode);
+ g_free(computer->os->distro);
+ g_free(computer->os->hostname);
+ g_free(computer->os->language);
+ g_free(computer->os->homedir);
+ g_free(computer->os->kernel_version);
+ g_free(computer->os->languages);
+ g_free(computer->os->desktop);
+ g_free(computer->os->username);
+ g_free(computer->os->boots);
+ g_free(computer->os);
+ }
+
+ computer_free_display(computer->display);
+
+ if (computer->alsa) {
+ g_slist_free(computer->alsa->cards);
+ g_free(computer->alsa);
+ }
+
+ g_free(computer->date_time);
+ g_free(computer);
+
+ moreinfo_del_with_prefix("COMP");
+}
+
+void hi_module_init(void)
+{
+ computer = g_new0(Computer, 1);
+ init_memory_labels();
+ kernel_module_icon_init();
+}
+
+const ModuleAbout *hi_module_get_about(void)
+{
+ static const ModuleAbout ma = {
+ .author = "L. A. F. Pereira",
+ .description = N_("Gathers high-level computer information"),
+ .version = VERSION,
+ .license = "GNU GPL version 2 or later.",};
+
+ return &ma;
+}
+
+static const gchar *hinote_kmod() {
+ static gchar note[note_max_len] = "";
+ gboolean ok = TRUE;
+ *note = 0; /* clear */
+ ok &= note_require_tool("lsmod", note, _("<i><b>lsmod</b></i> is required."));
+ return ok ? NULL : g_strstrip(note); /* remove last \n */
+}
+
+static const gchar *hinote_display() {
+ static gchar note[note_max_len] = "";
+ gboolean ok = TRUE;
+ *note = 0; /* clear */
+ ok &= note_require_tool("xrandr", note, _("X.org's <i><b>xrandr</b></i> utility provides additional details when available."));
+ ok &= note_require_tool("glxinfo", note, _("Mesa's <i><b>glxinfo</b></i> utility is required for OpenGL information."));
+ return ok ? NULL : g_strstrip(note); /* remove last \n */
+}
+
+const gchar *hi_note_func(gint entry)
+{
+ if (entry == ENTRY_KMOD) {
+ return hinote_kmod();
+ }
+ else if (entry == ENTRY_DISPLAY) {
+ return hinote_display();
+ }
+ return NULL;
+}
diff --git a/modules/computer/alsa.c b/modules/computer/alsa.c
new file mode 100644
index 00000000..f74d2752
--- /dev/null
+++ b/modules/computer/alsa.c
@@ -0,0 +1,71 @@
+/*
+ * HardInfo - Displays System Information
+ * Copyright (C) 2003-2006 L. A. F. Pereira <l@tia.mat.br>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, version 2 or later.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#include "hardinfo.h"
+#include "computer.h"
+
+gchar *
+computer_get_alsacards(Computer * computer)
+{
+ GSList *p;
+ gchar *tmp = g_strdup_printf("[%s]\n", _("Audio Devices"));
+ gint n = 0;
+
+ if (computer->alsa) {
+ for (p = computer->alsa->cards; p; p = p->next) {
+ AlsaCard *ac = (AlsaCard *) p->data;
+
+ tmp = h_strdup_cprintf("%s#%d=%s\n", tmp,
+ _("Audio Adapter"), ++n, ac->friendly_name);
+ }
+ }
+
+ return tmp;
+}
+
+AlsaInfo *
+computer_get_alsainfo(void)
+{
+ AlsaInfo *ai;
+ AlsaCard *ac;
+ FILE *cards;
+ gchar buffer[128];
+
+ cards = fopen("/proc/asound/cards", "r");
+ if (!cards)
+ return NULL;
+
+ ai = g_new0(AlsaInfo, 1);
+
+ while (fgets(buffer, 128, cards)) {
+ gchar **tmp;
+
+ ac = g_new0(AlsaCard, 1);
+
+ tmp = g_strsplit(buffer, ":", 0);
+
+ ac->friendly_name = g_strdup(tmp[1]);
+ ai->cards = g_slist_append(ai->cards, ac);
+
+ g_strfreev(tmp);
+ (void)fgets(buffer, 128, cards); /* skip next line */
+ }
+ fclose(cards);
+
+ return ai;
+}
diff --git a/modules/computer/boots.c b/modules/computer/boots.c
new file mode 100644
index 00000000..52c122e4
--- /dev/null
+++ b/modules/computer/boots.c
@@ -0,0 +1,68 @@
+/*
+ * HardInfo - Displays System Information
+ * Copyright (C) 2003-2007 L. A. F. Pereira <l@tia.mat.br>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, version 2 or later.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#include <stdio.h>
+#include <string.h>
+#include "hardinfo.h"
+#include "computer.h"
+
+extern void scan_os(gboolean reload);
+
+void
+scan_boots_real(void)
+{
+ gchar **tmp;
+ gboolean spawned;
+ gchar *out, *err, *p, *s, *next_nl;
+
+ scan_os(FALSE);
+
+ if (!computer->os->boots)
+ computer->os->boots = strdup("");
+ else
+ return;
+
+ spawned = hardinfo_spawn_command_line_sync("last",
+ &out, &err, NULL, NULL);
+ if (spawned && out != NULL) {
+ p = out;
+ while(next_nl = strchr(p, '\n')) {
+ strend(p, '\n');
+ if (strstr(p, "system boot")) {
+ s = p;
+ while (*s) {
+ if (*s == ' ' && *(s + 1) == ' ') {
+ memmove(s, s + 1, strlen(s) + 1);
+ s--;
+ } else {
+ s++;
+ }
+ }
+ tmp = g_strsplit(p, " ", 0);
+ computer->os->boots =
+ h_strdup_cprintf("\n%s %s %s %s=%s",
+ computer->os->boots,
+ tmp[4], tmp[5], tmp[6], tmp[7], tmp[3]);
+ g_strfreev(tmp);
+ }
+ p = next_nl + 1;
+ }
+ g_free(out);
+ g_free(err);
+ }
+}
diff --git a/modules/computer/display.c b/modules/computer/display.c
new file mode 100644
index 00000000..98cb7bf7
--- /dev/null
+++ b/modules/computer/display.c
@@ -0,0 +1,74 @@
+/*
+ * HardInfo - Displays System Information
+ * Copyright (C) 2003-2007 L. A. F. Pereira <l@tia.mat.br>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, version 2 or later.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#include <string.h>
+
+#include "hardinfo.h"
+#include "computer.h"
+
+DisplayInfo *computer_get_display(void) {
+ DisplayInfo *di = g_new0(DisplayInfo, 1);
+ wl_info *wl = get_walyand_info();
+ xinfo *xi = xinfo_get_info();
+ xrr_info *xrr = xi->xrr;
+ glx_info *glx = xi->glx;
+
+ di->width = di->height = 0;
+ if (xrr->screen_count > 0) {
+ di->width = xrr->screens[0].px_width;
+ di->height = xrr->screens[0].px_height;
+ }
+ di->vendor = xi->vendor;
+ di->session_type = wl->xdg_session_type;
+
+ if (strcmp(di->session_type, "x11") == 0 ) {
+ if (xi->nox) {
+ di->display_server = g_strdup(_("(Unknown)"));
+ /* assumed x11 previously, because it wasn't set */
+ free(wl->xdg_session_type);
+ di->session_type = wl->xdg_session_type = NULL;
+ } else if (xi->vendor && xi->version)
+ di->display_server = g_strdup_printf("%s %s", xi->vendor, xi->version );
+ else if (xi->vendor && xi->release_number)
+ di->display_server = g_strdup_printf("[X11] %s %s", xi->vendor, xi->release_number );
+ else
+ di->display_server = g_strdup("X11");
+ } else
+ if (strcmp(di->session_type, "wayland") == 0 ) {
+ di->display_server = g_strdup("Wayland");
+ } else
+ if (strcmp(di->session_type, "mir") == 0 ) {
+ di->display_server = g_strdup("Mir");
+ } else {
+ di->display_server = g_strdup(_("(Unknown)"));
+ }
+
+ di->xi = xi;
+ di->wl = wl;
+ return di;
+}
+
+void computer_free_display(DisplayInfo *di) {
+ /* fyi: DisplayInfo is in computer.h */
+ if (di) {
+ free(di->display_server);
+ xinfo_free(di->xi);
+ wl_free(di->wl);
+ free(di);
+ }
+}
diff --git a/modules/computer/environment.c b/modules/computer/environment.c
new file mode 100644
index 00000000..2f29c861
--- /dev/null
+++ b/modules/computer/environment.c
@@ -0,0 +1,45 @@
+/*
+ * HardInfo - Displays System Information
+ * Copyright (C) 2003-2008 L. A. F. Pereira <l@tia.mat.br>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, version 2 or later.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#include "hardinfo.h"
+#include "computer.h"
+
+static gchar *_env = NULL;
+void scan_env_var(gboolean reload)
+{
+ SCAN_START();
+
+ gchar **envlist;
+ gint i;
+
+ g_free(_env);
+
+ _env = g_strdup_printf("[%s]\n", _("Environment Variables") );
+ for (i = 0, envlist = g_listenv(); envlist[i]; i++) {
+ _env = h_strdup_cprintf("%s=%s\n", _env,
+ envlist[i], g_getenv(envlist[i]));
+ }
+ g_strfreev(envlist);
+
+ SCAN_END();
+}
+
+gchar *callback_env_var(void)
+{
+ return g_strdup(_env);
+}
diff --git a/modules/computer/filesystem.c b/modules/computer/filesystem.c
new file mode 100644
index 00000000..e9c84811
--- /dev/null
+++ b/modules/computer/filesystem.c
@@ -0,0 +1,108 @@
+/*
+ * HardInfo - Displays System Information
+ * Copyright (C) 2003-2006 L. A. F. Pereira <l@tia.mat.br>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, version 2 or later.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ * Some code from xfce4-mount-plugin, version 0.4.3
+ * Copyright (C) 2005 Jean-Baptiste jb_dul@yahoo.com
+ * Distributed under the terms of GNU GPL 2.
+ */
+
+#include <string.h>
+#include <sys/vfs.h>
+#include "hardinfo.h"
+#include "computer.h"
+
+gchar *fs_list = NULL;
+
+void
+scan_filesystems(void)
+{
+ FILE *mtab;
+ gchar buf[1024];
+ struct statfs sfs;
+ int count = 0;
+
+ g_free(fs_list);
+ fs_list = g_strdup("");
+ moreinfo_del_with_prefix("COMP:FS");
+
+ mtab = fopen("/etc/mtab", "r");
+ if (!mtab)
+ return;
+
+ while (fgets(buf, 1024, mtab)) {
+ gfloat size, used, avail;
+ gchar **tmp;
+
+ tmp = g_strsplit(buf, " ", 0);
+ if (!statfs(tmp[1], &sfs)) {
+ gfloat use_ratio;
+
+ size = (float) sfs.f_bsize * (float) sfs.f_blocks;
+ avail = (float) sfs.f_bsize * (float) sfs.f_bavail;
+ used = size - avail;
+
+ if (size == 0.0f) {
+ continue;
+ }
+
+ if (avail == 0.0f) {
+ use_ratio = 100.0f;
+ } else {
+ use_ratio = 100.0f * (used / size);
+ }
+
+ gchar *strsize = size_human_readable(size),
+ *stravail = size_human_readable(avail),
+ *strused = size_human_readable(used);
+
+ gchar *strhash;
+
+ gboolean rw = strstr(tmp[3], "rw") != NULL;
+
+ strreplacechr(tmp[0], "#", '_');
+ strhash = g_strdup_printf("[%s]\n"
+ "%s=%s\n"
+ "%s=%s\n"
+ "%s=%s\n"
+ "%s=%s\n"
+ "%s=%s\n"
+ "%s=%s\n",
+ tmp[0], /* path */
+ _("Filesystem"), tmp[2],
+ _("Mounted As"), rw ? _("Read-Write") : _("Read-Only"),
+ _("Mount Point"), tmp[1],
+ _("Size"), strsize,
+ _("Used"), strused,
+ _("Available"), stravail);
+ gchar *key = g_strdup_printf("FS%d", ++count);
+ moreinfo_add_with_prefix("COMP", key, strhash);
+ g_free(key);
+
+ fs_list = h_strdup_cprintf("$FS%d$%s%s=%.2f %% (%s of %s)|%s\n",
+ fs_list,
+ count, tmp[0], rw ? "" : "🔒",
+ use_ratio, stravail, strsize, tmp[1]);
+
+ g_free(strsize);
+ g_free(stravail);
+ g_free(strused);
+ }
+ g_strfreev(tmp);
+ }
+
+ fclose(mtab);
+}
diff --git a/modules/computer/groups.c b/modules/computer/groups.c
new file mode 100644
index 00000000..9371e211
--- /dev/null
+++ b/modules/computer/groups.c
@@ -0,0 +1,45 @@
+/*
+ * HardInfo - Displays System Information
+ * Copyright (C) 2012 L. A. F. Pereira <l@tia.mat.br>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, version 2 or later.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#include <sys/types.h>
+#include <grp.h>
+#include "hardinfo.h"
+#include "computer.h"
+
+gchar *groups = NULL;
+
+void
+scan_groups_do(void)
+{
+ struct group *group_;
+
+ setgrent();
+ group_ = getgrent();
+ if (!group_)
+ return;
+
+ g_free(groups);
+ groups = g_strdup("");
+
+ while (group_) {
+ groups = h_strdup_cprintf("%s=%d\n", groups, group_->gr_name, group_->gr_gid);
+ group_ = getgrent();
+ }
+
+ endgrent();
+}
diff --git a/modules/computer/languages.c b/modules/computer/languages.c
new file mode 100644
index 00000000..b2d4910f
--- /dev/null
+++ b/modules/computer/languages.c
@@ -0,0 +1,182 @@
+/*
+ * HardInfo - Displays System Information
+ * Copyright (C) 2003-2007 L. A. F. Pereira <l@tia.mat.br>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, version 2 or later.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#include <string.h>
+
+#include "hardinfo.h"
+#include "computer.h"
+#include "cpu_util.h" /* for UNKIFNULL() */
+
+typedef struct {
+ gchar name[32];
+ gchar *title,
+ *source,
+ *address,
+ *email,
+ *language,
+ *territory,
+ *revision,
+ *date,
+ *codeset;
+} locale_info;
+
+void locale_info_free(locale_info *s) {
+ if (s) {
+ g_free(s->title);
+ g_free(s->source);
+ g_free(s->address);
+ g_free(s->email);
+ g_free(s->language);
+ g_free(s->territory);
+ g_free(s->revision);
+ g_free(s->date);
+ g_free(s->codeset);
+ free(s);
+ }
+}
+
+/* TODO: use info_* */
+gchar *locale_info_section(locale_info *s) {
+ gchar *name = g_strdup(s->name);
+ gchar *title = g_strdup(s->title),
+ *source = g_strdup(s->source),
+ *address = g_strdup(s->address),
+ *email = g_strdup(s->email),
+ *language = g_strdup(s->language),
+ *territory = g_strdup(s->territory),
+ *revision = g_strdup(s->revision),
+ *date = g_strdup(s->date),
+ *codeset = g_strdup(s->codeset);
+
+ UNKIFNULL(title);
+ UNKIFNULL(source);
+ UNKIFNULL(address);
+ UNKIFNULL(email);
+ UNKIFNULL(language);
+ UNKIFNULL(territory);
+ UNKIFNULL(revision);
+ UNKIFNULL(date);
+ UNKIFNULL(codeset);
+
+ /* values may have & */
+ title = hardinfo_clean_value(title, 1);
+ source = hardinfo_clean_value(source, 1);
+ address = hardinfo_clean_value(address, 1);
+ email = hardinfo_clean_value(email, 1);
+ language = hardinfo_clean_value(language, 1);
+ territory = hardinfo_clean_value(territory, 1);
+
+ gchar *ret = g_strdup_printf("[%s]\n"
+ /* Name */ "%s=%s (%s)\n"
+ /* Source */ "%s=%s\n"
+ /* Address */ "%s=%s\n"
+ /* Email */ "%s=%s\n"
+ /* Language */ "%s=%s\n"
+ /* Territory */"%s=%s\n"
+ /* Revision */ "%s=%s\n"
+ /* Date */ "%s=%s\n"
+ /* Codeset */ "%s=%s\n",
+ _("Locale Information"),
+ _("Name"), name, title,
+ _("Source"), source,
+ _("Address"), address,
+ _("E-mail"), email,
+ _("Language"), language,
+ _("Territory"), territory,
+ _("Revision"), revision,
+ _("Date"), date,
+ _("Codeset"), codeset );
+ g_free(name);
+ g_free(title);
+ g_free(source);
+ g_free(address);
+ g_free(email);
+ g_free(language);
+ g_free(territory);
+ g_free(revision);
+ g_free(date);
+ g_free(codeset);
+ return ret;
+}
+
+void
+scan_languages(OperatingSystem * os)
+{
+ gboolean spawned;
+ gchar *out, *err, *p, *next_nl;
+
+ gchar *ret = NULL;
+ locale_info *curr = NULL;
+ int last = 0;
+
+ spawned = hardinfo_spawn_command_line_sync("locale -va",
+ &out, &err, NULL, NULL);
+ if (spawned) {
+ ret = g_strdup("");
+ p = out;
+ while(1) {
+ /* `locale -va` doesn't end the last locale block
+ * with an \n, which makes this more complicated */
+ next_nl = strchr(p, '\n');
+ if (next_nl == NULL)
+ next_nl = strchr(p, 0);
+ last = (*next_nl == 0) ? 1 : 0;
+ strend(p, '\n');
+ if (strncmp(p, "locale:", 7) == 0) {
+ curr = g_new0(locale_info, 1);
+ sscanf(p, "locale: %s", curr->name);
+ /* TODO: 'directory:' and 'archive:' */
+ } else if (strchr(p, '|')) {
+ do {/* get_str() has a continue in it,
+ * how fn frustrating that was to figure out */
+ gchar **tmp = g_strsplit(p, "|", 2);
+ tmp[0] = g_strstrip(tmp[0]);
+ if (tmp[1]) {
+ tmp[1] = g_strstrip(tmp[1]);
+ get_str("title", curr->title);
+ get_str("source", curr->source);
+ get_str("address", curr->address);
+ get_str("email", curr->email);
+ get_str("language", curr->language);
+ get_str("territory", curr->territory);
+ get_str("revision", curr->revision);
+ get_str("date", curr->date);
+ get_str("codeset", curr->codeset);
+ }
+ g_strfreev(tmp);
+ } while (0);
+ } else if (strstr(p, "------")) {
+ /* do nothing */
+ } else if (curr) {
+ /* a blank line is the end of a locale */
+ gchar *li_str = locale_info_section(curr);
+ gchar *clean_title = hardinfo_clean_value(curr->title, 0); /* may contain & */
+ ret = h_strdup_cprintf("$%s$%s=%s\n", ret, curr->name, curr->name, clean_title);
+ moreinfo_add_with_prefix("COMP", g_strdup(curr->name), li_str); /* becomes owned by moreinfo */
+ locale_info_free(curr);
+ curr = NULL;
+ g_free(clean_title);
+ }
+ if (last) break;
+ p = next_nl + 1;
+ }
+ g_free(out);
+ g_free(err);
+ }
+ os->languages = ret;
+}
diff --git a/modules/computer/loadavg.c b/modules/computer/loadavg.c
new file mode 100644
index 00000000..802ae2aa
--- /dev/null
+++ b/modules/computer/loadavg.c
@@ -0,0 +1,68 @@
+/*
+ * HardInfo - Displays System Information
+ * Copyright (C) 2003-2006 L. A. F. Pereira <l@tia.mat.br>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, version 2 or later.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#include <string.h>
+
+#include "hardinfo.h"
+#include "computer.h"
+
+static gboolean
+computer_get_loadinfo(LoadInfo *li)
+{
+ FILE *procloadavg;
+ char buf[64];
+ int ret;
+
+ procloadavg = fopen("/proc/loadavg", "r");
+ if (!procloadavg)
+ return FALSE;
+
+ if (!fgets(buf, sizeof(buf), procloadavg)) {
+ fclose(procloadavg);
+ return FALSE;
+ }
+
+ ret = sscanf(buf, "%f %f %f", &li->load1, &li->load5, &li->load15);
+ if (ret != 3) {
+ size_t len = strlen(buf);
+ size_t i;
+
+ for (i = 0; i < len; i++) {
+ if (buf[i] == '.')
+ buf[i] = ',';
+ }
+
+ ret = sscanf(buf, "%f %f %f", &li->load1, &li->load5, &li->load15);
+ }
+
+ fclose(procloadavg);
+
+ return ret == 3;
+}
+
+gchar *
+computer_get_formatted_loadavg()
+{
+ LoadInfo li;
+
+ if (!computer_get_loadinfo(&li))
+ return g_strdup(_("Couldn't obtain load average"));
+
+ return g_strdup_printf("%.2f, %.2f, %.2f", li.load1, li.load5,
+ li.load15);
+}
diff --git a/modules/computer/memory.c b/modules/computer/memory.c
new file mode 100644
index 00000000..994e4040
--- /dev/null
+++ b/modules/computer/memory.c
@@ -0,0 +1,62 @@
+/*
+ * HardInfo - Displays System Information
+ * Copyright (C) 2003-2006 L. A. F. Pereira <l@tia.mat.br>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, version 2 or later.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#include "hardinfo.h"
+#include "computer.h"
+
+MemoryInfo *
+computer_get_memory(void)
+{
+ MemoryInfo *mi;
+ FILE *procmem;
+ gchar buffer[128];
+
+ procmem = fopen("/proc/meminfo", "r");
+ if (!procmem)
+ return NULL;
+ mi = g_new0(MemoryInfo, 1);
+
+ while (fgets(buffer, 128, procmem)) {
+ gchar **tmp = g_strsplit(buffer, ":", 2);
+ if (tmp[1] == NULL) {
+ g_strfreev(tmp);
+ continue;
+ }
+ tmp[0] = g_strstrip(tmp[0]);
+ tmp[1] = g_strstrip(tmp[1]);
+
+ get_int("MemTotal", mi->total);
+ get_int("MemFree", mi->free);
+ get_int("Cached", mi->cached);
+
+ g_strfreev(tmp);
+ }
+ fclose(procmem);
+
+ mi->used = mi->total - mi->free;
+
+ mi->total /= 1000;
+ mi->cached /= 1000;
+ mi->used /= 1000;
+ mi->free /= 1000;
+
+ mi->used -= mi->cached;
+ mi->ratio = 1 - (gdouble) mi->used / mi->total;
+
+ return mi;
+}
diff --git a/modules/computer/memory_usage.c b/modules/computer/memory_usage.c
new file mode 100644
index 00000000..12c62fe7
--- /dev/null
+++ b/modules/computer/memory_usage.c
@@ -0,0 +1,125 @@
+/*
+ * HardInfo - Displays System Information
+ * Copyright (C) 2003-2007 L. A. F. Pereira <l@tia.mat.br>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, version 2 or later.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#include <string.h>
+#include "devices.h"
+
+GHashTable *memlabels = NULL;
+
+void scan_memory_do(void)
+{
+ gchar **keys, *tmp, *tmp_label, *trans_val;
+ static gint offset = -1;
+ gint i;
+
+ if (offset == -1) {
+ /* gah. linux 2.4 adds three lines of data we don't need in
+ * /proc/meminfo.
+ * The lines look something like this:
+ * total: used: free: shared: buffers: cached:
+ * Mem: 3301101568 1523159040 1777942528 0 3514368 1450356736
+ * Swap: 0 0 0
+ */
+ gchar *os_kernel = module_call_method("computer::getOSKernel");
+ if (os_kernel) {
+ offset = strstr(os_kernel, "Linux 2.4") ? 3 : 0;
+ g_free(os_kernel);
+ } else {
+ offset = 0;
+ }
+ }
+
+ g_file_get_contents("/proc/meminfo", &meminfo, NULL, NULL);
+
+ keys = g_strsplit(meminfo, "\n", 0);
+
+ g_free(meminfo);
+ g_free(lginterval);
+
+ meminfo = g_strdup("");
+ lginterval = g_strdup("");
+
+ for (i = offset; keys[i]; i++) {
+ gchar **newkeys = g_strsplit(keys[i], ":", 0);
+
+ if (!newkeys[0]) {
+ g_strfreev(newkeys);
+ break;
+ }
+
+ g_strstrip(newkeys[0]);
+ g_strstrip(newkeys[1]);
+
+ /* try to find a localizable label */
+ tmp = g_hash_table_lookup(memlabels, newkeys[0]);
+ if (tmp)
+ tmp_label = _(tmp);
+ else
+ tmp_label = ""; /* or newkeys[0] */
+ /* although it doesn't matter... */
+
+ if (strstr(newkeys[1], "kB")) {
+ trans_val = g_strdup_printf("%d %s", atoi(newkeys[1]), _("KiB") );
+ } else {
+ trans_val = strdup(newkeys[1]);
+ }
+
+ moreinfo_add_with_prefix("DEV", newkeys[0], g_strdup(trans_val));
+
+ tmp = g_strconcat(meminfo, newkeys[0], "=", trans_val, "|", tmp_label, "\n", NULL);
+ g_free(meminfo);
+ meminfo = tmp;
+
+ g_free(trans_val);
+
+ tmp = g_strconcat(lginterval,
+ "UpdateInterval$", newkeys[0], "=1000\n", NULL);
+ g_free(lginterval);
+ lginterval = tmp;
+
+ g_strfreev(newkeys);
+ }
+ g_strfreev(keys);
+}
+
+void init_memory_labels(void)
+{
+ static const struct {
+ char *proc_label;
+ char *real_label;
+ } proc2real[] = {
+ { "MemTotal", N_("Total Memory") },
+ { "MemFree", N_("Free Memory") },
+ { "SwapCached", N_("Cached Swap") },
+ { "HighTotal", N_("High Memory") },
+ { "HighFree", N_("Free High Memory") },
+ { "LowTotal", N_("Low Memory") },
+ { "LowFree", N_("Free Low Memory") },
+ { "SwapTotal", N_("Virtual Memory") },
+ { "SwapFree", N_("Free Virtual Memory") },
+ { NULL },
+ };
+ gint i;
+
+ memlabels = g_hash_table_new(g_str_hash, g_str_equal);
+
+ for (i = 0; proc2real[i].proc_label; i++) {
+ g_hash_table_insert(memlabels, proc2real[i].proc_label,
+ _(proc2real[i].real_label));
+ }
+}
diff --git a/modules/computer/modules.c b/modules/computer/modules.c
new file mode 100644
index 00000000..14028362
--- /dev/null
+++ b/modules/computer/modules.c
@@ -0,0 +1,400 @@
+/*
+ * HardInfo - Displays System Information
+ * Copyright (C) 2003-2006 L. A. F. Pereira <l@tia.mat.br>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, version 2 or later.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#include <string.h>
+#include <sys/utsname.h>
+#include <json-glib/json-glib.h>
+
+#include "syncmanager.h"
+#include "computer.h"
+#include "cpu_util.h" /* for STRIFNULL() */
+#include "hardinfo.h"
+
+#define GET_STR(field_name, ptr) \
+ if (!ptr && strstr(tmp[0], field_name)) { \
+ ptr = g_markup_escape_text(g_strstrip(tmp[1]), strlen(tmp[1])); \
+ g_strfreev(tmp); \
+ continue; \
+ }
+
+GHashTable *_module_hash_table = NULL;
+static gchar *kernel_modules_dir = NULL;
+
+enum icons {
+ ICON_INVALID = 0,
+
+ ICON_AUDIO,
+ ICON_BLUETOOTH,
+ ICON_CAMERA_WEB,
+ ICON_CDROM,
+ ICON_CRYPTOHASH,
+ ICON_DEVICES,
+ ICON_HDD,
+ ICON_INPUTDEVICES,
+ ICON_JOYSTICK,
+ ICON_KEYBOARD,
+ ICON_MEDIA_FLOPPY,
+ ICON_MEDIA_REMOVABLE,
+ ICON_MEMORY,
+ ICON_MONITOR,
+ ICON_MOUSE,
+ ICON_NETWORK,
+ ICON_NETWORK_CONNECTIONS,
+ ICON_NETWORK_INTERFACE,
+ ICON_THERM,
+ ICON_USB,
+ ICON_WIRELESS,
+
+ ICON_MAX,
+};
+
+static const char *icon_table[ICON_MAX] = {
+ [ICON_AUDIO] = "audio",
+ [ICON_BLUETOOTH] = "bluetooth",
+ [ICON_CAMERA_WEB] = "camera-web",
+ [ICON_CDROM] = "cdrom",
+ [ICON_CRYPTOHASH] = "cryptohash",
+ [ICON_DEVICES] = "devices",
+ [ICON_HDD] = "hdd",
+ [ICON_INPUTDEVICES] = "inputdevices",
+ [ICON_JOYSTICK] = "joystick",
+ [ICON_KEYBOARD] = "keyboard",
+ [ICON_MEDIA_FLOPPY] = "media-floppy",
+ [ICON_MEDIA_REMOVABLE] = "media-removable",
+ [ICON_MEMORY] = "memory",
+ [ICON_MONITOR] = "monitor",
+ [ICON_MOUSE] = "mouse",
+ [ICON_NETWORK] = "network",
+ [ICON_NETWORK_CONNECTIONS] = "network-connections",
+ [ICON_NETWORK_INTERFACE] = "network-interface",
+ [ICON_THERM] = "therm",
+ [ICON_USB] = "usb",
+ [ICON_WIRELESS] = "wireless",
+};
+
+/* Keep this sorted by reverse strlen(dir)! */
+static const struct {
+ const gchar *dir;
+ enum icons icon;
+} modules_icons[] = {
+ {"drivers/input/joystick/", ICON_JOYSTICK},
+ {"drivers/input/keyboard/", ICON_KEYBOARD},
+ {"drivers/media/usb/uvc/", ICON_CAMERA_WEB},
+ {"drivers/net/wireless/", ICON_WIRELESS},
+ {"drivers/net/ethernet/", ICON_NETWORK_INTERFACE},
+ {"drivers/input/mouse/", ICON_MOUSE},
+ {"drivers/bluetooth/", ICON_BLUETOOTH},
+ {"drivers/media/v4l", ICON_CAMERA_WEB},
+ {"arch/x86/crypto/", ICON_CRYPTOHASH},
+ {"drivers/crypto/", ICON_CRYPTOHASH},
+ {"net/bluetooth/", ICON_BLUETOOTH},
+ {"drivers/input/", ICON_INPUTDEVICES},
+ {"drivers/cdrom/", ICON_CDROM},
+ {"drivers/hwmon/", ICON_THERM},
+ {"drivers/iommu/", ICON_MEMORY},
+ {"net/wireless/", ICON_WIRELESS},
+ {"drivers/nvme/", ICON_HDD},
+ {"net/ethernet/", ICON_NETWORK_INTERFACE},
+ {"drivers/scsi/", ICON_HDD},
+ {"drivers/edac/", ICON_MEMORY},
+ {"drivers/hid/", ICON_INPUTDEVICES},
+ {"drivers/gpu/", ICON_MONITOR},
+ {"drivers/i2c/", ICON_MEMORY},
+ {"drivers/ata/", ICON_HDD},
+ {"drivers/usb/", ICON_USB},
+ {"drivers/pci/", ICON_DEVICES},
+ {"drivers/net/", ICON_NETWORK},
+ {"drivers/mmc/", ICON_MEDIA_REMOVABLE},
+ {"crypto/", ICON_CRYPTOHASH},
+ {"sound/", ICON_AUDIO},
+ {"net/", ICON_NETWORK_CONNECTIONS},
+ {"fs/", ICON_MEDIA_FLOPPY},
+ {},
+};
+
+static GHashTable *module_icons;
+
+static void build_icon_table_iter(JsonObject *object,
+ const gchar *key,
+ JsonNode *value,
+ gpointer user_data)
+{
+ char *key_copy = g_strdup(key);
+ char *p;
+
+ for (p = key_copy; *p; p++) {
+ if (*p == '_')
+ *p = '-';
+ }
+
+ enum icons icon;
+ const gchar *value_str = json_node_get_string(value);
+ for (icon = ICON_INVALID; icon < ICON_MAX; icon++) {
+ const char *icon_name = icon_table[icon];
+
+ if (!icon_name)
+ continue;
+
+ if (g_str_equal(value_str, icon_name)) {
+ g_hash_table_insert(module_icons,
+ key_copy, GINT_TO_POINTER(icon));
+ return;
+ }
+ }
+
+ g_free(key_copy);
+}
+
+void kernel_module_icon_init(void)
+{
+ gchar *icon_json;
+
+ static SyncEntry sync_entry = {
+ .name = N_("Update kernel module icon table"),
+ .file_name = "kernel-module-icons.json",
+ };
+ sync_manager_add_entry(&sync_entry);
+
+ icon_json = g_build_filename(g_get_user_config_dir(),
+ "hardinfo", "kernel-module-icons.json",
+ NULL);
+
+ module_icons = g_hash_table_new(g_str_hash, g_str_equal);
+
+ if (!g_file_test(icon_json, G_FILE_TEST_EXISTS))
+ goto out;
+
+ JsonParser *parser = json_parser_new();
+ if (!json_parser_load_from_file(parser, icon_json, NULL))
+ goto out_destroy_parser;
+
+ JsonNode *root = json_parser_get_root(parser);
+ if (json_node_get_node_type(root) != JSON_NODE_OBJECT)
+ goto out_destroy_parser;
+
+ JsonObject *icons = json_node_get_object(root);
+ if (!icons)
+ goto out_destroy_parser;
+
+ json_object_foreach_member(icons, build_icon_table_iter, NULL);
+
+out_destroy_parser:
+ g_object_unref(parser);
+
+out:
+ g_free(icon_json);
+}
+
+static const gchar* get_module_icon(const char *modname, const char *path)
+{
+ char *modname_temp = g_strdup(modname);
+ char *p;
+ for (p = modname_temp; *p; p++) {
+ if (*p == '_')
+ *p = '-';
+ }
+ gpointer icon = g_hash_table_lookup(module_icons, modname_temp);
+ g_free(modname_temp);
+ if (icon)
+ return icon_table[GPOINTER_TO_INT(icon)];
+
+ if (path == NULL) /* modinfo couldn't find module path */
+ return NULL;
+
+ if (kernel_modules_dir == NULL) {
+ struct utsname utsbuf;
+ uname(&utsbuf);
+ kernel_modules_dir = g_strdup_printf("/lib/modules/%s/kernel/", utsbuf.release);
+ }
+
+ if (!g_str_has_prefix(path, kernel_modules_dir))
+ return NULL;
+
+ const gchar *path_no_prefix = path + strlen(kernel_modules_dir);
+ const size_t path_no_prefix_len = strlen(path_no_prefix);
+ int i;
+
+ for (i = 0; modules_icons[i].dir; i++) {
+ if (g_str_has_prefix(path_no_prefix, modules_icons[i].dir))
+ return icon_table[modules_icons[i].icon];
+ }
+
+ return NULL;
+}
+
+void scan_modules_do(void) {
+ FILE *lsmod;
+ gchar buffer[1024];
+ gchar *lsmod_path;
+ gchar *module_icons;
+ const gchar *icon;
+
+ if (!_module_hash_table) { _module_hash_table = g_hash_table_new(g_str_hash, g_str_equal); }
+
+ g_free(module_list);
+
+ kernel_modules_dir = NULL;
+ module_list = NULL;
+ module_icons = NULL;
+ moreinfo_del_with_prefix("COMP:MOD");
+
+ lsmod_path = find_program("lsmod");
+ if (!lsmod_path) return;
+ lsmod = popen(lsmod_path, "r");
+ if (!lsmod) {
+ g_free(lsmod_path);
+ return;
+ }
+
+ (void)fgets(buffer, 1024, lsmod); /* Discards the first line */
+
+ while (fgets(buffer, 1024, lsmod)) {
+ gchar *buf, *strmodule, *hashkey;
+ gchar *author = NULL, *description = NULL, *license = NULL, *deps = NULL, *vermagic = NULL,
+ *filename = NULL, *srcversion = NULL, *version = NULL, *retpoline = NULL,
+ *intree = NULL, modname[64];
+ FILE *modi;
+ glong memory;
+
+ shell_status_pulse();
+
+ buf = buffer;
+
+ sscanf(buf, "%s %ld", modname, &memory);
+
+ hashkey = g_strdup_printf("MOD%s", modname);
+ buf = g_strdup_printf("/sbin/modinfo %s 2>/dev/null", modname);
+
+ modi = popen(buf, "r");
+ while (fgets(buffer, 1024, modi)) {
+ gchar **tmp = g_strsplit(buffer, ":", 2);
+
+ GET_STR("author", author);
+ GET_STR("description", description);
+ GET_STR("license", license);
+ GET_STR("depends", deps);
+ GET_STR("vermagic", vermagic);
+ GET_STR("filename", filename);
+ GET_STR("srcversion", srcversion); /* so "version" doesn't catch */
+ GET_STR("version", version);
+ GET_STR("retpoline", retpoline);
+ GET_STR("intree", intree);
+
+ g_strfreev(tmp);
+ }
+ pclose(modi);
+ g_free(buf);
+
+ /* old modutils includes quotes in some strings; strip them */
+ /*remove_quotes(modname);
+ remove_quotes(description);
+ remove_quotes(vermagic);
+ remove_quotes(author);
+ remove_quotes(license); */
+
+ /* old modutils displays <none> when there's no value for a
+ given field; this is not desirable in the module name
+ display, so change it to an empty string */
+ if (description && g_str_equal(description, "&lt;none&gt;")) {
+ g_free(description);
+ description = g_strdup("");
+
+ g_hash_table_insert(_module_hash_table, g_strdup(modname),
+ g_strdup_printf("Kernel module (%s)", modname));
+ } else {
+ g_hash_table_insert(_module_hash_table, g_strdup(modname), g_strdup(description));
+ }
+
+ /* append this module to the list of modules */
+ module_list = h_strdup_cprintf("$%s$%s=%s\n", module_list, hashkey, modname,
+ description ? description : "");
+ icon = get_module_icon(modname, filename);
+ module_icons = h_strdup_cprintf("Icon$%s$%s=%s.png\n", module_icons, hashkey,
+ modname, icon ? icon: "module");
+
+ STRIFNULL(filename, _("(Not available)"));
+ STRIFNULL(description, _("(Not available)"));
+ STRIFNULL(vermagic, _("(Not available)"));
+ STRIFNULL(author, _("(Not available)"));
+ STRIFNULL(license, _("(Not available)"));
+ STRIFNULL(version, _("(Not available)"));
+
+ gboolean ry = FALSE, ity = FALSE;
+ if (retpoline && *retpoline == 'Y') ry = TRUE;
+ if (intree && *intree == 'Y') ity = TRUE;
+
+ g_free(retpoline);
+ g_free(intree);
+
+ retpoline = g_strdup(ry ? _("Yes") : _("No"));
+ intree = g_strdup(ity ? _("Yes") : _("No"));
+
+ /* create the module information string */
+ strmodule = g_strdup_printf("[%s]\n"
+ "%s=%s\n"
+ "%s=%.2f %s\n"
+ "[%s]\n"
+ "%s=%s\n"
+ "%s=%s\n"
+ "%s=%s\n"
+ "%s=%s\n"
+ "%s=%s\n"
+ "%s=%s\n"
+ "[%s]\n"
+ "%s=%s\n"
+ "%s=%s\n",
+ _("Module Information"), _("Path"), filename, _("Used Memory"),
+ memory / 1024.0, _("KiB"), _("Description"), _("Name"), modname,
+ _("Description"), description, _("Version Magic"), vermagic,
+ _("Version"), version, _("In Linus' Tree"), intree,
+ _("Retpoline Enabled"), retpoline, _("Copyright"), _("Author"),
+ author, _("License"), license);
+
+ /* if there are dependencies, append them to that string */
+ if (deps && strlen(deps)) {
+ gchar **tmp = g_strsplit(deps, ",", 0);
+
+ strmodule = h_strconcat(strmodule, "\n[", _("Dependencies"), "]\n",
+ g_strjoinv("=\n", tmp), "=\n", NULL);
+ g_strfreev(tmp);
+ g_free(deps);
+ }
+
+ moreinfo_add_with_prefix("COMP", hashkey, strmodule);
+ g_free(hashkey);
+
+ g_free(license);
+ g_free(description);
+ g_free(author);
+ g_free(vermagic);
+ g_free(filename);
+ g_free(srcversion);
+ g_free(version);
+ g_free(retpoline);
+ g_free(intree);
+ }
+ pclose(lsmod);
+
+ g_free(lsmod_path);
+ g_free(kernel_modules_dir);
+
+ if (module_list != NULL && module_icons != NULL) {
+ module_list = h_strdup_cprintf("[$ShellParam$]\n%s", module_list, module_icons);
+ }
+ g_free(module_icons);
+}
diff --git a/modules/computer/os.c b/modules/computer/os.c
new file mode 100644
index 00000000..7648eef3
--- /dev/null
+++ b/modules/computer/os.c
@@ -0,0 +1,582 @@
+/*
+ * HardInfo - Displays System Information
+ * Copyright (C) 2003-2006 L. A. F. Pereira <l@tia.mat.br>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, version 2 or later.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#include <gdk/gdkx.h>
+#include <string.h>
+#include <sys/utsname.h>
+#include "hardinfo.h"
+#include "computer.h"
+#include "distro_flavors.h"
+
+static gchar *
+get_libc_version(void)
+{
+ static const struct {
+ const char *test_cmd;
+ const char *match_str;
+ const char *lib_name;
+ gboolean try_ver_str;
+ gboolean use_stderr;
+ } libs[] = {
+ { "ldd --version", "GLIBC", N_("GNU C Library"), TRUE, FALSE},
+ { "ldd --version", "GNU libc", N_("GNU C Library"), TRUE, FALSE},
+ { "ldconfig -V", "GLIBC", N_("GNU C Library"), TRUE, FALSE},
+ { "ldconfig -V", "GNU libc", N_("GNU C Library"), TRUE, FALSE},
+ { "ldconfig -v", "uClibc", N_("uClibc or uClibc-ng"), FALSE, FALSE},
+ { "diet", "diet version", N_("diet libc"), TRUE, TRUE},
+ { NULL }
+ };
+ int i;
+
+ for (i = 0; libs[i].test_cmd; i++) {
+ gboolean spawned;
+ gchar *out, *err, *p;
+
+ spawned = hardinfo_spawn_command_line_sync(libs[i].test_cmd,
+ &out, &err, NULL, NULL);
+ if (!spawned)
+ continue;
+
+ if (libs[i].use_stderr) {
+ p = strend(idle_free(err), '\n');
+ g_free(out);
+ } else {
+ p = strend(idle_free(out), '\n');
+ g_free(err);
+ }
+
+ if (!p || !strstr(p, libs[i].match_str))
+ continue;
+
+ if (libs[i].try_ver_str) {
+ /* skip the first word, likely "ldconfig" or name of utility */
+ const gchar *ver_str = strchr(p, ' ');
+
+ if (ver_str) {
+ return g_strdup_printf("%s / %s", _(libs[i].lib_name),
+ ver_str + 1);
+ }
+ }
+
+ return g_strdup(_(libs[i].lib_name));
+ }
+
+ return g_strdup(_("Unknown"));
+}
+
+static gchar *detect_kde_version(void)
+{
+ const gchar *cmd;
+ const gchar *tmp = g_getenv("KDE_SESSION_VERSION");
+ gchar *out;
+ gboolean spawned;
+
+ if (tmp && tmp[0] == '4') {
+ cmd = "kwin --version";
+ } else {
+ cmd = "kcontrol --version";
+ }
+
+ spawned = hardinfo_spawn_command_line_sync(cmd, &out, NULL, NULL, NULL);
+ if (!spawned)
+ return NULL;
+
+ tmp = strstr(idle_free(out), "KDE: ");
+ return tmp ? g_strdup(tmp + strlen("KDE: ")) : NULL;
+}
+
+static gchar *
+detect_gnome_version(void)
+{
+ gchar *tmp;
+ gchar *out;
+ gboolean spawned;
+
+ spawned = hardinfo_spawn_command_line_sync(
+ "gnome-shell --version", &out, NULL, NULL, NULL);
+ if (spawned) {
+ tmp = strstr(idle_free(out), _("GNOME Shell "));
+
+ if (tmp) {
+ tmp += strlen(_("GNOME Shell "));
+ return g_strdup_printf("GNOME %s", strend(tmp, '\n'));
+ }
+ }
+
+ spawned = hardinfo_spawn_command_line_sync(
+ "gnome-about --gnome-version", &out, NULL, NULL, NULL);
+ if (spawned) {
+ tmp = strstr(idle_free(out), _("Version: "));
+
+ if (tmp) {
+ tmp += strlen(_("Version: "));
+ return g_strdup_printf("GNOME %s", strend(tmp, '\n'));
+ }
+ }
+
+ return NULL;
+}
+
+
+static gchar *
+detect_mate_version(void)
+{
+ gchar *tmp;
+ gchar *out;
+ gboolean spawned;
+
+ spawned = hardinfo_spawn_command_line_sync(
+ "mate-about --version", &out, NULL, NULL, NULL);
+ if (spawned) {
+ tmp = strstr(idle_free(out), _("MATE Desktop Environment "));
+
+ if (tmp) {
+ tmp += strlen(_("MATE Desktop Environment "));
+ return g_strdup_printf("MATE %s", strend(tmp, '\n'));
+ }
+ }
+
+ return NULL;
+}
+
+static gchar *
+detect_window_manager(void)
+{
+ const gchar *curdesktop;
+ const gchar* windowman;
+ GdkScreen *screen = gdk_screen_get_default();
+
+#if GTK_CHECK_VERSION(3,0,0)
+ if (GDK_IS_X11_SCREEN(screen)) {
+#else
+ if (screen && GDK_IS_SCREEN(screen)) {
+#endif
+ windowman = gdk_x11_screen_get_window_manager_name(screen);
+ } else return g_strdup("Not X11");
+
+ if (g_str_equal(windowman, "Xfwm4"))
+ return g_strdup("XFCE 4");
+
+ curdesktop = g_getenv("XDG_CURRENT_DESKTOP");
+ if (curdesktop) {
+ const gchar *desksession = g_getenv("DESKTOP_SESSION");
+
+ if (desksession && !g_str_equal(curdesktop, desksession))
+ return g_strdup(desksession);
+ }
+
+ return g_strdup_printf(_("Unknown (Window Manager: %s)"), windowman);
+}
+
+static gchar *
+desktop_with_session_type(const gchar *desktop_env)
+{
+ const char *tmp;
+
+ tmp = g_getenv("XDG_SESSION_TYPE");
+ if (tmp) {
+ if (!g_str_equal(tmp, "unspecified"))
+ return g_strdup_printf(_(/*/{desktop environment} on {session type}*/ "%s on %s"), desktop_env, tmp);
+ }
+
+ return g_strdup(desktop_env);
+}
+
+static gchar *
+detect_xdg_environment(const gchar *env_var)
+{
+ const gchar *tmp;
+
+ tmp = g_getenv(env_var);
+ if (!tmp)
+ return NULL;
+
+ if (g_str_equal(tmp, "GNOME") || g_str_equal(tmp, "gnome")) {
+ gchar *maybe_gnome = detect_gnome_version();
+
+ if (maybe_gnome)
+ return maybe_gnome;
+ }
+ if (g_str_equal(tmp, "KDE") || g_str_equal(tmp, "kde")) {
+ gchar *maybe_kde = detect_kde_version();
+
+ if (maybe_kde)
+ return maybe_kde;
+ }
+ if (g_str_equal(tmp, "MATE") || g_str_equal(tmp, "mate")) {
+ gchar *maybe_mate = detect_mate_version();
+
+ if (maybe_mate)
+ return maybe_mate;
+ }
+
+ return g_strdup(tmp);
+}
+
+static gchar *
+detect_desktop_environment(void)
+{
+ const gchar *tmp;
+ gchar *windowman;
+
+ windowman = detect_xdg_environment("XDG_CURRENT_DESKTOP");
+ if (windowman)
+ return windowman;
+ windowman = detect_xdg_environment("XDG_SESSION_DESKTOP");
+ if (windowman)
+ return windowman;
+
+ tmp = g_getenv("KDE_FULL_SESSION");
+ if (tmp) {
+ gchar *maybe_kde = detect_kde_version();
+
+ if (maybe_kde)
+ return maybe_kde;
+ }
+ tmp = g_getenv("GNOME_DESKTOP_SESSION_ID");
+ if (tmp) {
+ gchar *maybe_gnome = detect_gnome_version();
+
+ if (maybe_gnome)
+ return maybe_gnome;
+ }
+
+ windowman = detect_window_manager();
+ if (windowman)
+ return windowman;
+
+ if (!g_getenv("DISPLAY"))
+ return g_strdup(_("Terminal"));
+
+ return g_strdup(_("Unknown"));
+}
+
+gchar *
+computer_get_dmesg_status(void)
+{
+ gchar *out = NULL, *err = NULL;
+ int ex = 1, result = 0;
+ hardinfo_spawn_command_line_sync("dmesg", &out, &err, &ex, NULL);
+ g_free(out);
+ g_free(err);
+ result += (getuid() == 0) ? 2 : 0;
+ result += ex ? 1 : 0;
+ switch(result) {
+ case 0: /* readable, user */
+ return g_strdup(_("User access allowed"));
+ case 1: /* unreadable, user */
+ return g_strdup(_("User access forbidden"));
+ case 2: /* readable, root */
+ return g_strdup(_("Access allowed (running as superuser)"));
+ case 3: /* unreadable, root */
+ return g_strdup(_("Access forbidden? (running as superuser)"));
+ }
+ return g_strdup(_("(Unknown)"));
+}
+
+gchar *
+computer_get_aslr(void)
+{
+ switch (h_sysfs_read_int("/proc/sys/kernel", "randomize_va_space")) {
+ case 0:
+ return g_strdup(_("Disabled"));
+ case 1:
+ return g_strdup(_("Partially enabled (mmap base+stack+VDSO base)"));
+ case 2:
+ return g_strdup(_("Fully enabled (mmap base+stack+VDSO base+heap)"));
+ default:
+ return g_strdup(_("Unknown"));
+ }
+}
+
+gchar *
+computer_get_entropy_avail(void)
+{
+ gchar tab_entropy_fstr[][32] = {
+ N_(/*/bits of entropy for rng (0)*/ "(None or not available)"),
+ N_(/*/bits of entropy for rng (low/poor value)*/ "%d bits (low)"),
+ N_(/*/bits of entropy for rng (medium value)*/ "%d bits (medium)"),
+ N_(/*/bits of entropy for rng (high/good value)*/ "%d bits (healthy)")
+ };
+ gint bits = h_sysfs_read_int("/proc/sys/kernel/random", "entropy_avail");
+ if (bits > 3000) return g_strdup_printf(_(tab_entropy_fstr[3]), bits);
+ if (bits > 200) return g_strdup_printf(_(tab_entropy_fstr[2]), bits);
+ if (bits > 1) return g_strdup_printf(_(tab_entropy_fstr[1]), bits);
+ return g_strdup_printf(_(tab_entropy_fstr[0]), bits);
+}
+
+gchar *
+computer_get_language(void)
+{
+ gchar *tab_lang_env[] =
+ { "LANGUAGE", "LANG", "LC_ALL", "LC_MESSAGES", NULL };
+ gchar *lc = NULL, *env = NULL, *ret = NULL;
+ gint i = 0;
+
+ lc = setlocale(LC_ALL, NULL);
+
+ while (tab_lang_env[i] != NULL) {
+ env = g_strdup( g_getenv(tab_lang_env[i]) );
+ if (env != NULL) break;
+ i++;
+ }
+
+ if (env != NULL)
+ if (lc != NULL)
+ ret = g_strdup_printf("%s (%s)", lc, env);
+ else
+ ret = g_strdup_printf("%s", env);
+ else
+ if (lc != NULL)
+ ret = g_strdup_printf("%s", lc);
+
+ if (ret == NULL)
+ ret = g_strdup( _("(Unknown)") );
+
+ return ret;
+}
+
+static Distro
+parse_os_release(void)
+{
+ gchar *pretty_name = NULL;
+ gchar *id = NULL;
+ gchar *codename = NULL;
+ gchar **split, *contents, **line;
+
+ if (!g_file_get_contents("/usr/lib/os-release", &contents, NULL, NULL))
+ return (Distro) {};
+
+ split = g_strsplit(idle_free(contents), "\n", 0);
+ if (!split)
+ return (Distro) {};
+
+ for (line = split; *line; line++) {
+ if (!strncmp(*line, "ID=", sizeof("ID=") - 1)) {
+ id = g_strdup(*line + strlen("ID="));
+ } else if (!strncmp(*line, "CODENAME=", sizeof("CODENAME=") - 1)) {
+ codename = g_strdup(*line + strlen("CODENAME="));
+ } else if (!strncmp(*line, "PRETTY_NAME=", sizeof("PRETTY_NAME=") - 1)) {
+ pretty_name = g_strdup(*line +
+ strlen("PRETTY_NAME=\""));
+ strend(pretty_name, '"');
+ }
+ }
+
+ g_strfreev(split);
+
+ if (pretty_name)
+ return (Distro) { .distro = pretty_name, .codename = codename, .id = id };
+
+ g_free(id);
+ return (Distro) {};
+}
+
+static Distro
+parse_lsb_release(void)
+{
+ gchar *pretty_name = NULL;
+ gchar *id = NULL;
+ gchar *codename = NULL;
+ gchar **split, *contents, **line;
+
+ if (!hardinfo_spawn_command_line_sync("/usr/bin/lsb_release -di", &contents, NULL, NULL, NULL))
+ return (Distro) {};
+
+ split = g_strsplit(idle_free(contents), "\n", 0);
+ if (!split)
+ return (Distro) {};
+
+ for (line = split; *line; line++) {
+ if (!strncmp(*line, "Distributor ID:\t", sizeof("Distributor ID:\t") - 1)) {
+ id = g_utf8_strdown(*line + strlen("Distributor ID:\t"), -1);
+ } else if (!strncmp(*line, "Codename:\t", sizeof("Codename:\t") - 1)) {
+ codename = g_utf8_strdown(*line + strlen("Codename:\t"), -1);
+ } else if (!strncmp(*line, "Description:\t", sizeof("Description:\t") - 1)) {
+ pretty_name = g_strdup(*line + strlen("Description:\t"));
+ }
+ }
+
+ g_strfreev(split);
+
+ if (pretty_name)
+ return (Distro) { .distro = pretty_name, .codename = codename, .id = id };
+
+ g_free(id);
+ return (Distro) {};
+}
+
+static Distro
+detect_distro(void)
+{
+ static const struct {
+ const gchar *file;
+ const gchar *codename;
+ const gchar *override;
+ } distro_db[] = {
+#define DB_PREFIX "/etc/"
+ { DB_PREFIX "arch-release", "arch", "Arch Linux" },
+ { DB_PREFIX "fatdog-version", "fatdog" },
+ { DB_PREFIX "debian_version", "debian" },
+ { DB_PREFIX "slackware-version", "slk" },
+ { DB_PREFIX "mandrake-release", "mdk" },
+ { DB_PREFIX "mandriva-release", "mdv" },
+ { DB_PREFIX "fedora-release", "fedora" },
+ { DB_PREFIX "coas", "coas" },
+ { DB_PREFIX "environment.corel", "corel"},
+ { DB_PREFIX "gentoo-release", "gnt" },
+ { DB_PREFIX "conectiva-release", "cnc" },
+ { DB_PREFIX "versão-conectiva", "cnc" },
+ { DB_PREFIX "turbolinux-release", "tl" },
+ { DB_PREFIX "yellowdog-release", "yd" },
+ { DB_PREFIX "sabayon-release", "sbn" },
+ { DB_PREFIX "enlisy-release", "enlsy" },
+ { DB_PREFIX "SuSE-release", "suse" },
+ { DB_PREFIX "sun-release", "sun" },
+ { DB_PREFIX "zenwalk-version", "zen" },
+ { DB_PREFIX "DISTRO_SPECS", "ppy", "Puppy Linux" },
+ { DB_PREFIX "puppyversion", "ppy", "Puppy Linux" },
+ { DB_PREFIX "distro-release", "fl" },
+ { DB_PREFIX "vine-release", "vine" },
+ { DB_PREFIX "PartedMagic-version", "pmag" },
+ /*
+ * RedHat must be the *last* one to be checked, since
+ * some distros (like Mandrake) includes a redhat-relase
+ * file too.
+ */
+ { DB_PREFIX "redhat-release", "rh" },
+#undef DB_PREFIX
+ { NULL, NULL }
+ };
+ Distro distro;
+ gchar *contents;
+ int i;
+
+ distro = parse_os_release();
+ if (distro.distro)
+ return distro;
+
+ distro = parse_lsb_release();
+ if (distro.distro)
+ return distro;
+
+ for (i = 0; distro_db[i].file; i++) {
+ if (!g_file_get_contents(distro_db[i].file, &contents, NULL, NULL))
+ continue;
+
+ if (distro_db[i].override) {
+ g_free(contents);
+ return (Distro) { .distro = g_strdup(distro_db[i].override),
+ .codename = g_strdup(distro_db[i].codename) };
+ }
+
+ if (g_str_equal(distro_db[i].codename, "debian")) {
+ /* HACK: Some Debian systems doesn't include the distribuition
+ * name in /etc/debian_release, so add them here. */
+ if (isdigit(contents[0]) || contents[0] != 'D')
+ return (Distro) {
+ .distro = g_strdup_printf("Debian GNU/Linux %s", (char*)idle_free(contents)),
+ .codename = g_strdup(distro_db[i].codename)
+ };
+ }
+
+ if (g_str_equal(distro_db[i].codename, "fatdog")) {
+ return (Distro) {
+ .distro = g_strdup_printf("Fatdog64 [%.10s]", (char*)idle_free(contents)),
+ .codename = g_strdup(distro_db[i].codename)
+ };
+ }
+
+ return (Distro) { .distro = contents, .codename = g_strdup(distro_db[i].codename) };
+ }
+
+ return (Distro) { .distro = g_strdup(_("Unknown")) };
+}
+
+OperatingSystem *
+computer_get_os(void)
+{
+ struct utsname utsbuf;
+ OperatingSystem *os;
+ int i;
+
+ os = g_new0(OperatingSystem, 1);
+
+ Distro distro = detect_distro();
+ os->distro = g_strstrip(distro.distro);
+ os->distroid = distro.id;
+ os->distrocode = distro.codename;
+
+ /* Kernel and hostname info */
+ uname(&utsbuf);
+ os->kernel_version = g_strdup(utsbuf.version);
+ os->kernel = g_strdup_printf("%s %s (%s)", utsbuf.sysname,
+ utsbuf.release, utsbuf.machine);
+ os->kcmdline = h_sysfs_read_string("/proc", "cmdline");
+ os->hostname = g_strdup(utsbuf.nodename);
+ os->language = computer_get_language();
+ os->homedir = g_strdup(g_get_home_dir());
+ os->username = g_strdup_printf("%s (%s)",
+ g_get_user_name(), g_get_real_name());
+ os->libc = get_libc_version();
+ scan_languages(os);
+
+ os->desktop = detect_desktop_environment();
+ if (os->desktop)
+ os->desktop = desktop_with_session_type(idle_free(os->desktop));
+
+ os->entropy_avail = computer_get_entropy_avail();
+
+ if (g_strcmp0(os->distrocode, "ubuntu") == 0) {
+ GSList *flavs = ubuntu_flavors_scan();
+ if (flavs) {
+ /* just use the first one */
+ os->distro_flavor = (DistroFlavor*)flavs->data;
+ }
+ g_slist_free(flavs);
+ }
+
+ return os;
+}
+
+const gchar *
+computer_get_selinux(void)
+{
+ int r;
+ gboolean spawned = hardinfo_spawn_command_line_sync("selinuxenabled",
+ NULL, NULL, &r, NULL);
+
+ if (!spawned)
+ return _("Not installed");
+
+ if (r == 0)
+ return _("Enabled");
+
+ return _("Disabled");
+}
+
+gchar *
+computer_get_lsm(void)
+{
+ gchar *contents;
+
+ if (!g_file_get_contents("/sys/kernel/security/lsm", &contents, NULL, NULL))
+ return g_strdup(_("Unknown"));
+
+ return contents;
+}
diff --git a/modules/computer/ubuntu_flavors.c b/modules/computer/ubuntu_flavors.c
new file mode 100644
index 00000000..ac67d665
--- /dev/null
+++ b/modules/computer/ubuntu_flavors.c
@@ -0,0 +1,90 @@
+/*
+ * HardInfo - Displays System Information
+ * Copyright (C) 2003-2019 L. A. F. Pereira <l@tia.mat.br>
+ * This file Burt P. <pburt0@gmail.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, version 2 or later.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#include <hardinfo.h>
+#include "distro_flavors.h"
+#include "util_sysobj.h" /* for appfsp() */
+
+static const UbuntuFlavor ubuntu_flavors[] = {
+ { "Vanilla Server", "distros/ubuntu.svg", "https://ubuntu.org", "ubuntu-server" },
+ { "Vanilla Desktop", "distros/ubuntu.svg", "https://ubuntu.org", "ubuntu-desktop" },
+ { "Xubuntu", "distros/xubuntu.svg", "https://xubuntu.org", "xubuntu-desktop" },
+ { "Kubuntu", "distros/kubuntu.png", "https://kubuntu.org", "kubuntu-desktop" },
+ { "Lubuntu", "distros/lubuntu.png", "https://lubuntu.me", "lubuntu-desktop" }, /* formerly or also lubuntu.net? */
+ { "Ubuntu MATE", "distros/ubuntu-mate.png", "https://ubuntu-mate.org", "ubuntu-mate-desktop" },
+ { "Ubuntu Budgie", "distros/ubuntu-budgie.png", "https://ubuntubudgie.org", "ubuntu-budgie-desktop" },
+ { "UbuntuKylin (做最有中国味的操作系统)", "distros/ubuntu-kylin.png", "https://www.ubuntukylin.com", "ubuntukylin-desktop" },
+ { "UbuntuStudio", "distros/ubuntu-studio.png", "https://ubuntustudio.org", "ubuntustudio-desktop" },
+ /* Deprecated flavors */
+ { "Ubuntu GNOME", "distros/ubuntu-gnome.png", "https://ubuntugnome.org", "ubuntu-gnome-desktop" },
+ // Edubuntu
+ // Mythbuntu
+ { NULL }
+};
+
+static const UbuntuFlavor *_find_flavor(const gchar *pkg) {
+ int i = 0;
+ for(; ubuntu_flavors[i].base.name; i++) {
+ if (SEQ(ubuntu_flavors[i].package, pkg))
+ return &ubuntu_flavors[i];
+ }
+ return NULL;
+}
+
+/* items are const; free with g_slist_free() */
+GSList *ubuntu_flavors_scan(void) {
+ GSList *ret = NULL;
+ gboolean spawned;
+ gchar *out, *err, *p, *next_nl;
+ gint exit_status;
+ const UbuntuFlavor *f = NULL;
+ gchar *cmd_line = g_strdup("apt-cache policy");
+ int i;
+ for(i = 0; ubuntu_flavors[i].base.name; i++) {
+ cmd_line = appfsp(cmd_line, "%s", ubuntu_flavors[i].package);
+ }
+ if (!i)
+ return NULL;
+
+ spawned = hardinfo_spawn_command_line_sync(cmd_line,
+ &out, &err, &exit_status, NULL);
+ if (spawned) {
+ p = out;
+ while(next_nl = strchr(p, '\n')) {
+ strend(p, '\n');
+ int mc = 0;
+ char pkg[32] = "";
+ if (*p != ' ' && *p != '\t')
+ mc = sscanf(p, "%31s", pkg);
+ if (mc == 1) {
+ strend(pkg, ':');
+ f = _find_flavor(pkg);
+ } else if
+ (g_strstr_len(p, -1, "Installed:")
+ && !g_strstr_len(p, -1, "(none)") ) {
+ ret = g_slist_append(ret, (void*)f);
+ }
+ p = next_nl + 1;
+ }
+ g_free(out);
+ g_free(err);
+ }
+ g_free(cmd_line);
+ return ret;
+}
diff --git a/modules/computer/uptime.c b/modules/computer/uptime.c
new file mode 100644
index 00000000..8aef1530
--- /dev/null
+++ b/modules/computer/uptime.c
@@ -0,0 +1,72 @@
+/*
+ * HardInfo - Displays System Information
+ * Copyright (C) 2003-2006 L. A. F. Pereira <l@tia.mat.br>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, version 2 or later.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+#include "hardinfo.h"
+#include "computer.h"
+
+UptimeInfo *
+computer_get_uptime(void)
+{
+ UptimeInfo *ui = g_new0(UptimeInfo, 1);
+ FILE *procuptime;
+ gulong minutes;
+
+ if ((procuptime = fopen("/proc/uptime", "r")) != NULL) {
+ (void)fscanf(procuptime, "%lu", &minutes);
+ ui->minutes = minutes / 60;
+ fclose(procuptime);
+ } else {
+ g_free(ui);
+ return NULL;
+ }
+
+ ui->hours = ui->minutes / 60;
+ ui->minutes %= 60;
+ ui->days = ui->hours / 24;
+ ui->hours %= 24;
+
+ return ui;
+}
+
+gchar *
+computer_get_formatted_uptime()
+{
+ UptimeInfo *ui;
+ const gchar *days_fmt, *hours_fmt, *minutes_fmt;
+ gchar *full_fmt = NULL, *ret = NULL;
+
+ ui = computer_get_uptime();
+
+ days_fmt = ngettext("%d day", "%d days", ui->days);
+ hours_fmt = ngettext("%d hour", "%d hours", ui->hours);
+ minutes_fmt = ngettext("%d minute", "%d minutes", ui->minutes);
+
+ if (ui->days < 1) {
+ if (ui->hours < 1) {
+ ret = g_strdup_printf(minutes_fmt, ui->minutes);
+ } else {
+ full_fmt = g_strdup_printf("%s %s", hours_fmt, minutes_fmt);
+ ret = g_strdup_printf(full_fmt, ui->hours, ui->minutes);
+ }
+ } else {
+ full_fmt = g_strdup_printf("%s %s %s", days_fmt, hours_fmt, minutes_fmt);
+ ret = g_strdup_printf(full_fmt, ui->days, ui->hours, ui->minutes);
+ }
+ g_free(full_fmt);
+ g_free(ui);
+ return ret;
+}
diff --git a/modules/computer/users.c b/modules/computer/users.c
new file mode 100644
index 00000000..75e24f26
--- /dev/null
+++ b/modules/computer/users.c
@@ -0,0 +1,61 @@
+/*
+ * HardInfo - Displays System Information
+ * Copyright (C) 2003-2009 L. A. F. Pereira <l@tia.mat.br>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, version 2 or later.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#include <pwd.h>
+#include "hardinfo.h"
+#include "computer.h"
+
+gchar *users = NULL;
+
+void
+scan_users_do(void)
+{
+ struct passwd *passwd_;
+ passwd_ = getpwent();
+ if (!passwd_)
+ return;
+
+ if (users) {
+ g_free(users);
+ moreinfo_del_with_prefix("COMP:USER");
+ }
+
+ users = g_strdup("");
+
+ while (passwd_) {
+ gchar *key = g_strdup_printf("USER%s", passwd_->pw_name);
+ gchar *val = g_strdup_printf("[%s]\n"
+ "%s=%d\n"
+ "%s=%d\n"
+ "%s=%s\n"
+ "%s=%s\n",
+ _("User Information"),
+ _("User ID"), (gint) passwd_->pw_uid,
+ _("Group ID"), (gint) passwd_->pw_gid,
+ _("Home Directory"), passwd_->pw_dir,
+ _("Default Shell"), passwd_->pw_shell);
+ moreinfo_add_with_prefix("COMP", key, val);
+
+ strend(passwd_->pw_gecos, ',');
+ users = h_strdup_cprintf("$%s$%s=%s\n", users, key, passwd_->pw_name, passwd_->pw_gecos);
+ passwd_ = getpwent();
+ g_free(key);
+ }
+
+ endpwent();
+}
diff --git a/modules/devices.c b/modules/devices.c
new file mode 100644
index 00000000..574896ee
--- /dev/null
+++ b/modules/devices.c
@@ -0,0 +1,946 @@
+/*
+ * HardInfo - Displays System Information
+ * Copyright (C) 2003-2007 L. A. F. Pereira <l@tia.mat.br>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, version 2 or later.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#ifndef __USE_XOPEN
+#define __USE_XOPEN
+#endif /* __USE_XOPEN */
+
+#include <gtk/gtk.h>
+#include <config.h>
+#include <string.h>
+
+#include <hardinfo.h>
+#include <shell.h>
+#include <iconcache.h>
+#include <syncmanager.h>
+
+#include <expr.h>
+#include <socket.h>
+
+#include "devices.h"
+#include "dt_util.h"
+#include "udisks2_util.h"
+#include "storage_util.h"
+#include "pci_util.h"
+
+gchar *callback_processors();
+gchar *callback_gpu();
+gchar *callback_monitors();
+gchar *callback_battery();
+gchar *callback_pci();
+gchar *callback_sensors();
+gchar *callback_printers();
+gchar *callback_storage();
+gchar *callback_input();
+gchar *callback_usb();
+gchar *callback_dmi();
+gchar *callback_dmi_mem();
+gchar *callback_firmware();
+gchar *callback_dtree();
+gchar *callback_device_resources();
+
+void scan_processors(gboolean reload);
+void scan_gpu(gboolean reload);
+void scan_monitors(gboolean reload);
+void scan_battery(gboolean reload);
+void scan_pci(gboolean reload);
+void scan_sensors(gboolean reload);
+void scan_printers(gboolean reload);
+void scan_storage(gboolean reload);
+void scan_input(gboolean reload);
+void scan_usb(gboolean reload);
+void scan_dmi(gboolean reload);
+void scan_dmi_mem(gboolean reload);
+void scan_firmware(gboolean reload);
+void scan_dtree(gboolean reload);
+void scan_device_resources(gboolean reload);
+
+gboolean root_required_for_resources(void);
+gboolean spd_decode_show_hinote(const char**);
+
+gchar *hi_more_info(gchar *entry);
+
+enum {
+ ENTRY_DTREE,
+ ENTRY_DMI,
+ ENTRY_PROCESSOR,
+ ENTRY_GPU,
+ ENTRY_MONITORS,
+ ENTRY_DMI_MEM,
+ ENTRY_PCI,
+ ENTRY_USB,
+ ENTRY_FW,
+ ENTRY_PRINTERS,
+ ENTRY_BATTERY,
+ ENTRY_SENSORS,
+ ENTRY_INPUT,
+ ENTRY_STORAGE,
+ ENTRY_RESOURCES
+};
+
+static ModuleEntry entries[] = {
+ [ENTRY_PROCESSOR] = {N_("Processor"), "processor.png", callback_processors, scan_processors, MODULE_FLAG_NONE},
+ [ENTRY_GPU] = {N_("Graphics Processors"), "devices.png", callback_gpu, scan_gpu, MODULE_FLAG_NONE},
+ [ENTRY_MONITORS] = {N_("Monitors"), "monitor.png", callback_monitors, scan_monitors, MODULE_FLAG_NONE},
+ [ENTRY_PCI] = {N_("PCI Devices"), "devices.png", callback_pci, scan_pci, MODULE_FLAG_NONE},
+ [ENTRY_USB] = {N_("USB Devices"), "usb.png", callback_usb, scan_usb, MODULE_FLAG_NONE},
+ [ENTRY_FW] = {N_("Firmware"), "processor.png", callback_firmware, scan_firmware, MODULE_FLAG_NONE},
+ [ENTRY_PRINTERS] = {N_("Printers"), "printer.png", callback_printers, scan_printers, MODULE_FLAG_NONE},
+ [ENTRY_BATTERY] = {N_("Battery"), "battery.png", callback_battery, scan_battery, MODULE_FLAG_NONE},
+ [ENTRY_SENSORS] = {N_("Sensors"), "therm.png", callback_sensors, scan_sensors, MODULE_FLAG_NONE},
+ [ENTRY_INPUT] = {N_("Input Devices"), "inputdevices.png", callback_input, scan_input, MODULE_FLAG_NONE},
+ [ENTRY_STORAGE] = {N_("Storage"), "hdd.png", callback_storage, scan_storage, MODULE_FLAG_NONE},
+ [ENTRY_DMI] = {N_("System DMI"), "computer.png", callback_dmi, scan_dmi, MODULE_FLAG_NONE},
+ [ENTRY_DMI_MEM] = {N_("Memory Devices"), "memory.png", callback_dmi_mem, scan_dmi_mem, MODULE_FLAG_NONE},
+#if defined(ARCH_x86) || defined(ARCH_x86_64)
+ [ENTRY_DTREE] = {N_("Device Tree"), "devices.png", callback_dtree, scan_dtree, MODULE_FLAG_HIDE},
+#else
+ [ENTRY_DTREE] = {N_("Device Tree"), "devices.png", callback_dtree, scan_dtree, MODULE_FLAG_NONE},
+#endif /* x86 or x86_64 */
+ [ENTRY_RESOURCES] = {N_("Resources"), "resources.png", callback_device_resources, scan_device_resources, MODULE_FLAG_NONE},
+ { NULL }
+};
+
+static GSList *processors = NULL;
+gchar *printer_list = NULL;
+gchar *printer_icons = NULL;
+gchar *pci_list = NULL;
+gchar *input_list = NULL;
+gboolean storage_no_nvme = FALSE;
+gchar *storage_list = NULL;
+gchar *battery_list = NULL;
+
+/* in dmi_memory.c */
+gchar *memory_devices_get_info();
+gboolean memory_devices_hinote(const char **msg);
+gchar *memory_devices_info = NULL;
+
+/* in firmware.c */
+gchar *firmware_get_info();
+gboolean firmware_hinote(const char **msg);
+gchar *firmware_info = NULL;
+
+/* in monitors.c */
+gchar *monitors_get_info();
+gboolean monitors_hinote(const char **msg);
+gchar *monitors_info = NULL;
+
+#include <vendor.h>
+
+extern gchar *gpu_summary;
+const gchar *get_gpu_summary() {
+ if (gpu_summary == NULL)
+ scan_gpu(FALSE);
+ return gpu_summary;
+}
+
+static gint proc_cmp_model_name(Processor *a, Processor *b) {
+ return g_strcmp0(a->model_name, b->model_name);
+}
+
+static gint proc_cmp_max_freq(Processor *a, Processor *b) {
+ if (a->cpu_mhz == b->cpu_mhz)
+ return 0;
+ if (a->cpu_mhz > b->cpu_mhz)
+ return -1;
+ return 1;
+}
+
+gchar *processor_describe_default(GSList * processors)
+{
+ int packs, cores, threads, nodes;
+ const gchar *packs_fmt, *cores_fmt, *threads_fmt, *nodes_fmt;
+ gchar *ret, *full_fmt;
+
+ cpu_procs_cores_threads_nodes(&packs, &cores, &threads, &nodes);
+
+ /* NOTE: If this is changed, look at get_cpu_desc() in bench_results.c! */
+
+ /* if topology info was available, else fallback to old method */
+ if (cores > 0) {
+ packs_fmt = ngettext("%d physical processor", "%d physical processors", packs);
+ cores_fmt = ngettext("%d core", "%d cores", cores);
+ threads_fmt = ngettext("%d thread", "%d threads", threads);
+ if (nodes > 1) {
+ nodes_fmt = ngettext("%d NUMA node", "%d NUMA nodes", nodes);
+ full_fmt = g_strdup_printf(_(/*/NP procs; NC cores across NN nodes; NT threads*/ "%s; %s across %s; %s"), packs_fmt, cores_fmt, nodes_fmt, threads_fmt);
+ ret = g_strdup_printf(full_fmt, packs, cores * nodes, nodes, threads);
+ } else {
+ full_fmt = g_strdup_printf(_(/*/NP procs; NC cores; NT threads*/ "%s; %s; %s"), packs_fmt, cores_fmt, threads_fmt);
+ ret = g_strdup_printf(full_fmt, packs, cores, threads);
+ }
+ g_free(full_fmt);
+ return ret;
+ } else {
+ return processor_describe_by_counting_names(processors);
+ }
+}
+
+gchar *processor_name_default(GSList * processors)
+{
+ gchar *ret = g_strdup("");
+ GSList *tmp, *l;
+ Processor *p;
+ gchar *cur_str = NULL;
+ gint cur_count = 0;
+
+ tmp = g_slist_copy(processors);
+ tmp = g_slist_sort(tmp, (GCompareFunc)proc_cmp_model_name);
+
+ for (l = tmp; l; l = l->next) {
+ p = (Processor*)l->data;
+ if (cur_str == NULL) {
+ cur_str = p->model_name;
+ cur_count = 1;
+ } else {
+ if(g_strcmp0(cur_str, p->model_name)) {
+ ret = h_strdup_cprintf("%s%s", ret, strlen(ret) ? "; " : "", cur_str);
+ cur_str = p->model_name;
+ cur_count = 1;
+ } else {
+ cur_count++;
+ }
+ }
+ }
+ ret = h_strdup_cprintf("%s%s", ret, strlen(ret) ? "; " : "", cur_str);
+ g_slist_free(tmp);
+ return ret;
+}
+
+/* TODO: prefix counts are threads when they should be cores. */
+gchar *processor_describe_by_counting_names(GSList * processors)
+{
+ gchar *ret = g_strdup("");
+ GSList *tmp, *l;
+ Processor *p;
+ gchar *cur_str = NULL;
+ gint cur_count = 0;
+
+ tmp = g_slist_copy(processors);
+ tmp = g_slist_sort(tmp, (GCompareFunc)proc_cmp_model_name);
+
+ for (l = tmp; l; l = l->next) {
+ p = (Processor*)l->data;
+ if (cur_str == NULL) {
+ cur_str = p->model_name;
+ cur_count = 1;
+ } else {
+ if(g_strcmp0(cur_str, p->model_name)) {
+ ret = h_strdup_cprintf("%s%dx %s", ret, strlen(ret) ? " + " : "", cur_count, cur_str);
+ cur_str = p->model_name;
+ cur_count = 1;
+ } else {
+ cur_count++;
+ }
+ }
+ }
+ ret = h_strdup_cprintf("%s%dx %s", ret, strlen(ret) ? " + " : "", cur_count, cur_str);
+ g_slist_free(tmp);
+ return ret;
+}
+
+gchar *get_processor_name(void)
+{
+ scan_processors(FALSE);
+ return processor_name(processors);
+}
+
+gchar *get_processor_desc(void)
+{
+ scan_processors(FALSE);
+ return processor_describe(processors);
+}
+
+gchar *get_processor_name_and_desc(void)
+{
+ scan_processors(FALSE);
+ gchar* name = processor_name(processors);
+ gchar* desc = processor_describe(processors);
+ gchar* nd = g_strdup_printf("%s\n%s", name, desc);
+ g_free(name);
+ g_free(desc);
+ return nd;
+}
+
+gchar *get_storage_devices_simple(void)
+{
+ scan_storage(FALSE);
+
+ struct Info *info = info_unflatten(storage_list);
+ if (!info) {
+ return "";
+ }
+
+ guint i, fi;
+ struct InfoGroup *group;
+ struct InfoField *field;
+ gchar *storage_devs = NULL, *tmp;
+ const gchar *dev_label, *model_wo_tags;
+
+ GRegex *regex;
+ regex = g_regex_new ("<.*>", 0, 0, NULL);
+
+ for (i = 0; i < info->groups->len; i++) {
+ group = &g_array_index(info->groups, struct InfoGroup, info->groups->len - 1);
+ if (!group)
+ continue;
+
+ info_group_strip_extra(group);
+ for (fi = 0; fi < group->fields->len; fi++) {
+ field = &g_array_index(group->fields, struct InfoField, fi);
+ if (!field->value)
+ continue;
+
+ tmp = g_regex_replace(regex, field->value, -1, 0, "", 0, NULL); // remove html tags
+ storage_devs = h_strdup_cprintf("%s\n", storage_devs, g_strchug(tmp));
+ g_free(tmp);
+ }
+ }
+ g_free(info);
+
+ return storage_devs;
+}
+
+gchar *get_storage_devices(void)
+{
+ scan_storage(FALSE);
+
+ return storage_list;
+}
+
+gchar *get_printers(void)
+{
+ scan_printers(FALSE);
+
+ return printer_list;
+}
+
+gchar *get_input_devices(void)
+{
+ scan_input(FALSE);
+
+ return input_list;
+}
+
+gchar *get_processor_count(void)
+{
+ scan_processors(FALSE);
+
+ return g_strdup_printf("%d", g_slist_length(processors));
+}
+
+/* TODO: maybe move into processor.c along with processor_name() etc.
+ * Could mention the big.LITTLE cluster arangement for ARM that kind of thing.
+ * TODO: prefix counts are threads when they should be cores. */
+gchar *processor_frequency_desc(GSList * processors)
+{
+ gchar *ret = g_strdup("");
+ GSList *tmp, *l;
+ Processor *p;
+ float cur_val = -1;
+ gint cur_count = 0;
+
+ tmp = g_slist_copy(processors);
+ tmp = g_slist_sort(tmp, (GCompareFunc)proc_cmp_max_freq);
+
+ for (l = tmp; l; l = l->next) {
+ p = (Processor*)l->data;
+ if (cur_val == -1) {
+ cur_val = p->cpu_mhz;
+ cur_count = 1;
+ } else {
+ if(cur_val != p->cpu_mhz) {
+ ret = h_strdup_cprintf("%s%dx %.2f %s", ret, strlen(ret) ? " + " : "", cur_count, cur_val, _("MHz") );
+ cur_val = p->cpu_mhz;
+ cur_count = 1;
+ } else {
+ cur_count++;
+ }
+ }
+ }
+ ret = h_strdup_cprintf("%s%dx %.2f %s", ret, strlen(ret) ? " + " : "", cur_count, cur_val, _("MHz"));
+ g_slist_free(tmp);
+ return ret;
+}
+
+gchar *get_processor_frequency_desc(void)
+{
+ scan_processors(FALSE);
+ return processor_frequency_desc(processors);
+}
+
+gchar *get_processor_max_frequency(void)
+{
+ GSList *l;
+ Processor *p;
+ float max_freq = 0;
+
+ scan_processors(FALSE);
+
+ for (l = processors; l; l = l->next) {
+ p = (Processor*)l->data;
+ if (p->cpu_mhz > max_freq)
+ max_freq = p->cpu_mhz;
+ }
+
+ if (max_freq == 0.0f) {
+ return g_strdup(N_("Unknown"));
+ } else {
+ return g_strdup_printf("%.2f %s", max_freq, _("MHz") );
+ }
+}
+
+gchar *get_motherboard(void)
+{
+ gchar *board_name, *board_vendor, *board_version;
+ gchar *product_name, *product_vendor, *product_version;
+ gchar *board_part = NULL, *product_part = NULL;
+ const gchar *tmp;
+ int b = 0, p = 0;
+
+ gchar *ret;
+
+#if defined(ARCH_x86) || defined(ARCH_x86_64)
+ scan_dmi(FALSE);
+
+ board_name = dmi_get_str("baseboard-product-name");
+ board_version = dmi_get_str("baseboard-version");
+ board_vendor = dmi_get_str("baseboard-manufacturer");
+ if (board_vendor) {
+ /* attempt to shorten */
+ tmp = vendor_get_shortest_name(board_vendor);
+ if (tmp && tmp != board_vendor) {
+ g_free(board_vendor);
+ board_vendor = g_strdup(tmp);
+ }
+ }
+
+ product_name = dmi_get_str("system-product-name");
+ product_version = dmi_get_str("system-version");
+ product_vendor = dmi_get_str("system-manufacturer");
+ if (product_vendor) {
+ /* attempt to shorten */
+ tmp = vendor_get_shortest_name(product_vendor);
+ if (tmp && tmp != product_vendor) {
+ g_free(product_vendor);
+ product_vendor = g_strdup(tmp);
+ }
+ }
+
+ if (board_vendor && product_vendor &&
+ strcmp(board_vendor, product_vendor) == 0) {
+ /* ignore duplicate vendor */
+ g_free(product_vendor);
+ product_vendor = NULL;
+ }
+
+ if (board_name && product_name &&
+ strcmp(board_name, product_name) == 0) {
+ /* ignore duplicate name */
+ g_free(product_name);
+ product_name = NULL;
+ }
+
+ if (board_version && product_version &&
+ strcmp(board_version, product_version) == 0) {
+ /* ignore duplicate version */
+ g_free(product_version);
+ product_version = NULL;
+ }
+
+ if (board_name) b += 1;
+ if (board_vendor) b += 2;
+ if (board_version) b += 4;
+
+ switch(b) {
+ case 1: /* only name */
+ board_part = g_strdup(board_name);
+ break;
+ case 2: /* only vendor */
+ board_part = g_strdup(board_vendor);
+ break;
+ case 3: /* only name and vendor */
+ board_part = g_strdup_printf("%s %s", board_vendor, board_name);
+ break;
+ case 4: /* only version? Seems unlikely */
+ board_part = g_strdup(board_version);
+ break;
+ case 5: /* only name and version? */
+ board_part = g_strdup_printf("%s %s", board_name, board_version);
+ break;
+ case 6: /* only vendor and version (like lpereira's Thinkpad) */
+ board_part = g_strdup_printf("%s %s", board_vendor, board_version);
+ break;
+ case 7: /* all */
+ board_part = g_strdup_printf("%s %s %s", board_vendor, board_name, board_version);
+ break;
+ }
+
+ if (product_name) p += 1;
+ if (product_vendor) p += 2;
+ if (product_version) p += 4;
+
+ switch(p) {
+ case 1: /* only name */
+ product_part = g_strdup(product_name);
+ break;
+ case 2: /* only vendor */
+ product_part = g_strdup(product_vendor);
+ break;
+ case 3: /* only name and vendor */
+ product_part = g_strdup_printf("%s %s", product_vendor, product_name);
+ break;
+ case 4: /* only version? Seems unlikely */
+ product_part = g_strdup(product_version);
+ break;
+ case 5: /* only name and version? */
+ product_part = g_strdup_printf("%s %s", product_name, product_version);
+ break;
+ case 6: /* only vendor and version? */
+ product_part = g_strdup_printf("%s %s", product_vendor, product_version);
+ break;
+ case 7: /* all */
+ product_part = g_strdup_printf("%s %s %s", product_vendor, product_name, product_version);
+ break;
+ }
+
+ if (board_part && product_part) {
+ ret = g_strdup_printf("%s (%s)", board_part, product_part);
+ g_free(board_part);
+ g_free(product_part);
+ } else if (board_part)
+ ret = board_part;
+ else if (product_part)
+ ret = product_part;
+ else
+ ret = g_strdup(_("(Unknown)"));
+
+ g_free(board_name);
+ g_free(board_vendor);
+ g_free(board_version);
+ g_free(product_name);
+ g_free(product_vendor);
+ g_free(product_version);
+
+ return ret;
+#endif
+
+ /* use device tree "model" */
+ board_vendor = dtr_get_string("/model", 0);
+ if (board_vendor != NULL)
+ return board_vendor;
+
+ return g_strdup(_("Unknown"));
+}
+
+const ShellModuleMethod *hi_exported_methods(void)
+{
+ static const ShellModuleMethod m[] = {
+ {"getProcessorCount", get_processor_count},
+ {"getProcessorName", get_processor_name},
+ {"getProcessorDesc", get_processor_desc},
+ {"getProcessorNameAndDesc", get_processor_name_and_desc},
+ {"getProcessorFrequency", get_processor_max_frequency},
+ {"getProcessorFrequencyDesc", get_processor_frequency_desc},
+ {"getStorageDevices", get_storage_devices},
+ {"getStorageDevicesSimple", get_storage_devices_simple},
+ {"getPrinters", get_printers},
+ {"getInputDevices", get_input_devices},
+ {"getMotherboard", get_motherboard},
+ {"getGPUList", get_gpu_summary},
+ {NULL},
+ };
+
+ return m;
+}
+
+gchar *hi_more_info(gchar * entry)
+{
+ gchar *info = moreinfo_lookup_with_prefix("DEV", entry);
+
+ if (info)
+ return g_strdup(info);
+
+ return g_strdup("?");
+}
+
+gchar *hi_get_field(gchar * field)
+{
+ gchar *info = moreinfo_lookup_with_prefix("DEV", field);
+ if (info)
+ return g_strdup(info);
+
+ return g_strdup(field);
+}
+
+void scan_dmi(gboolean reload)
+{
+ SCAN_START();
+ __scan_dmi();
+ SCAN_END();
+}
+
+void scan_dmi_mem(gboolean reload)
+{
+ SCAN_START();
+ if (memory_devices_info)
+ g_free(memory_devices_info);
+ memory_devices_info = memory_devices_get_info();
+ SCAN_END();
+}
+
+void scan_monitors(gboolean reload)
+{
+ SCAN_START();
+ if (monitors_info)
+ g_free(monitors_info);
+ monitors_info = monitors_get_info();
+ SCAN_END();
+}
+
+void scan_firmware(gboolean reload)
+{
+ SCAN_START();
+ if (firmware_info)
+ g_free(firmware_info);
+ firmware_info = firmware_get_info();
+ SCAN_END();
+}
+
+void scan_dtree(gboolean reload)
+{
+ SCAN_START();
+ __scan_dtree();
+ SCAN_END();
+}
+
+void scan_processors(gboolean reload)
+{
+ SCAN_START();
+ if (!processors)
+ processors = processor_scan();
+ SCAN_END();
+}
+
+void scan_battery(gboolean reload)
+{
+ SCAN_START();
+ scan_battery_do();
+ SCAN_END();
+}
+
+void scan_gpu(gboolean reload)
+{
+ SCAN_START();
+ scan_gpu_do();
+ SCAN_END();
+}
+
+void scan_pci(gboolean reload)
+{
+ SCAN_START();
+ scan_pci_do();
+ SCAN_END();
+}
+
+void scan_sensors(gboolean reload)
+{
+ SCAN_START();
+ scan_sensors_do();
+ SCAN_END();
+}
+
+void scan_printers(gboolean reload)
+{
+ SCAN_START();
+ scan_printers_do();
+ SCAN_END();
+}
+
+void scan_storage(gboolean reload)
+{
+ SCAN_START();
+ g_free(storage_list);
+ storage_list = g_strdup("");
+ storage_no_nvme = FALSE;
+ if (!__scan_udisks2_devices()) {
+ storage_no_nvme = TRUE;
+ __scan_ide_devices();
+ __scan_scsi_devices();
+ }
+ SCAN_END();
+}
+
+void scan_input(gboolean reload)
+{
+ SCAN_START();
+ __scan_input_devices();
+ SCAN_END();
+}
+
+void scan_usb(gboolean reload)
+{
+ SCAN_START();
+ __scan_usb();
+ SCAN_END();
+}
+
+gchar *callback_processors()
+{
+ return processor_get_info(processors);
+}
+
+gchar *callback_dmi()
+{
+ return g_strdup_printf("%s"
+ "[$ShellParam$]\n"
+ "ViewType=5\n",
+ dmi_info);
+}
+
+gchar *callback_dmi_mem()
+{
+ return g_strdup(memory_devices_info);
+}
+
+gchar *callback_monitors()
+{
+ return g_strdup(monitors_info);
+}
+
+gchar *callback_firmware()
+{
+ return g_strdup(firmware_info);
+}
+
+gchar *callback_dtree()
+{
+ return g_strdup_printf("%s"
+ "[$ShellParam$]\n"
+ "ViewType=1\n", dtree_info);
+}
+
+gchar *callback_battery()
+{
+ return g_strdup_printf("%s\n"
+ "[$ShellParam$]\n"
+ "ViewType=5\n"
+ "ReloadInterval=4000\n", battery_list);
+}
+
+gchar *callback_pci()
+{
+ return g_strdup(pci_list);
+}
+
+gchar *callback_gpu()
+{
+ return g_strdup(gpu_list);
+}
+
+gchar *callback_sensors()
+{
+ return g_strdup_printf("%s\n"
+ "[$ShellParam$]\n"
+ "ViewType=2\n"
+ "LoadGraphSuffix=\n"
+ "ColumnTitle$TextValue=%s\n"
+ "ColumnTitle$Value=%s\n"
+ "ColumnTitle$Extra1=%s\n"
+ "ShowColumnHeaders=true\n"
+ "RescanInterval=5000\n"
+ "%s\n"
+ "%s",
+ sensors,
+ _("Sensor"), _("Value"),
+ SENSORS_GROUP_BY_TYPE ? _("Driver"): _("Type"),
+ lginterval,
+ sensor_icons);
+}
+
+gchar *callback_printers()
+{
+ return g_strdup_printf("%s\n"
+ "[$ShellParam$]\n"
+ "ViewType=1\n"
+ "ReloadInterval=5000\n"
+ "%s", printer_list, printer_icons);
+}
+
+gchar *callback_storage()
+{
+ return g_strdup_printf("%s\n"
+ "[$ShellParam$]\n"
+ "ReloadInterval=5000\n"
+ "ColumnTitle$TextValue=%s\n"
+ "ColumnTitle$Value=%s\n"
+ "ColumnTitle$Extra1=%s\n"
+ "ShowColumnHeaders=true\n"
+ "ViewType=1\n%s", storage_list, _("Device"), _("Size"), _("Model"), storage_icons);
+}
+
+gchar *callback_input()
+{
+ return g_strdup_printf("[Input Devices]\n"
+ "%s"
+ "[$ShellParam$]\n"
+ "ViewType=1\n"
+ "ColumnTitle$TextValue=%s\n"
+ "ColumnTitle$Value=%s\n"
+ "ColumnTitle$Extra1=%s\n"
+ "ShowColumnHeaders=true\n"
+ "ReloadInterval=5000\n%s",
+ input_list, _("Device"), _("Vendor"), _("Type"),
+ input_icons);
+}
+
+gchar *callback_usb()
+{
+ return g_strdup_printf("%s"
+ "[$ShellParam$]\n"
+ "ViewType=1\n"
+ "ReloadInterval=5000\n%s", usb_list, usb_icons);
+
+}
+
+ModuleEntry *hi_module_get_entries(void)
+{
+ return entries;
+}
+
+gchar *hi_module_get_name(void)
+{
+ return g_strdup(_("Devices"));
+}
+
+guchar hi_module_get_weight(void)
+{
+ return 85;
+}
+
+void hi_module_init(void)
+{
+ static SyncEntry entries[] = {
+ {
+ .name = N_("Update PCI ID listing"),
+ .file_name = "pci.ids",
+ },
+ {
+ .name = N_("Update USB ID listing"),
+ .file_name = "usb.ids",
+ },
+ {
+ .name = N_("Update EDID vendor codes"),
+ .file_name = "edid.ids",
+ },
+ {
+ .name = N_("Update IEEE OUI vendor codes"),
+ .file_name = "ieee_oui.ids",
+ },
+ {
+ .name = N_("Update SD card manufacturer information"),
+ .file_name = "sdcard.ids",
+ },
+#ifdef ARCH_x86
+ {
+ .name = N_("Update CPU flags database"),
+ .file_name = "cpuflags.json",
+ },
+#endif
+ };
+ guint i;
+
+ for (i = 0; i < G_N_ELEMENTS(entries); i++)
+ sync_manager_add_entry(&entries[i]);
+
+ init_cups();
+ sensor_init();
+ udisks2_init();
+
+#ifdef ARCH_x86
+ void cpuflags_x86_init(void);
+ cpuflags_x86_init();
+#endif
+}
+
+void hi_module_deinit(void)
+{
+ moreinfo_del_with_prefix("DEV");
+ sensor_shutdown();
+ storage_shutdown();
+ udisks2_shutdown();
+ if(cups) g_module_close(cups);
+}
+
+const ModuleAbout *hi_module_get_about(void)
+{
+ static const ModuleAbout ma = {
+ .author = "L. A. F. Pereira",
+ .description = N_("Gathers information about hardware devices"),
+ .version = VERSION,
+ .license = "GNU GPL version 2 or later.",
+ };
+
+ return &ma;
+}
+
+gchar **hi_module_get_dependencies(void)
+{
+ static gchar *deps[] = { "computer.so", NULL };
+
+ return deps;
+}
+
+const gchar *hi_note_func(gint entry)
+{
+ if (entry == ENTRY_PCI
+ || entry == ENTRY_GPU) {
+ const gchar *ids = find_pci_ids_file();
+ if (!ids) {
+ return g_strdup(_("A copy of <i><b>pci.ids</b></i> is not available on the system."));
+ }
+ if (ids && strstr(ids, ".min")) {
+ return g_strdup(_("A full <i><b>pci.ids</b></i> is not available on the system."));
+ }
+ }
+ if (entry == ENTRY_RESOURCES) {
+ if (root_required_for_resources()) {
+ return g_strdup(_("Resource information requires superuser privileges"));
+ }
+ }
+ else if (entry == ENTRY_STORAGE){
+ if (storage_no_nvme) {
+ return g_strdup(
+ _("Any NVMe storage devices present are not listed.\n"
+ "<b><i>udisksd</i></b> is required for NVMe devices."));
+ }
+ }
+ else if (entry == ENTRY_DMI_MEM){
+ const char *msg;
+ if (memory_devices_hinote(&msg)) {
+ return msg;
+ }
+ }
+ else if (entry == ENTRY_FW) {
+ const char *msg;
+ if (firmware_hinote(&msg)) {
+ return msg;
+ }
+ }
+ return NULL;
+}
diff --git a/modules/devices/alpha/processor.c b/modules/devices/alpha/processor.c
new file mode 100644
index 00000000..0e625316
--- /dev/null
+++ b/modules/devices/alpha/processor.c
@@ -0,0 +1,94 @@
+/*
+ * HardInfo - Displays System Information
+ * Copyright (C) 2003-2006 L. A. F. Pereira <l@tia.mat.br>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, version 2 or later.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#include "hardinfo.h"
+#include "devices.h"
+#include "cpu_util.h"
+
+GSList *
+processor_scan(void)
+{
+ Processor *processor;
+ FILE *cpuinfo;
+ gchar buffer[128];
+ long long hz = 0;
+
+ cpuinfo = fopen(PROC_CPUINFO, "r");
+ if (!cpuinfo)
+ return NULL;
+
+ processor = g_new0(Processor, 1);
+ while (fgets(buffer, 128, cpuinfo)) {
+ gchar **tmp = g_strsplit(buffer, ":", 2);
+
+ if (tmp[0] && tmp[1]) {
+ tmp[0] = g_strstrip(tmp[0]);
+ tmp[1] = g_strstrip(tmp[1]);
+
+ get_str("cpu model", processor->model_name);
+ get_float("BogoMIPS", processor->bogomips);
+ get_str("platform string", processor->strmodel);
+ get_str("cycle frequency [Hz]", processor->cycle_frequency_hz_str);
+
+ }
+ g_strfreev(tmp);
+ }
+
+ fclose(cpuinfo);
+
+ gchar *tmp = g_strconcat("Alpha ", processor->model_name, NULL);
+ g_free(processor->model_name);
+ processor->model_name = tmp;
+
+ if (processor->cycle_frequency_hz_str) {
+ hz = atoll(processor->cycle_frequency_hz_str);
+ processor->cpu_mhz = hz;
+ processor->cpu_mhz /= 1000000;
+ } else
+ processor->cpu_mhz = 0.0f;
+
+ return g_slist_append(NULL, processor);
+}
+
+gchar *processor_name(GSList * processors) {
+ return processor_name_default(processors);
+}
+
+gchar *processor_describe(GSList * processors) {
+ return processor_describe_default(processors);
+}
+
+gchar *
+processor_get_info(GSList *processors)
+{
+ Processor *processor = (Processor *)processors->data;
+
+ return g_strdup_printf("[%s]\n"
+ "%s=%s\n"
+ "%s=%s\n"
+ "%s=%.2f %s\n" /* frequency */
+ "%s=%.2f\n" /* bogomips */
+ "%s=%s\n", /* byte order */
+ _("Processor"),
+ _("Model"), processor->model_name,
+ _("Platform String"), processor->strmodel,
+ _("Frequency"), processor->cpu_mhz, _("MHz"),
+ _("BogoMips"), processor->bogomips,
+ _("Byte Order"), byte_order_str()
+ );
+}
diff --git a/modules/devices/arm/arm_data.c b/modules/devices/arm/arm_data.c
new file mode 100644
index 00000000..aece272f
--- /dev/null
+++ b/modules/devices/arm/arm_data.c
@@ -0,0 +1,235 @@
+/*
+ * rpiz - https://github.com/bp0/rpiz
+ * 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; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include "arm_data.h"
+
+#ifndef C_
+#define C_(Ctx, String) String
+#endif
+#ifndef NC_
+#define NC_(Ctx, String) String
+#endif
+
+/* sources:
+ * https://unix.stackexchange.com/a/43563
+ * git:linux/arch/arm/kernel/setup.c
+ * git:linux/arch/arm64/kernel/cpuinfo.c
+ */
+static struct {
+ char *name, *meaning;
+} tab_flag_meaning[] = {
+ /* arm/hw_cap */
+ { "swp", NC_("arm-flag", /*/flag:swp*/ "SWP instruction (atomic read-modify-write)") },
+ { "half", NC_("arm-flag", /*/flag:half*/ "Half-word loads and stores") },
+ { "thumb", NC_("arm-flag", /*/flag:thumb*/ "Thumb (16-bit instruction set)") },
+ { "26bit", NC_("arm-flag", /*/flag:26bit*/ "26-Bit Model (Processor status register folded into program counter)") },
+ { "fastmult", NC_("arm-flag", /*/flag:fastmult*/ "32x32->64-bit multiplication") },
+ { "fpa", NC_("arm-flag", /*/flag:fpa*/ "Floating point accelerator") },
+ { "vfp", NC_("arm-flag", /*/flag:vfp*/ "VFP (early SIMD vector floating point instructions)") },
+ { "edsp", NC_("arm-flag", /*/flag:edsp*/ "DSP extensions (the 'e' variant of the ARM9 CPUs, and all others above)") },
+ { "java", NC_("arm-flag", /*/flag:java*/ "Jazelle (Java bytecode accelerator)") },
+ { "iwmmxt", NC_("arm-flag", /*/flag:iwmmxt*/ "SIMD instructions similar to Intel MMX") },
+ { "crunch", NC_("arm-flag", /*/flag:crunch*/ "MaverickCrunch coprocessor (if kernel support enabled)") },
+ { "thumbee", NC_("arm-flag", /*/flag:thumbee*/ "ThumbEE") },
+ { "neon", NC_("arm-flag", /*/flag:neon*/ "Advanced SIMD/NEON on AArch32") },
+ { "evtstrm", NC_("arm-flag", /*/flag:evtstrm*/ "Kernel event stream using generic architected timer") },
+ { "vfpv3", NC_("arm-flag", /*/flag:vfpv3*/ "VFP version 3") },
+ { "vfpv3d16", NC_("arm-flag", /*/flag:vfpv3d16*/ "VFP version 3 with 16 D-registers") },
+ { "vfpv4", NC_("arm-flag", /*/flag:vfpv4*/ "VFP version 4 with fast context switching") },
+ { "vfpd32", NC_("arm-flag", /*/flag:vfpd32*/ "VFP with 32 D-registers") },
+ { "tls", NC_("arm-flag", /*/flag:tls*/ "TLS register") },
+ { "idiva", NC_("arm-flag", /*/flag:idiva*/ "SDIV and UDIV hardware division in ARM mode") },
+ { "idivt", NC_("arm-flag", /*/flag:idivt*/ "SDIV and UDIV hardware division in Thumb mode") },
+ { "lpae", NC_("arm-flag", /*/flag:lpae*/ "40-bit Large Physical Address Extension") },
+ /* arm/hw_cap2 */
+ { "pmull", NC_("arm-flag", /*/flag:pmull*/ "64x64->128-bit F2m multiplication (arch>8)") },
+ { "aes", NC_("arm-flag", /*/flag:aes*/ "Crypto:AES (arch>8)") },
+ { "sha1", NC_("arm-flag", /*/flag:sha1*/ "Crypto:SHA1 (arch>8)") },
+ { "sha2", NC_("arm-flag", /*/flag:sha2*/ "Crypto:SHA2 (arch>8)") },
+ { "crc32", NC_("arm-flag", /*/flag:crc32*/ "CRC32 checksum instructions (arch>8)") },
+ /* arm64/hw_cap */
+ { "fp", NULL },
+ { "asimd", NC_("arm-flag", /*/flag:asimd*/ "Advanced SIMD/NEON on AArch64 (arch>8)") },
+ { "atomics", NULL },
+ { "fphp", NULL },
+ { "asimdhp", NULL },
+ { "cpuid", NULL },
+ { "asimdrdm", NULL },
+ { "jscvt", NULL },
+ { "fcma", NULL },
+ { "lrcpc", NULL },
+ { NULL, NULL }
+};
+
+static struct {
+ char *code; char *name; char *more;
+} tab_arm_arch[] = {
+ { "7", "AArch32", "AArch32 (ARMv7)" },
+ { "8", "AArch64", "AArch64 (ARMv8)" },
+ { "AArch32", "AArch32", "AArch32 (ARMv7)" },
+ { "AArch64", "AArch64", "AArch64 (ARMv8)" },
+ { NULL, NULL, NULL },
+};
+
+static char all_flags[1024] = "";
+
+#define APPEND_FLAG(f) strcat(all_flags, f); strcat(all_flags, " ");
+const char *arm_flag_list() {
+ int i = 0, built = 0;
+ built = strlen(all_flags);
+ if (!built) {
+ while(tab_flag_meaning[i].name != NULL) {
+ APPEND_FLAG(tab_flag_meaning[i].name);
+ i++;
+ }
+ }
+ return all_flags;
+}
+
+const char *arm_flag_meaning(const char *flag) {
+ int i = 0;
+ if (flag)
+ while(tab_flag_meaning[i].name != NULL) {
+ if (strcmp(tab_flag_meaning[i].name, flag) == 0) {
+ if (tab_flag_meaning[i].meaning != NULL)
+ return C_("arm-flag", tab_flag_meaning[i].meaning);
+ else return NULL;
+ }
+ i++;
+ }
+ return NULL;
+}
+
+#include "util_ids.h"
+
+gchar *arm_ids_file = NULL;
+
+void find_arm_ids_file() {
+ if (arm_ids_file) return;
+ char *file_search_order[] = {
+ g_build_filename(g_get_user_config_dir(), "hardinfo2", "arm.ids", NULL),
+ g_build_filename(params.path_data, "arm.ids", NULL),
+ NULL
+ };
+ int n;
+ for(n = 0; file_search_order[n]; n++) {
+ if (!arm_ids_file && !access(file_search_order[n], R_OK))
+ arm_ids_file = (gchar*) auto_free_on_exit( file_search_order[n] );
+ else
+ g_free(file_search_order[n]);
+ }
+}
+
+void arm_part(const char *imp_code, const char *part_code, char **imp, char **part) {
+ gchar *qpath = NULL;
+ ids_query_result result = {};
+ unsigned int i,p;
+
+ if (!arm_ids_file)
+ find_arm_ids_file();
+
+ i = strtol(imp_code, NULL, 0);
+ p = strtol(part_code, NULL, 0);
+ qpath = g_strdup_printf("%02x/%03x", i, p);
+ scan_ids_file(arm_ids_file, qpath, &result, -1);
+ g_free(qpath);
+ if (imp)
+ *imp = result.results[0]
+ ? g_strdup(result.results[0])
+ : NULL;
+ if (part)
+ *part = result.results[1]
+ ? g_strdup(result.results[1])
+ : NULL;
+}
+
+const char *arm_arch(const char *cpuinfo_arch_str) {
+ int i = 0;
+ if (cpuinfo_arch_str)
+ while(tab_arm_arch[i].code) {
+ if (strcmp(tab_arm_arch[i].code, cpuinfo_arch_str) == 0)
+ return tab_arm_arch[i].name;
+ i++;
+ }
+ return cpuinfo_arch_str;
+}
+
+const char *arm_arch_more(const char *cpuinfo_arch_str) {
+ int i = 0;
+ if (cpuinfo_arch_str)
+ while(tab_arm_arch[i].code) {
+ if (strcmp(tab_arm_arch[i].code, cpuinfo_arch_str) == 0)
+ return tab_arm_arch[i].more;
+ i++;
+ }
+ return cpuinfo_arch_str;
+}
+
+char *arm_decoded_name(const char *imp, const char *part, const char *var, const char *rev, const char *arch, const char *model_name) {
+ char *dnbuff;
+ char *imp_name = NULL, *part_desc = NULL, *arch_name = NULL;
+ int r = 0, p = 0;
+ dnbuff = malloc(256);
+ if (dnbuff) {
+ memset(dnbuff, 0, 256);
+
+ if (imp && arch && part && rev) {
+ /* http://infocenter.arm.com/help/index.jsp?topic=/com.arm.doc.dui0395b/CIHCAGHH.html
+ * variant and revision can be rendered r{variant}p{revision} */
+ r = strtol(var, NULL, 0);
+ p = strtol(rev, NULL, 0);
+ arm_part(imp, part, &imp_name, &part_desc);
+ arch_name = (char*) arm_arch(arch);
+ if (imp_name || part_desc) {
+ if (arch_name != arch)
+ sprintf(dnbuff, "%s %s r%dp%d (%s)",
+ (imp_name) ? imp_name : imp,
+ (part_desc) ? part_desc : part,
+ r, p, arch_name);
+ else
+ sprintf(dnbuff, "%s %s r%dp%d (arch:%s)",
+ (imp_name) ? imp_name : imp,
+ (part_desc) ? part_desc : part,
+ r, p, arch);
+ } else {
+ /* fallback for now */
+ sprintf(dnbuff, "%s [imp:%s part:%s r%dp%d arch:%s]",
+ model_name,
+ (imp_name) ? imp_name : imp,
+ (part_desc) ? part_desc : part,
+ r, p, arch);
+ }
+ g_free(imp_name);
+ g_free(part_desc);
+ } else {
+ /* prolly not ARM arch at all */
+ if (model_name)
+ sprintf(dnbuff, "%s", model_name);
+ else {
+ free(dnbuff);
+ return NULL;
+ }
+ }
+ }
+ return dnbuff;
+}
diff --git a/modules/devices/arm/arm_data.h b/modules/devices/arm/arm_data.h
new file mode 100644
index 00000000..0e93d323
--- /dev/null
+++ b/modules/devices/arm/arm_data.h
@@ -0,0 +1,39 @@
+/*
+ * rpiz - https://github.com/bp0/rpiz
+ * 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; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#ifndef _ARMDATA_H_
+#define _ARMDATA_H_
+
+/* table lookups */
+void arm_part(const char *imp_code, const char *part_code, char **imp, char **part);
+const char *arm_arch(const char *cpuinfo_arch_str);
+const char *arm_arch_more(const char *cpuinfo_arch_str);
+
+/* cpu_implementer, cpu_part, cpu_variant, cpu_revision, cpu_architecture from /proc/cpuinfo
+ * strdup(model_name) is returned as a fallback if not enough data is known */
+char *arm_decoded_name(
+ const char *imp, const char *part, const char *var, const char *rev,
+ const char *arch, const char *model_name);
+
+/* cpu flags from /proc/cpuinfo */
+const char *arm_flag_list(void); /* list of all known flags */
+const char *arm_flag_meaning(const char *flag); /* lookup flag meaning */
+
+#endif
diff --git a/modules/devices/arm/processor.c b/modules/devices/arm/processor.c
new file mode 100644
index 00000000..9446108d
--- /dev/null
+++ b/modules/devices/arm/processor.c
@@ -0,0 +1,515 @@
+/*
+ * HardInfo - Displays System Information
+ * Copyright (C) 2003-2006 L. A. F. Pereira <l@tia.mat.br>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, version 2 or later.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#include "hardinfo.h"
+#include "devices.h"
+#include "cpu_util.h"
+#include "dt_util.h"
+
+#include "arm_data.h"
+#include "arm_data.c"
+
+enum {
+ ARM_A32 = 0,
+ ARM_A64 = 1,
+ ARM_A32_ON_A64 = 2,
+};
+
+static const gchar *arm_mode_str[] = {
+ "A32",
+ "A64",
+ "A32 on A64",
+};
+
+GSList *
+processor_scan(void)
+{
+ GSList *procs = NULL;
+ Processor *processor = NULL;
+ FILE *cpuinfo;
+ gchar buffer[128];
+ gchar *rep_pname = NULL;
+ GSList *pi = NULL;
+ dtr *dt = dtr_new(NULL);
+
+ cpuinfo = fopen(PROC_CPUINFO, "r");
+ if (!cpuinfo)
+ return NULL;
+
+#define CHECK_FOR(k) (g_str_has_prefix(tmp[0], k))
+ while (fgets(buffer, 128, cpuinfo)) {
+ gchar **tmp = g_strsplit(buffer, ":", 2);
+ if (tmp[0] && tmp[1]) {
+ tmp[0] = g_strstrip(tmp[0]);
+ tmp[1] = g_strstrip(tmp[1]);
+ } else {
+ g_strfreev(tmp);
+ continue;
+ }
+
+ get_str("Processor", rep_pname);
+
+ if ( CHECK_FOR("processor") ) {
+ /* finish previous */
+ if (processor) {
+ procs = g_slist_append(procs, processor);
+ }
+
+ /* start next */
+ processor = g_new0(Processor, 1);
+ processor->id = atol(tmp[1]);
+
+ if (rep_pname)
+ processor->linux_name = g_strdup(rep_pname);
+
+ g_strfreev(tmp);
+ continue;
+ }
+
+ if (!processor &&
+ ( CHECK_FOR("model name")
+ || CHECK_FOR("Features")
+ || CHECK_FOR("BogoMIPS") ) ) {
+
+ /* single proc/core may not have "processor : n" */
+ processor = g_new0(Processor, 1);
+ processor->id = 0;
+
+ if (rep_pname)
+ processor->linux_name = g_strdup(rep_pname);
+ }
+
+ if (processor) {
+ get_str("model name", processor->linux_name);
+ get_str("Features", processor->flags);
+ get_float("BogoMIPS", processor->bogomips);
+
+ get_str("CPU implementer", processor->cpu_implementer);
+ get_str("CPU architecture", processor->cpu_architecture);
+ get_str("CPU variant", processor->cpu_variant);
+ get_str("CPU part", processor->cpu_part);
+ get_str("CPU revision", processor->cpu_revision);
+ }
+ g_strfreev(tmp);
+ }
+
+ if (processor)
+ procs = g_slist_append(procs, processor);
+
+ g_free(rep_pname);
+ fclose(cpuinfo);
+
+ /* re-duplicate missing data for /proc/cpuinfo variant that de-duplicated it */
+#define REDUP(f) if (dproc->f && !processor->f) processor->f = g_strdup(dproc->f);
+ Processor *dproc;
+ GSList *l;
+ l = procs = g_slist_reverse(procs);
+ while (l) {
+ processor = l->data;
+ if (processor->flags) {
+ dproc = processor;
+ } else if (dproc) {
+ REDUP(flags);
+ REDUP(cpu_implementer);
+ REDUP(cpu_architecture);
+ REDUP(cpu_variant);
+ REDUP(cpu_part);
+ REDUP(cpu_revision);
+ }
+ l = g_slist_next(l);
+ }
+ procs = g_slist_reverse(procs);
+
+ /* data not from /proc/cpuinfo */
+ for (pi = procs; pi; pi = pi->next) {
+ processor = (Processor *) pi->data;
+
+ /* strings can't be null or segfault later */
+ STRIFNULL(processor->linux_name, _("ARM Processor") );
+ EMPIFNULL(processor->flags);
+ UNKIFNULL(processor->cpu_implementer);
+ UNKIFNULL(processor->cpu_architecture);
+ UNKIFNULL(processor->cpu_variant);
+ UNKIFNULL(processor->cpu_part);
+ UNKIFNULL(processor->cpu_revision);
+
+ processor->model_name = arm_decoded_name(
+ processor->cpu_implementer, processor->cpu_part,
+ processor->cpu_variant, processor->cpu_revision,
+ processor->cpu_architecture, processor->linux_name);
+ UNKIFNULL(processor->model_name);
+
+ /* topo & freq */
+ processor->cpufreq = cpufreq_new(processor->id);
+ processor->cputopo = cputopo_new(processor->id);
+
+ if (processor->cpufreq->cpukhz_max)
+ processor->cpu_mhz = processor->cpufreq->cpukhz_max / 1000;
+ else
+ processor->cpu_mhz = 0.0f;
+
+ /* Try OPP, although if it exists, it should have been available
+ * via cpufreq. */
+ if (dt && processor->cpu_mhz == 0.0f) {
+ gchar *dt_cpu_path = g_strdup_printf("/cpus/cpu@%d", processor->id);
+ dt_opp_range *opp = dtr_get_opp_range(dt, dt_cpu_path);
+ if (opp) {
+ processor->cpu_mhz = (double)opp->khz_max / 1000;
+ g_free(opp);
+ }
+ g_free(dt_cpu_path);
+ }
+
+ /* mode */
+ processor->mode = ARM_A32;
+ if ( processor_has_flag(processor->flags, "pmull")
+ || processor_has_flag(processor->flags, "crc32") ) {
+#ifdef __aarch64__
+ processor->mode = ARM_A64;
+#else
+ processor->mode = ARM_A32_ON_A64;
+#endif
+ }
+ }
+ dtr_free(dt);
+
+ return procs;
+}
+
+gchar *processor_get_capabilities_from_flags(gchar * strflags)
+{
+ gchar **flags, **old;
+ gchar *tmp = NULL;
+ gint j = 0;
+
+ flags = g_strsplit(strflags, " ", 0);
+ old = flags;
+
+ while (flags[j]) {
+ const gchar *meaning = arm_flag_meaning( flags[j] );
+
+ if (meaning) {
+ tmp = h_strdup_cprintf("%s=%s\n", tmp, flags[j], meaning);
+ } else {
+ tmp = h_strdup_cprintf("%s=\n", tmp, flags[j]);
+ }
+ j++;
+ }
+ if (tmp == NULL || g_strcmp0(tmp, "") == 0)
+ tmp = g_strdup_printf("%s=%s\n", "empty", _("Empty List"));
+
+ g_strfreev(old);
+ return tmp;
+}
+
+#define khzint_to_mhzdouble(k) (((double)k)/1000)
+#define cmp_clocks_test(f) if (a->f < b->f) return -1; if (a->f > b->f) return 1;
+
+static gint cmp_cpufreq_data(cpufreq_data *a, cpufreq_data *b) {
+ gint i = 0;
+ i = g_strcmp0(a->shared_list, b->shared_list); if (i!=0) return i;
+ cmp_clocks_test(cpukhz_max);
+ cmp_clocks_test(cpukhz_min);
+ return 0;
+}
+
+static gint cmp_cpufreq_data_ignore_affected(cpufreq_data *a, cpufreq_data *b) {
+ gint i = 0;
+ cmp_clocks_test(cpukhz_max);
+ cmp_clocks_test(cpukhz_min);
+ return 0;
+}
+
+gchar *clocks_summary(GSList * processors)
+{
+ gchar *ret = g_strdup_printf("[%s]\n", _("Clocks"));
+ GSList *all_clocks = NULL, *uniq_clocks = NULL;
+ GSList *tmp, *l;
+ Processor *p;
+ cpufreq_data *c, *cur = NULL;
+ gint cur_count = 0, i = 0;
+
+ /* create list of all clock references */
+ for (l = processors; l; l = l->next) {
+ p = (Processor*)l->data;
+ if (p->cpufreq && p->cpufreq->cpukhz_max > 0) {
+ all_clocks = g_slist_prepend(all_clocks, p->cpufreq);
+ }
+ }
+
+ if (g_slist_length(all_clocks) == 0) {
+ ret = h_strdup_cprintf("%s=\n", ret, _("(Not Available)") );
+ g_slist_free(all_clocks);
+ return ret;
+ }
+
+ /* ignore duplicate references */
+ all_clocks = g_slist_sort(all_clocks, (GCompareFunc)cmp_cpufreq_data);
+ for (l = all_clocks; l; l = l->next) {
+ c = (cpufreq_data*)l->data;
+ if (!cur) {
+ cur = c;
+ } else {
+ if (cmp_cpufreq_data(cur, c) != 0) {
+ uniq_clocks = g_slist_prepend(uniq_clocks, cur);
+ cur = c;
+ }
+ }
+ }
+ uniq_clocks = g_slist_prepend(uniq_clocks, cur);
+ uniq_clocks = g_slist_reverse(uniq_clocks);
+ cur = 0, cur_count = 0;
+
+ /* count and list clocks */
+ for (l = uniq_clocks; l; l = l->next) {
+ c = (cpufreq_data*)l->data;
+ if (!cur) {
+ cur = c;
+ cur_count = 1;
+ } else {
+ if (cmp_cpufreq_data_ignore_affected(cur, c) != 0) {
+ ret = h_strdup_cprintf(_("%.2f-%.2f %s=%dx\n"),
+ ret,
+ khzint_to_mhzdouble(cur->cpukhz_min),
+ khzint_to_mhzdouble(cur->cpukhz_max),
+ _("MHz"),
+ cur_count);
+ cur = c;
+ cur_count = 1;
+ } else {
+ cur_count++;
+ }
+ }
+ }
+ ret = h_strdup_cprintf(_("%.2f-%.2f %s=%dx\n"),
+ ret,
+ khzint_to_mhzdouble(cur->cpukhz_min),
+ khzint_to_mhzdouble(cur->cpukhz_max),
+ _("MHz"),
+ cur_count);
+
+ g_slist_free(all_clocks);
+ g_slist_free(uniq_clocks);
+ return ret;
+}
+
+gchar *
+processor_get_detailed_info(Processor *processor)
+{
+ gchar *tmp_flags, *tmp_imp = NULL, *tmp_part = NULL,
+ *tmp_arch, *tmp_cpufreq, *tmp_topology, *ret;
+ tmp_flags = processor_get_capabilities_from_flags(processor->flags);
+ arm_part(processor->cpu_implementer, processor->cpu_part, &tmp_imp, &tmp_part);
+ tmp_arch = (char*)arm_arch_more(processor->cpu_architecture);
+
+ tmp_topology = cputopo_section_str(processor->cputopo);
+ tmp_cpufreq = cpufreq_section_str(processor->cpufreq);
+
+ ret = g_strdup_printf("[%s]\n"
+ "%s=%s\n" /* linux name */
+ "%s=%s\n" /* decoded name */
+ "%s=%s\n" /* mode */
+ "%s=%.2f %s\n" /* frequency */
+ "%s=%.2f\n" /* bogomips */
+ "%s=%s\n" /* byte order */
+ "%s" /* topology */
+ "%s" /* frequency scaling */
+ "[%s]\n" /* ARM */
+ "%s=[%s] %s\n" /* implementer */
+ "%s=[%s] %s\n" /* part */
+ "%s=[%s] %s\n" /* architecture */
+ "%s=%s\n" /* variant */
+ "%s=%s\n" /* revision */
+ "[%s]\n" /* flags */
+ "%s"
+ "%s", /* empty */
+ _("Processor"),
+ _("Linux Name"), processor->linux_name,
+ _("Decoded Name"), processor->model_name,
+ _("Mode"), arm_mode_str[processor->mode],
+ _("Frequency"), processor->cpu_mhz, _("MHz"),
+ _("BogoMips"), processor->bogomips,
+ _("Byte Order"), byte_order_str(),
+ tmp_topology,
+ tmp_cpufreq,
+ _("ARM"),
+ _("Implementer"), processor->cpu_implementer, (tmp_imp) ? tmp_imp : "",
+ _("Part"), processor->cpu_part, (tmp_part) ? tmp_part : "",
+ _("Architecture"), processor->cpu_architecture, (tmp_arch) ? tmp_arch : "",
+ _("Variant"), processor->cpu_variant,
+ _("Revision"), processor->cpu_revision,
+ _("Capabilities"), tmp_flags,
+ "");
+ g_free(tmp_flags);
+ g_free(tmp_cpufreq);
+ g_free(tmp_topology);
+ return ret;
+}
+
+gchar *processor_name(GSList *processors) {
+ /* 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.
+ */
+#define UNKSOC "(Unknown)" /* don't translate this */
+ const struct {
+ char *search_str;
+ char *vendor;
+ char *soc;
+ } dt_compat_searches[] = {
+ { "brcm,bcm2838", "Broadcom", "BCM2838" }, // RPi 4
+ { "brcm,bcm2837", "Broadcom", "BCM2837" }, // RPi 3
+ { "brcm,bcm2836", "Broadcom", "BCM2836" }, // RPi 2
+ { "brcm,bcm2835", "Broadcom", "BCM2835" }, // RPi 1
+ { "rockchip,rk3288", "Rockchip", "RK3288" }, // Asus Tinkerboard
+ { "rockchip,rk3328", "Rockchip", "RK3328" }, // Firefly Renegade
+ { "rockchip,rk3399", "Rockchip", "RK3399" }, // Firefly Renegade Elite
+ { "rockchip,rk32", "Rockchip", "RK32xx-family" },
+ { "rockchip,rk33", "Rockchip", "RK33xx-family" },
+ { "ti,omap5432", "Texas Instruments", "OMAP5432" },
+ { "ti,omap5430", "Texas Instruments", "OMAP5430" },
+ { "ti,omap4470", "Texas Instruments", "OMAP4470" },
+ { "ti,omap4460", "Texas Instruments", "OMAP4460" },
+ { "ti,omap4430", "Texas Instruments", "OMAP4430" },
+ { "ti,omap3620", "Texas Instruments", "OMAP3620" },
+ { "ti,omap3450", "Texas Instruments", "OMAP3450" },
+ { "ti,omap5", "Texas Instruments", "OMAP5-family" },
+ { "ti,omap4", "Texas Instruments", "OMAP4-family" },
+ { "ti,omap3", "Texas Instruments", "OMAP3-family" },
+ { "ti,omap2", "Texas Instruments", "OMAP2-family" },
+ { "ti,omap1", "Texas Instruments", "OMAP1-family" },
+ { "mediatek,mt6799", "MediaTek", "MT6799 Helio X30" },
+ { "mediatek,mt6799", "MediaTek", "MT6799 Helio X30" },
+ { "mediatek,mt6797x", "MediaTek", "MT6797X Helio X27" },
+ { "mediatek,mt6797t", "MediaTek", "MT6797T Helio X25" },
+ { "mediatek,mt6797", "MediaTek", "MT6797 Helio X20" },
+ { "mediatek,mt6757T", "MediaTek", "MT6757T Helio P25" },
+ { "mediatek,mt6757", "MediaTek", "MT6757 Helio P20" },
+ { "mediatek,mt6795", "MediaTek", "MT6795 Helio X10" },
+ { "mediatek,mt6755", "MediaTek", "MT6755 Helio P10" },
+ { "mediatek,mt6750t", "MediaTek", "MT6750T" },
+ { "mediatek,mt6750", "MediaTek", "MT6750" },
+ { "mediatek,mt6753", "MediaTek", "MT6753" },
+ { "mediatek,mt6752", "MediaTek", "MT6752" },
+ { "mediatek,mt6738", "MediaTek", "MT6738" },
+ { "mediatek,mt6737t", "MediaTek", "MT6737T" },
+ { "mediatek,mt6735", "MediaTek", "MT6735" },
+ { "mediatek,mt6732", "MediaTek", "MT6732" },
+ { "qcom,msm8939", "Qualcomm", "Snapdragon 615"},
+ { "qcom,msm", "Qualcomm", "Snapdragon-family"},
+ { "nvidia,tegra", "nVidia", "Tegra-family" },
+ { "brcm,", "Broadcom", UNKSOC },
+ { "nvidia,", "nVidia", UNKSOC },
+ { "rockchip,", "Rockchip", UNKSOC },
+ { "ti,", "Texas Instruments", UNKSOC },
+ { "qcom,", "Qualcom", UNKSOC },
+ { "mediatek,", "MediaTek", UNKSOC },
+ { "amlogic,", "Amlogic", UNKSOC },
+ { "allwinner,", "Allwinner", UNKSOC },
+ { NULL, NULL }
+ };
+ gchar *ret = NULL;
+ gchar *compat = NULL;
+ int i;
+
+ compat = dtr_get_string("/compatible", 1);
+
+ if (compat != NULL) {
+ i = 0;
+ while(dt_compat_searches[i].search_str != NULL) {
+ if (strstr(compat, dt_compat_searches[i].search_str) != NULL) {
+ ret = g_strdup_printf("%s %s", dt_compat_searches[i].vendor, dt_compat_searches[i].soc);
+ break;
+ }
+ i++;
+ }
+ }
+ g_free(compat);
+ UNKIFNULL(ret);
+ return ret;
+}
+
+gchar *processor_describe(GSList * processors) {
+ return processor_describe_by_counting_names(processors);
+}
+
+gchar *processor_meta(GSList * processors) {
+ gchar *meta_soc = processor_name(processors);
+ gchar *meta_cpu_desc = processor_describe(processors);
+ gchar *meta_cpu_topo = processor_describe_default(processors);
+ gchar *meta_freq_desc = processor_frequency_desc(processors);
+ gchar *meta_clocks = clocks_summary(processors);
+ gchar *ret = NULL;
+ UNKIFNULL(meta_cpu_desc);
+ ret = g_strdup_printf("[%s]\n"
+ "%s=%s\n"
+ "%s=%s\n"
+ "%s=%s\n"
+ "%s=%s\n"
+ "%s",
+ _("SOC/Package"),
+ _("Name"), meta_soc,
+ _("Description"), meta_cpu_desc,
+ _("Topology"), meta_cpu_topo,
+ _("Logical CPU Config"), meta_freq_desc,
+ meta_clocks );
+ g_free(meta_soc);
+ g_free(meta_cpu_desc);
+ g_free(meta_cpu_topo);
+ g_free(meta_freq_desc);
+ g_free(meta_clocks);
+ return ret;
+}
+
+gchar *processor_get_info(GSList * processors)
+{
+ Processor *processor;
+ gchar *ret, *tmp, *hashkey;
+ gchar *meta; /* becomes owned by more_info? no need to free? */
+ GSList *l;
+
+ tmp = g_strdup_printf("$!CPU_META$%s=\n", _("SOC/Package Information") );
+
+ meta = processor_meta(processors);
+ moreinfo_add_with_prefix("DEV", "CPU_META", meta);
+
+ for (l = processors; l; l = l->next) {
+ processor = (Processor *) l->data;
+
+ tmp = g_strdup_printf("%s$CPU%d$%s=%.2f %s\n",
+ tmp, processor->id,
+ processor->model_name,
+ processor->cpu_mhz, _("MHz"));
+
+ hashkey = g_strdup_printf("CPU%d", processor->id);
+ moreinfo_add_with_prefix("DEV", hashkey,
+ processor_get_detailed_info(processor));
+ g_free(hashkey);
+ }
+
+ ret = g_strdup_printf("[$ShellParam$]\n"
+ "ViewType=1\n"
+ "[Processors]\n"
+ "%s", tmp);
+ g_free(tmp);
+
+ return ret;
+}
diff --git a/modules/devices/battery.c b/modules/devices/battery.c
new file mode 100644
index 00000000..e356c14a
--- /dev/null
+++ b/modules/devices/battery.c
@@ -0,0 +1,385 @@
+/*
+ * HardInfo - Displays System Information
+ * Copyright (C) 2003-2006 L. A. F. Pereira <l@tia.mat.br>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, version 2 or later.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#include <string.h>
+#include <time.h>
+
+#include "hardinfo.h"
+#include "devices.h"
+
+const struct {
+ gchar *key, *name;
+} ups_fields[] = {
+ { "UPS Status", NULL },
+ { "STATUS", "Status" },
+ { "TIMELEFT", "Time Left" },
+ { "LINEV", "Line Voltage" },
+ { "LOADPCT", "Load Percent" },
+
+ { "UPS Battery Information", NULL },
+ { "BATTV", "Battery Voltage" },
+ { "BCHARGE", "Battery Charge" },
+ { "BATTDATE", "Battery Date" },
+
+ { "UPS Information", NULL },
+ { "APCMODEL", "Model" },
+ { "FIRMWARE", "Firmware Version" },
+ { "SERIALNO", "Serial Number" },
+ { "UPSMODE", "UPS Mode" },
+ { "CABLE", "Cable" },
+ { "UPSNAME", "UPS Name" },
+
+ { "UPS Nominal Values", NULL },
+ { "NOMINV", "Voltage" },
+ { "NOMBATTV", "Battery Voltage" },
+ { "NOMPOWER", "Power" }
+};
+
+
+static void
+__scan_battery_apcupsd(void)
+{
+ GHashTable *ups_data;
+ FILE *apcaccess;
+ char buffer[512], *apcaccess_path;
+ guint i;
+
+ apcaccess_path = find_program("apcaccess");
+ if (apcaccess_path && (apcaccess = popen(apcaccess_path, "r"))) {
+ /* first line isn't important */
+ if (fgets(buffer, 512, apcaccess)) {
+ /* allocate the key, value hash table */
+ ups_data = g_hash_table_new(g_str_hash, g_str_equal);
+
+ /* read up all the apcaccess' output, saving it in the key, value hash table */
+ while (fgets(buffer, 512, apcaccess)) {
+ buffer[9] = '\0';
+
+ g_hash_table_insert(ups_data,
+ g_strdup(g_strstrip(buffer)),
+ g_strdup(g_strstrip(buffer + 10)));
+ }
+
+ /* builds the ups info string, respecting the field order as found in ups_fields */
+ for (i = 0; i < G_N_ELEMENTS(ups_fields); i++) {
+ if (!ups_fields[i].name) {
+ /* there's no name: make a group with the key as its name */
+ battery_list = h_strdup_cprintf("[%s]\n", battery_list, ups_fields[i].key);
+ } else {
+ /* there's a name: adds a line */
+ const gchar *name = g_hash_table_lookup(ups_data, ups_fields[i].key);
+ battery_list = h_strdup_cprintf("%s=%s\n", battery_list,
+ ups_fields[i].name, name);
+ }
+ }
+
+ g_hash_table_destroy(ups_data);
+ }
+
+ pclose(apcaccess);
+ }
+
+ g_free(apcaccess_path);
+}
+
+static void
+__scan_battery_acpi(void)
+{
+ gchar *acpi_path;
+
+ gchar *present = NULL;
+ gchar *capacity = NULL;
+ gchar *technology = NULL;
+ gchar *voltage = NULL;
+ gchar *model = NULL, *serial = NULL, *type = NULL;
+ gchar *state = NULL, *rate = NULL;
+ gchar *remaining = NULL;
+ gchar *manufacturer = NULL;
+
+ acpi_path = g_strdup("/proc/acpi/battery");
+ if (g_file_test(acpi_path, G_FILE_TEST_EXISTS)) {
+ GDir *acpi;
+
+ if ((acpi = g_dir_open(acpi_path, 0, NULL))) {
+ const gchar *entry;
+
+ while ((entry = g_dir_read_name(acpi))) {
+ gchar *path = g_strdup_printf("%s/%s/info", acpi_path, entry);
+ FILE *f;
+ gchar buffer[256];
+ gdouble charge_rate = 1.0;
+
+ f = fopen(path, "r");
+ g_free(path);
+
+ if (!f)
+ goto cleanup;
+
+ while (fgets(buffer, 256, f)) {
+ gchar **tmp = g_strsplit(buffer, ":", 2);
+
+ GET_STR("present", present);
+ GET_STR("design capacity", capacity);
+ GET_STR("battery technology", technology);
+ GET_STR("design voltage", voltage);
+ GET_STR("model number", model);
+ GET_STR("serial number", serial);
+ GET_STR("battery type", type);
+ GET_STR("OEM info", manufacturer);
+
+ g_strfreev(tmp);
+ }
+ fclose(f);
+
+ path = g_strdup_printf("%s/%s/state", acpi_path, entry);
+ f = fopen(path, "r");
+ g_free(path);
+
+ if (!f)
+ goto cleanup;
+
+ while (fgets(buffer, 256, f)) {
+ gchar **tmp = g_strsplit(buffer, ":", 2);
+
+ GET_STR("charging state", state);
+ GET_STR("present rate", rate);
+ GET_STR("remaining capacity", remaining);
+
+ g_strfreev(tmp);
+ }
+
+ fclose(f);
+
+ gchar *tmp = vendor_get_link(manufacturer);
+ g_free(manufacturer);
+ manufacturer = tmp;
+
+ if (g_str_equal(present, "yes")) {
+ if (remaining && capacity)
+ charge_rate = atof(remaining) / atof(capacity);
+ else
+ charge_rate = 0;
+
+ battery_list = h_strdup_cprintf(_("\n[Battery: %s]\n"
+ "State=%s (load: %s)\n"
+ "Capacity=%s / %s (%.2f%%)\n"
+ "Battery Technology=%s (%s)\n"
+ "Manufacturer=%s\n"
+ "Model Number=%s\n"
+ "Serial Number=%s\n"),
+ battery_list,
+ entry,
+ state, rate,
+ remaining, capacity, charge_rate * 100.0,
+ technology, type,
+ manufacturer,
+ model,
+ serial);
+ }
+
+ cleanup:
+ g_free(present);
+ g_free(capacity);
+ g_free(technology);
+ g_free(type);
+ g_free(model);
+ g_free(serial);
+ g_free(state);
+ g_free(remaining);
+ g_free(rate);
+ g_free(manufacturer);
+
+ present = capacity = technology = type = \
+ model = serial = state = remaining = rate = manufacturer = NULL;
+ }
+
+ g_dir_close(acpi);
+ }
+ }
+
+ g_free(acpi_path);
+}
+
+static gchar *
+read_contents(const gchar *base, const gchar *key)
+{
+ gchar *value;
+ gchar *path;
+
+ path = g_strdup_printf("%s/%s", base, key);
+ if (!path)
+ return NULL;
+
+ if (!g_file_get_contents(path, &value, NULL, NULL)) {
+ g_free(path);
+ return NULL;
+ }
+
+ g_free(path);
+ return g_strchomp(value);
+}
+
+static void
+__scan_battery_sysfs_add_battery(const gchar *name)
+{
+ gchar *path = g_strdup_printf("/sys/class/power_supply/%s", name);
+ gchar *status, *capacity, *capacity_level, *technology, *manufacturer,
+ *model_name, *serial_number;
+
+ if (!path)
+ return;
+
+ status = read_contents(path, "status");
+ capacity = read_contents(path, "capacity");
+ capacity_level = read_contents(path, "capacity_level");
+ technology = read_contents(path, "technology");
+ manufacturer = read_contents(path, "manufacturer");
+ model_name = read_contents(path, "model_name");
+ serial_number = read_contents(path, "serial_number");
+
+ battery_list = h_strdup_cprintf(_("\n[Battery: %s]\n"
+ "State=%s\n"
+ "Capacity=%s / %s\n"
+ "Battery Technology=%s\n"
+ "Manufacturer=%s\n"
+ "Model Number=%s\n"
+ "Serial Number=%s\n"),
+ battery_list,
+ name,
+ status,
+ capacity, capacity_level,
+ technology,
+ manufacturer,
+ model_name,
+ serial_number);
+
+ free(status);
+ free(capacity);
+ free(capacity_level);
+ free(technology);
+ free(manufacturer);
+ free(model_name);
+ free(serial_number);
+}
+
+static void
+__scan_battery_sysfs(void)
+{
+ GDir *dir;
+ const gchar *entry;
+
+ dir = g_dir_open("/sys/class/power_supply", 0, NULL);
+ if (!dir)
+ return;
+
+ while ((entry = g_dir_read_name(dir))) {
+ if (g_str_has_prefix(entry, "BAT"))
+ __scan_battery_sysfs_add_battery(entry);
+ }
+
+ g_dir_close(dir);
+}
+
+static void
+__scan_battery_apm(void)
+{
+ FILE *procapm;
+ static char *sremaining = NULL, *stotal = NULL;
+ static unsigned int last_time = 0;
+ static int percentage = 0;
+ const char *ac_status[] = { "Battery",
+ "AC Power",
+ "Charging" };
+ int ac_bat;
+ char apm_bios_ver[16], apm_drv_ver[16];
+ char trash[10];
+
+ if ((procapm = fopen("/proc/apm", "r"))) {
+ int old_percentage = percentage;
+
+ (void)fscanf(procapm, "%s %s %s 0x%x %s %s %d%%",
+ apm_drv_ver, apm_bios_ver, trash,
+ &ac_bat, trash, trash, &percentage);
+ fclose(procapm);
+
+ if (last_time == 0) {
+ last_time = time(NULL);
+ sremaining = stotal = NULL;
+ }
+
+ if (old_percentage - percentage > 0) {
+ if (sremaining && stotal) {
+ g_free(sremaining);
+ g_free(stotal);
+ }
+
+ int secs_remaining = (time(NULL) - last_time) * percentage /
+ (old_percentage - percentage);
+ sremaining = seconds_to_string(secs_remaining);
+ stotal = seconds_to_string((secs_remaining * 100) / percentage);
+
+ last_time = time(NULL);
+ }
+ } else {
+ return;
+ }
+
+ if (stotal && sremaining) {
+ battery_list = h_strdup_cprintf(_("\n[Battery (APM)]\n"
+ "Charge=%d%%\n"
+ "Remaining Charge=%s of %s\n"
+ "Using=%s\n"
+ "APM driver version=%s\n"
+ "APM BIOS version=%s\n"),
+ battery_list,
+ percentage,
+ sremaining, stotal,
+ ac_status[ac_bat],
+ apm_drv_ver, apm_bios_ver);
+ } else {
+ battery_list = h_strdup_cprintf(_("\n[Battery (APM)]\n"
+ "Charge=%d%%\n"
+ "Using=%s\n"
+ "APM driver version=%s\n"
+ "APM BIOS version=%s\n"),
+ battery_list,
+ percentage,
+ ac_status[ac_bat],
+ apm_drv_ver, apm_bios_ver);
+
+ }
+}
+
+void
+scan_battery_do(void)
+{
+ g_free(battery_list);
+ battery_list = g_strdup("");
+
+ __scan_battery_sysfs();
+ __scan_battery_acpi();
+ __scan_battery_apm();
+ __scan_battery_apcupsd();
+
+ if (*battery_list == '\0') {
+ g_free(battery_list);
+
+ battery_list = g_strdup(_("[No batteries]\n"
+ "No batteries found on this system=\n"));
+ }
+}
diff --git a/modules/devices/devicetree.c b/modules/devices/devicetree.c
new file mode 100644
index 00000000..7c798670
--- /dev/null
+++ b/modules/devices/devicetree.c
@@ -0,0 +1,307 @@
+/*
+ * HardInfo - Displays System Information
+ * Copyright (C) 2003-2007 L. A. F. Pereira <l@tia.mat.br>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, version 2 or later.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+/*
+ * Device Tree support by Burt P. <pburt0@gmail.com>
+ * Sources:
+ * http://elinux.org/Device_Tree_Usage
+ * http://elinux.org/Device_Tree_Mysteries
+ */
+#include <unistd.h>
+#include <sys/types.h>
+#include <stdint.h>
+#include "hardinfo.h"
+#include "devices.h"
+#include "cpu_util.h"
+#include "dt_util.h"
+#include "appf.h"
+
+gchar *dtree_info = NULL;
+const char *dtree_mem_str = NULL; /* used by memory devices when nothing else is available */
+
+/* These should really go into CMakeLists.txt */
+#if defined(__arm__)
+#include "devicetree/rpi_data.c"
+#elif defined(__powerpc__)
+#include "devicetree/pmac_data.c"
+#endif
+
+static gchar *get_node(dtr *dt, char *np) {
+ gchar *nodes = NULL, *props = NULL, *ret = NULL;
+ gchar *tmp = NULL, *pstr = NULL, *lstr = NULL;
+ gchar *dir_path;
+ gchar *node_path;
+ const gchar *fn;
+ GDir *dir;
+ dtr_obj *node, *child;
+
+ props = g_strdup_printf("[%s]\n", _("Properties") );
+ nodes = g_strdup_printf("[%s]\n", _("Children") );
+ node = dtr_obj_read(dt, np);
+ dir_path = dtr_obj_full_path(node);
+
+ dir = g_dir_open(dir_path, 0 , NULL);
+ if (dir) {
+ while((fn = g_dir_read_name(dir)) != NULL) {
+ child = dtr_get_prop_obj(dt, node, fn);
+ pstr = hardinfo_clean_value(dtr_str(child), 1);
+ lstr = hardinfo_clean_label(fn, 0);
+ if (dtr_obj_type(child) == DT_NODE) {
+ tmp = g_strdup_printf("%s%s=%s\n",
+ nodes, lstr, pstr);
+ g_free(nodes);
+ nodes = tmp;
+ } else {
+ tmp = g_strdup_printf("%s%s=%s\n",
+ props, lstr, pstr);
+ g_free(props);
+ props = tmp;
+ }
+ dtr_obj_free(child);
+ g_free(pstr);
+ g_free(lstr);
+ }
+ }
+ g_dir_close(dir);
+ g_free(dir_path);
+
+ lstr = dtr_obj_alias(node);
+ pstr = dtr_obj_symbol(node);
+ ret = g_strdup_printf("[%s]\n"
+ "%s=%s\n"
+ "%s=%s\n"
+ "%s=%s\n"
+ "%s%s",
+ _("Node"),
+ _("Node Path"), dtr_obj_path(node),
+ _("Alias"), (lstr != NULL) ? lstr : _("(None)"),
+ _("Symbol"), (pstr != NULL) ? pstr : _("(None)"),
+ props, nodes);
+
+ dtr_obj_free(node);
+ g_free(props);
+ g_free(nodes);
+
+ return ret;
+}
+
+/* different from dtr_get_string() in that it re-uses the existing dt */
+static char *get_dt_string(dtr *dt, char *path, gboolean decode) {
+ char *ret;
+
+ if (decode) {
+ dtr_obj *obj = dtr_get_prop_obj(dt, NULL, path);
+
+ ret = dtr_str(obj);
+
+ dtr_obj_free(obj);
+ } else {
+ ret = dtr_get_prop_str(dt, NULL, path);
+ }
+
+ return ret;
+}
+
+static gchar *get_summary(dtr *dt) {
+ char *model = NULL, *compat = NULL;
+ char *ret = NULL;
+
+ model = get_dt_string(dt, "/model", 0);
+ compat = get_dt_string(dt, "/compatible", 1);
+ UNKIFNULL(model);
+ EMPIFNULL(compat);
+
+#if defined(__arm__)
+ /* Expand on the DT information from known machines, like RPi.
+ * RPi stores a revision value in /proc/cpuinfo that can be used
+ * to look up details. This is just a nice place to pull it all
+ * together for DT machines, with a nice fallback.
+ * PPC Macs could be handled this way too. They store
+ * machine identifiers in /proc/cpuinfo. */
+ if (strstr(model, "Raspberry Pi")
+ || strstr(compat, "raspberrypi")) {
+ gchar *gpu_compat = get_dt_string(dt, "/soc/gpu/compatible", 1);
+ gchar *rpi_details = rpi_board_details();
+ gchar *basic_info;
+
+ basic_info = g_strdup_printf(
+ "[%s]\n"
+ "%s=%s\n"
+ "%s=%s\n",
+ _("Platform"),
+ _("Compatible"), compat,
+ _("GPU-compatible"), gpu_compat);
+
+ if (rpi_details) {
+ ret = g_strconcat(rpi_details, basic_info, NULL);
+
+ g_free(rpi_details);
+ } else {
+ gchar *serial_number = get_dt_string(dt, "/serial-number", 1);
+
+ ret = g_strdup_printf(
+ "[%s]\n"
+ "%s=%s\n"
+ "%s=%s\n"
+ "%s=%s\n"
+ "%s",
+ _("Raspberry Pi or Compatible"),
+ _("Model"), model,
+ _("Serial Number"), serial_number,
+ _("RCode"), _("No revision code available; unable to lookup model details."),
+ basic_info);
+
+ g_free(serial_number);
+ }
+
+ g_free(gpu_compat);
+ g_free(basic_info);
+ }
+#endif
+
+#if defined(__powerpc__)
+ /* Power Macintosh */
+ if (strstr(compat, "PowerBook") != NULL
+ || strstr(compat, "MacRISC") != NULL
+ || strstr(compat, "Power Macintosh") != NULL) {
+ gchar *mac_details = ppc_mac_details();
+
+ if (mac_details) {
+ gchar *serial_number = get_dt_string(dt, "/serial-number", 1);
+
+ ret = g_strdup_printf(
+ "%s[%s]\n"
+ "%s=%s\n",
+ mac_details,
+ _("More"),
+ _("Serial Number"), serial_number);
+
+ free(mac_details);
+ free(serial_number);
+ }
+ }
+#endif
+
+ /* fallback */
+ if (!ret) {
+ gchar *serial_number = get_dt_string(dt, "/serial-number", 1);
+ EMPIFNULL(serial_number);
+ ret = g_strdup_printf(
+ "[%s]\n"
+ "%s=%s\n"
+ "%s=%s\n"
+ "%s=%s\n",
+ _("Board"),
+ _("Model"), model,
+ _("Serial Number"), serial_number,
+ _("Compatible"), compat);
+ free(serial_number);
+ }
+
+ free(model);
+ free(compat);
+
+ return ret;
+}
+
+static void mi_add(const char *key, const char *value, int report_details) {
+ gchar *ckey, *rkey;
+
+ ckey = hardinfo_clean_label(key, 0);
+ rkey = g_strdup_printf("%s:%s", "DTREE", ckey);
+
+ dtree_info = h_strdup_cprintf("$%s%s$%s=\n", dtree_info,
+ (report_details) ? "!" : "", rkey, ckey);
+ moreinfo_add_with_prefix("DEV", rkey, g_strdup(value));
+
+ g_free(ckey);
+ g_free(rkey);
+}
+
+static void add_keys(dtr *dt, char *np) {
+ gchar *dir_path, *dt_path;
+ gchar *ftmp, *ntmp;
+ gchar *n_info;
+ const gchar *fn;
+ GDir *dir;
+ dtr_obj *obj;
+
+ dir_path = g_strdup_printf("%s/%s", dtr_base_path(dt), np);
+ dir = g_dir_open(dir_path, 0 , NULL);
+ if(!dir){ /* add self */
+ obj = dtr_obj_read(dt, np);
+ dt_path = dtr_obj_path(obj);
+ n_info = get_node(dt, dt_path);
+ mi_add(dt_path, n_info, 0);
+ }else { //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);
+ if(strlen(ntmp)>0) add_keys(dt, ntmp);
+ g_free(ntmp);
+ }
+ g_free(ftmp);
+ }
+ g_dir_close(dir);
+ }
+ g_free(dir_path);
+}
+
+static char *msg_section(dtr *dt, int dump) {
+ gchar *aslbl = NULL;
+ gchar *messages = dtr_messages(dt);
+ gchar *ret = g_strdup_printf("[%s]", _("Messages"));
+ gchar **lines = g_strsplit(messages, "\n", 0);
+ int i = 0;
+ while(lines[i] != NULL) {
+ aslbl = hardinfo_clean_label(lines[i], 0);
+ ret = appfnl(ret, "%s=", aslbl);
+ g_free(aslbl);
+ i++;
+ }
+ g_strfreev(lines);
+ if (dump)
+ printf("%s", messages);
+ g_free(messages);
+ return ret;
+}
+
+void __scan_dtree()
+{
+ dtr *dt = dtr_new(NULL);
+ gchar *summary = get_summary(dt);
+ gchar *maps = dtr_maps_info(dt);
+ gchar *messages = NULL;
+
+ dtree_info = g_strdup("[Device Tree]\n");
+ mi_add("Summary", summary, 1);
+ mi_add("Maps", maps, 0);
+
+ if(dtr_was_found(dt))
+ add_keys(dt, "/");
+ messages = msg_section(dt, 0);
+ mi_add("Messages", messages, 0);
+
+ g_free(summary);
+ g_free(maps);
+ g_free(messages);
+ dtr_free(dt);
+}
diff --git a/modules/devices/devicetree/pmac_data.c b/modules/devices/devicetree/pmac_data.c
new file mode 100644
index 00000000..8c5471f0
--- /dev/null
+++ b/modules/devices/devicetree/pmac_data.c
@@ -0,0 +1,98 @@
+/*
+ * HardInfo - Displays System Information
+ * Copyright (C) 2003-2007 L. A. F. Pereira <l@tia.mat.br>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, version 2 or later.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#include "cpu_util.h" /* for PROC_CPUINFO */
+
+static gchar *ppc_mac_details(void) {
+ int i = 0;
+ gchar *ret = NULL;
+ gchar *platform = NULL;
+ gchar *model = NULL;
+ gchar *machine = NULL;
+ gchar *motherboard = NULL;
+ gchar *detected_as = NULL;
+ gchar *pmac_flags = NULL;
+ gchar *l2_cache = NULL;
+ gchar *pmac_gen = NULL;
+
+ FILE *cpuinfo;
+ gchar buffer[128];
+
+ cpuinfo = fopen(PROC_CPUINFO, "r");
+ if (!cpuinfo)
+ return NULL;
+ while (fgets(buffer, 128, cpuinfo)) {
+ gchar **tmp = g_strsplit(buffer, ":", 2);
+ if (tmp[1] == NULL) {
+ g_strfreev(tmp);
+ continue;
+ }
+ tmp[0] = g_strstrip(tmp[0]);
+ tmp[1] = g_strstrip(tmp[1]);
+ get_str("platform", platform);
+ get_str("model", model);
+ get_str("machine", machine);
+ get_str("motherboard", motherboard);
+ get_str("detected as", detected_as);
+ get_str("pmac flags", pmac_flags);
+ get_str("L2 cache", l2_cache);
+ get_str("pmac-generation", pmac_gen);
+ }
+ fclose(cpuinfo);
+
+ if (machine == NULL)
+ goto pmd_exit;
+
+ UNKIFNULL(platform);
+ UNKIFNULL(model);
+ UNKIFNULL(motherboard);
+ UNKIFNULL(detected_as);
+ UNKIFNULL(pmac_flags);
+ UNKIFNULL(l2_cache);
+ UNKIFNULL(pmac_gen);
+
+ ret = g_strdup_printf("[%s]\n"
+ "%s=%s\n"
+ "%s=%s\n"
+ "%s=%s\n"
+ "%s=%s\n"
+ "%s=%s\n"
+ "%s=%s\n"
+ "%s=%s\n"
+ "%s=%s\n",
+ _("Apple Power Macintosh"),
+ _("Platform"), platform,
+ _("Model"), model,
+ _("Machine"), machine,
+ _("Motherboard"), motherboard,
+ _("Detected as"), detected_as,
+ _("PMAC Flags"), pmac_flags,
+ _("L2 Cache"), l2_cache,
+ _("PMAC Generation"), pmac_gen );
+
+pmd_exit:
+ g_free(platform);
+ g_free(model);
+ g_free(machine);
+ g_free(motherboard);
+ g_free(detected_as);
+ g_free(pmac_flags);
+ g_free(l2_cache);
+ g_free(pmac_gen);
+ return ret;
+}
diff --git a/modules/devices/devicetree/rpi_data.c b/modules/devices/devicetree/rpi_data.c
new file mode 100644
index 00000000..63daa7f3
--- /dev/null
+++ b/modules/devices/devicetree/rpi_data.c
@@ -0,0 +1,179 @@
+/*
+ * HardInfo - Displays System Information
+ * Copyright (C) 2003-2007 L. A. F. Pereira <l@tia.mat.br>
+ * This file from: rpiz - https://github.com/bp0/rpiz
+ * Copyright (C) 2017 Burt P. <pburt0@gmail.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, version 2 or later.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+static char unk[] = "(Unknown)";
+
+/* information table from: http://elinux.org/RPi_HardwareHistory */
+typedef struct {
+ char *value, *intro, *model, *pcb, *mem, *mfg, *soc;
+} RaspberryPiBoard;
+
+static const RaspberryPiBoard rpi_boardinfo[] = {
+/* Value Introduction Model Name PCB rev. Memory(spec) Manufacturer SOC(spec) *
+ * Raspberry Pi %s */
+ { unk, unk, unk, unk, unk, unk, NULL },
+ { "Beta", "Q1 2012", "B (Beta)", unk, "256 MB", "(Beta board)", NULL },
+ { "0002", "Q1 2012", "B", "1.0", "256 MB", unk, "BCM2835" },
+ { "0003", "Q3 2012", "B (ECN0001)", "1.0", "256 MB", "(Fuses mod and D14 removed)", NULL },
+ { "0004", "Q3 2012", "B", "2.0", "256 MB", "Sony", NULL },
+ { "0005", "Q4 2012", "B", "2.0", "256 MB", "Qisda", NULL },
+ { "0006", "Q4 2012", "B", "2.0", "256 MB", "Egoman", NULL },
+ { "0007", "Q1 2013", "A", "2.0", "256 MB", "Egoman", NULL },
+ { "0008", "Q1 2013", "A", "2.0", "256 MB", "Sony", NULL },
+ { "0009", "Q1 2013", "A", "2.0", "256 MB", "Qisda", NULL },
+ { "000d", "Q4 2012", "B", "2.0", "512 MB", "Egoman", NULL },
+ { "000e", "Q4 2012", "B", "2.0", "512 MB", "Sony", NULL },
+ { "000f", "Q4 2012", "B", "2.0", "512 MB", "Qisda", NULL },
+ { "0010", "Q3 2014", "B+", "1.0", "512 MB", "Sony", NULL },
+ { "0011", "Q2 2014", "Compute Module 1", "1.0", "512 MB", "Sony", NULL },
+ { "0012", "Q4 2014", "A+", "1.1", "256 MB", "Sony", NULL },
+ { "0013", "Q1 2015", "B+", "1.2", "512 MB", unk, NULL },
+ { "0014", "Q2 2014", "Compute Module 1", "1.0", "512 MB", "Embest", NULL },
+ { "0015", unk, "A+", "1.1", "256 MB/512 MB", "Embest", NULL },
+ { "a01040", unk, "2 Model B", "1.0", "1024 MB DDR2", "Sony", "BCM2836" },
+ { "a01041", "Q1 2015", "2 Model B", "1.1", "1024 MB DDR2", "Sony", "BCM2836" },
+ { "a21041", "Q1 2015", "2 Model B", "1.1", "1024 MB DDR2", "Embest", "BCM2836" },
+ { "a22042", "Q3 2016", "2 Model B", "1.2", "1024 MB DDR2", "Embest", "BCM2837" }, /* (with BCM2837) */
+ { "900021", "Q3 2016", "A+", "1.1", "512 MB", "Sony", NULL },
+ { "900032", "Q2 2016?", "B+", "1.2", "512 MB", "Sony", NULL },
+ { "900092", "Q4 2015", "Zero", "1.2", "512 MB", "Sony", NULL },
+ { "900093", "Q2 2016", "Zero", "1.3", "512 MB", "Sony", NULL },
+ { "920093", "Q4 2016?", "Zero", "1.3", "512 MB", "Embest", NULL },
+ { "9000c1", "Q1 2017", "Zero W", "1.1", "512 MB", "Sony", NULL },
+ { "a02082", "Q1 2016", "3 Model B", "1.2", "1024 MB DDR2", "Sony", "BCM2837" },
+ { "a020a0", "Q1 2017", "Compute Module 3 or CM3 Lite", "1.0", "1024 MB DDR2", "Sony", NULL },
+ { "a22082", "Q1 2016", "3 Model B", "1.2", "1024 MB DDR2", "Embest", "BCM2837" },
+ { "a32082", "Q4 2016", "3 Model B", "1.2", "1024 MB DDR2", "Sony Japan", NULL },
+ { "a020d3", "Q1 2018", "3 Model B+", "1.3", "1024 MB DDR2", "Sony", "BCM2837" },
+ { "9020e0", "Q4 2018", "3 Model A+", "1.0", "512 MB DDR2", "Sony", "BCM2837" },
+
+ { "a03111", "Q2 2019", "4 Model B", "1.0", "1024 MB DDR4", "Sony", "BCM2838" },
+ { "b03111", "Q2 2019", "4 Model B", "1.0", "2048 MB DDR4", "Sony", "BCM2838" },
+ { "c03111", "Q2 2019", "4 Model B", "1.1", "4096 MB DDR4", "Sony", "BCM2838" },
+ { "c03114", "Q2 2020", "4 Model B", "1.4", "8192 MB DDR4", "Sony", "BCM2838" },
+
+ { NULL, NULL, NULL, NULL, NULL, NULL, NULL }
+};
+
+/* return number of chars to skip */
+static int rpi_ov_check(const char *r_code) {
+ /* sources differ. prefix is either 1000... or just 1... */
+ //if (strncmp(r, "1000", 4) == 0)
+ // return 4;
+ if (strncmp(r_code, "1", 1) == 0)
+ return 1;
+ return 0;
+}
+
+static int rpi_code_match(const char* code0, const char* code1) {
+ int c0, c1;
+ if (code0 == NULL || code1 == NULL) return 0;
+ c0 = strtol(code0, NULL, 16);
+ c1 = strtol(code1, NULL, 16);
+ if (c0 && c1)
+ return (c0 == c1) ? 1 : 0;
+ else
+ return (strcmp(code0, code1) == 0) ? 1 : 0;
+}
+
+static int rpi_find_board(const char *r_code) {
+ int i = 0;
+ char *r = (char*)r_code;
+ if (r_code == NULL)
+ return 0;
+ /* ignore the overvolt prefix */
+ r += rpi_ov_check(r_code);
+ while (rpi_boardinfo[i].value != NULL) {
+ if (rpi_code_match(r, rpi_boardinfo[i].value))
+ return i;
+
+ i++;
+ }
+ return 0;
+}
+
+/* ------------------------- */
+
+#include "cpu_util.h" /* for PROC_CPUINFO */
+
+static gchar *rpi_board_details(void) {
+ int i = 0;
+ gchar *ret = NULL;
+ gchar *soc = NULL;
+ gchar *serial = NULL;
+ gchar *revision = NULL;
+ int ov = 0;
+ FILE *cpuinfo;
+ gchar buffer[128];
+
+ cpuinfo = fopen(PROC_CPUINFO, "r");
+ if (!cpuinfo)
+ return NULL;
+ while (fgets(buffer, 128, cpuinfo)) {
+ gchar **tmp = g_strsplit(buffer, ":", 2);
+ if (tmp[1] == NULL) {
+ g_strfreev(tmp);
+ continue;
+ }
+ tmp[0] = g_strstrip(tmp[0]);
+ tmp[1] = g_strstrip(tmp[1]);
+ get_str("Revision", revision);
+ get_str("Hardware", soc);
+ get_str("Serial", serial);
+ }
+ fclose(cpuinfo);
+
+ if (revision == NULL || soc == NULL) {
+ g_free(soc);
+ g_free(revision);
+ return NULL;
+ }
+
+ ov = rpi_ov_check(revision);
+ i = rpi_find_board(revision);
+ ret = g_strdup_printf("[%s]\n"
+ "%s=%s %s\n"
+ "%s=%s\n"
+ "%s=%s\n"
+ "%s=%s\n"
+ "%s=%s\n"
+ "%s=%s\n"
+ "%s=%s\n"
+ "%s=%s\n"
+ "%s=%s\n",
+ _("Raspberry Pi"),
+ _("Board Name"), _("Raspberry Pi"), rpi_boardinfo[i].model,
+ _("PCB Revision"), rpi_boardinfo[i].pcb,
+ _("Introduction"), rpi_boardinfo[i].intro,
+ _("Manufacturer"), rpi_boardinfo[i].mfg,
+ _("RCode"), (rpi_boardinfo[i].value != unk) ? rpi_boardinfo[i].value : revision,
+ _("SOC (spec)"), rpi_boardinfo[i].soc,
+ _("Memory (spec)"), rpi_boardinfo[i].mem,
+ _("Serial Number"), serial,
+ _("Permanent overvolt bit"), (ov) ? C_("rpi-ov-bit", "Set") : C_("rpi-ov-bit", "Not set") );
+
+ g_free(soc);
+ g_free(revision);
+
+ if (rpi_boardinfo[i].mem)
+ dtree_mem_str = rpi_boardinfo[i].mem;
+
+ return ret;
+}
diff --git a/modules/devices/dmi.c b/modules/devices/dmi.c
new file mode 100644
index 00000000..34374fbe
--- /dev/null
+++ b/modules/devices/dmi.c
@@ -0,0 +1,168 @@
+/*
+ * HardInfo - Displays System Information
+ * Copyright (C) 2003-2007 L. A. F. Pereira <l@tia.mat.br>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, version 2 or later.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+/*
+ * DMI support based on patch by Stewart Adam <s.adam@diffingo.com>
+ */
+#include <unistd.h>
+#include <sys/types.h>
+
+#include "devices.h"
+#include "dmi_util.h"
+
+typedef struct _DMIInfo DMIInfo;
+
+struct _DMIInfo {
+ const gchar *name;
+ const gchar *id_str;
+ int group;
+ gboolean maybe_vendor;
+};
+
+DMIInfo dmi_info_table[] = {
+ { N_("Product"), NULL, 1 },
+ { N_("Name"), "system-product-name", 0 },
+ { N_("Family"), "system-product-family", 0 },
+ { N_("Vendor"), "system-manufacturer", 0, TRUE },
+ { N_("Version"), "system-version", 0 },
+ { N_("Serial Number"), "system-serial-number", 0 },
+ { N_("SKU"), "system-sku", 0 },
+ { N_("BIOS"), NULL, 1 },
+ { N_("Date"), "bios-release-date", 0 },
+ { N_("Vendor"), "bios-vendor", 0, TRUE },
+ { N_("Version"), "bios-version", 0 },
+ { N_("Board"), NULL, 1 },
+ { N_("Name"), "baseboard-product-name", 0 },
+ { N_("Vendor"), "baseboard-manufacturer", 0, TRUE },
+ { N_("Version"), "baseboard-version", 0 },
+ { N_("Serial Number"), "baseboard-serial-number", 0 },
+ { N_("Asset Tag"), "baseboard-asset-tag", 0 },
+ { N_("Chassis"), NULL, 1 },
+ { N_("Vendor"), "chassis-manufacturer", 0, TRUE },
+ { N_("Type"), "chassis-type", 0 },
+ { N_("Version"), "chassis-version", 0 },
+ { N_("Serial Number"), "chassis-serial-number", 0 },
+ { N_("Asset Tag"), "chassis-asset-tag", 0 },
+};
+
+gchar *dmi_info = NULL;
+
+static void add_to_moreinfo(const char *group, const char *key, char *value)
+{
+ char *new_key = g_strconcat("DMI:", group, ":", key, NULL);
+ moreinfo_add_with_prefix("DEV", new_key, g_strdup(g_strstrip(value)));
+}
+
+gboolean dmi_get_info(void)
+{
+ const gchar *group = NULL;
+ DMIInfo *info;
+ gboolean dmi_succeeded = FALSE;
+ guint i;
+ gchar *value;
+ const gchar *vendor;
+
+ if (dmi_info) {
+ g_free(dmi_info);
+ dmi_info = NULL;
+ }
+
+ for (i = 0; i < G_N_ELEMENTS(dmi_info_table); i++) {
+ info = &dmi_info_table[i];
+
+ if (info->group) {
+ group = info->name;
+ dmi_info = h_strdup_cprintf("[%s]\n", dmi_info, _(info->name));
+ } else if (group && info->id_str) {
+ int state = 3;
+
+ if (strcmp(info->id_str, "chassis-type") == 0) {
+ value = dmi_chassis_type_str(-1, 1);
+ if (value == NULL)
+ state = (getuid() == 0) ? 0 : 1;
+ }
+ else {
+ switch (dmi_str_status(info->id_str)) {
+ case 0:
+ value = NULL;
+ state = (getuid() == 0) ? 0 : 1;
+ break;
+ case -1:
+ state = 2;
+ value = dmi_get_str_abs(info->id_str);
+ break;
+ case 1:
+ value = dmi_get_str_abs(info->id_str);
+ break;
+ }
+ }
+
+ switch (state) {
+ case 0: /* no value, root */
+ dmi_info = h_strdup_cprintf("%s=%s\n", dmi_info, _(info->name),
+ _("(Not available)"));
+ break;
+ case 1: /* no value, no root */
+ dmi_info = h_strdup_cprintf("%s=%s\n", dmi_info, _(info->name),
+ _("(Not available; Perhaps try "
+ "running HardInfo as root.)"));
+ break;
+ case 2: /* ignored value */
+ if (params.markup_ok)
+ dmi_info = h_strdup_cprintf("%s=<s>%s</s>\n", dmi_info,
+ _(info->name), value);
+ else
+ dmi_info = h_strdup_cprintf("%s=[X]\"%s\"\n", dmi_info,
+ _(info->name), value);
+ break;
+ case 3: /* good value */
+ {
+ dmi_info =
+ h_strdup_cprintf("%s%s=%s\n", dmi_info,
+ info->maybe_vendor ? "$^$" : "",
+ _(info->name), value);
+ add_to_moreinfo(group, info->name, value);
+ dmi_succeeded = TRUE;
+ break;
+ }
+ }
+ }
+ }
+
+ if (!dmi_succeeded) {
+ g_free(dmi_info);
+ dmi_info = NULL;
+ }
+
+ return dmi_succeeded;
+}
+
+void __scan_dmi(void)
+{
+ gboolean dmi_ok;
+
+ dmi_ok = dmi_get_info();
+
+ if (!dmi_ok) {
+ dmi_info = g_strdup_printf("[%s]\n%s=\n",
+ _("DMI Unavailable"),
+ (getuid() == 0)
+ ? _("DMI is not avaliable. Perhaps this platform does not provide DMI.")
+ : _("DMI is not available; Perhaps try running HardInfo as root.") );
+
+ }
+}
diff --git a/modules/devices/dmi_memory.c b/modules/devices/dmi_memory.c
new file mode 100644
index 00000000..eba26c8b
--- /dev/null
+++ b/modules/devices/dmi_memory.c
@@ -0,0 +1,1050 @@
+/*
+ * HardInfo - Displays System Information
+ * Copyright (C) 2003-2019 L. A. F. Pereira <l@tia.mat.br>
+ * Copyright (C) 2019 Burt P. <pburt0@gmail.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, version 2 or later.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#define _GNU_SOURCE
+
+#include "hardinfo.h"
+#include "devices.h"
+#include "vendor.h"
+#include <inttypes.h>
+
+extern const char *dtree_mem_str; /* in devicetree.c */
+
+/* in monitors.c */
+gchar **get_output_lines(const char *cmd_line);
+
+#include "util_sysobj.h" /* for appfsp() */
+#define dmi_spd_msg(...) /* fprintf (stderr, __VA_ARGS__) */
+
+typedef uint64_t dmi_mem_size;
+
+#include "spd-decode.c"
+
+gboolean no_handles = FALSE;
+gboolean sketchy_info = FALSE;
+
+int dmi_ram_types = 0; /* bits using enum RamType */
+
+/* strings from dmidecode */
+static const char empty_mem_str[] = "No Module Installed";
+static const char unknown_mfgr_str[] = "<BAD INDEX>";
+static const char mobo_location[] = "System Board Or Motherboard";
+static const char mobo_shorter[] = "Mainboard";
+static const dmi_type dta = 16; /* array */
+static const dmi_type dtm = 17; /* socket */
+static const char mem_icon[] = "memory.png";
+static const char array_icon[] = "devices.png";
+static const char empty_icon[] = "module.png";
+
+#define UNKNOWN_MEM_TYPE_STRING _("RAM")
+#define UNKIFNULL2(f) ((f) ? f : _("(Unknown)"))
+#define UNKIFEMPTY2(f) ((*f) ? f : _("(Unknown)"))
+#define STR_IGNORE(str, ignore) if (SEQ(str, ignore)) { *str = 0; null_if_empty(&str); }
+
+dmi_mem_size dmi_read_memory_str_to_MiB(const char *memstr) {
+ dmi_mem_size ret = 0, v = 0;
+ char l[7] = "";
+ /* dmidecode units: "bytes", "kB", "MB", "GB", "TB", "PB", "EB", "ZB" */
+ int mc = sscanf(memstr, "%"PRId64" %6s", &v, l);
+ if (mc == 2) {
+ if (SEQ(l, "ZB")) ret = v * 1024 * 1024 * 1024 * 1024 * 1024;
+ else if (SEQ(l, "EB")) ret = v * 1024 * 1024 * 1024 * 1024;
+ else if (SEQ(l, "PB")) ret = v * 1024 * 1024 * 1024;
+ else if (SEQ(l, "TB")) ret = v * 1024 * 1024;
+ else if (SEQ(l, "GB")) ret = v * 1024;
+ else if (SEQ(l, "MB")) ret = v;
+ else if (SEQ(l, "kB")) {
+ /* should never appear */
+ if (v % 1024) { dmi_spd_msg("OMG kB!"); }
+ ret = v / 1024;
+ }
+ else if (SEQ(l, "bytes")) {
+ /* should never appear */
+ if (v % 1024) { dmi_spd_msg("OMG bytes!"); }
+ ret = v / (1024 * 1024);
+ }
+ }
+ return ret;
+}
+
+typedef struct {
+ dmi_handle array_handle;
+ gboolean is_main_memory;
+ gchar *locator;
+ gchar *use;
+ gchar *ecc;
+ int devs;
+ int devs_populated;
+ dmi_mem_size size_MiB_max;
+ dmi_mem_size size_MiB_present;
+ dmi_mem_size size_MiB_rom;
+ int ram_types; /* bits using enum RamType */
+} dmi_mem_array;
+
+dmi_mem_array *dmi_mem_array_new(dmi_handle h) {
+ dmi_mem_array *s = g_new0(dmi_mem_array, 1);
+ s->array_handle = h;
+ s->use = dmidecode_match("Use", &dta, &h);
+ if (SEQ(s->use, "System Memory"))
+ s->is_main_memory = TRUE;
+ s->ecc = dmidecode_match("Error Correction Type", &dta, &h);
+ s->locator = dmidecode_match("Location", &dta, &h);
+ if (SEQ(s->locator, mobo_location)) {
+ g_free(s->locator);
+ s->locator = g_strdup(mobo_shorter);
+ s->is_main_memory = TRUE;
+ }
+
+ gchar *array_max_size = dmidecode_match("Maximum Capacity", &dta, &h);
+ if (array_max_size) {
+ s->size_MiB_max = dmi_read_memory_str_to_MiB(array_max_size);
+ g_free(array_max_size);
+ }
+ gchar *array_devs = dmidecode_match("Number Of Devices", &dta, &h);
+ if (array_devs) {
+ s->devs = strtol(array_devs, NULL, 10);
+ g_free(array_devs);
+ }
+ return s;
+}
+
+void dmi_mem_array_free(dmi_mem_array* s) {
+ if (s) {
+ g_free(s->locator);
+ g_free(s->use);
+ g_free(s->ecc);
+ g_free(s);
+ }
+}
+
+typedef struct dmi_mem_socket {
+ dmi_handle handle;
+ dmi_handle array_handle;
+ gboolean populated;
+ gchar *locator;
+ gchar *full_locator;
+ gchar *short_locator;
+ gchar *size_str;
+ dmi_mem_size size_MiB;
+
+ gboolean is_not_ram; /* maybe is_rom, maybe elsewise, but don't include in RAM total */
+ gboolean is_rom;
+
+ gchar *type;
+ gchar *type_detail;
+ int ram_type; /* using enum RamType */
+ gchar *array_locator;
+ gchar *bank_locator;
+ gchar *rank;
+ gchar *form_factor;
+ gchar *speed_str;
+ gchar *configured_clock_str;
+ gchar *voltage_min_str;
+ gchar *voltage_max_str;
+ gchar *voltage_conf_str;
+ gchar *partno;
+ gchar *data_width;
+ gchar *total_width;
+ gchar *mfgr;
+ gboolean has_jedec_mfg_id;
+ int mfgr_bank, mfgr_index;
+
+ const Vendor *vendor;
+ spd_data *spd;
+} dmi_mem_socket;
+
+typedef struct {
+ gboolean empty;
+ GSList *arrays;
+ GSList *sockets;
+ GSList *spd;
+ dmi_mem_size spd_size_MiB;
+ int spd_ram_types; /* bits using enum RamType */
+
+ dmi_mem_size system_memory_MiB;
+ int system_memory_ram_types; /* bits using enum RamType */
+
+ /* ->short_locator is unique among *sockets */
+ gboolean unique_short_locators;
+} dmi_mem;
+
+gboolean null_if_empty(gchar **str) {
+ if (str && *str) {
+ gchar *p = *str;
+ while(p && *p) {
+ if (isalnum(*p))
+ return FALSE;
+ p++;
+ }
+ *str = NULL;
+ }
+ return TRUE;
+}
+
+dmi_mem_socket *dmi_mem_socket_new(dmi_handle h) {
+ dmi_mem_socket *s = g_new0(dmi_mem_socket, 1);
+ s->handle = h;
+ s->locator = dmidecode_match("Locator", &dtm, &h);
+ s->size_str = dmidecode_match("Size", &dtm, &h);
+ if (s->size_str)
+ s->size_MiB = dmi_read_memory_str_to_MiB(s->size_str);
+
+ s->bank_locator = dmidecode_match("Bank Locator", &dtm, &h);
+ STR_IGNORE(s->bank_locator, "Unknown");
+ STR_IGNORE(s->bank_locator, "Not Specified");
+ null_if_empty(&s->bank_locator);
+
+ gchar *ah = dmidecode_match("Array Handle", &dtm, &h);
+ STR_IGNORE(ah, "Unknown");
+ if (ah) {
+ s->array_handle = strtol(ah, NULL, 16);
+ g_free(ah);
+ s->array_locator = dmidecode_match("Location", &dta, &s->array_handle);
+ if (SEQ(s->array_locator, mobo_location)) {
+ g_free(s->array_locator);
+ s->array_locator = g_strdup(mobo_shorter);
+ }
+ }
+
+ gchar *ah_str = g_strdup_printf("0x%"PRIx32, s->array_handle);
+ gchar *h_str = g_strdup_printf("0x%"PRIx32, s->handle);
+ s->short_locator = g_strdup_printf("%s \u27A4 %s",
+ s->array_locator ? s->array_locator : ah_str,
+ s->locator ? s->locator : h_str);
+
+ if (s->bank_locator)
+ s->full_locator = g_strdup_printf("%s \u27A4 %s \u27A4 %s",
+ s->array_locator ? s->array_locator : ah_str,
+ s->bank_locator,
+ s->locator ? s->locator : h_str);
+ else
+ s->full_locator = g_strdup(s->short_locator);
+
+ g_free(ah_str);
+ g_free(h_str);
+
+ if (!g_str_has_prefix(s->size_str, empty_mem_str)) {
+ s->populated = 1;
+
+ s->form_factor = dmidecode_match("Form Factor", &dtm, &h);
+ s->type = dmidecode_match("Type", &dtm, &h);
+ STR_IGNORE(s->type, "Unknown");
+ if (SEQ(s->type, "Flash") || SEQ(s->type, "ROM")) {
+ s->is_rom = TRUE;
+ s->is_not_ram = TRUE;
+ } else {
+ if (SEQ(s->type, "DDR")) s->ram_type = DDR_SDRAM;
+ if (SEQ(s->type, "DDR2")) s->ram_type = DDR2_SDRAM;
+ if (SEQ(s->type, "DDR3")) s->ram_type = DDR3_SDRAM;
+ if (SEQ(s->type, "DDR4")) s->ram_type = DDR4_SDRAM;
+ if (SEQ(s->type, "DRDRAM")) s->ram_type = DIRECT_RAMBUS;
+ if (SEQ(s->type, "RDRAM")) s->ram_type = RAMBUS;
+ if (s->ram_type)
+ dmi_ram_types |= (1 << (s->ram_type-1));
+ }
+ s->type_detail = dmidecode_match("Type Detail", &dtm, &h);
+ STR_IGNORE(s->type_detail, "None");
+
+ s->speed_str = dmidecode_match("Speed", &dtm, &h);
+ s->configured_clock_str = dmidecode_match("Configured Clock Speed", &dtm, &h);
+ if (!s->configured_clock_str)
+ s->configured_clock_str = dmidecode_match("Configured Memory Speed", &dtm, &h);
+
+ s->voltage_min_str = dmidecode_match("Minimum Voltage", &dtm, &h);
+ s->voltage_max_str = dmidecode_match("Maximum Voltage", &dtm, &h);
+ s->voltage_conf_str = dmidecode_match("Configured Voltage", &dtm, &h);
+ STR_IGNORE(s->voltage_min_str, "Unknown");
+ STR_IGNORE(s->voltage_max_str, "Unknown");
+ STR_IGNORE(s->voltage_conf_str, "Unknown");
+
+ s->partno = dmidecode_match("Part Number", &dtm, &h);
+ STR_IGNORE(s->partno, "PartNum0");
+ STR_IGNORE(s->partno, "PartNum1");
+ STR_IGNORE(s->partno, "PartNum2");
+ STR_IGNORE(s->partno, "PartNum3");
+ null_if_empty(&s->partno);
+
+ s->data_width = dmidecode_match("Data Width", &dtm, &h);
+ s->total_width = dmidecode_match("Total Width", &dtm, &h);
+
+ s->rank = dmidecode_match("Rank", &dtm, &h);
+
+ s->mfgr = dmidecode_match("Manufacturer", &dtm, &h);
+ STR_IGNORE(s->mfgr, unknown_mfgr_str);
+ STR_IGNORE(s->mfgr, "Manufacturer0");
+ STR_IGNORE(s->mfgr, "Manufacturer1");
+ STR_IGNORE(s->mfgr, "Manufacturer2");
+ STR_IGNORE(s->mfgr, "Manufacturer3");
+ STR_IGNORE(s->mfgr, "Unknown");
+ null_if_empty(&s->mfgr);
+
+ gchar *mfgr_id_str = dmidecode_match("Module Manufacturer ID", &dtm, &h);
+ STR_IGNORE(mfgr_id_str, "Unknown");
+ if (mfgr_id_str) {
+ static const char dmi_mfg_id_fmt[] = "Bank %d, Hex 0x%02X"; /* from dmidecode.c */
+ int mc = sscanf(strstr(mfgr_id_str, "Bank"), dmi_mfg_id_fmt, &s->mfgr_bank, &s->mfgr_index);
+ if (mc > 0 && !s->mfgr) {
+ s->has_jedec_mfg_id = TRUE;
+ s->mfgr = g_strdup(JEDEC_MFG_STR(s->mfgr_bank, s->mfgr_index));
+ }
+ }
+
+ if (s->mfgr && !s->has_jedec_mfg_id && strlen(s->mfgr) == 4) {
+ /* Some BIOS put the code bytes into the mfgr string
+ * if they don't know the manufacturer.
+ * It's not always reliable, but what is lost
+ * by trying it? */
+ if (isxdigit(s->mfgr[0])
+ && isxdigit(s->mfgr[1])
+ && isxdigit(s->mfgr[2])
+ && isxdigit(s->mfgr[3]) ) {
+ int codes = strtol(s->mfgr, NULL, 16);
+ char *mstr = NULL;
+ decode_ddr34_manufacturer(codes >> 8, codes & 0xff,
+ &mstr, &s->mfgr_bank, &s->mfgr_index);
+ s->has_jedec_mfg_id = TRUE;
+ g_free(s->mfgr);
+ s->mfgr = NULL;
+ if (mstr)
+ s->mfgr = g_strdup(mstr);
+ }
+ }
+
+ s->vendor = vendor_match(s->mfgr, NULL);
+ }
+ return s;
+}
+
+void dmi_mem_socket_free(dmi_mem_socket* s) {
+ if (s) {
+ g_free(s->locator);
+ g_free(s->full_locator);
+ g_free(s->short_locator);
+ g_free(s->size_str);
+ g_free(s->type);
+ g_free(s->type_detail);
+ g_free(s->bank_locator);
+ g_free(s->rank);
+ g_free(s->array_locator);
+ g_free(s->form_factor);
+ g_free(s->speed_str);
+ g_free(s->configured_clock_str);
+ g_free(s->voltage_min_str);
+ g_free(s->voltage_max_str);
+ g_free(s->voltage_conf_str);
+ g_free(s->partno);
+ g_free(s->data_width);
+ g_free(s->total_width);
+ g_free(s->mfgr);
+
+ g_free(s);
+ }
+}
+
+dmi_mem_array *dmi_mem_find_array(dmi_mem *s, unsigned int handle) {
+ GSList *l = NULL;
+ for(l = s->arrays; l; l = l->next) {
+ dmi_mem_array *a = (dmi_mem_array*)l->data;
+ if (a->array_handle == handle)
+ return a;
+ }
+ return NULL;
+}
+
+static int dmi_spd_match_score(dmi_mem_socket *s, spd_data *e) {
+ int score = 0;
+ if (SEQ(s->partno, e->partno))
+ score += 20;
+ if (s->size_MiB == e->size_MiB)
+ score += 10;
+ if (s->vendor == e->vendor)
+ score += 5;
+ return score;
+}
+
+/* fill in missing from SPD */
+static void dmi_fill_from_spd(dmi_mem_socket *s) {
+ if (!s->spd)
+ return;
+
+ if (!s->mfgr && s->spd->vendor_str) {
+ s->mfgr = g_strdup(s->spd->vendor_str);
+ s->vendor = s->spd->vendor;
+ }
+ if (!s->has_jedec_mfg_id) {
+ s->mfgr_bank = s->spd->vendor_bank;
+ s->mfgr_index = s->spd->vendor_index;
+ s->has_jedec_mfg_id = TRUE;
+ }
+
+ //Always true - FIXME
+ //if (!s->partno && s->spd->partno)
+ s->partno = g_strdup(s->spd->partno);
+
+ if (!s->form_factor && s->spd->form_factor)
+ s->form_factor = g_strdup(s->spd->form_factor);
+
+ //Always true - FIXME
+ //if (!s->type_detail && s->spd->type_detail)
+ s->type_detail = g_strdup(s->spd->type_detail);
+}
+
+static dmi_mem_size size_of_online_memory_blocks() {
+ gchar *block_size_bytes_str = NULL;
+ dmi_mem_size block_size_bytes = 0;
+ dmi_mem_size ret = 0;
+
+ if (g_file_get_contents("/sys/devices/system/memory/block_size_bytes", &block_size_bytes_str, NULL, NULL) ) {
+ block_size_bytes = strtoll(block_size_bytes_str, NULL, 16);
+ }
+ if (!block_size_bytes)
+ return 0;
+
+ const gchar *f = NULL;
+ GDir *d = g_dir_open("/sys/devices/system/memory", 0, NULL);
+ if (!d) return 0;
+
+ while((f = g_dir_read_name(d))) {
+ gchar *p = g_strdup_printf("/sys/devices/system/memory/%s/online", f);
+ gchar *ol = NULL;
+ if (g_file_get_contents(p, &ol, NULL, NULL) ) {
+ if (1 == strtol(ol, NULL, 0)) {
+ ret += block_size_bytes;
+ }
+ }
+ g_free(ol);
+ g_free(p);
+ }
+ g_dir_close(d);
+ return ret;
+}
+
+dmi_mem *dmi_mem_new() {
+ dmi_mem *m = g_new0(dmi_mem, 1);
+
+ dmi_handle_list *hla = dmidecode_handles(&dta);
+ if (hla) {
+ unsigned int i = 0;
+ for(i = 0; i < hla->count; i++) {
+ dmi_handle h = hla->handles[i];
+ m->arrays = g_slist_append(m->arrays, dmi_mem_array_new(h));
+ }
+ dmi_handle_list_free(hla);
+ }
+
+ dmi_handle_list *hlm = dmidecode_handles(&dtm);
+ if (hlm) {
+ unsigned int i = 0;
+ for(i = 0; i < hlm->count; i++) {
+ dmi_handle h = hlm->handles[i];
+ m->sockets = g_slist_append(m->sockets, dmi_mem_socket_new(h));
+ }
+ dmi_handle_list_free(hlm);
+ }
+
+ m->spd = spd_scan();
+
+ if (!m->sockets && !m->arrays && !m->spd) {
+ m->empty = 1;
+ goto dmi_mem_new_last_chance;
+ }
+
+ GSList *l = NULL, *l2 = NULL;
+
+ /* totals for SPD */
+ for(l2 = m->spd; l2; l2 = l2->next) {
+ spd_data *e = (spd_data*)l2->data;
+ m->spd_size_MiB += e->size_MiB;
+ if (e->type)
+ m->spd_ram_types |= (1 << (e->type-1));
+ }
+
+ m->unique_short_locators = TRUE;
+ for(l = m->sockets; l; l = l->next) {
+ dmi_mem_socket *s = (dmi_mem_socket*)l->data;
+
+ /* check for duplicate short_locator */
+ if (m->unique_short_locators) {
+ for(l2 = l->next; l2; l2 = l2->next) {
+ dmi_mem_socket *d = (dmi_mem_socket*)l2->data;
+ if (SEQ(s->short_locator, d->short_locator)) {
+ m->unique_short_locators = FALSE;
+ break;
+ }
+ }
+ }
+
+ /* update array present devices/size */
+ dmi_mem_array *a = dmi_mem_find_array(m, s->array_handle);
+ if (a) {
+ if (s->is_not_ram) {
+ if (s->is_rom)
+ a->size_MiB_rom += s->size_MiB;
+ } else {
+ a->size_MiB_present += s->size_MiB;
+ if (s->populated)
+ a->devs_populated++;
+ if (s->ram_type)
+ a->ram_types |= (1 << (s->ram_type-1));
+ }
+ }
+ }
+
+ if (m->sockets && m->spd) {
+ /* attempt to match DMI and SPD data */
+ GSList *sock_queue = g_slist_copy(m->sockets);
+ int loops = g_slist_length(sock_queue) * 4;
+ while(sock_queue) {
+ if (loops-- <= 0) break; /* something is wrong, give up */
+ spd_data *best = NULL;
+ int best_score = 0;
+ dmi_mem_socket *s = (dmi_mem_socket*)sock_queue->data;
+ /* pop that one off */
+ sock_queue = g_slist_delete_link(sock_queue, sock_queue);
+ if (!s->populated)
+ continue;
+ for(l2 = m->spd; l2; l2 = l2->next) {
+ spd_data *e = (spd_data*)l2->data;
+ int score = dmi_spd_match_score(s, e);
+ if (score > best_score) {
+ if (score > e->match_score) {
+ best = e;
+ best_score = score;
+ }
+ }
+ }
+ if (best) {
+ if (best->dmi_socket) {
+ /* displace */
+ dmi_mem_socket *old_sock = best->dmi_socket;
+ old_sock->spd = NULL;
+ sock_queue = g_slist_append(sock_queue, old_sock);
+
+ best->dmi_socket = s;
+ best->match_score = best_score;
+ s->spd = best;
+ } else {
+ best->dmi_socket = s;
+ best->match_score = best_score;
+ s->spd = best;
+ }
+ }
+ }
+
+ /* fill any missing data in DMI that is
+ * provided by the matched SPD */
+ for(l = m->sockets; l; l = l->next) {
+ dmi_mem_socket *s = (dmi_mem_socket*)l->data;
+ dmi_fill_from_spd(s);
+ }
+
+ } /* end if (m->sockets && m->spd) */
+
+ /* Look for arrays with "System Memory" use,
+ * or Mainboard as locator */
+ for(l = m->arrays; l; l = l->next) {
+ dmi_mem_array *a = (dmi_mem_array*)l->data;
+ if (a->is_main_memory) {
+ m->system_memory_MiB += a->size_MiB_present;
+ m->system_memory_ram_types |= a->ram_types;
+ }
+ }
+
+ /* If no arrays, then try the SPD total */
+ if (!m->system_memory_MiB) {
+ m->system_memory_MiB = m->spd_size_MiB;
+ m->system_memory_ram_types |= m->spd_ram_types;
+ }
+
+dmi_mem_new_last_chance:
+ if (m->empty) {
+ /* reach */
+ if (dtree_mem_str) {
+ int rt = 0;
+ m->system_memory_MiB = dmi_read_memory_str_to_MiB(dtree_mem_str);
+ if (strstr(dtree_mem_str, "DDR4")) rt = DDR4_SDRAM;
+ else if (strstr(dtree_mem_str, "DDR3")) rt = DDR3_SDRAM;
+ else if (strstr(dtree_mem_str, "DDR2")) rt = DDR2_SDRAM;
+ else if (strstr(dtree_mem_str, "DDR")) rt = DDR_SDRAM;
+ else if (strstr(dtree_mem_str, "DRDRAM")) rt = DIRECT_RAMBUS;
+ else if (strstr(dtree_mem_str, "RDRAM")) rt = RAMBUS;
+ if (rt)
+ m->system_memory_ram_types |= (1 << (rt-1));
+ }
+ }
+
+ /* Try to sum the online blocks for a physical memory total */
+ if (!m->system_memory_MiB)
+ m->system_memory_MiB = size_of_online_memory_blocks() / 1024 / 1024;
+
+ return m;
+}
+
+void dmi_mem_free(dmi_mem* s) {
+ if (s) {
+ g_slist_free_full(s->arrays, (GDestroyNotify)dmi_mem_array_free);
+ g_slist_free_full(s->sockets, (GDestroyNotify)dmi_mem_socket_free);
+ g_slist_free_full(s->spd, (GDestroyNotify)spd_data_free);
+ g_free(s);
+ }
+}
+
+gchar *make_spd_section(spd_data *spd) {
+ gchar *ret = NULL;
+ if (spd) {
+ gchar *full_spd = NULL;
+ switch(spd->type) {
+ case SDR_SDRAM:
+ full_spd = decode_sdr_sdram_extra(spd->bytes);
+ break;
+ case DDR_SDRAM:
+ full_spd = decode_ddr_sdram_extra(spd->bytes);
+ break;
+ case DDR2_SDRAM:
+ full_spd = decode_ddr2_sdram_extra(spd->bytes);
+ break;
+ case DDR3_SDRAM:
+ full_spd = decode_ddr3_sdram_extra(spd->bytes);
+ break;
+ case DDR4_SDRAM:
+ full_spd = decode_ddr4_sdram_extra(spd->bytes, spd->spd_size);
+ break;
+ default:
+ DEBUG("blug for type: %d %s\n", spd->type, ram_types[spd->type]);
+ }
+ gchar *size_str = NULL;
+ if (!spd->size_MiB)
+ size_str = g_strdup(_("(Unknown)"));
+ else
+ size_str = g_strdup_printf("%"PRId64" %s", spd->size_MiB, _("MiB") );
+
+ gchar *mfg_date_str = NULL;
+ if (spd->year)
+ mfg_date_str = g_strdup_printf("%d / %d", spd->week, spd->year);
+
+ ret = g_strdup_printf("[%s]\n"
+ "%s=%s (%s)%s\n"
+ "%s=%d.%d\n"
+ "%s=%s\n"
+ "%s=%s\n"
+ "$^$%s=[%02x%02x] %s\n" /* module vendor */
+ "$^$%s=[%02x%02x] %s\n" /* dram vendor */
+ "%s=%s\n" /* part */
+ "%s=%s\n" /* size */
+ "%s=%s\n" /* mfg date */
+ "%s",
+ _("Serial Presence Detect (SPD)"),
+ _("Source"), spd->dev, spd->spd_driver,
+ (spd->type == DDR4_SDRAM && strcmp(spd->spd_driver, "ee1004") != 0) ? problem_marker() : "",
+ _("SPD Revision"), spd->spd_rev_major, spd->spd_rev_minor,
+ _("Form Factor"), UNKIFNULL2(spd->form_factor),
+ _("Type"), UNKIFEMPTY2(spd->type_detail),
+ _("Module Vendor"), spd->vendor_bank, spd->vendor_index,
+ UNKIFNULL2(spd->vendor_str),
+ _("DRAM Vendor"), spd->dram_vendor_bank, spd->dram_vendor_index,
+ UNKIFNULL2(spd->dram_vendor_str),
+ _("Part Number"), UNKIFEMPTY2(spd->partno),
+ _("Size"), size_str,
+ _("Manufacturing Date (Week / Year)"), UNKIFNULL2(mfg_date_str),
+ full_spd ? full_spd : ""
+ );
+ g_free(full_spd);
+ g_free(size_str);
+ g_free(mfg_date_str);
+ }
+ return ret;
+}
+
+static gchar *tag_make_safe_inplace(gchar *tag) {
+ if (!tag)
+ return tag;
+ if (!g_utf8_validate(tag, -1, NULL))
+ return tag; //TODO: reconsider
+ gchar *p = tag, *pd = tag;
+ while(*p) {
+ gchar *np = g_utf8_next_char(p);
+ gunichar c = g_utf8_get_char_validated(p, -1);
+ int l = g_unichar_to_utf8(c, NULL);
+ if (l == 1 && g_unichar_isalnum(c)) {
+ g_unichar_to_utf8(c, pd);
+ } else {
+ *pd = '_';
+ }
+ p = np;
+ pd++;
+ }
+ return tag;
+}
+
+gchar *memory_devices_get_info() {
+ gchar *icons = g_strdup("");
+ gchar *ret = g_strdup_printf("[%s]\n", _("Memory Device List"));
+ GSList *l = NULL;
+ sketchy_info = FALSE;
+ gchar tag_prefix[] = "DEV";
+
+ dmi_mem *mem = dmi_mem_new();
+
+ /* Arrays */
+ for(l = mem->arrays; l; l = l->next) {
+ dmi_mem_array *a = (dmi_mem_array*)l->data;
+ gchar *tag = g_strdup_printf("%s", a->locator);
+ gchar *size_str = NULL, *rom_size_str = NULL;
+
+ tag_make_safe_inplace(tag);
+
+ if (a->size_MiB_max > 1024 && (a->size_MiB_max % 1024 == 0)
+ && a->size_MiB_present > 1024 && (a->size_MiB_present % 1024 == 0) )
+ size_str = g_strdup_printf("%"PRId64" / %"PRId64" %s", a->size_MiB_present / 1024, a->size_MiB_max / 1024, _("GiB"));
+ else
+ size_str = g_strdup_printf("%"PRId64" / %"PRId64" %s", a->size_MiB_present, a->size_MiB_max, _("MiB"));
+
+ if (a->size_MiB_max < a->size_MiB_present) {
+ sketchy_info = TRUE;
+ size_str = h_strdup_cprintf(" %s", size_str, problem_marker());
+ }
+
+ if (a->size_MiB_rom > 1024 && (a->size_MiB_rom % 1024 == 0))
+ rom_size_str = g_strdup_printf("%"PRId64" %s", a->size_MiB_rom / 1024, _("GiB"));
+ else
+ rom_size_str = g_strdup_printf("%"PRId64" %s", a->size_MiB_rom, _("MiB"));
+
+ gchar *types_str = NULL;
+ int i;
+ for(i = 1; i < N_RAM_TYPES; i++) {
+ int bit = 1 << (i-1);
+ if (a->ram_types & bit)
+ types_str = appfsp(types_str, "%s", GET_RAM_TYPE_STR(i));
+ }
+
+ gchar *details = g_strdup_printf("[%s]\n"
+ "%s=0x%"PRIx32"\n"
+ "%s=%s\n"
+ "%s=%s\n"
+ "%s=%s\n"
+ "%s=%s\n"
+ "%s=%d / %d\n"
+ "%s=[0x%x] %s\n"
+ "%s=%s\n",
+ _("Memory Array"),
+ _("DMI Handle"), a->array_handle,
+ _("Locator"), a->locator ? a->locator : ".",
+ _("Use"), UNKIFNULL2(a->use),
+ _("Error Correction Type"), UNKIFNULL2(a->ecc),
+ _("Size (Present / Max)"), size_str,
+ _("Devices (Populated / Sockets)"), a->devs_populated, a->devs,
+ _("Types Present"), a->ram_types, UNKIFNULL2(types_str),
+ _("ROM Size"), rom_size_str
+ );
+ moreinfo_add_with_prefix(tag_prefix, tag, details); /* moreinfo now owns *details */
+ ret = h_strdup_cprintf("$!%s$%s=%s|%s\n",
+ ret,
+ tag, a->locator, UNKIFNULL2(types_str), size_str
+ );
+ icons = h_strdup_cprintf("Icon$%s$=%s\n", icons, tag, array_icon);
+ g_free(tag);
+ g_free(size_str);
+ g_free(rom_size_str);
+ g_free(types_str);
+ }
+
+ /* Sockets */
+ for(l = mem->sockets; l; l = l->next) {
+ dmi_mem_socket *s = (dmi_mem_socket*)l->data;
+ gchar *tag = g_strdup_printf("%s", s->full_locator);
+ tag_make_safe_inplace(tag);
+
+ if (s->populated) {
+ gchar *size_str = NULL;
+ if (!s->size_str)
+ size_str = g_strdup(_("(Unknown)"));
+ else if (!s->size_MiB)
+ size_str = g_strdup(s->size_str);
+ else
+ size_str = g_strdup_printf("%"PRId64" %s", s->size_MiB, _("MiB") );
+
+ gchar *spd = s->spd ? make_spd_section(s->spd) : NULL;
+
+ gchar *details = g_strdup_printf("[%s]\n"
+ "%s=0x%"PRIx32", 0x%"PRIx32"\n"
+ "%s=%s\n"
+ "%s=%s\n"
+ "%s=%s\n"
+ "%s=%s / %s\n"
+ "$^$%s=[%02x%02x] %s\n"
+ "%s=%s\n"
+ "%s=%s\n"
+ "%s=%s\n"
+ "%s=%s\n"
+ "%s=%s / %s\n"
+ "%s=%s\n"
+ "%s=%s\n"
+ "%s=%s\n"
+ "%s=%s\n"
+ "%s", /* spd */
+ _("Memory Socket"),
+ _("DMI Handles (Array, Socket)"), s->array_handle, s->handle,
+ _("Locator"), s->full_locator,
+ _("Bank Locator"), UNKIFNULL2(s->bank_locator),
+ _("Form Factor"), UNKIFNULL2(s->form_factor),
+ _("Type"), UNKIFNULL2(s->type), UNKIFNULL2(s->type_detail),
+ _("Vendor"),
+ s->mfgr_bank, s->mfgr_index, UNKIFNULL2(s->mfgr),
+ _("Part Number"), UNKIFNULL2(s->partno),
+ _("Size"), size_str,
+ _("Rated Speed"), UNKIFNULL2(s->speed_str),
+ _("Configured Speed"), UNKIFNULL2(s->configured_clock_str),
+ _("Data Width/Total Width"), UNKIFNULL2(s->data_width), UNKIFNULL2(s->total_width),
+ _("Rank"), UNKIFNULL2(s->rank),
+ _("Minimum Voltage"), UNKIFNULL2(s->voltage_min_str),
+ _("Maximum Voltage"), UNKIFNULL2(s->voltage_max_str),
+ _("Configured Voltage"), UNKIFNULL2(s->voltage_conf_str),
+ spd ? spd : ""
+ );
+ g_free(spd);
+ moreinfo_add_with_prefix(tag_prefix, tag, details); /* moreinfo now owns *details */
+ gchar *mfgr = s->mfgr ? vendor_match_tag(s->mfgr, params.fmt_opts) : NULL;
+ ret = h_strdup_cprintf("$!%s$%s=%s|%s|%s\n",
+ ret,
+ tag,
+ mem->unique_short_locators ? s->short_locator : s->full_locator,
+ UNKIFNULL2(s->partno), size_str, UNKIFNULL2(mfgr)
+ );
+ icons = h_strdup_cprintf("Icon$%s$=%s\n", icons, tag, mem_icon);
+ g_free(size_str);
+ g_free(mfgr);
+ } else {
+ gchar *details = g_strdup_printf("[%s]\n"
+ "%s=0x%"PRIx32", 0x%"PRIx32"\n"
+ "%s=%s\n"
+ "%s=%s\n"
+ "%s=%s\n",
+ _("Memory Socket"),
+ _("DMI Handles (Array, Socket)"), s->array_handle, s->handle,
+ _("Locator"), s->full_locator,
+ _("Bank Locator"), UNKIFNULL2(s->bank_locator),
+ _("Size"), _("(Empty)")
+ );
+ moreinfo_add_with_prefix(tag_prefix, tag, details); /* moreinfo now owns *details */
+ ret = h_strdup_cprintf("$%s$%s=|%s\n",
+ ret,
+ tag,
+ mem->unique_short_locators ? s->short_locator : s->full_locator,
+ _("(Empty)")
+ );
+ icons = h_strdup_cprintf("Icon$%s$=%s\n", icons, tag, empty_icon);
+ }
+ g_free(tag);
+ }
+
+ /* No DMI Array, show SPD totals */
+ if (!mem->arrays && mem->spd) {
+ gchar *key = g_strdup("SPD:*");
+ gchar *tag = g_strdup(key);
+ tag_make_safe_inplace(tag);
+ gchar *types_str = NULL;
+ gchar *size_str = NULL;
+
+ if (mem->spd_size_MiB > 1024 && (mem->spd_size_MiB % 1024 == 0) )
+ size_str = g_strdup_printf("%"PRId64" %s", mem->spd_size_MiB / 1024, _("GiB"));
+ else
+ size_str = g_strdup_printf("%"PRId64" %s", mem->spd_size_MiB, _("MiB"));
+
+ int i;
+ for(i = 1; i < N_RAM_TYPES; i++) {
+ int bit = 1 << (i-1);
+ if (mem->spd_ram_types & bit)
+ types_str = appfsp(types_str, "%s", GET_RAM_TYPE_STR(i));
+ }
+
+ gchar *details = g_strdup_printf("[%s]\n"
+ "%s=%s\n"
+ "%s=%d\n"
+ "%s=[0x%x] %s\n",
+ _("Serial Presence Detect (SPD) Summary"),
+ _("Size"), size_str,
+ _("Devices"), g_slist_length(mem->spd),
+ _("Types Present"), mem->spd_ram_types, UNKIFNULL2(types_str)
+ );
+ moreinfo_add_with_prefix(tag_prefix, tag, details); /* moreinfo now owns *details */
+ ret = h_strdup_cprintf("$!%s$%s=%s|%s\n",
+ ret,
+ tag, key, UNKIFNULL2(types_str), size_str
+ );
+ icons = h_strdup_cprintf("Icon$%s$=%s\n", icons, tag, array_icon);
+ g_free(key);
+ g_free(tag);
+ g_free(size_str);
+ g_free(types_str);
+ }
+
+ /* Unmatched SPD */
+ for(l = mem->spd; l; l = l->next) {
+ spd_data *s = (spd_data*)l->data;
+ if (s->dmi_socket) continue; /* claimed by DMI */
+ gchar *key = g_strdup_printf("SPD:%s", s->dev);
+ gchar *tag = g_strdup(key);
+ tag_make_safe_inplace(tag);
+
+ gchar *vendor_str = NULL;
+ if (s->vendor) {
+ vendor_str = vendor_get_link_from_vendor(s->vendor);
+ }
+ gchar *size_str = NULL;
+ if (!s->size_MiB)
+ size_str = g_strdup(_("(Unknown)"));
+ else
+ size_str = g_strdup_printf("%"PRId64" %s", s->size_MiB, _("MiB") );
+
+ gchar *details = make_spd_section(s);
+
+ moreinfo_add_with_prefix(tag_prefix, tag, details); /* moreinfo now owns *details */
+ const gchar *mfgr = s->vendor_str ? vendor_get_shortest_name(s->vendor_str) : NULL;
+ ret = h_strdup_cprintf("$!%s$%s%s=%s|%s|%s\n",
+ ret,
+ tag, key, problem_marker(), UNKIFEMPTY2(s->partno), size_str, UNKIFNULL2(mfgr)
+ );
+ icons = h_strdup_cprintf("Icon$%s$=%s\n", icons, tag, mem_icon);
+ g_free(vendor_str);
+ g_free(size_str);
+ g_free(key);
+ g_free(tag);
+ }
+
+ no_handles = FALSE;
+ if(mem->empty) {
+ no_handles = TRUE;
+ g_free(ret);
+ ret = g_strdup_printf("[%s]\n%s=%s\n" "[$ShellParam$]\nViewType=0\n",
+ _("Memory Device List"), _("Result"),
+ (getuid() == 0)
+ ? _("(Not available)")
+ : _("(Not available; Perhaps try running HardInfo as root.)") );
+ } else {
+ ret = h_strdup_cprintf(
+ "[$ShellParam$]\nViewType=1\n"
+ "ColumnTitle$TextValue=%s\n" /* Locator */
+ "ColumnTitle$Extra1=%s\n" /* Size */
+ "ColumnTitle$Extra2=%s\n" /* Vendor */
+ "ColumnTitle$Value=%s\n" /* Part */
+ "ShowColumnHeaders=true\n"
+ "%s",
+ ret,
+ _("Locator"),
+ _("Size"),
+ _("Vendor"),
+ _("Part"),
+ icons
+ );
+ }
+
+ g_free(icons);
+ dmi_mem_free(mem);
+ return ret;
+}
+
+gchar *memory_devices_get_system_memory_types_str() {
+ gchar *ret = NULL, *types_str = NULL;
+ int i, rtypes;
+
+ dmi_mem *lmem = dmi_mem_new();
+ rtypes = lmem->system_memory_ram_types;
+ dmi_mem_free(lmem);
+
+ for(i = 1; i < N_RAM_TYPES; i++) {
+ int bit = 1 << (i-1);
+ if (rtypes & bit)
+ types_str = appfsp(types_str, "%s", GET_RAM_TYPE_STR(i));
+ }
+
+ if (types_str)
+ ret = g_strdup(types_str);
+ else
+ ret = g_strdup(UNKNOWN_MEM_TYPE_STRING);
+ g_free(types_str);
+ return ret;
+}
+
+uint64_t memory_devices_get_system_memory_MiB() {
+ dmi_mem *mem = dmi_mem_new();
+ int ret = (int)mem->system_memory_MiB;
+ dmi_mem_free(mem);
+ return ret;
+}
+
+gchar *memory_devices_get_system_memory_str() {
+ gchar *ret = NULL;
+ dmi_mem_size m = memory_devices_get_system_memory_MiB();
+ if (m) {
+ if (m > 1024 && (m % 1024 == 0) )
+ ret = g_strdup_printf("%"PRId64" %s", m/1024, _("GiB"));
+ else
+ ret = g_strdup_printf("%"PRId64" %s", m, _("MiB"));
+ }
+ return ret;
+}
+
+static gchar note_state[note_max_len] = "";
+
+gboolean memory_devices_hinote(const char **msg) {
+ gchar *want_dmi = _(" <b><i>dmidecode</i></b> utility available");
+ gchar *want_root = _(" ... <i>and</i> HardInfo running with superuser privileges");
+ gchar *want_at24 = _(" <b><i>at24</i></b> (or eeprom) module loaded (for SDR, DDR, DDR2, DDR3)");
+ gchar *want_ee1004 = _(" ... <i>or</i> <b><i>ee1004</i></b> module loaded <b>and configured!</b> (for DDR4)");
+
+ gboolean has_root = (getuid() == 0);
+ gboolean has_dmi = !no_handles;
+ gboolean has_at24eep = g_file_test("/sys/bus/i2c/drivers/at24", G_FILE_TEST_IS_DIR) ||
+ g_file_test("/sys/bus/i2c/drivers/eeprom", G_FILE_TEST_IS_DIR);
+ gboolean has_ee1004 = g_file_test("/sys/bus/i2c/drivers/ee1004", G_FILE_TEST_IS_DIR);
+
+ *note_state = 0; /* clear */
+ note_printf(note_state, "%s\n", _("Memory information requires <b>one or both</b> of the following:"));
+ note_print(note_state, "<tt>1. </tt>");
+ note_cond_bullet(has_dmi, note_state, want_dmi);
+ note_print(note_state, "<tt> </tt>");
+ note_cond_bullet(has_root, note_state, want_root);
+ note_print(note_state, "<tt>2. </tt>");
+ note_cond_bullet(has_at24eep, note_state, want_at24);
+ note_print(note_state, "<tt> </tt>");
+ note_cond_bullet(has_ee1004, note_state, want_ee1004);
+ g_strstrip(note_state); /* remove last \n */
+
+ gboolean ddr3_ee1004 = ((dmi_ram_types & (1<<(DDR3_SDRAM-1))) && has_ee1004);
+
+ gboolean best_state = FALSE;
+ if (has_dmi && has_root &&
+ ((has_at24eep && !spd_ddr4_partial_data)
+ || (has_ee1004 && !ddr3_ee1004) ) )
+ best_state = TRUE;
+
+ if (!best_state) {
+ *msg = note_state;
+ return TRUE;
+ }
+
+ if (sketchy_info) {
+ *msg = g_strdup(
+ _("\"More often than not, information contained in the DMI tables is inaccurate,\n"
+ "incomplete or simply wrong.\" -<i><b>dmidecode</b></i> manual page"));
+ return TRUE;
+ }
+
+ return FALSE;
+}
diff --git a/modules/devices/e2k/processor.c b/modules/devices/e2k/processor.c
new file mode 100644
index 00000000..74476853
--- /dev/null
+++ b/modules/devices/e2k/processor.c
@@ -0,0 +1,420 @@
+/*
+ * HardInfo - Displays System Information
+ * Copyright (C) 2020 EntityFX <artem.solopiy@gmail.com> and MCST Elbrus Team
+ * modified by Boris Afonot <boris.afonot@gmail.com> (2022)
+ * 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 "devices.h"
+#include "cpu_util.h"
+
+static gchar *__cache_get_info_as_string(Processor *processor)
+{
+ gchar *result = g_strdup("");
+ GSList *cache_list;
+ ProcessorCache *cache;
+
+ if (!processor->cache) {
+ return g_strdup(_("Cache information not available=\n"));
+ }
+
+ for (cache_list = processor->cache; cache_list; cache_list = cache_list->next) {
+ cache = (ProcessorCache *)cache_list->data;
+
+ result = h_strdup_cprintf(_("Level %d (%s)=%d-way set-associative, %d sets, %dKB size\n"),
+ result,
+ cache->level,
+ C_("cache-type", cache->type),
+ cache->ways_of_associativity,
+ cache->number_of_sets,
+ cache->size);
+ }
+
+ return result;
+}
+
+/* This is not used directly, but creates translatable strings for
+ * the type string returned from /sys/.../cache */
+static const char* cache_types[] = {
+ NC_("cache-type", /*/cache type, as appears in: Level 1 (Data)*/ "Data"),
+ NC_("cache-type", /*/cache type, as appears in: Level 1 (Instruction)*/ "Instruction"),
+ NC_("cache-type", /*/cache type, as appears in: Level 2 (Unified)*/ "Unified")
+};
+
+static void __cache_obtain_info(Processor *processor)
+{
+ ProcessorCache *cache;
+ gchar *endpoint, *entry, *index;
+ gchar *uref = NULL;
+ gint i;
+ gint processor_number = processor->id;
+
+ endpoint = g_strdup_printf("/sys/devices/system/cpu/cpu%d/cache", processor_number);
+
+ for (i = 0; ; i++) {
+ cache = g_new0(ProcessorCache, 1);
+
+ index = g_strdup_printf("index%d/", i);
+
+ entry = g_strconcat(index, "type", NULL);
+ cache->type = h_sysfs_read_string(endpoint, entry);
+ g_free(entry);
+
+ if (!cache->type) {
+ g_free(cache);
+ g_free(index);
+ goto fail;
+ }
+
+ entry = g_strconcat(index, "level", NULL);
+ cache->level = h_sysfs_read_int(endpoint, entry);
+ g_free(entry);
+
+ entry = g_strconcat(index, "number_of_sets", NULL);
+ cache->number_of_sets = h_sysfs_read_int(endpoint, entry);
+ g_free(entry);
+
+ entry = g_strconcat(index, "physical_line_partition", NULL);
+ cache->physical_line_partition = h_sysfs_read_int(endpoint, entry);
+ g_free(entry);
+
+ entry = g_strconcat(index, "size", NULL);
+ cache->size = h_sysfs_read_int(endpoint, entry);
+ g_free(entry);
+
+ entry = g_strconcat(index, "ways_of_associativity", NULL);
+ cache->ways_of_associativity = h_sysfs_read_int(endpoint, entry);
+ g_free(entry);
+
+ /* unique cache references: id is nice, but share_cpu_list can be
+ * used if it is not available. */
+ entry = g_strconcat(index, "id", NULL);
+ uref = h_sysfs_read_string(endpoint, entry);
+ g_free(entry);
+ if (uref != NULL && *uref != 0 )
+ cache->uid = atoi(uref);
+ else
+ cache->uid = -1;
+ g_free(uref);
+ entry = g_strconcat(index, "shared_cpu_list", NULL);
+ cache->shared_cpu_list = h_sysfs_read_string(endpoint, entry);
+ g_free(entry);
+
+ /* reacharound */
+ entry = g_strconcat(index, "../../topology/physical_package_id", NULL);
+ cache->phy_sock = h_sysfs_read_int(endpoint, entry);
+ g_free(entry);
+
+ g_free(index);
+
+ processor->cache = g_slist_append(processor->cache, cache);
+ }
+
+fail:
+ g_free(endpoint);
+}
+
+#define cmp_cache_test(f) if (a->f < b->f) return -1; if (a->f > b->f) return 1;
+
+static gint cmp_cache(ProcessorCache *a, ProcessorCache *b) {
+ gint i = 0;
+ cmp_cache_test(phy_sock);
+ i = g_strcmp0(a->type, b->type); if (i!=0) return i;
+ cmp_cache_test(level);
+ cmp_cache_test(size);
+ cmp_cache_test(uid); /* uid is unique among caches with the same (type, level) */
+ if (a->uid == -1) {
+ /* if id wasn't available, use shared_cpu_list as a unique ref */
+ i = g_strcmp0(a->shared_cpu_list, b->shared_cpu_list); if (i!=0)
+ return i;
+ }
+ return 0;
+}
+
+static gint cmp_cache_ignore_id(ProcessorCache *a, ProcessorCache *b) {
+ gint i = 0;
+ cmp_cache_test(phy_sock);
+ i = g_strcmp0(a->type, b->type); if (i!=0) return i;
+ cmp_cache_test(level);
+ cmp_cache_test(size);
+ return 0;
+}
+
+gchar *caches_summary(GSList * processors)
+{
+ gchar *ret = g_strdup_printf("[%s]\n", _("Caches"));
+ GSList *all_cache = NULL, *uniq_cache = NULL;
+ GSList *tmp, *l;
+ Processor *p;
+ ProcessorCache *c, *cur = NULL;
+ gint cur_count = 0, i = 0;
+
+ /* create list of all cache references */
+ for (l = processors; l; l = l->next) {
+ p = (Processor*)l->data;
+ if (p->cache) {
+ tmp = g_slist_copy(p->cache);
+ if (all_cache) {
+ all_cache = g_slist_concat(all_cache, tmp);
+ } else {
+ all_cache = tmp;
+ }
+ }
+ }
+
+ if (g_slist_length(all_cache) == 0) {
+ ret = h_strdup_cprintf("%s=\n", ret, _("(Not Available)") );
+ g_slist_free(all_cache);
+ return ret;
+ }
+
+ /* ignore duplicate references */
+ all_cache = g_slist_sort(all_cache, (GCompareFunc)cmp_cache);
+ for (l = all_cache; l; l = l->next) {
+ c = (ProcessorCache*)l->data;
+ if (!cur) {
+ cur = c;
+ } else {
+ if (cmp_cache(cur, c) != 0) {
+ uniq_cache = g_slist_prepend(uniq_cache, cur);
+ cur = c;
+ }
+ }
+ }
+ uniq_cache = g_slist_prepend(uniq_cache, cur);
+ uniq_cache = g_slist_reverse(uniq_cache);
+ cur = 0, cur_count = 0;
+
+ /* count and list caches */
+ for (l = uniq_cache; l; l = l->next) {
+ c = (ProcessorCache*)l->data;
+ if (!cur) {
+ cur = c;
+ cur_count = 1;
+ } else {
+ if (cmp_cache_ignore_id(cur, c) != 0) {
+ ret = h_strdup_cprintf(_("Level %d (%s)#%d=%dx %dKB (%dKB), %d-way set-associative, %d sets\n"),
+ ret,
+ cur->level,
+ C_("cache-type", cur->type),
+ cur->phy_sock,
+ cur_count,
+ cur->size,
+ cur->size * cur_count,
+ cur->ways_of_associativity,
+ cur->number_of_sets);
+ cur = c;
+ cur_count = 1;
+ } else {
+ cur_count++;
+ }
+ }
+ }
+ ret = h_strdup_cprintf(_("Level %d (%s)#%d=%dx %dKB (%dKB), %d-way set-associative, %d sets\n"),
+ ret,
+ cur->level,
+ C_("cache-type", cur->type),
+ cur->phy_sock,
+ cur_count,
+ cur->size,
+ cur->size * cur_count,
+ cur->ways_of_associativity,
+ cur->number_of_sets);
+
+ g_slist_free(all_cache);
+ g_slist_free(uniq_cache);
+ return ret;
+}
+
+static gchar *processor_get_full_name(const gchar *model_name)
+{
+ if(g_strcmp0(model_name ,"E2S") == 0)
+ return "Elbrus-4C";
+ else if(g_strcmp0(model_name ,"E1C+") == 0)
+ return "Elbrus-1C+";
+ else if(g_strcmp0(model_name ,"E2C+DSP") == 0)
+ return "Elbrus-2C+";
+ else if(g_strcmp0(model_name ,"E8C") == 0)
+ return "Elbrus-8C";
+ else if(g_strcmp0(model_name ,"E8C2") == 0)
+ return "Elbrus-8CB";
+ else if(g_strcmp0(model_name ,"E12C") == 0)
+ return "Elbrus-12C";
+ else if(g_strcmp0(model_name ,"E16C") == 0)
+ return "Elbrus-16C";
+ else if(g_strcmp0(model_name ,"E2C3") == 0)
+ return "Elbrus-2C3";
+ else
+ return (gchar *)model_name;
+}
+
+GSList *processor_scan(void)
+{
+ GSList *procs = NULL, *l = NULL;
+ Processor *processor = NULL;
+ FILE *cpuinfo;
+ gchar buffer[1024];
+
+ cpuinfo = fopen(PROC_CPUINFO, "r");
+ if (!cpuinfo)
+ return NULL;
+
+ while (fgets(buffer, 1024, cpuinfo)) {
+ gchar **tmp = g_strsplit(buffer, ":", 2);
+ if (!tmp[1] || !tmp[0]) {
+ g_strfreev(tmp);
+ continue;
+ }
+
+ tmp[0] = g_strstrip(tmp[0]);
+ tmp[1] = g_strstrip(tmp[1]);
+
+ if (g_str_has_prefix(tmp[0], "processor")) {
+ /* finish previous */
+ if (processor)
+ procs = g_slist_append(procs, processor);
+
+ /* start next */
+ processor = g_new0(Processor, 1);
+ processor->id = atol(tmp[1]);
+ g_strfreev(tmp);
+ continue;
+ }
+
+ if (processor) {
+ if (g_str_has_prefix(tmp[0], "model name")) {
+ const gchar *model_name = processor_get_full_name(tmp[1]);
+ processor->model_name = g_strdup(model_name);
+ g_strfreev(tmp);
+ continue;
+ }
+
+ get_str("vendor_id", processor->vendor_id);
+ get_int("cpu family", processor->family);
+ get_int("model", processor->model);
+ get_int("revision", processor->revision);
+
+ get_float("cpu MHz", processor->cpu_mhz);
+ get_float("bogomips", processor->bogomips);
+ }
+
+ //populate processor structure
+ g_strfreev(tmp);
+ }
+
+ //appent to the list
+ if (processor)
+ procs = g_slist_append(procs, processor);
+
+ for (l = procs; l; l = l->next) {
+ processor = (Processor *) l->data;
+ __cache_obtain_info(processor);
+ }
+
+ fclose(cpuinfo);
+
+ return procs;
+}
+
+gchar *processor_name(GSList * processors) {
+ return processor_name_default(processors);
+}
+
+gchar *processor_describe(GSList * processors) {
+ return processor_describe_default(processors);
+}
+
+gchar *
+processor_get_detailed_info(Processor *processor)
+{
+ gchar *ret;
+ gchar *cache_info;
+ cache_info = __cache_get_info_as_string(processor);
+
+ ret = g_strdup_printf("[%s]\n"
+ "$^$%s=%s\n" /* name */
+ "$^$%s=%s\n" /* vendor */
+ "%s=%d\n" /* family */
+ "%s=%d\n" /* model */
+ "%s=%d\n" /* revision */
+ "%s=%.2f %s\n" /* frequency */
+ "%s=%.2f\n" /* bogomips */
+ "%s=%s\n" /* byte order */
+ "[%s]\n" /* cache */
+ "%s\n",
+ _("Processor"),
+ _("Name"), processor->model_name,
+ _("Vendor"), processor->vendor_id,
+ _("Family"), processor->family,
+ _("Model"), processor->model,
+ _("Revision"), processor->revision,
+ _("Frequency"), processor->cpu_mhz, _("MHz"),
+ _("BogoMips"), processor->bogomips,
+ _("Byte Order"), byte_order_str(),
+ _("Cache"), cache_info
+ );
+ g_free(cache_info);
+ return ret;
+}
+
+//prepare processor info for all cpus
+gchar *processor_get_info(GSList * processors)
+{
+ Processor *processor;
+
+ gchar *ret, *tmp, *hashkey;
+ GSList *l;
+
+ tmp = g_strdup("");
+
+ for (l = processors; l; l = l->next) {
+ processor = (Processor *) l->data;
+ gchar *model_name = g_strdup_printf("MCST %s", processor->model_name);
+
+ /* change vendor id of 8CB processor for correct parse from vendor.ids */
+ if (!g_strcmp0(processor->vendor_id, "E8C")) {
+ gchar *orig_vendor_id = processor->vendor_id;
+ processor->vendor_id = g_strdup_printf("%s-SWTX", orig_vendor_id);
+ free(orig_vendor_id);
+ }
+
+ const Vendor *v = vendor_match(processor->vendor_id, NULL);
+ if (v)
+ tag_vendor(&model_name, 0, v->name_short ? v->name_short : v->name, v->ansi_color, params.fmt_opts);
+
+ tmp = g_strdup_printf("%s$CPU%d$cpu%d=%.2f %s|%s\n",
+ tmp, processor->id,
+ processor->id,
+ processor->cpu_mhz, _("MHz"),
+ model_name);
+
+ hashkey = g_strdup_printf("CPU%d", processor->id);
+ moreinfo_add_with_prefix("DEV", hashkey,
+ processor_get_detailed_info(processor));
+ g_free(hashkey);
+ }
+
+ ret = g_strdup_printf("[$ShellParam$]\n"
+ "ViewType=1\n"
+ "ColumnTitle$TextValue=%s\n"
+ "ColumnTitle$Value=%s\n"
+ "ColumnTitle$Extra1=%s\n"
+ "ShowColumnHeaders=true\n"
+ "[Processors]\n"
+ "%s", _("Device"), _("Frequency"), _("Model"), tmp);
+ g_free(tmp);
+
+ return ret;
+}
diff --git a/modules/devices/firmware.c b/modules/devices/firmware.c
new file mode 100644
index 00000000..ea6ce532
--- /dev/null
+++ b/modules/devices/firmware.c
@@ -0,0 +1,261 @@
+/*
+ * HardInfo - Displays System Information
+ * Copyright (C) 2003-2019 L. A. F. Pereira <l@tia.mat.br>
+ * Copyright (C) 2019 Burt P. <pburt0@gmail.com>
+ * Copyright (C) 2020 Ondrej Čerman
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, version 2 or later.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#include "hardinfo.h"
+#include "devices.h"
+#include <inttypes.h>
+#include <gio/gio.h>
+#include "util_sysobj.h" /* for SEQ() and appf() */
+
+#define fw_msg(msg, ...) fprintf (stderr, "[%s] " msg "\n", __FUNCTION__, ##__VA_ARGS__) /**/
+
+#define FWUPT_INTERFACE "org.freedesktop.fwupd"
+
+gboolean fail_no_fwupd = TRUE;
+
+char *decode_flags(guint64 flags) {
+ /* https://github.com/hughsie/fwupd/blob/master/libfwupd/fwupd-enums.{h,c} */
+ static const struct { guint64 b; char *flag, *def; } flag_defs[] = {
+ { (1u << 0), "internal", N_("Device cannot be removed easily") },
+ { (1u << 1), "updatable", N_("Device is updatable in this or any other mode") },
+ { (1u << 2), "only-offline", N_("Update can only be done from offline mode") },
+ { (1u << 3), "require-ac", N_("Requires AC power") },
+ { (1u << 4), "locked", N_("Is locked and can be unlocked") },
+ { (1u << 5), "supported", N_("Is found in current metadata") },
+ { (1u << 6), "needs-bootloader", N_("Requires a bootloader mode to be manually enabled by the user") },
+ { (1u << 7), "registered", N_("Has been registered with other plugins") },
+ { (1u << 8), "needs-reboot", N_("Requires a reboot to apply firmware or to reload hardware") },
+ { (1u << 17), "needs-shutdown", N_("Requires system shutdown to apply firmware") },
+ { (1u << 9), "reported", N_("Has been reported to a metadata server") },
+ { (1u << 10), "notified", N_("User has been notified") },
+ { (1u << 11), "use-runtime-version", N_("Always use the runtime version rather than the bootloader") },
+ { (1u << 12), "install-parent-first", N_("Install composite firmware on the parent before the child") },
+ { (1u << 13), "is-bootloader", N_("Is currently in bootloader mode") },
+ { (1u << 14), "wait-for-replug", N_("The hardware is waiting to be replugged") },
+ { (1u << 15), "ignore-validation", N_("Ignore validation safety checks when flashing this device") },
+ { (1u << 18), "another-write-required", N_("Requires the update to be retried with a new plugin") },
+ { (1u << 19), "no-auto-instance-ids", N_("Do not add instance IDs from the device baseclass") },
+ { (1u << 20), "needs-activation", N_("Device update needs to be separately activated") },
+ { (1u << 21), "ensure-semver", N_("Ensure the version is a valid semantic version, e.g. numbers separated with dots") },
+ { (1u << 16), "trusted", N_("Extra metadata can be exposed about this device") },
+ { 0, NULL }
+ };
+
+ gchar *flag_list = g_strdup("");
+
+ int i;
+ for (i = 0; flag_defs[i].flag; i++) {
+ if (flags & flag_defs[i].b)
+ flag_list = appfnl(flag_list, "[%s] %s", flag_defs[i].flag, flag_defs[i].def);
+ }
+ return flag_list;
+}
+
+const char *find_translation(const char *str) {
+ /* TODO: https://github.com/hughsie/fwupd/blob/master/src/README.md */
+ static const char *translatable[] = {
+ N_("DeviceId"), N_("Guid"), N_("Summary"), N_("Plugin"), N_("Flags"),
+ N_("Vendor"), N_("VendorId"), N_("Version"), N_("VersionBootloader"),
+ N_("Icon"), N_("InstallDuration"), N_("Created"),
+ NULL
+ };
+ int i;
+ if (!str) return NULL;
+ for (i = 0; translatable[i]; i++) {
+ if (SEQ(str, translatable[i]))
+ return _(translatable[i]);
+ }
+ return str;
+};
+
+/* map lvfs icon names to hardinfo icon names */
+const char *find_icon(const char *lvfs_name) {
+ /* icon names found by looking for fu_device_add_icon ()
+ * in the fwupd source. */
+ static const
+ struct { char *lvfs, *hi; } imap[] = {
+ { "applications-internet", "dns.png" },
+ { "audio-card", "audio.png" },
+ { "computer", "computer.png" },
+ { "drive-harddisk", "hdd.png" },
+ { "input-gaming", "joystick.png" },
+ { "input-tablet", NULL },
+ { "network-modem", "wireless.png" },
+ { "preferences-desktop-keyboard", "keyboard.png" },
+ { "thunderbolt", NULL },
+ { "touchpad-disabled", NULL },
+ /* default */
+ { NULL, "memory.png" } /* a device with firmware maybe */
+ };
+ unsigned int i = 0;
+ for(; imap[i].lvfs; i++) {
+ if (SEQ(imap[i].lvfs, lvfs_name) && imap[i].hi)
+ return imap[i].hi;
+ }
+ return imap[i].hi;
+}
+
+gchar *fwupdmgr_get_devices_info() {
+ struct Info *info = info_new();
+ struct InfoGroup *this_group = NULL;
+ gboolean has_vendor_field = FALSE;
+ gboolean updatable = FALSE;
+ const Vendor *gv = NULL;
+ int gc = 0;
+
+ GDBusConnection *conn;
+ GDBusProxy *proxy;
+ GVariant *devices, *value;
+ GVariantIter *deviter, *dictiter, *iter;
+ const gchar *key, *tmpstr;
+
+ conn = g_bus_get_sync(G_BUS_TYPE_SYSTEM, NULL, NULL);
+ if (!conn)
+ return g_strdup("");
+
+ proxy = g_dbus_proxy_new_sync(conn, G_DBUS_PROXY_FLAGS_NONE, NULL,
+ FWUPT_INTERFACE, "/", FWUPT_INTERFACE,
+ NULL, NULL);
+ if (!proxy) {
+ g_object_unref(conn);
+ return g_strdup("");
+ }
+
+ fail_no_fwupd = FALSE;
+ devices = g_dbus_proxy_call_sync(proxy, "GetDevices", NULL,
+ G_DBUS_CALL_FLAGS_NONE, -1, NULL, NULL);
+
+ if (devices) {
+ g_variant_get(devices, "(aa{sv})", &deviter);
+ while(g_variant_iter_loop(deviter, "a{sv}", &dictiter)){
+
+ this_group = info_add_group(info, _("Unknown"), info_field_last());
+ this_group->sort = INFO_GROUP_SORT_NAME_DESCENDING;
+ has_vendor_field = FALSE;
+ updatable = FALSE;
+ gv = NULL;
+
+ while (g_variant_iter_loop(dictiter, "{&sv}", &key, &value)) {
+ if (SEQ(key, "Name")) {
+ tmpstr = g_variant_get_string(value, NULL);
+ this_group->name = hardinfo_clean_grpname(tmpstr, 0);
+ gv = vendor_match(tmpstr, NULL);
+ } else if (SEQ(key, "Vendor")) {
+ has_vendor_field = TRUE;
+ tmpstr = g_variant_get_string(value, NULL);
+
+ const Vendor* v = vendor_match(tmpstr, NULL);
+ if (v) {
+ info_group_add_field(this_group,
+ info_field(_("Vendor"), v->name,
+ .value_has_vendor = TRUE,
+ .free_value_on_flatten = FALSE) );
+ } else {
+ info_group_add_field(this_group,
+ info_field(_("Vendor"), g_strdup(tmpstr),
+ .free_value_on_flatten = TRUE) );
+ }
+ } else if (SEQ(key, "Icon")) {
+ g_variant_get(value, "as", &iter);
+ while (g_variant_iter_loop(iter, "s", &tmpstr)) {
+ info_group_add_field(this_group,
+ info_field(_("Icon"), g_strdup(tmpstr),
+ .free_value_on_flatten = TRUE,
+ .icon = g_strdup(find_icon(tmpstr)) ) );
+ }
+ } else if (SEQ(key, "Guid")) {
+ g_variant_get(value, "as", &iter);
+ while (g_variant_iter_loop(iter, "s", &tmpstr)) {
+ info_group_add_field(this_group,
+ info_field(_("Guid"), g_strdup(tmpstr),
+ .tag = g_strdup_printf("guid%d", gc++),
+ .free_value_on_flatten = TRUE) );
+ }
+ g_variant_iter_free(iter);
+ } else if (SEQ(key, "Created")) {
+ guint64 created = g_variant_get_uint64(value);
+ GDateTime *dt = g_date_time_new_from_unix_local(created);
+ if (dt) {
+ info_group_add_field(this_group,
+ info_field(_("Created"), g_date_time_format(dt, "%x"),
+ .free_value_on_flatten = TRUE) );
+ g_date_time_unref(dt);
+ }
+ } else if (SEQ(key, "Flags")) {
+ guint64 flags = g_variant_get_uint64(value);
+ updatable = (gboolean)(flags & (1u << 1));
+ info_group_add_field(this_group,
+ info_field(_("Flags"), decode_flags(flags),
+ .free_value_on_flatten = TRUE) );
+ } else {
+ if (g_variant_is_of_type(value, G_VARIANT_TYPE_STRING)) {
+ info_group_add_field(this_group,
+ info_field(find_translation(key),
+ g_variant_dup_string(value, NULL),
+ .free_value_on_flatten = TRUE) );
+ }
+ }
+ }
+
+ if (gv && !has_vendor_field) {
+ info_group_add_field(this_group,
+ info_field(_("Vendor"), gv->name,
+ .value_has_vendor = TRUE,
+ .free_value_on_flatten = FALSE) );
+ }
+
+ // hide devices that are not updatable
+ if (!updatable) {
+ info_remove_group(info, info->groups->len - 1);
+ }
+ }
+ g_variant_iter_free(deviter);
+ g_variant_unref(devices);
+ }
+
+ g_object_unref(proxy);
+ g_object_unref(conn);
+
+ gchar *ret = NULL;
+ if (info->groups->len) {
+ info_set_view_type(info, SHELL_VIEW_DETAIL);
+ //fw_msg("flatten...");
+ ret = info_flatten(info);
+ //fw_msg("ret: %s", ret);
+ } else {
+ ret = g_strdup_printf("[%s]\n%s=%s\n" "[$ShellParam$]\nViewType=0\n",
+ _("Firmware List"),
+ _("Result"), _("(Not available)") );
+ }
+ return ret;
+}
+
+gchar *firmware_get_info() {
+ return fwupdmgr_get_devices_info();
+}
+
+gboolean firmware_hinote(const char **msg) {
+ if (fail_no_fwupd) {
+ *msg = g_strdup(
+ _("Requires the <i><b>fwupd</b></i> daemon."));
+ return TRUE;
+ }
+ return FALSE;
+}
diff --git a/modules/devices/gpu.c b/modules/devices/gpu.c
new file mode 100644
index 00000000..96520161
--- /dev/null
+++ b/modules/devices/gpu.c
@@ -0,0 +1,291 @@
+/*
+ * HardInfo - Displays System Information
+ * Copyright (C) 2003-2017 L. A. F. Pereira <l@tia.mat.br>
+ * This file
+ * Copyright (C) 2018 Burt P. <pburt0@gmail.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, version 2 or later.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#include <string.h>
+
+#include "hardinfo.h"
+#include "devices.h"
+#include "gpu_util.h"
+
+gchar *gpu_list = NULL;
+gchar *gpu_summary = NULL;
+
+void gpu_summary_add(const char *gpu_name) {
+ if (strlen(gpu_summary) == 0) {
+ /* first one */
+ gpu_summary = h_strdup_cprintf("%s", gpu_summary, gpu_name);
+ } else {
+ /* additional */
+ gpu_summary = h_strdup_cprintf(" + %s", gpu_summary, gpu_name);
+ }
+}
+
+#define UNKIFNULL_AC(f) (f != NULL) ? f : _("(Unknown)");
+
+static void _gpu_pci_dev(gpud* gpu) {
+ pcid *p = gpu->pci_dev;
+ gchar *str;
+ gchar *vendor, *svendor, *product, *sproduct;
+ gchar *name, *key;
+ gchar *drm_path = NULL;
+
+ gboolean vendor_is_svendor = (p->vendor_id == p->sub_vendor_id && p->device_id == p->sub_device_id);
+
+ vendor = UNKIFNULL_AC(p->vendor_id_str);
+ svendor = UNKIFNULL_AC(p->sub_vendor_id_str);
+ product = UNKIFNULL_AC(p->device_id_str);
+ sproduct = UNKIFNULL_AC(p->sub_device_id_str);
+ if (gpu->drm_dev)
+ drm_path = g_strdup_printf("/dev/dri/%s", gpu->drm_dev);
+ else
+ drm_path = g_strdup(_("(Unknown)"));
+
+ gchar *ven_tag = vendor_match_tag(p->vendor_id_str, params.fmt_opts);
+ gchar *sven_tag = vendor_match_tag(p->sub_vendor_id_str, params.fmt_opts);
+ if (ven_tag) {
+ if (sven_tag && !vendor_is_svendor) {
+ name = g_strdup_printf("%s %s %s", sven_tag, ven_tag, product);
+ } else {
+ name = g_strdup_printf("%s %s", ven_tag, product);
+ }
+ } else {
+ name = g_strdup_printf("%s %s", vendor, product);
+ }
+ g_free(ven_tag);
+ g_free(sven_tag);
+
+ key = g_strdup_printf("GPU%s", gpu->id);
+
+ gpu_summary_add((gpu->nice_name) ? gpu->nice_name : name);
+ gpu_list = h_strdup_cprintf("$!%s$%s=%s\n", gpu_list, key, gpu->id, name);
+
+ gchar *vendor_device_str;
+ if (p->vendor_id == p->sub_vendor_id && p->device_id == p->sub_device_id) {
+ vendor_device_str = g_strdup_printf(
+ /* Vendor */ "$^$%s=[%04x] %s\n"
+ /* Device */ "%s=[%04x] %s\n",
+ _("Vendor"), p->vendor_id, vendor,
+ _("Device"), p->device_id, product);
+ } else {
+ vendor_device_str = g_strdup_printf(
+ /* Vendor */ "$^$%s=[%04x] %s\n"
+ /* Device */ "%s=[%04x] %s\n"
+ /* Sub-device vendor */ "$^$%s=[%04x] %s\n"
+ /* Sub-device */ "%s=[%04x] %s\n",
+ _("Vendor"), p->vendor_id, vendor,
+ _("Device"), p->device_id, product,
+ _("SVendor"), p->sub_vendor_id, svendor,
+ _("SDevice"), p->sub_device_id, sproduct);
+ }
+
+ gchar *pcie_str;
+ if (p->pcie_width_curr) {
+ pcie_str = g_strdup_printf("[%s]\n"
+ /* Width (max) */ "%s=x%u\n"
+ /* Speed (max) */ "%s=%0.1f %s\n",
+ _("PCI Express"),
+ _("Maximum Link Width"), p->pcie_width_max,
+ _("Maximum Link Speed"), p->pcie_speed_max, _("GT/s") );
+ } else
+ pcie_str = strdup("");
+
+ gchar *nv_str;
+ if (gpu->nv_info) {
+ nv_str = g_strdup_printf("[%s]\n"
+ /* model */ "%s=%s\n"
+ /* bios */ "%s=%s\n"
+ /* uuid */ "%s=%s\n",
+ _("NVIDIA"),
+ _("Model"), gpu->nv_info->model,
+ _("BIOS Version"), gpu->nv_info->bios_version,
+ _("UUID"), gpu->nv_info->uuid );
+ } else
+ nv_str = strdup("");
+
+ gchar *freq = g_strdup(_("(Unknown)"));
+ if (gpu->khz_max > 0) {
+ if (gpu->khz_min > 0 && gpu->khz_min != gpu->khz_max)
+ freq = g_strdup_printf("%0.2f-%0.2f %s", (double) gpu->khz_min / 1000, (double) gpu->khz_max / 1000, _("MHz"));
+ else
+ freq = g_strdup_printf("%0.2f %s", (double) gpu->khz_max / 1000, _("MHz"));
+ }
+
+ gchar *mem_freq = g_strdup(_("(Unknown)"));
+ if (gpu->mem_khz_max > 0) {
+ if (gpu->mem_khz_min > 0 && gpu->mem_khz_min != gpu->mem_khz_max)
+ mem_freq = g_strdup_printf("%0.2f-%0.2f %s", (double) gpu->mem_khz_min / 1000, (double) gpu->mem_khz_max / 1000, _("MHz"));
+ else
+ mem_freq = g_strdup_printf("%0.2f %s", (double) gpu->mem_khz_max / 1000, _("MHz"));
+ }
+
+ str = g_strdup_printf("[%s]\n"
+ /* Location */ "%s=%s\n"
+ /* DRM Dev */ "%s=%s\n"
+ /* Class */ "%s=[%04x] %s\n"
+ "%s"
+ /* Revision */ "%s=%02x\n"
+ "[%s]\n"
+ /* Core freq */ "%s=%s\n"
+ /* Mem freq */ "%s=%s\n"
+ /* NV */ "%s"
+ /* PCIe */ "%s"
+ "[%s]\n"
+ /* Driver */ "%s=%s\n"
+ /* Modules */ "%s=%s\n",
+ _("Device Information"),
+ _("Location"), gpu->location,
+ _("DRM Device"), drm_path,
+ _("Class"), p->class, p->class_str,
+ vendor_device_str,
+ _("Revision"), p->revision,
+ _("Clocks"),
+ _("Core"), freq,
+ _("Memory"), mem_freq,
+ nv_str,
+ pcie_str,
+ _("Driver"),
+ _("In Use"), (p->driver) ? p->driver : _("(Unknown)"),
+ _("Kernel Modules"), (p->driver_list) ? p->driver_list : _("(Unknown)")
+ );
+
+ moreinfo_add_with_prefix("DEV", key, str); /* str now owned by morinfo */
+
+ g_free(drm_path);
+ g_free(pcie_str);
+ g_free(nv_str);
+ g_free(vendor_device_str);
+ g_free(name);
+ g_free(key);
+}
+
+int _dt_soc_gpu(gpud *gpu) {
+ static char UNKSOC[] = "(Unknown)"; /* don't translate this */
+ gchar *vendor = gpu->vendor_str;
+ gchar *device = gpu->device_str;
+ if (vendor == NULL) vendor = UNKSOC;
+ if (device == NULL) device = UNKSOC;
+ gchar *freq = g_strdup(_("(Unknown)"));
+ if (gpu->khz_max > 0) {
+ if (gpu->khz_min > 0)
+ freq = g_strdup_printf("%0.2f-%0.2f %s", (double) gpu->khz_min / 1000, (double) gpu->khz_max / 1000, _("MHz"));
+ else
+ freq = g_strdup_printf("%0.2f %s", (double) gpu->khz_max / 1000, _("MHz"));
+ }
+ gchar *key = g_strdup(gpu->id);
+
+ gchar *name = NULL;
+ gchar *vtag = vendor_match_tag(gpu->vendor_str, params.fmt_opts);
+ if (vtag) {
+ name = g_strdup_printf("%s %s", vtag, device);
+ } else {
+ name = (vendor == UNKSOC && device == UNKSOC)
+ ? g_strdup(_("Unknown integrated GPU"))
+ : g_strdup_printf("%s %s", vendor, device);
+ }
+ g_free(vtag);
+
+ gchar *opp_str;
+ if (gpu->dt_opp) {
+ static const char *freq_src[] = {
+ N_("clock-frequency property"),
+ N_("Operating Points (OPPv1)"),
+ N_("Operating Points (OPPv2)"),
+ };
+ opp_str = g_strdup_printf("[%s]\n"
+ /* MinFreq */ "%s=%d %s\n"
+ /* MaxFreq */ "%s=%d %s\n"
+ /* Latency */ "%s=%d %s\n"
+ /* Source */ "%s=%s\n",
+ _("Frequency Scaling"),
+ _("Minimum"), gpu->dt_opp->khz_min, _("kHz"),
+ _("Maximum"), gpu->dt_opp->khz_max, _("kHz"),
+ _("Transition Latency"), gpu->dt_opp->clock_latency_ns, _("ns"),
+ _("Source"), _(freq_src[gpu->dt_opp->version]) );
+ } else
+ opp_str = strdup("");
+
+ gpu_summary_add((gpu->nice_name) ? gpu->nice_name : name);
+ gpu_list = h_strdup_cprintf("$!%s$%s=%s\n", gpu_list, key, key, name);
+ gchar *str = g_strdup_printf("[%s]\n"
+ /* Location */ "%s=%s\n"
+ /* Vendor */ "$^$%s=%s\n"
+ /* Device */ "%s=%s\n"
+ "[%s]\n"
+ /* Freq */ "%s=%s\n"
+ /* opp-v2 */ "%s"
+ "[%s]\n"
+ /* Path */ "%s=%s\n"
+ /* Compat */ "%s=%s\n"
+ /* Status */ "%s=%s\n"
+ /* Name */ "%s=%s\n",
+ _("Device Information"),
+ _("Location"), gpu->location,
+ _("Vendor"), vendor,
+ _("Device"), device,
+ _("Clocks"),
+ _("Core"), freq,
+ opp_str,
+ _("Device Tree Node"),
+ _("Path"), gpu->dt_path,
+ _("Compatible"), gpu->dt_compat,
+ _("Status"), gpu->dt_status,
+ _("Name"), gpu->dt_name
+ );
+ moreinfo_add_with_prefix("DEV", key, str); /* str now owned by morinfo */
+ g_free(freq);
+ g_free(opp_str);
+ return 1;
+}
+
+void scan_gpu_do(void) {
+ if (gpu_summary)
+ g_free(gpu_summary);
+ if (gpu_list) {
+ moreinfo_del_with_prefix("DEV:GPU");
+ g_free(gpu_list);
+ }
+ gpu_summary = strdup("");
+ gpu_list = g_strdup_printf("[%s]\n", _("GPUs"));
+
+ gpud *gpus = gpu_get_device_list();
+ gpud *curr = gpus;
+
+ int c = gpud_list_count(gpus);
+
+ if (c > 0) {
+ while(curr) {
+ if (curr->pci_dev) {
+ _gpu_pci_dev(curr);
+ }
+ if (curr->dt_compat) {
+ _dt_soc_gpu(curr);
+ }
+ curr=curr->next;
+ }
+ }
+ gpud_list_free(gpus);
+
+ if (c)
+ gpu_list = g_strconcat(gpu_list, "[$ShellParam$]\n", "ViewType=1\n", NULL);
+ else {
+ /* NO GPU? */
+ gpu_list = g_strconcat(gpu_list, _("No GPU devices found"), "=\n", NULL);
+ }
+}
diff --git a/modules/devices/ia64/processor.c b/modules/devices/ia64/processor.c
new file mode 100644
index 00000000..f31813bc
--- /dev/null
+++ b/modules/devices/ia64/processor.c
@@ -0,0 +1,215 @@
+/*
+ * HardInfo - Displays System Information
+ * Copyright (C) 2003-2006 L. A. F. Pereira <l@tia.mat.br>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, version 2 or later.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#include "hardinfo.h"
+#include "devices.h"
+#include "cpu_util.h"
+
+GSList *
+processor_scan(void)
+{
+ GSList *procs = NULL;
+ Processor *processor = NULL;
+ FILE *cpuinfo;
+ gchar buffer[128];
+ gchar *rep_pname = NULL;
+ GSList *pi = NULL;
+
+ cpuinfo = fopen(PROC_CPUINFO, "r");
+ if (!cpuinfo)
+ return NULL;
+
+#define CHECK_FOR(k) (g_str_has_prefix(tmp[0], k))
+ while (fgets(buffer, 128, cpuinfo)) {
+ gchar **tmp = g_strsplit(buffer, ":", 2);
+ if (tmp[0] && tmp[1]) {
+ tmp[0] = g_strstrip(tmp[0]);
+ tmp[1] = g_strstrip(tmp[1]);
+ } else {
+ g_strfreev(tmp);
+ continue;
+ }
+
+ get_str("Processor", rep_pname);
+
+ if ( CHECK_FOR("processor") ) {
+ /* finish previous */
+ if (processor) {
+ procs = g_slist_append(procs, processor);
+ }
+
+ /* start next */
+ processor = g_new0(Processor, 1);
+ processor->id = atol(tmp[1]);
+
+ if (rep_pname)
+ processor->model_name = g_strdup(rep_pname);
+
+ g_strfreev(tmp);
+ continue;
+ }
+
+ if (!processor &&
+ ( CHECK_FOR("vendor")
+ || CHECK_FOR("arch")
+ || CHECK_FOR("family") ) ) {
+
+ /* single proc/core may not have "processor : n" */
+ processor = g_new0(Processor, 1);
+ processor->id = 0;
+
+ if (rep_pname)
+ processor->model_name = g_strdup(rep_pname);
+ }
+
+ if (processor) {
+ get_str("vendor", processor->vendor_id);
+ get_str("archrev", processor->archrev);
+ get_str("arch", processor->arch);
+ get_str("family", processor->family);
+ get_str("features", processor->features);
+ get_int("model", processor->model);
+ get_int("revision", processor->revision);
+ get_float("BogoMIPS", processor->bogomips);
+ get_float("cpu MHz", processor->cpu_mhz);
+ get_int("cpu regs", processor->cpu_regs);
+ }
+ g_strfreev(tmp);
+ }
+
+ if (processor)
+ procs = g_slist_append(procs, processor);
+
+ g_free(rep_pname);
+ fclose(cpuinfo);
+
+ /* TODO: redup */
+
+ /* data not from /proc/cpuinfo */
+ for (pi = procs; pi; pi = pi->next) {
+ processor = (Processor *) pi->data;
+
+ /* strings can't be null or segfault later */
+ STRIFNULL(processor->model_name, _("IA64 Processor") );
+ UNKIFNULL(processor->vendor_id);
+ STRIFNULL(processor->arch, "IA-64");
+ STRIFNULL(processor->archrev, "0");
+ UNKIFNULL(processor->family);
+ UNKIFNULL(processor->features);
+
+ /* topo & freq */
+ processor->cpufreq = cpufreq_new(processor->id);
+ processor->cputopo = cputopo_new(processor->id);
+
+ if (processor->cpufreq->cpukhz_max)
+ processor->cpu_mhz = processor->cpufreq->cpukhz_max / 1000;
+
+ }
+
+ return procs;
+}
+
+gchar *processor_name(GSList * processors) {
+ return processor_name_default(processors);
+}
+
+gchar *processor_describe(GSList * processors) {
+ return processor_describe_default(processors);
+}
+
+gchar *
+processor_get_detailed_info(Processor *processor)
+{
+ gchar *tmp_cpufreq, *tmp_topology, *ret;
+
+ tmp_topology = cputopo_section_str(processor->cputopo);
+ tmp_cpufreq = cpufreq_section_str(processor->cpufreq);
+
+ ret = g_strdup_printf("[%s]\n"
+ "%s=%s\n" /* name */
+ "%s=%s\n" /* vendor */
+ "%s=%s\n" /* arch */
+ "%s=%s\n" /* archrev */
+ "%s=%s\n" /* family */
+ "%s=%d\n" /* model no. */
+ "%s=%d\n" /* revision */
+ "%s=%.2f %s\n" /* frequency */
+ "%s=%.2f\n" /* bogomips */
+ "%s=%s\n" /* byte order */
+ "%s=%d\n" /* regs */
+ "%s=%s\n" /* features */
+ "%s" /* topology */
+ "%s" /* frequency scaling */
+ "%s",/* empty */
+ _("Processor"),
+ _("Name"), processor->model_name,
+ _("Vendor"), processor->vendor_id,
+ _("Architecture"), processor->arch,
+ _("Architecture Revision"), processor->archrev,
+ _("Family"), processor->family,
+ _("Model"), processor->model,
+ _("Revision"), processor->revision,
+ _("Frequency"), processor->cpu_mhz, _("MHz"),
+ _("BogoMips"), processor->bogomips,
+ _("Byte Order"), byte_order_str(),
+ _("CPU regs"), processor->cpu_regs,
+ _("Features"), processor->features,
+ tmp_topology,
+ tmp_cpufreq,
+ "");
+ g_free(tmp_cpufreq);
+ g_free(tmp_topology);
+ return ret;
+}
+
+gchar *processor_get_info(GSList * processors)
+{
+ Processor *processor;
+
+ if (g_slist_length(processors) > 1) {
+ gchar *ret, *tmp, *hashkey;
+ GSList *l;
+
+ tmp = g_strdup("");
+
+ for (l = processors; l; l = l->next) {
+ processor = (Processor *) l->data;
+
+ tmp = g_strdup_printf("%s$CPU%d$%s=%.2f %s\n",
+ tmp, processor->id,
+ processor->model_name,
+ processor->cpu_mhz, _("MHz"));
+
+ hashkey = g_strdup_printf("CPU%d", processor->id);
+ moreinfo_add_with_prefix("DEV", hashkey,
+ processor_get_detailed_info(processor));
+ g_free(hashkey);
+ }
+
+ ret = g_strdup_printf("[$ShellParam$]\n"
+ "ViewType=1\n"
+ "[Processors]\n"
+ "%s", tmp);
+ g_free(tmp);
+
+ return ret;
+ }
+
+ processor = (Processor *) processors->data;
+ return processor_get_detailed_info(processor);
+}
diff --git a/modules/devices/inputdevices.c b/modules/devices/inputdevices.c
new file mode 100644
index 00000000..301641d4
--- /dev/null
+++ b/modules/devices/inputdevices.c
@@ -0,0 +1,171 @@
+/*
+ * HardInfo - Displays System Information
+ * Copyright (C) 2003-2006 L. A. F. Pereira <l@tia.mat.br>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, version 2 or later.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#include <string.h>
+
+#include "hardinfo.h"
+#include "devices.h"
+#include "usb_util.h"
+
+gchar *input_icons = NULL;
+
+static struct {
+ char *name;
+ char *icon;
+} input_devices[] = {
+ { NULL, "module.png" }, // UNKNOWN
+ { "Keyboard", "keyboard.png" },
+ { "Joystick", "joystick.png" },
+ { "Mouse", "mouse.png" },
+ { "Speaker", "audio.png" },
+ { "Audio", "audio.png" }
+};
+
+// source: https://elixir.bootlin.com/linux/v5.9/source/include/uapi/linux/input.h#L251
+static const gchar *bus_types[] = {
+ NULL, "PCI", "ISA PnP", "USB", // 0x0 - 0x3
+ "HIL", "Bluetooth", "Virtual", NULL, // 0x4 - 0x7
+ NULL, NULL, NULL, NULL, // 0x8 - 0xB
+ NULL, NULL, NULL, NULL, // 0xC - 0xF
+ "ISA", "i8042", "XT Keyboard bus", "RS232", // 0x10 - 0x13
+ "Game port", "Parallel port", "Amiga bus", "ADB", // 0x14 - 0x17
+ "I²C", "HOST", "GSC", "Atari bus", // 0x18 - 0x1B
+ "SPI", "RMI", "CEC", "Intel ISHTP" // 0x1C - 0x1F
+};
+
+#define UNKWNIFNULL(f) ((f) ? f : _("(Unknown)"))
+#define EMPTYIFNULL(f) ((f) ? f : "")
+
+void
+__scan_input_devices(void)
+{
+ FILE *dev;
+ gchar buffer[1024];
+ vendor_list vl = NULL;
+ gchar *tmp, *name = NULL, *phys = NULL;
+ gchar *vendor_str = NULL, *product_str = NULL, *vendor_tags = NULL;
+ gint bus = 0, vendor = 0, product = 0, version = 0;
+ const gchar *bus_str = NULL;
+ int d = 0, n = 0;
+
+ dev = fopen("/proc/bus/input/devices", "r");
+ if (!dev)
+ return;
+
+ if (input_list) {
+ moreinfo_del_with_prefix("DEV:INP");
+ g_free(input_list);
+ g_free(input_icons);
+ }
+ input_list = g_strdup("");
+ input_icons = g_strdup("");
+
+ while (fgets(buffer, sizeof(buffer), dev)) {
+ tmp = buffer;
+
+ switch (*tmp) {
+ case 'N':
+ tmp = strreplacechr(tmp + strlen("N: Name="), "=", ':');
+ name = g_strdup(tmp);
+ remove_quotes(name);
+ break;
+ case 'P':
+ phys = g_strdup(tmp + strlen("P: Phys="));
+ break;
+ case 'I':
+ sscanf(tmp, "I: Bus=%x Vendor=%x Product=%x Version=%x",
+ &bus, &vendor, &product, &version);
+ break;
+ case 'H':
+ if (strstr(tmp, "kbd"))
+ d = 1; //INPUT_KEYBOARD;
+ else if (strstr(tmp, "js"))
+ d = 2; //INPUT_JOYSTICK;
+ else if (strstr(tmp, "mouse"))
+ d = 3; //INPUT_MOUSE;
+ else
+ d = 0; //INPUT_UNKNOWN;
+ break;
+ case '\n':
+ if (name && strstr(name, "PC Speaker")) {
+ d = 4; // INPUT_PCSPKR
+ }
+ if (d == 0 && g_strcmp0(phys, "ALSA")) {
+ d = 5; // INPUT_AUDIO
+ }
+
+ if (vendor > 0 && product > 0 && g_str_has_prefix(phys, "usb-")) {
+ usb_lookup_ids_vendor_product_str(vendor, product, &vendor_str, &product_str);
+ }
+
+ if (bus >= 0 && (guint)bus < sizeof(bus_types) / sizeof(gchar*)) {
+ bus_str = bus_types[bus];
+ }
+
+ vl = vendor_list_remove_duplicates_deep(vendors_match(name, vendor_str, NULL));
+ vendor_tags = vendor_list_ribbon(vl, params.fmt_opts);
+
+ tmp = g_strdup_printf("INP%d", ++n);
+ input_list = h_strdup_cprintf("$%s$%s=%s|%s\n",
+ input_list,
+ tmp, name, EMPTYIFNULL(vendor_tags),
+ EMPTYIFNULL(input_devices[d].name));
+ input_icons = h_strdup_cprintf("Icon$%s$%s=%s\n",
+ input_icons,
+ tmp, name,
+ input_devices[d].icon);
+
+ gchar *strhash = g_strdup_printf("[%s]\n"
+ /* Name */ "$^$%s=%s\n"
+ /* Type */ "%s=%s\n"
+ /* Bus */ "%s=[0x%x] %s\n"
+ /* Vendor */ "$^$%s=[0x%x] %s\n"
+ /* Product */"%s=[0x%x] %s\n"
+ /* Version */"%s=0x%x\n",
+ _("Device Information"),
+ _("Name"), name,
+ _("Type"), UNKWNIFNULL(input_devices[d].name),
+ _("Bus"), bus, UNKWNIFNULL(bus_str),
+ _("Vendor"), vendor, UNKWNIFNULL(vendor_str),
+ _("Product"), product, UNKWNIFNULL(product_str),
+ _("Version"), version );
+
+ if (phys && phys[1] != 0) {
+ strhash = h_strdup_cprintf("%s=%s\n", strhash, _("Connected to"), phys);
+ }
+
+ if (phys && strstr(phys, "ir")) {
+ strhash = h_strdup_cprintf("%s=%s\n", strhash, _("InfraRed port"), _("Yes") );
+ }
+
+ moreinfo_add_with_prefix("DEV", tmp, strhash);
+ g_free(tmp);
+ g_free(phys);
+ g_free(name);
+ g_free(vendor_str);
+ g_free(vendor_tags);
+ g_free(product_str);
+ bus_str = NULL;
+ vendor_str = NULL;
+ product_str = NULL;
+ vendor_tags = NULL;
+ }
+ }
+
+ fclose(dev);
+}
diff --git a/modules/devices/loongarch64/processor.c b/modules/devices/loongarch64/processor.c
new file mode 100644
index 00000000..08f3645e
--- /dev/null
+++ b/modules/devices/loongarch64/processor.c
@@ -0,0 +1,90 @@
+/*
+ * HardInfo - Displays System Information
+ * Copyright (C) 2003-2006 L. A. F. Pereira <l@tia.mat.br>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, version 2 or later.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#include "hardinfo.h"
+#include "devices.h"
+#include "cpu_util.h"
+
+GSList *
+processor_scan(void)
+{
+ Processor *processor;
+ FILE *cpuinfo;
+ gchar buffer[128];
+
+ cpuinfo = fopen(PROC_CPUINFO, "r");
+ if (!cpuinfo)
+ return NULL;
+
+ processor = g_new0(Processor, 1);
+ while (fgets(buffer, 128, cpuinfo)) {
+ gchar **tmp = g_strsplit(buffer, ":", 2);
+
+ if (tmp[0] && tmp[1]) {
+ tmp[0] = g_strstrip(tmp[0]);
+ tmp[1] = g_strstrip(tmp[1]);
+
+ get_str("system type", processor->vendor_id);
+ get_str("CPU Family", processor->family);
+ get_str("Model Name", processor->model_name);
+ get_int("CPU Revision", processor->revision);
+ get_float("CPU MHz", processor->cpu_mhz);
+ get_float("BogoMIPS", processor->bogomips);
+ get_str("Features", processor->features);
+ }
+ g_strfreev(tmp);
+ }
+
+ fclose(cpuinfo);
+
+ return g_slist_append(NULL, processor);
+}
+
+gchar *processor_name(GSList * processors) {
+ return processor_name_default(processors);
+}
+
+gchar *processor_describe(GSList * processors) {
+ return processor_describe_default(processors);
+}
+
+gchar *
+processor_get_info(GSList *processors)
+{
+ Processor *processor = (Processor *)processors->data;
+
+ return g_strdup_printf("[%s]\n"
+ "%s=%s\n" /* vendor */
+ "%s=%s\n" /* family */
+ "%s=%s\n" /* name */
+ "%s=%d\n" /* revision */
+ "%s=%.2f %s\n" /* frequency */
+ "%s=%.2f\n" /* bogoMIPS */
+ "%s=%s\n" /* features */
+ "%s=%s\n", /* byte order */
+ _("Processor"),
+ _("System Type"), processor->vendor_id,
+ _("Family"), processor->family,
+ _("Model"), processor->model_name,
+ _("Revision"), processor->revision,
+ _("Frequency"), processor->cpu_mhz, _("MHz"),
+ _("BogoMIPS"), processor->bogomips,
+ _("Features"), processor->features,
+ _("Byte Order"), byte_order_str()
+ );
+}
diff --git a/modules/devices/m68k/processor.c b/modules/devices/m68k/processor.c
new file mode 100644
index 00000000..a9d71835
--- /dev/null
+++ b/modules/devices/m68k/processor.c
@@ -0,0 +1,92 @@
+/*
+ * HardInfo - Displays System Information
+ * Copyright (C) 2003-2006 L. A. F. Pereira <l@tia.mat.br>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, version 2 or later.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#include "hardinfo.h"
+#include "devices.h"
+#include "cpu_util.h"
+
+GSList *
+processor_scan(void)
+{
+ Processor *processor;
+ FILE *cpuinfo;
+ gchar buffer[128];
+
+ cpuinfo = fopen(PROC_CPUINFO, "r");
+ if (!cpuinfo)
+ return NULL;
+
+ processor = g_new0(Processor, 1);
+ while (fgets(buffer, 128, cpuinfo)) {
+ gchar **tmp = g_strsplit(buffer, ":", 2);
+
+ if (tmp[0] && tmp[1]) {
+ tmp[0] = g_strstrip(tmp[0]);
+ tmp[1] = g_strstrip(tmp[1]);
+
+ get_str("CPU", processor->model_name);
+ get_str("MMU", processor->mmu_name);
+ get_str("FPU", processor->fpu_name);
+ get_float("Clocking", processor->cpu_mhz);
+ get_float("BogoMips", processor->bogomips);
+ get_str("Calibration", processor->calibration);
+ }
+ g_strfreev(tmp);
+ }
+
+ gchar *tmp;
+ tmp = g_strconcat("Motorola ", processor->model_name, NULL);
+ g_free(processor->model_name);
+ processor->model_name = tmp;
+
+ fclose(cpuinfo);
+
+ return g_slist_append(NULL, processor);
+}
+
+gchar *processor_name(GSList * processors) {
+ return processor_name_default(processors);
+}
+
+gchar *processor_describe(GSList * processors) {
+ return processor_describe_default(processors);
+}
+
+gchar *
+processor_get_info(GSList *processors)
+{
+ Processor *processor = (Processor *)processors->data;
+
+ return g_strdup_printf("[%s]\n"
+ "%s=%s\n" /* cpu */
+ "%s=%s\n" /* mmu */
+ "%s=%s\n" /* fpu */
+ "%s=%.2f %s\n" /* frequency */
+ "%s=%.2f\n" /* bogomips */
+ "%s=%s\n" /* byte order */
+ "%s=%s\n", /* calibration */
+ _("Processor"),
+ _("Model"), processor->model_name,
+ _("MMU"), processor->mmu_name,
+ _("FPU"), processor->fpu_name,
+ _("Frequency"), processor->cpu_mhz, _("MHz"),
+ _("BogoMips"), processor->bogomips,
+ _("Byte Order"), byte_order_str(),
+ _("Calibration"), processor->calibration
+ );
+}
diff --git a/modules/devices/mips/processor.c b/modules/devices/mips/processor.c
new file mode 100644
index 00000000..112a3c3b
--- /dev/null
+++ b/modules/devices/mips/processor.c
@@ -0,0 +1,81 @@
+/*
+ * HardInfo - Displays System Information
+ * Copyright (C) 2003-2006 L. A. F. Pereira <l@tia.mat.br>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, version 2 or later.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#include "hardinfo.h"
+#include "devices.h"
+#include "cpu_util.h"
+
+GSList *
+processor_scan(void)
+{
+ Processor *processor;
+ FILE *cpuinfo;
+ gchar buffer[128];
+
+ cpuinfo = fopen(PROC_CPUINFO, "r");
+ if (!cpuinfo)
+ return NULL;
+
+ processor = g_new0(Processor, 1);
+ while (fgets(buffer, 128, cpuinfo)) {
+ gchar **tmp = g_strsplit(buffer, ":", 2);
+
+ if (tmp[0] && tmp[1]) {
+ tmp[0] = g_strstrip(tmp[0]);
+ tmp[1] = g_strstrip(tmp[1]);
+
+ get_str("system type", processor->vendor_id);
+ get_str("cpu model", processor->model_name);
+ get_float("cpu MHz", processor->cpu_mhz);
+ get_float("BogoMIPS", processor->bogomips);
+ }
+ g_strfreev(tmp);
+ }
+
+ fclose(cpuinfo);
+
+ return g_slist_append(NULL, processor);
+}
+
+gchar *processor_name(GSList * processors) {
+ return processor_name_default(processors);
+}
+
+gchar *processor_describe(GSList * processors) {
+ return processor_describe_default(processors);
+}
+
+gchar *
+processor_get_info(GSList *processors)
+{
+ Processor *processor = (Processor *)processors->data;
+
+ return g_strdup_printf("[%s]\n"
+ "%s=%s\n"
+ "%s=%s\n"
+ "%s=%.2f %s\n" /* frequency */
+ "%s=%.2f\n" /* bogomips */
+ "%s=%s\n", /* byte order */
+ _("Processor"),
+ _("Model"), processor->model_name,
+ _("System Type"), processor->vendor_id,
+ _("Frequency"), processor->cpu_mhz, _("MHz"),
+ _("BogoMips"), processor->bogomips,
+ _("Byte Order"), byte_order_str()
+ );
+}
diff --git a/modules/devices/monitors.c b/modules/devices/monitors.c
new file mode 100644
index 00000000..02fb1d67
--- /dev/null
+++ b/modules/devices/monitors.c
@@ -0,0 +1,515 @@
+/*
+ * HardInfo - Displays System Information
+ * Copyright (C) 2003-2019 L. A. F. Pereira <l@tia.mat.br>
+ * Copyright (C) 2019 Burt P. <pburt0@gmail.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, version 2 or later.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#include "devices.h"
+#include "util_sysobj.h"
+#include "util_edid.h"
+#include "util_ids.h"
+
+static const char monitor_icon[] = "monitor.png";
+
+#define UNKIFNULL2(f) ((f) ? f : _("(Unknown)"))
+#define UNKIFEMPTY2(f) ((*f) ? f : _("(Unknown)"))
+#define UNSPECIFNULL2(f) ((f) ? f : _("(Unspecified)"))
+
+gboolean no_monitors = FALSE;
+
+gchar *edid_ids_file = NULL;
+gchar *ieee_oui_ids_file = NULL;
+
+void find_edid_ids_file() {
+ if (edid_ids_file) return;
+ char *file_search_order[] = {
+ g_build_filename(g_get_user_config_dir(), "hardinfo2", "edid.ids", NULL),
+ g_build_filename(params.path_data, "edid.ids", NULL),
+ NULL
+ };
+ int n;
+ for(n = 0; file_search_order[n]; n++) {
+ if (!edid_ids_file && !access(file_search_order[n], R_OK))
+ edid_ids_file = file_search_order[n];
+ else
+ g_free(file_search_order[n]);
+ }
+ auto_free(edid_ids_file);
+}
+
+void find_ieee_oui_ids_file() {
+ if (ieee_oui_ids_file) return;
+ char *file_search_order[] = {
+ g_build_filename(g_get_user_config_dir(), "hardinfo2", "ieee_oui.ids", NULL),
+ g_build_filename(params.path_data, "ieee_oui.ids", NULL),
+ NULL
+ };
+ int n;
+ for(n = 0; file_search_order[n]; n++) {
+ if (!ieee_oui_ids_file && !access(file_search_order[n], R_OK))
+ ieee_oui_ids_file = file_search_order[n];
+ else
+ g_free(file_search_order[n]);
+ }
+ auto_free(ieee_oui_ids_file);
+}
+
+typedef struct {
+ gchar *drm_path;
+ gchar *drm_connection;
+ gchar *drm_status;
+ gchar *drm_enabled;
+ edid *e;
+ gchar *_vstr; /* use monitor_vendor_str() */
+} monitor;
+#define monitor_new() g_new0(monitor, 1)
+
+monitor *monitor_new_from_sysfs(const gchar *sysfs_edid_file) {
+ gchar *edid_bin = NULL;
+ gsize edid_len = 0;
+ if (!sysfs_edid_file || !*sysfs_edid_file) return NULL;
+ monitor *m = monitor_new();
+ m->drm_path = g_path_get_dirname(sysfs_edid_file);
+ m->drm_connection = g_path_get_basename(m->drm_path);
+ gchar *drm_enabled_file = g_strdup_printf("%s/%s", m->drm_path, "enabled");
+ gchar *drm_status_file = g_strdup_printf("%s/%s", m->drm_path, "status");
+ g_file_get_contents(drm_enabled_file, &m->drm_enabled, NULL, NULL);
+ if (m->drm_enabled) g_strstrip(m->drm_enabled);
+ g_file_get_contents(drm_status_file, &m->drm_status, NULL, NULL);
+ if (m->drm_status) g_strstrip(m->drm_status);
+ g_file_get_contents(sysfs_edid_file, &edid_bin, &edid_len, NULL);
+ if (edid_len)
+ m->e = edid_new(edid_bin, edid_len);
+ g_free(drm_enabled_file);
+ g_free(drm_status_file);
+ return m;
+}
+
+void monitor_free(monitor *m) {
+ if (m) {
+ g_free(m->_vstr);
+ g_free(m->drm_connection);
+ edid_free(m->e);
+ g_free(m);
+ }
+}
+
+gchar *monitor_vendor_str(monitor *m, gboolean include_value, gboolean link_name) {
+ if (!m || !m->e) return NULL;
+ edid_ven ven = m->e->ven;
+ gchar v[20] = "", t[4] = "";
+ ids_query_result result;// = {};
+
+ memset(&result,0,sizeof(ids_query_result));
+ if (ven.type == VEN_TYPE_PNP) {
+ strcpy(v, ven.pnp);
+ strcpy(t, "PNP");
+ } else if (ven.type == VEN_TYPE_OUI) {
+ strcpy(v, ven.oui_str);
+ strcpy(t, "OUI");
+ }
+
+ if (!m->_vstr) {
+ if (ven.type == VEN_TYPE_PNP) {
+ if (!edid_ids_file)
+ find_edid_ids_file();
+ scan_ids_file(edid_ids_file, v, &result, -1);
+ if (result.results[0])
+ m->_vstr = g_strdup(result.results[0]);
+ } else if (ven.type == VEN_TYPE_OUI) {
+ if (!ieee_oui_ids_file)
+ find_ieee_oui_ids_file();
+ scan_ids_file(ieee_oui_ids_file, v, &result, -1);
+ if (result.results[0])
+ m->_vstr = g_strdup(result.results[0]);
+ }
+ }
+
+ gchar *ret = NULL;
+ if (include_value)
+ ret = g_strdup_printf("[%s:%s]", t, v);
+ if (m->_vstr) {
+ if (link_name) {
+ gchar *lv = vendor_get_link(m->_vstr);
+ ret = appfsp(ret, "%s", lv);
+ g_free(lv);
+ } else
+ ret = appfsp(ret, "%s", m->_vstr);
+ } else if (!include_value && ven.type == VEN_TYPE_PNP) {
+ ret = appfsp(ret, "%s", ven.pnp);
+ } else
+ ret = appfsp(ret, "%s", _("(Unknown)"));
+ return ret;
+}
+
+gchar *monitor_name(monitor *m, gboolean include_vendor) {
+ if (!m) return NULL;
+ gchar *desc = NULL;
+ edid *e = m->e;
+ if (!e)
+ return g_strdup(_("(Unknown)"));
+
+ if (include_vendor) {
+ if (e->ven.type != VEN_TYPE_INVALID) {
+ gchar *vstr = monitor_vendor_str(m, FALSE, FALSE);
+ gchar *vtag = vendor_match_tag(vstr, params.fmt_opts);
+ desc = appfsp(desc, "%s", vtag ? vtag : vstr);
+ g_free(vstr);
+ g_free(vtag);
+ } else
+ desc = appfsp(desc, "%s", "Unknown");
+ }
+
+ if (e->img_max.diag_in)
+ desc = appfsp(desc, "%s", e->img_max.class_inch);
+
+ if (e->name)
+ desc = appfsp(desc, "%s", e->name);
+ else
+ desc = appfsp(desc, "%s %s", e->a_or_d ? "Digital" : "Analog", "Display");
+
+ return desc;
+}
+
+gchar **get_output_lines(const char *cmd_line) {
+ gboolean spawned;
+ gchar *out, *err;
+ gchar **ret = NULL;
+
+ spawned = g_spawn_command_line_sync(cmd_line,
+ &out, &err, NULL, NULL);
+ if (spawned) {
+ ret = g_strsplit(out, "\n", -1);
+ g_free(out);
+ g_free(err);
+ }
+ return ret;
+}
+
+static gchar *tag_make_safe_inplace(gchar *tag) {
+ if (!tag)
+ return tag;
+ if (!g_utf8_validate(tag, -1, NULL))
+ return tag; //TODO: reconsider
+ gchar *p = tag, *pd = tag;
+ while(*p) {
+ gchar *np = g_utf8_next_char(p);
+ gunichar c = g_utf8_get_char_validated(p, -1);
+ int l = g_unichar_to_utf8(c, NULL);
+ if (l == 1 && g_unichar_isalnum(c)) {
+ g_unichar_to_utf8(c, pd);
+ } else {
+ *pd = '_';
+ }
+ p = np;
+ pd++;
+ }
+ return tag;
+}
+
+static gchar *make_edid_section(monitor *m) {
+ int i;
+ edid *e = m->e;
+ if (e->len) {
+ gchar *vstr = monitor_vendor_str(m, TRUE, FALSE);
+
+ gchar *dom = NULL;
+ if (!e->dom.is_model_year && e->dom.week && e->dom.year)
+ dom = g_strdup_printf(_("Week %d of %d"), e->dom.week, e->dom.year);
+ else if (e->dom.year)
+ dom = g_strdup_printf("%d", e->dom.year);
+
+ gchar *bpcc = NULL;
+ if (e->bpc)
+ bpcc = g_strdup_printf("%d", e->bpc);
+
+ int aok = e->checksum_ok;
+ if (e->ext_blocks_fail) aok = 0;
+ gchar *csum = aok ? _("Ok") : _("Fail");
+
+ gchar *iface;
+ if (e->interface && e->di.exists) {
+ gchar *tmp = g_strdup_printf("[%x] %s\n[DI-EXT:%x] %s",
+ e->interface, _(edid_interface(e->interface)),
+ e->di.interface, _(edid_di_interface(e->di.interface)) );
+ iface = gg_key_file_parse_string_as_value(tmp, '|');
+ g_free(tmp);
+ } else if (e->di.exists) {
+ iface = g_strdup_printf("[DI-EXT:%x] %s",
+ e->di.interface, _(edid_di_interface(e->di.interface)) );
+ } else {
+ iface = g_strdup_printf("[%x] %s",
+ e->interface,
+ e->interface ? _(edid_interface(e->interface)) : _("(Unspecified)") );
+ }
+
+ gchar *d_list, *ext_list, *dtd_list, *cea_list,
+ *etb_list, *std_list, *svd_list, *sad_list,
+ *didt_list, *did_string_list;
+
+ etb_list = NULL;
+ for(i = 0; i < e->etb_count; i++) {
+ char *desc = edid_output_describe(&e->etbs[i]);
+ etb_list = appfnl(etb_list, "etb%d=%s", i, desc);
+ g_free(desc);
+ }
+ if (!etb_list) etb_list = g_strdup_printf("%s=\n", _("(Empty List)"));
+
+ std_list = NULL;
+ for(i = 0; i < e->std_count; i++) {
+ char *desc = edid_output_describe(&e->stds[i].out);
+ std_list = appfnl(std_list, "std%d=%s", i, desc);
+ g_free(desc);
+ }
+ if (!std_list) std_list = g_strdup_printf("%s=\n", _("(Empty List)"));
+
+ d_list = NULL;
+ for(i = 0; i < 4; i++) {
+ char *desc = edid_base_descriptor_describe(&e->d[i]);
+ d_list = appfnl(d_list, "descriptor%d=%s", i, desc);
+ g_free(desc);
+ }
+ if (!d_list) d_list = g_strdup_printf("%s=\n", _("(Empty List)"));
+
+ ext_list = NULL;
+ for(i = 0; i < e->ext_blocks; i++) {
+ int type = e->u8[(i+1)*128];
+ int version = e->u8[(i+1)*128 + 1];
+ ext_list = appfnl(ext_list, "ext%d = ([%02x:v%02x] %s) %s", i,
+ type, version, _(edid_ext_block_type(type)),
+ e->ext_ok[i] ? "ok" : "fail"
+ );
+ }
+ if (!ext_list) ext_list = g_strdup_printf("%s=\n", _("(Empty List)"));
+
+ dtd_list = NULL;
+ for(i = 0; i < e->dtd_count; i++) {
+ char *desc = edid_dtd_describe(&e->dtds[i], 0);
+ dtd_list = appfnl(dtd_list, "dtd%d = %s", i, desc);
+ free(desc);
+ }
+ if (!dtd_list) dtd_list = g_strdup_printf("%s=\n", _("(Empty List)"));
+
+ cea_list = NULL;
+ for(i = 0; i < e->cea_block_count; i++) {
+ char *desc = edid_cea_block_describe(&e->cea_blocks[i]);
+ cea_list = appfnl(cea_list, "cea_block%d = %s", i, desc);
+ }
+ if (!cea_list) cea_list = g_strdup_printf("%s=\n", _("(Empty List)"));
+
+ svd_list = NULL;
+ for(i = 0; i < e->svd_count; i++) {
+ char *desc = edid_output_describe(&e->svds[i].out);
+ svd_list = appfnl(svd_list, "svd%d=%s", i, desc);
+ g_free(desc);
+ }
+ if (!svd_list) svd_list = g_strdup_printf("%s=\n", _("(Empty List)"));
+
+ sad_list = NULL;
+ for(i = 0; i < e->sad_count; i++) {
+ char *desc = edid_cea_audio_describe(&e->sads[i]);
+ sad_list = appfnl(sad_list, "sad%d=%s", i, desc);
+ g_free(desc);
+ }
+ if (!sad_list) sad_list = g_strdup_printf("%s=\n", _("(Empty List)"));
+
+ didt_list = NULL;
+ for(i = 0; i < e->didt_count; i++) {
+ char *desc = edid_output_describe(&e->didts[i]);
+ didt_list = appfnl(didt_list, "didt%d=%s", i, desc);
+ g_free(desc);
+ }
+ if (!didt_list) didt_list = g_strdup_printf("%s=\n", _("(Empty List)"));
+
+ did_string_list = NULL;
+ for(i = 0; i < e->did_string_count; i++) {
+ did_string_list = appfnl(did_string_list, "did_string%d=%s", i, e->did_strings[i].str);
+ }
+ if (!did_string_list) did_string_list = g_strdup_printf("%s=\n", _("(Empty List)"));
+
+ gchar *speakers = NULL;
+ if (e->speaker_alloc_bits) {
+ gchar *spk_tmp = edid_cea_speaker_allocation_describe(e->speaker_alloc_bits, 0);
+ speakers = gg_key_file_parse_string_as_value(spk_tmp, '|');
+ g_free(spk_tmp);
+ } else
+ speakers = g_strdup(_("(Unspecified)"));
+
+ gchar *hex = edid_dump_hex(e, 0, 1);
+ gchar *hex_esc = gg_key_file_parse_string_as_value(hex, '|');
+ g_free(hex);
+ if (params.markup_ok)
+ hex = g_strdup_printf("<tt>%s</tt>", hex_esc);
+ else
+ hex = g_strdup(hex_esc);
+ g_free(hex_esc);
+
+ gchar *ret = g_strdup_printf(
+ /* extending "Connection" section */
+ "%s=%s\n" /* sig type */
+ "%s=%s\n" /* interface */
+ "%s=%s\n" /* bpcc */
+ "%s=%s\n" /* speakers */
+ "[%s]\n"
+ "%s=%s\n" /* base out */
+ "%s=%s\n" /* ext out */
+ "[%s]\n"
+ "$^$%s=%s\n" /* vendor */
+ "%s=%s\n" /* name */
+ "%s=[%04x-%08x] %u-%u\n" /* model, n_serial */
+ "%s=%s\n" /* serial */
+ "%s=%s\n" /* dom */
+ "[%s]\n"
+ "%s=%d %s\n" /* size */
+ "%s=%d.%d\n" /* version */
+ "%s=%d\n" /* ext block */
+ "%s=%s\n" /* ext to */
+ "%s=%s %s\n" /* checksum */
+ "[%s]\n%s\n"
+ "[%s]\n%s\n"
+ "[%s]\n%s\n"
+ "[%s]\n%s\n"
+ "[%s]\n%s\n"
+ "[%s]\n%s\n"
+ "[%s]\n%s\n"
+ "[%s]\n%s\n"
+ "[%s]\n%s\n"
+ "[%s]\n%s\n"
+ "[%s]\n%s=%s\n"
+ ,
+ _("Signal Type"), e->a_or_d ? _("Digital") : _("Analog"),
+ _("Interface"), iface,
+ _("Bits per Color Channel"), UNSPECIFNULL2(bpcc),
+ _("Speaker Allocation"), speakers,
+ _("Output (Max)"),
+ edid_output_src(e->img.src), edid_output_describe(&e->img),
+ edid_output_src(e->img_max.src), edid_output_describe(&e->img_max),
+ _("EDID Device"),
+ _("Vendor"), vstr,
+ _("Name"), e->name,
+ _("Model"), e->product, e->n_serial, e->product, e->n_serial,
+ _("Serial"), UNKIFNULL2(e->serial),
+ _("Manufacture Date"), UNKIFNULL2(dom),
+ _("EDID Meta"),
+ _("Data Size"), e->len, _("bytes"),
+ _("Version"), (int)e->ver_major, (int)e->ver_minor,
+ _("Extension Blocks"), e->ext_blocks,
+ _("Extended to"), e->std ? _(edid_standard(e->std)) : _("(None)"),
+ _("Checksum"), csum, aok ? "" : problem_marker(),
+ _("EDID Descriptors"), d_list,
+ _("Detailed Timing Descriptors (DTD)"), dtd_list,
+ _("Established Timings Bitmap (ETB)"), etb_list,
+ _("Standard Timings (STD)"), std_list,
+ _("E-EDID Extension Blocks"), ext_list,
+ _("EIA/CEA-861 Data Blocks"), cea_list,
+ _("EIA/CEA-861 Short Audio Descriptors"), sad_list,
+ _("EIA/CEA-861 Short Video Descriptors"), svd_list,
+ _("DisplayID Timings"), didt_list,
+ _("DisplayID Strings"), did_string_list,
+ _("Hex Dump"), _("Data"), hex
+ );
+ g_free(bpcc);
+ g_free(dom);
+
+ g_free(d_list);
+ g_free(ext_list);
+ g_free(etb_list);
+ g_free(std_list);
+ g_free(dtd_list);
+ g_free(cea_list);
+ g_free(sad_list);
+ g_free(svd_list);
+ g_free(didt_list);
+ g_free(did_string_list);
+ g_free(iface);
+ g_free(vstr);
+ g_free(hex);
+ //printf("ret: %s\n", ret);
+ return ret;
+ } else
+ return g_strdup("");
+}
+
+gchar *monitors_get_info() {
+ gchar *icons = g_strdup("");
+ gchar *ret = g_strdup_printf("[%s]\n", _("Monitors"));
+ gchar tag_prefix[] = "DEV";
+
+ gchar **edid_files = get_output_lines("find /sys/devices -name edid");
+ //gchar **edid_files = get_output_lines("find /home/pburt/github/verbose-spork/junk/testing/.testing/edid2/ -name edid.*");
+ int i, found = 0;
+ for(i = 0; edid_files[i]; i++) {
+ monitor *m = monitor_new_from_sysfs(edid_files[i]);
+ //if (m && m->e->std < STD_DISPLAYID) continue;
+ //if (m && !m->e->interface) continue;
+ //if (m && m->e->interface != 1) continue;
+ if (m && !SEQ(m->drm_status, "disconnected")) {
+ gchar *tag = g_strdup_printf("%d-%s", found, m->drm_connection);
+ tag_make_safe_inplace(tag);
+ gchar *desc = monitor_name(m, TRUE);
+ gchar *edid_section = NULL;
+ edid *e = m->e;
+ if (e && e->checksum_ok)
+ edid_section = make_edid_section(m);
+
+ gchar *details = g_strdup_printf("[%s]\n"
+ "%s=%s\n"
+ "%s=%s %s\n"
+ "%s\n",
+ _("Connection"),
+ _("DRM"), m->drm_connection,
+ _("Status"), m->drm_status, m->drm_enabled,
+ edid_section ? edid_section : ""
+ );
+ moreinfo_add_with_prefix(tag_prefix, tag, details); /* moreinfo now owns *details */
+ ret = h_strdup_cprintf("$!%s$%s=%s\n",
+ ret, tag, m->drm_connection, desc);
+ icons = h_strdup_cprintf("Icon$%s$=%s\n", icons, tag, monitor_icon);
+ g_free(desc);
+ g_free(edid_section);
+ found++;
+ }
+ monitor_free(m);
+ }
+ g_strfreev(edid_files);
+
+ no_monitors = FALSE;
+ if(!found) {
+ no_monitors = TRUE;
+ g_free(ret);
+ ret = g_strdup_printf("[%s]\n%s=%s\n" "[$ShellParam$]\nViewType=0\n",
+ _("Monitors"), _("Result"), _("(Empty)") );
+ } else {
+ ret = h_strdup_cprintf(
+ "[$ShellParam$]\nViewType=1\n"
+ "ColumnTitle$TextValue=%s\n" /* DRM connection */
+ "ColumnTitle$Value=%s\n" /* Name */
+ "ShowColumnHeaders=true\n"
+ "%s",
+ ret,
+ _("Connection"),
+ _("Name"),
+ icons
+ );
+ }
+
+ return ret;
+}
+
+gboolean monitors_hinote(const char **msg) {
+ PARAM_NOT_UNUSED(msg);
+ return FALSE;
+}
diff --git a/modules/devices/parisc/processor.c b/modules/devices/parisc/processor.c
new file mode 100644
index 00000000..c749bc5a
--- /dev/null
+++ b/modules/devices/parisc/processor.c
@@ -0,0 +1,210 @@
+/*
+ * HardInfo - Displays System Information
+ * Copyright (C) 2003-2006 L. A. F. Pereira <l@tia.mat.br>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, version 2 or later.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#include "hardinfo.h"
+#include "devices.h"
+#include "cpu_util.h"
+
+GSList *
+processor_scan(void)
+{
+ GSList *procs = NULL;
+ Processor *processor = NULL;
+ FILE *cpuinfo;
+ gchar buffer[128];
+ gchar *rep_pname = NULL;
+ GSList *pi = NULL;
+
+ cpuinfo = fopen(PROC_CPUINFO, "r");
+ if (!cpuinfo)
+ return NULL;
+
+#define CHECK_FOR(k) (g_str_has_prefix(tmp[0], k))
+ while (fgets(buffer, 128, cpuinfo)) {
+ gchar **tmp = g_strsplit(buffer, ":", 2);
+ if (tmp[0] && tmp[1]) {
+ tmp[0] = g_strstrip(tmp[0]);
+ tmp[1] = g_strstrip(tmp[1]);
+ } else {
+ g_strfreev(tmp);
+ continue;
+ }
+
+ get_str("Processor", rep_pname);
+
+ if ( CHECK_FOR("processor") ) {
+ /* finish previous */
+ if (processor) {
+ procs = g_slist_append(procs, processor);
+ }
+
+ /* start next */
+ processor = g_new0(Processor, 1);
+ processor->id = atol(tmp[1]);
+
+ if (rep_pname)
+ processor->model_name = g_strdup(rep_pname);
+
+ g_strfreev(tmp);
+ continue;
+ }
+
+ if (!processor &&
+ ( CHECK_FOR("cpu family")
+ || CHECK_FOR("cpu MHz")
+ || CHECK_FOR("cpu") ) ) {
+
+ /* single proc/core may not have "processor : n" */
+ processor = g_new0(Processor, 1);
+ processor->id = 0;
+
+ if (rep_pname)
+ processor->model_name = g_strdup(rep_pname);
+ }
+
+ if (processor) {
+ get_str("cpu family", processor->cpu_family);
+ get_float("cpu MHz", processor->cpu_mhz);
+ get_str("cpu", processor->model_name);
+ get_float("bogomips", processor->bogomips);
+ get_str("model name", processor->strmodel);
+ get_str("I-cache", processor->icache_str);
+ get_str("D-cache", processor->dcache_str);
+ get_str("hversion", processor->hversion);
+ get_str("sversion", processor->sversion);
+ }
+ g_strfreev(tmp);
+ }
+
+ if (processor)
+ procs = g_slist_append(procs, processor);
+
+ g_free(rep_pname);
+ fclose(cpuinfo);
+
+ /* TODO: redup */
+
+ /* data not from /proc/cpuinfo */
+ for (pi = procs; pi; pi = pi->next) {
+ processor = (Processor *) pi->data;
+
+ /* strings can't be null or segfault later */
+ STRIFNULL(processor->model_name, _("PA-RISC Processor") );
+ STRIFNULL(processor->cpu_family, "PA-RISC");
+ UNKIFNULL(processor->strmodel);
+
+ /* topo & freq */
+ processor->cpufreq = cpufreq_new(processor->id);
+ processor->cputopo = cputopo_new(processor->id);
+
+ if (processor->cpufreq->cpukhz_max)
+ processor->cpu_mhz = processor->cpufreq->cpukhz_max / 1000;
+
+ }
+
+ return procs;
+}
+
+gchar *processor_name(GSList * processors) {
+ return processor_name_default(processors);
+}
+
+gchar *processor_describe(GSList * processors) {
+ return processor_describe_default(processors);
+}
+
+gchar *
+processor_get_detailed_info(Processor *processor)
+{
+ gchar *tmp_cpufreq, *tmp_topology, *ret;
+
+ tmp_topology = cputopo_section_str(processor->cputopo);
+ tmp_cpufreq = cpufreq_section_str(processor->cpufreq);
+
+ ret = g_strdup_printf("[%s]\n"
+ "%s=%s\n"
+ "%s=%s\n"
+ "%s=%s\n" /* model name */
+ "%s=%.2f %s\n" /* frequency */
+ "%s=%.2f\n" /* bogomips */
+ "%s=%s\n" /* byte order */
+ "%s=%s\n" /* hversion */
+ "%s=%s\n" /* sversion */
+ "[%s]\n"
+ "I-Cache=%s\n"
+ "D-Cache=%s\n"
+ "%s" /* topology */
+ "%s" /* frequency scaling */
+ "%s",/* empty */
+ _("Processor"),
+ _("Model"), processor->model_name,
+ _("Architecture"), processor->cpu_family,
+ _("System"), processor->strmodel,
+ _("Frequency"), processor->cpu_mhz, _("MHz"),
+ _("BogoMips"), processor->bogomips,
+ _("Byte Order"), byte_order_str(),
+ _("HVersion"), processor->hversion,
+ _("SVersion"), processor->sversion,
+ _("Cache"),
+ processor->icache_str,
+ processor->dcache_str,
+ tmp_topology,
+ tmp_cpufreq,
+ "");
+
+ g_free(tmp_cpufreq);
+ g_free(tmp_topology);
+ return ret;
+}
+
+gchar *processor_get_info(GSList * processors)
+{
+ Processor *processor;
+
+ if (g_slist_length(processors) > 1) {
+ gchar *ret, *tmp, *hashkey;
+ GSList *l;
+
+ tmp = g_strdup("");
+
+ for (l = processors; l; l = l->next) {
+ processor = (Processor *) l->data;
+
+ tmp = g_strdup_printf("%s$CPU%d$%s=%.2f %s\n",
+ tmp, processor->id,
+ processor->model_name,
+ processor->cpu_mhz, _("MHz"));
+
+ hashkey = g_strdup_printf("CPU%d", processor->id);
+ moreinfo_add_with_prefix("DEV", hashkey,
+ processor_get_detailed_info(processor));
+ g_free(hashkey);
+ }
+
+ ret = g_strdup_printf("[$ShellParam$]\n"
+ "ViewType=1\n"
+ "[Processors]\n"
+ "%s", tmp);
+ g_free(tmp);
+
+ return ret;
+ }
+
+ processor = (Processor *) processors->data;
+ return processor_get_detailed_info(processor);
+}
diff --git a/modules/devices/pci.c b/modules/devices/pci.c
new file mode 100644
index 00000000..859fa339
--- /dev/null
+++ b/modules/devices/pci.c
@@ -0,0 +1,200 @@
+/*
+ * HardInfo - Displays System Information
+ * Copyright (C) 2003-2006 L. A. F. Pereira <l@tia.mat.br>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, version 2 or later.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#include <string.h>
+
+#include "hardinfo.h"
+#include "devices.h"
+#include "pci_util.h"
+
+#define UNKIFNULL_AC(f) (f != NULL) ? f : _("(Unknown)");
+
+static const struct {
+ const gchar *icon;
+ uint32_t class;
+} class2icon[] = {
+ { .class = 0x0200, .icon = "network-interface.png" },
+ { .class = 0x0c03, .icon = "usb.png" },
+ { .class = 0x0403, .icon = "audio.png" },
+ { .class = 0x0805, .icon = "usbfldisk.png" },
+ { .class = 0x0d11, .icon = "bluetooth.png" },
+ { .class = 0x0703, .icon = "modem.png" },
+ { .class = 0x01, .icon = "hdd.png" },
+ { .class = 0x02, .icon = "network.png" },
+ { .class = 0x03, .icon = "monitor.png" },
+ { .class = 0x05, .icon = "memory.png" },
+ { .class = 0x07, .icon = "network-connections.png" },
+ { .class = 0x09, .icon = "inputdevices.png" },
+ { .class = 0x10, .icon = "cryptohash.png" },
+};
+
+static const gchar *find_icon_for_class(uint32_t class)
+{
+ guint i;
+
+ for (i = 0; i < G_N_ELEMENTS(class2icon); i++) {
+ if (class2icon[i].class <= 0xff) {
+ if ((class & 0xff00) == class2icon[i].class << 8)
+ return class2icon[i].icon;
+ } else if (class == class2icon[i].class) {
+ return class2icon[i].icon;
+ }
+ }
+
+ return "devices.png";
+}
+
+static gchar *_pci_dev(const pcid *p, gchar *icons) {
+ gchar *str;
+ const gchar *class, *vendor, *svendor, *product, *sproduct, *lproduct;
+ gchar *name, *key;
+
+ gboolean device_is_sdevice = (p->vendor_id == p->sub_vendor_id && p->device_id == p->sub_device_id);
+
+ class = UNKIFNULL_AC(p->class_str);
+ vendor = UNKIFNULL_AC(p->vendor_id_str);
+ svendor = UNKIFNULL_AC(p->sub_vendor_id_str);
+ product = UNKIFNULL_AC(p->device_id_str);
+ sproduct = UNKIFNULL_AC(p->sub_device_id_str);
+ lproduct = p->device_id_str ? p->device_id_str : p->class_str;
+ lproduct = UNKIFNULL_AC(lproduct);
+
+ gchar *ven_tag = vendor_match_tag(p->vendor_id_str, params.fmt_opts);
+ gchar *sven_tag = vendor_match_tag(p->sub_vendor_id_str, params.fmt_opts);
+ if (ven_tag) {
+ if (sven_tag && p->vendor_id != p->sub_vendor_id) {
+ name = g_strdup_printf("%s %s %s", sven_tag, ven_tag, lproduct);
+ } else {
+ name = g_strdup_printf("%s %s", ven_tag, lproduct);
+ }
+ } else {
+ name = g_strdup_printf("%s %s", vendor, lproduct);
+ }
+ g_free(ven_tag);
+ g_free(sven_tag);
+
+ key = g_strdup_printf("PCI%04x:%02x:%02x.%01x", p->domain, p->bus, p->device, p->function);
+
+ pci_list = h_strdup_cprintf("$%s$%04x:%02x:%02x.%01x=%s\n", pci_list, key, p->domain, p->bus, p->device, p->function, name);
+ icons = h_strdup_cprintf("Icon$%s$%04x:%02x:%02x.%01x=%s\n", icons, key, p->domain, p->bus, p->device, p->function, find_icon_for_class(p->class));
+
+ gchar *vendor_device_str;
+ if (device_is_sdevice) {
+ vendor_device_str = g_strdup_printf(
+ /* Vendor */ "$^$%s=[%04x] %s\n"
+ /* Device */ "%s=[%04x] %s\n",
+ _("Vendor"), p->vendor_id, vendor,
+ _("Device"), p->device_id, product);
+ } else {
+ vendor_device_str = g_strdup_printf(
+ /* Vendor */ "$^$%s=[%04x] %s\n"
+ /* Device */ "%s=[%04x] %s\n"
+ /* Sub-device vendor */ "$^$%s=[%04x] %s\n"
+ /* Sub-device */ "%s=[%04x] %s\n",
+ _("Vendor"), p->vendor_id, vendor,
+ _("Device"), p->device_id, product,
+ _("SVendor"), p->sub_vendor_id, svendor,
+ _("SDevice"), p->sub_device_id, sproduct);
+ }
+
+ gchar *pcie_str;
+ if (p->pcie_width_curr) {
+ pcie_str = g_strdup_printf("[%s]\n"
+ /* Width */ "%s=x%u\n"
+ /* Width (max) */ "%s=x%u\n"
+ /* Speed */ "%s=%0.1f %s\n"
+ /* Speed (max) */ "%s=%0.1f %s\n",
+ _("PCI Express"),
+ _("Link Width"), p->pcie_width_curr,
+ _("Maximum Link Width"), p->pcie_width_max,
+ _("Link Speed"), p->pcie_speed_curr, _("GT/s"),
+ _("Maximum Link Speed"), p->pcie_speed_max, _("GT/s") );
+ } else
+ pcie_str = strdup("");
+
+ str = g_strdup_printf("[%s]\n"
+ /* Class */ "%s=[%04x] %s\n"
+ "%s"
+ /* Revision */ "%s=%02x\n"
+ /* PCIE? */ "%s"
+ "[%s]\n"
+ /* Driver */ "%s=%s\n"
+ /* Modules */ "%s=%s\n"
+ "[%s]\n"
+ /* Domain */ "%s=%04x\n"
+ /* Bus */ "%s=%02x\n"
+ /* Device */ "%s=%02x\n"
+ /* Function */ "%s=%01x\n",
+ _("Device Information"),
+ _("Class"), p->class, class,
+ vendor_device_str,
+ _("Revision"), p->revision,
+ pcie_str,
+ _("Driver"),
+ _("In Use"), (p->driver) ? p->driver : _("(Unknown)"),
+ _("Kernel Modules"), (p->driver_list) ? p->driver_list : _("(Unknown)"),
+ _("Connection"),
+ _("Domain"), p->domain,
+ _("Bus"), p->bus,
+ _("Device"), p->device,
+ _("Function"), p->function
+ );
+
+ g_free(pcie_str);
+
+ moreinfo_add_with_prefix("DEV", key, str); /* str now owned by morinfo */
+
+ g_free(vendor_device_str);
+ g_free(name);
+ g_free(key);
+
+ return icons;
+}
+
+void scan_pci_do(void) {
+
+ if (pci_list) {
+ moreinfo_del_with_prefix("DEV:PCI");
+ g_free(pci_list);
+ }
+ pci_list = g_strdup_printf("[%s]\n", _("PCI Devices"));
+
+ gchar *pci_icons = g_strdup("");
+
+ pcid_list list = pci_get_device_list(0,0);
+ list = g_slist_sort(list, (GCompareFunc)pcid_cmp_by_addy);
+ GSList *l = list;
+
+ int c = 0;
+ while(l) {
+ pcid *curr = (pcid*)l->data;
+ pci_icons = _pci_dev(curr, pci_icons);
+ c++;
+ l=l->next;
+ }
+ pcid_list_free(list);
+
+ if (c) {
+ pci_list = g_strconcat(pci_list, "[$ShellParam$]\n", "ViewType=1\n", pci_icons, NULL);
+ } else {
+ /* NO PCI? */
+ pci_list = g_strconcat(pci_list, _("No PCI devices found"), "=\n", NULL);
+ }
+
+ g_free(pci_icons);
+}
diff --git a/modules/devices/ppc/processor.c b/modules/devices/ppc/processor.c
new file mode 100644
index 00000000..12c575a6
--- /dev/null
+++ b/modules/devices/ppc/processor.c
@@ -0,0 +1,206 @@
+/*
+ * HardInfo - Displays System Information
+ * Copyright (C) 2003-2006 L. A. F. Pereira <l@tia.mat.br>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, version 2 or later.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#include "hardinfo.h"
+#include "devices.h"
+#include "cpu_util.h"
+
+GSList *
+processor_scan(void)
+{
+ GSList *procs = NULL;
+ Processor *processor = NULL;
+ FILE *cpuinfo;
+ gchar buffer[128];
+ gchar *rep_pname = NULL;
+ GSList *pi = NULL;
+
+ cpuinfo = fopen(PROC_CPUINFO, "r");
+ if (!cpuinfo)
+ return NULL;
+
+#define CHECK_FOR(k) (g_str_has_prefix(tmp[0], k))
+ while (fgets(buffer, 128, cpuinfo)) {
+ gchar **tmp = g_strsplit(buffer, ":", 2);
+ if (tmp[0] && tmp[1]) {
+ tmp[0] = g_strstrip(tmp[0]);
+ tmp[1] = g_strstrip(tmp[1]);
+ } else {
+ g_strfreev(tmp);
+ continue;
+ }
+
+ get_str("Processor", rep_pname);
+
+ if ( CHECK_FOR("processor") ) {
+ /* finish previous */
+ if (processor) {
+ procs = g_slist_append(procs, processor);
+ }
+
+ /* start next */
+ processor = g_new0(Processor, 1);
+ processor->id = atol(tmp[1]);
+
+ if (rep_pname)
+ processor->model_name = g_strdup(rep_pname);
+
+ g_strfreev(tmp);
+ continue;
+ }
+
+ if (!processor &&
+ ( CHECK_FOR("cpu")
+ || CHECK_FOR("clock")
+ || CHECK_FOR("revision") ) ) {
+
+ /* single proc/core may not have "processor : n" */
+ processor = g_new0(Processor, 1);
+ processor->id = 0;
+
+ if (rep_pname)
+ processor->model_name = g_strdup(rep_pname);
+ }
+
+ if (processor) {
+ get_str("cpu", processor->model_name);
+ get_str("revision", processor->revision);
+ get_float("clock", processor->cpu_mhz);
+ get_float("BogoMIPS", processor->bogomips);
+ }
+ g_strfreev(tmp);
+ }
+
+ if (processor)
+ procs = g_slist_append(procs, processor);
+
+ g_free(rep_pname);
+ fclose(cpuinfo);
+
+ /* re-duplicate missing data for /proc/cpuinfo variant that de-duplicated it */
+#define REDUP(f) if (dproc->f && !processor->f) processor->f = g_strdup(dproc->f);
+ Processor *dproc;
+ GSList *l;
+ l = procs = g_slist_reverse(procs);
+ while (l) {
+ processor = l->data;
+ if (processor->model_name) {
+ dproc = processor;
+ } else if (dproc) {
+ REDUP(model_name);
+ REDUP(revision);
+ }
+ l = g_slist_next(l);
+ }
+ procs = g_slist_reverse(procs);
+
+ /* data not from /proc/cpuinfo */
+ for (pi = procs; pi; pi = pi->next) {
+ processor = (Processor *) pi->data;
+
+ /* strings can't be null or segfault later */
+ STRIFNULL(processor->model_name, _("POWER Processor") );
+ UNKIFNULL(processor->revision);
+
+ /* topo & freq */
+ processor->cpufreq = cpufreq_new(processor->id);
+ processor->cputopo = cputopo_new(processor->id);
+
+ if (processor->cpufreq->cpukhz_max)
+ processor->cpu_mhz = processor->cpufreq->cpukhz_max / 1000;
+
+ }
+
+ return procs;
+}
+
+gchar *processor_name(GSList * processors) {
+ return processor_name_default(processors);
+}
+
+gchar *processor_describe(GSList * processors) {
+ return processor_describe_default(processors);
+}
+
+gchar *
+processor_get_detailed_info(Processor *processor)
+{
+ gchar *tmp_cpufreq, *tmp_topology, *ret;
+
+ tmp_topology = cputopo_section_str(processor->cputopo);
+ tmp_cpufreq = cpufreq_section_str(processor->cpufreq);
+
+ ret = g_strdup_printf("[%s]\n"
+ "%s=%s\n" /* model */
+ "%s=%s\n" /* revision */
+ "%s=%.2f %s\n" /* frequency */
+ "%s=%.2f\n" /* bogomips */
+ "%s=%s\n" /* byte order */
+ "%s" /* topology */
+ "%s" /* frequency scaling */
+ "%s",/* empty */
+ _("Processor"),
+ _("Model"), processor->model_name,
+ _("Revision"), processor->revision,
+ _("Frequency"), processor->cpu_mhz, _("MHz"),
+ _("BogoMips"), processor->bogomips,
+ _("Byte Order"), byte_order_str(),
+ tmp_topology,
+ tmp_cpufreq,
+ "");
+ g_free(tmp_cpufreq);
+ g_free(tmp_topology);
+ return ret;
+}
+
+gchar *processor_get_info(GSList * processors)
+{
+ Processor *processor;
+
+ if (g_slist_length(processors) > 1) {
+ gchar *ret, *tmp, *hashkey;
+ GSList *l;
+
+ tmp = g_strdup("");
+
+ for (l = processors; l; l = l->next) {
+ processor = (Processor *) l->data;
+
+ tmp = g_strdup_printf("%s$CPU%d$%s=%.2f %s\n",
+ tmp, processor->id,
+ processor->model_name,
+ processor->cpu_mhz, _("MHz"));
+
+ hashkey = g_strdup_printf("CPU%d", processor->id);
+ moreinfo_add_with_prefix("DEV", hashkey,
+ processor_get_detailed_info(processor));
+ g_free(hashkey);
+ }
+
+ ret = g_strdup_printf("[$ShellParam$]\n"
+ "ViewType=1\n"
+ "[Processors]\n"
+ "%s", tmp);
+ g_free(tmp);
+
+ return ret;
+ }
+
+ processor = (Processor *) processors->data;
+ return processor_get_detailed_info(processor);
+}
diff --git a/modules/devices/printers.c b/modules/devices/printers.c
new file mode 100644
index 00000000..fb9389ac
--- /dev/null
+++ b/modules/devices/printers.c
@@ -0,0 +1,272 @@
+/*
+ * HardInfo - Displays System Information
+ * Copyright (C) 2003-2007 L. A. F. Pereira <l@tia.mat.br>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, version 2 or later.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#ifndef _XOPEN_SOURCE
+ #define _XOPEN_SOURCE
+#endif
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <time.h>
+
+#include "devices.h"
+
+typedef struct _CUPSDest CUPSDest;
+typedef struct _CUPSOption CUPSOption;
+
+struct _CUPSOption {
+ char *name, *value;
+};
+
+struct _CUPSDest {
+ char *name, *instance;
+ int is_default;
+ int num_options;
+ CUPSOption *options;
+};
+
+static int (*cups_dests_get) (CUPSDest **dests) = NULL;
+static int (*cups_dests_free) (int num_dests, CUPSDest *dests) = NULL;
+static gboolean cups_init = FALSE;
+
+GModule *cups;
+
+void
+init_cups(void)
+{
+ const char *libcups[] = { "libcups", "libcups.so", "libcups.so.1", "libcups.so.2", NULL };
+
+ if (!(cups_dests_get && cups_dests_free)) {
+ int i;
+
+ for (i = 0; libcups[i] != NULL; i++) {
+ cups = g_module_open(libcups[i], G_MODULE_BIND_LAZY);
+ if (cups)
+ break;
+ }
+
+ if (!cups) {
+ cups_init = FALSE;
+ return;
+ }
+
+ if (!g_module_symbol(cups, "cupsGetDests", (gpointer) & cups_dests_get)
+ || !g_module_symbol(cups, "cupsFreeDests", (gpointer) & cups_dests_free)) {
+ if(cups) g_module_close(cups);
+ cups_init = FALSE;
+ }
+ }
+
+ cups_init = TRUE;
+}
+
+gchar *__cups_callback_ptype(gchar *strvalue)
+{
+ if (strvalue) {
+ unsigned value = atoi(strvalue);
+ gchar *output = g_strdup("\n");
+
+ if (value & 0x0004)
+ output = h_strdup_cprintf(_("\342\232\254 Can do black and white printing=\n"), output);
+ if (value & 0x0008)
+ output = h_strdup_cprintf(_("\342\232\254 Can do color printing=\n"), output);
+ if (value & 0x0010)
+ output = h_strdup_cprintf(_("\342\232\254 Can do duplexing=\n"), output);
+ if (value & 0x0020)
+ output = h_strdup_cprintf(_("\342\232\254 Can do staple output=\n"), output);
+ if (value & 0x0040)
+ output = h_strdup_cprintf(_("\342\232\254 Can do copies=\n"), output);
+ if (value & 0x0080)
+ output = h_strdup_cprintf(_("\342\232\254 Can collate copies=\n"), output);
+ if (value & 0x80000)
+ output = h_strdup_cprintf(_("\342\232\254 Printer is rejecting jobs=\n"), output);
+ if (value & 0x1000000)
+ output = h_strdup_cprintf(_("\342\232\254 Printer was automatically discovered and added=\n"), output);
+
+ return output;
+ } else {
+ return g_strdup(_("Unknown"));
+ }
+}
+
+gchar *__cups_callback_state(gchar *value)
+{
+ if (!value) {
+ return g_strdup(_("Unknown"));
+ }
+
+ if (g_str_equal(value, "3")) {
+ return g_strdup(_("Idle"));
+ } else if (g_str_equal(value, "4")) {
+ return g_strdup(_("Printing a Job"));
+ } else if (g_str_equal(value, "5")) {
+ return g_strdup(_("Stopped"));
+ } else {
+ return g_strdup(_("Unknown"));
+ }
+}
+
+gchar *__cups_callback_state_change_time(gchar *value)
+{
+ struct tm tm;
+ char buf[255];
+
+ if (value) {
+ strptime(value, "%s", &tm);
+ strftime(buf, sizeof(buf), "%c", &tm);
+
+ return g_strdup(buf);
+ } else {
+ return g_strdup(_("Unknown"));
+ }
+}
+
+gchar *__cups_callback_boolean(gchar *value)
+{
+ if (value) {
+ return g_strdup(g_str_equal(value, "1") ? _("Yes") : _("No"));
+ } else {
+ return g_strdup(_("Unknown"));
+ }
+}
+
+const struct {
+ char *key, *name;
+ gchar *(*callback)(gchar *value);
+ gboolean maybe_vendor;
+} cups_fields[] = {
+ { "Printer Information", NULL, NULL },
+ { "printer-info", "Destination Name", NULL },
+ { "printer-make-and-model", "Make and Model", NULL, TRUE },
+
+ { "Capabilities", NULL, NULL },
+ { "printer-type", "#", __cups_callback_ptype },
+
+ { "Printer State", NULL, NULL },
+ { "printer-state", "State", __cups_callback_state },
+ { "printer-state-change-time", "Change Time", __cups_callback_state_change_time },
+ { "printer-state-reasons", "State Reasons" },
+
+ { "Sharing Information", NULL, NULL },
+ { "printer-is-shared", "Shared?", __cups_callback_boolean },
+ { "printer-location", "Physical Location" },
+ { "auth-info-required", "Authentication Required", __cups_callback_boolean },
+
+ { "Jobs", NULL, NULL },
+ { "job-hold-until", "Hold Until", NULL },
+ { "job-priority", "Priority", NULL },
+ { "printer-is-accepting-jobs", "Accepting Jobs", __cups_callback_boolean },
+
+ { "Media", NULL, NULL },
+ { "media", "Media", NULL },
+ { "finishings", "Finishings", NULL },
+ { "copies", "Copies", NULL },
+};
+
+void
+scan_printers_do(void)
+{
+ guint num_dests, j, i;
+ CUPSDest *dests;
+ gchar *prn_id, *prn_moreinfo;
+
+ g_free(printer_list);
+ g_free(printer_icons);
+
+ if (!cups_init) {
+ init_cups();
+
+ printer_icons = g_strdup("");
+ printer_list = g_strdup(_("[Printers]\n"
+ "No suitable CUPS library found="));
+ return;
+ }
+
+ /* remove old devices from global device table */
+ moreinfo_del_with_prefix("DEV:PRN");
+
+ num_dests = cups_dests_get(&dests);
+ if (num_dests > 0) {
+ printer_list = g_strdup_printf(_("[Printers (CUPS)]\n"));
+ printer_icons = g_strdup("");
+ for (i = 0; i < num_dests; i++) {
+ GHashTable *options;
+
+ options = g_hash_table_new(g_str_hash, g_str_equal);
+
+ for (j = 0; (int)j < dests[i].num_options; j++) {
+ g_hash_table_insert(options,
+ g_strdup(dests[i].options[j].name),
+ g_strdup(dests[i].options[j].value));
+ }
+
+ prn_id = g_strdup_printf("PRN%d", i);
+
+ printer_list = h_strdup_cprintf("\n$%s$%s=%s\n",
+ printer_list,
+ prn_id,
+ dests[i].name,
+ dests[i].is_default ? ((params.markup_ok) ? "<i>Default</i>" : "(Default)") : "");
+ printer_icons = h_strdup_cprintf("\nIcon$%s$%s=printer.png",
+ printer_icons,
+ prn_id,
+ dests[i].name);
+
+ prn_moreinfo = g_strdup("");
+ for (j = 0; j < G_N_ELEMENTS(cups_fields); j++) {
+ if (!cups_fields[j].name) {
+ prn_moreinfo = h_strdup_cprintf("[%s]\n",
+ prn_moreinfo,
+ cups_fields[j].key);
+ } else {
+ gchar *temp;
+
+ temp = g_hash_table_lookup(options, cups_fields[j].key);
+
+ if (cups_fields[j].callback) {
+ temp = cups_fields[j].callback(temp);
+ } else {
+ if (temp) {
+ /* FIXME Do proper escaping */
+ temp = g_strdup(strreplacechr(temp, "&=", ' '));
+ } else {
+ temp = g_strdup(_("Unknown"));
+ }
+ }
+
+ prn_moreinfo = h_strdup_cprintf("%s%s=%s\n",
+ prn_moreinfo,
+ cups_fields[j].maybe_vendor ? "$^$" : "",
+ cups_fields[j].name,
+ temp);
+
+ g_free(temp);
+ }
+ }
+
+ moreinfo_add_with_prefix("DEV", prn_id, prn_moreinfo);
+ g_free(prn_id);
+ g_hash_table_destroy(options);
+ }
+
+ cups_dests_free(num_dests, dests);
+ } else {
+ printer_list = g_strdup(_("[Printers]\n"
+ "No printers found=\n"));
+ }
+}
diff --git a/modules/devices/resources.c b/modules/devices/resources.c
new file mode 100644
index 00000000..c9d1ccb5
--- /dev/null
+++ b/modules/devices/resources.c
@@ -0,0 +1,129 @@
+/*
+ * HardInfo - Displays System Information
+ * Copyright (C) 2003-2008 L. A. F. Pereira <l@tia.mat.br>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, version 2 or later.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#include <string.h>
+
+#include "devices.h"
+
+static gchar *_resources = NULL;
+static gboolean _require_root = FALSE;
+
+#if GLIB_CHECK_VERSION(2,14,0)
+static GRegex *_regex_pci = NULL,
+ *_regex_module = NULL;
+
+static gchar *_resource_obtain_name(gchar *name)
+{
+ gchar *temp;
+
+ if (!_regex_pci && !_regex_module) {
+ _regex_pci = g_regex_new("^[0-9a-fA-F]{4}:[0-9a-fA-F]{2}:"
+ "[0-9a-fA-F]{2}\\.[0-9a-fA-F]{1}$",
+ 0, 0, NULL);
+ _regex_module = g_regex_new("^[0-9a-zA-Z\\_\\-]+$", 0, 0, NULL);
+ }
+
+ name = g_strstrip(name);
+
+ if (g_regex_match(_regex_pci, name, 0, NULL)) {
+ temp = module_call_method_param("devices::getPCIDeviceDescription", name);
+ if (temp) {
+ if (params.markup_ok)
+ return g_strdup_printf("<b><small>PCI</small></b> %s", (gchar *)idle_free(temp));
+ else
+ return g_strdup_printf("PCI %s", (gchar *)idle_free(temp));
+ }
+ } else if (g_regex_match(_regex_module, name, 0, NULL)) {
+ temp = module_call_method_param("computer::getKernelModuleDescription", name);
+ if (temp) {
+ if (params.markup_ok)
+ return g_strdup_printf("<b><small>Module</small></b> %s", (gchar *)idle_free(temp));
+ else
+ return g_strdup_printf("Module %s", (gchar *)idle_free(temp));
+ }
+ }
+
+ return g_strdup(name);
+}
+#else
+static gchar *_resource_obtain_name(gchar *name)
+{
+ return g_strdup(name);
+}
+#endif
+
+void scan_device_resources(gboolean reload)
+{
+ SCAN_START();
+ FILE *io;
+ gchar buffer[256];
+ guint i;
+ gint zero_to_zero_addr = 0;
+
+ struct {
+ gchar *file;
+ gchar *description;
+ } resources[] = {
+ { "/proc/ioports", "[I/O Ports]\n" },
+ { "/proc/iomem", "[Memory]\n" },
+ { "/proc/dma", "[DMA]\n" }
+ };
+
+ g_free(_resources);
+ _resources = g_strdup("");
+
+ for (i = 0; i < G_N_ELEMENTS(resources); i++) {
+ if ((io = fopen(resources[i].file, "r"))) {
+ _resources = h_strconcat(_resources, resources[i].description, NULL);
+
+ while (fgets(buffer, 256, io)) {
+ gchar **temp = g_strsplit(buffer, ":", 2);
+ gchar *name = _resource_obtain_name(temp[1]);
+
+ if (strstr(temp[0], "0000-0000"))
+ zero_to_zero_addr++;
+
+ if (params.markup_ok)
+ _resources = h_strdup_cprintf("<tt>%s</tt>=%s\n", _resources,
+ temp[0], name);
+ else
+ _resources = h_strdup_cprintf(">%s=%s\n", _resources,
+ temp[0], name);
+
+ g_strfreev(temp);
+ g_free(name);
+ }
+
+ fclose(io);
+ }
+ }
+
+ _require_root = zero_to_zero_addr > 16;
+
+ SCAN_END();
+}
+
+gchar *callback_device_resources(void)
+{
+ return g_strdup(_resources);
+}
+
+gboolean root_required_for_resources(void)
+{
+ return _require_root;
+}
diff --git a/modules/devices/riscv/processor.c b/modules/devices/riscv/processor.c
new file mode 100644
index 00000000..f2e51c91
--- /dev/null
+++ b/modules/devices/riscv/processor.c
@@ -0,0 +1,233 @@
+/*
+ * HardInfo - Displays System Information
+ * Copyright (C) 2003-2006 L. A. F. Pereira <l@tia.mat.br>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, version 2 or later.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+/* source: https://github.com/riscv/riscv-linux/blob/riscv-next/arch/riscv/kernel/cpu.c */
+
+#include "hardinfo.h"
+#include "devices.h"
+#include "cpu_util.h"
+
+#include "riscv_data.h"
+#include "riscv_data.c"
+
+GSList *
+processor_scan(void)
+{
+ GSList *procs = NULL;
+ Processor *processor = NULL;
+ FILE *cpuinfo;
+ gchar buffer[128];
+ gchar *rep_pname = NULL;
+ gchar *tmpfreq_str = NULL;
+ GSList *pi = NULL;
+
+ cpuinfo = fopen(PROC_CPUINFO, "r");
+ if (!cpuinfo)
+ return NULL;
+
+#define CHECK_FOR(k) (g_str_has_prefix(tmp[0], k))
+ while (fgets(buffer, 128, cpuinfo)) {
+ gchar **tmp = g_strsplit(buffer, ":", 2);
+ if (tmp[0] && tmp[1]) {
+ tmp[0] = g_strstrip(tmp[0]);
+ tmp[1] = g_strstrip(tmp[1]);
+ } else {
+ g_strfreev(tmp);
+ continue;
+ }
+
+ //get_str("Processor", rep_pname);
+
+ if ( CHECK_FOR("hart") ) {
+ /* finish previous */
+ if (processor) {
+ procs = g_slist_append(procs, processor);
+ }
+
+ /* start next */
+ processor = g_new0(Processor, 1);
+ processor->id = atol(tmp[1]);
+
+ if (rep_pname)
+ processor->model_name = g_strdup(rep_pname);
+
+ g_strfreev(tmp);
+ continue;
+ }
+
+ if (!processor &&
+ ( CHECK_FOR("mmu")
+ || CHECK_FOR("isa")
+ || CHECK_FOR("uarch") ) ) {
+
+ /* single proc/core may not have "hart : n" */
+ processor = g_new0(Processor, 1);
+ processor->id = 0;
+
+ if (rep_pname)
+ processor->model_name = g_strdup(rep_pname);
+ }
+
+ if (processor) {
+ get_str("mmu", processor->mmu);
+ get_str("isa", processor->isa);
+ get_str("uarch", processor->uarch);
+ }
+ g_strfreev(tmp);
+ }
+
+ if (processor)
+ procs = g_slist_append(procs, processor);
+
+ g_free(rep_pname);
+ fclose(cpuinfo);
+
+ /* TODO: redup */
+
+ /* data not from /proc/cpuinfo */
+ for (pi = procs; pi; pi = pi->next) {
+ processor = (Processor *) pi->data;
+
+ /* strings can't be null or segfault later */
+ STRIFNULL(processor->model_name, _("RISC-V Processor") );
+ UNKIFNULL(processor->mmu);
+ UNKIFNULL(processor->isa);
+ UNKIFNULL(processor->uarch);
+
+ processor->flags = riscv_isa_to_flags(processor->isa);
+
+ /* topo & freq */
+ processor->cpufreq = cpufreq_new(processor->id);
+ processor->cputopo = cputopo_new(processor->id);
+
+ if (processor->cpufreq->cpukhz_max)
+ processor->cpu_mhz = processor->cpufreq->cpukhz_max / 1000;
+ else
+ processor->cpu_mhz = 0.0f;
+ }
+
+ return procs;
+}
+
+gchar *processor_get_capabilities_from_flags(gchar * strflags)
+{
+ gchar **flags, **old;
+ gchar *tmp = NULL;
+ gint j = 0;
+
+ flags = g_strsplit(strflags, " ", 0);
+ old = flags;
+
+ while (flags[j]) {
+ const gchar *meaning = riscv_ext_meaning( flags[j] );
+
+ if (meaning) {
+ tmp = h_strdup_cprintf("%s=%s\n", tmp, flags[j], meaning);
+ } else {
+ tmp = h_strdup_cprintf("%s=\n", tmp, flags[j]);
+ }
+ j++;
+ }
+ if (tmp == NULL || g_strcmp0(tmp, "") == 0)
+ tmp = g_strdup_printf("%s=%s\n", "empty", _("Empty List"));
+
+ g_strfreev(old);
+ return tmp;
+}
+
+gchar *processor_name(GSList * processors) {
+ return processor_name_default(processors);
+}
+
+gchar *processor_describe(GSList * processors) {
+ return processor_describe_default(processors);
+}
+
+gchar *
+processor_get_detailed_info(Processor *processor)
+{
+ gchar *tmp_flags, *tmp_cpufreq, *tmp_topology, *ret;
+ tmp_flags = processor_get_capabilities_from_flags(processor->flags);
+ tmp_topology = cputopo_section_str(processor->cputopo);
+ tmp_cpufreq = cpufreq_section_str(processor->cpufreq);
+
+ ret = g_strdup_printf("[%s]\n"
+ "%s=%s\n" /* model */
+ "%s=%s\n" /* isa */
+ "%s=%s\n" /* uarch */
+ "%s=%s\n" /* mmu */
+ "%s=%.2f %s\n" /* frequency */
+ "%s=%s\n" /* byte order */
+ "%s" /* topology */
+ "%s" /* frequency scaling */
+ "[%s]\n" /* extensions */
+ "%s"
+ "%s",/* empty */
+ _("Processor"),
+ _("Model"), processor->model_name,
+ _("Architecture"), processor->isa,
+ _("uarch"), processor->uarch,
+ _("MMU"), processor->mmu,
+ _("Frequency"), processor->cpu_mhz, _("MHz"),
+ _("Byte Order"), byte_order_str(),
+ tmp_topology,
+ tmp_cpufreq,
+ _("Capabilities"), tmp_flags,
+ "");
+ g_free(tmp_flags);
+ g_free(tmp_cpufreq);
+ g_free(tmp_topology);
+ return ret;
+}
+
+gchar *processor_get_info(GSList * processors)
+{
+ Processor *processor;
+
+ if (g_slist_length(processors) > 1) {
+ gchar *ret, *tmp, *hashkey;
+ GSList *l;
+
+ tmp = g_strdup("");
+
+ for (l = processors; l; l = l->next) {
+ processor = (Processor *) l->data;
+
+ tmp = g_strdup_printf("%s$CPU%d$%s=%.2f %s\n",
+ tmp, processor->id,
+ processor->model_name,
+ processor->cpu_mhz, _("MHz"));
+
+ hashkey = g_strdup_printf("CPU%d", processor->id);
+ moreinfo_add_with_prefix("DEV", hashkey,
+ processor_get_detailed_info(processor));
+ g_free(hashkey);
+ }
+
+ ret = g_strdup_printf("[$ShellParam$]\n"
+ "ViewType=1\n"
+ "[Processors]\n"
+ "%s", tmp);
+ g_free(tmp);
+
+ return ret;
+ }
+
+ processor = (Processor *) processors->data;
+ return processor_get_detailed_info(processor);
+}
diff --git a/modules/devices/riscv/riscv_data.c b/modules/devices/riscv/riscv_data.c
new file mode 100644
index 00000000..917e8e06
--- /dev/null
+++ b/modules/devices/riscv/riscv_data.c
@@ -0,0 +1,212 @@
+/*
+ * rpiz - https://github.com/bp0/rpiz
+ * 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; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <ctype.h>
+#include "riscv_data.h"
+
+#ifndef C_
+#define C_(Ctx, String) String
+#endif
+#ifndef NC_
+#define NC_(Ctx, String) String
+#endif
+
+static struct {
+ char *name, *meaning;
+} tab_ext_meaning[] = {
+ { "RV32", NC_("rv-ext", /*/ext:RV32*/ "RISC-V 32-bit") },
+ { "RV64", NC_("rv-ext", /*/ext:RV64*/ "RISC-V 64-bit") },
+ { "RV128", NC_("rv-ext", /*/ext:RV128*/ "RISC-V 128-bit") },
+ { "E", NC_("rv-ext", /*/ext:E*/ "Base embedded integer instructions (15 registers)") },
+ { "I", NC_("rv-ext", /*/ext:I*/ "Base integer instructions (31 registers)") },
+ { "M", NC_("rv-ext", /*/ext:M*/ "Hardware integer multiply and divide") },
+ { "A", NC_("rv-ext", /*/ext:A*/ "Atomic memory operations") },
+ { "C", NC_("rv-ext", /*/ext:C*/ "Compressed 16-bit instructions") },
+ { "F", NC_("rv-ext", /*/ext:F*/ "Floating-point instructions, single-precision") },
+ { "D", NC_("rv-ext", /*/ext:D*/ "Floating-point instructions, double-precision") },
+ { "Q", NC_("rv-ext", /*/ext:Q*/ "Floating-point instructions, quad-precision") },
+ { "B", NC_("rv-ext", /*/ext:B*/ "Bit manipulation instructions") },
+ { "V", NC_("rv-ext", /*/ext:V*/ "Vector operations") },
+ { "T", NC_("rv-ext", /*/ext:T*/ "Transactional memory") },
+ { "P", NC_("rv-ext", /*/ext:P*/ "Packed SIMD instructions") },
+ { "L", NC_("rv-ext", /*/ext:L*/ "Decimal floating-point instructions") },
+ { "J", NC_("rv-ext", /*/ext:J*/ "Dynamically translated languages") },
+ { "N", NC_("rv-ext", /*/ext:N*/ "User-level interrupts") },
+ { NULL, NULL }
+};
+
+static char all_extensions[1024] = "";
+
+#define APPEND_EXT(f) strcat(all_extensions, f); strcat(all_extensions, " ");
+const char *riscv_ext_list() {
+ int i = 0, built = 0;
+ built = strlen(all_extensions);
+ if (!built) {
+ while(tab_ext_meaning[i].name != NULL) {
+ APPEND_EXT(tab_ext_meaning[i].name);
+ i++;
+ }
+ }
+ return all_extensions;
+}
+
+const char *riscv_ext_meaning(const char *ext) {
+ int i = 0, l = 0;
+ char *c = NULL;
+ if (ext) {
+ c = strchr(ext, ':'); /* allow extension:version, ignore version */
+ if (c != NULL)
+ l = c - ext;
+ else
+ l = strlen(ext);
+ while(tab_ext_meaning[i].name != NULL) {
+ if (strncasecmp(tab_ext_meaning[i].name, ext, l) == 0) {
+ if (tab_ext_meaning[i].meaning != NULL)
+ return C_("rv-ext", tab_ext_meaning[i].meaning);
+ else return NULL;
+ }
+ i++;
+ }
+ }
+ return NULL;
+}
+
+/* see RISC-V spec 2.2: Chapter 22: ISA Subset Naming Conventions */
+
+/* Spec says case-insensitive, but I prefer single-letter extensions
+ * capped and version string (like "2p1") with a lowercase p. */
+#define RV_FIX_CASE(fstr,vo) \
+ p = fstr; while (*p != 0 && *p != ':') { if (!vo) *p = toupper(*p); p++; } \
+ if (*p == ':') while (*p != 0) { if (*p == 'P') *p = 'p'; p++; }
+
+static int riscv_isa_next(const char *isap, char *flag) {
+ char *p = NULL, *start = NULL;
+ char *next_sep = NULL, *next_digit = NULL;
+ int skip_len = 0, tag_len = 0, ver_len = 0;
+ char ext_str[32], ver_str[32];
+
+ if (isap == NULL)
+ return 0;
+
+ /* find start by skipping any '_' */
+ start = (char*)isap;
+ while (*start != 0 && *start == '_') { start++; skip_len++; };
+ if (*start == 0)
+ return 0;
+
+ /* find next '_' or \0 */
+ p = start; while (*p != 0 && *p != '_') { p++; }; next_sep = p;
+
+ /* find next digit that may be a version, find length of version */
+ p = start; while (*p != 0 && !isdigit(*p)) { p++; };
+ if (isdigit(*p)) next_digit = p;
+ if (next_digit) {
+ while (*p != 0 && (isdigit(*p) || *p == 'p' || *p == 'P') ) {
+ if ((*p == 'p' || *p == 'P') && !isdigit(*(p+1)) )
+ break;
+ ver_len++;
+ p++;
+ }
+ }
+
+ /* is next version nearer than next separator */
+ p = start;
+ if (next_digit && next_digit < next_sep)
+ tag_len = next_digit - p;
+ else {
+ tag_len = next_sep - p;
+ ver_len = 0;
+ }
+
+ switch(*p) {
+ case 'S': case 's': /* supervisor extension */
+ case 'X': case 'x': /* custom extension */
+ /* custom supervisor extension (SX..) handled by S */
+ break;
+ default: /* single character (standard) extension */
+ tag_len = 1;
+ if (next_digit != p+1) ver_len = 0;
+ break;
+ }
+
+ memset(ext_str, 0, 32);
+ memset(ver_str, 0, 32);
+ if (ver_len) {
+ strncpy(ext_str, p, tag_len);
+ strncpy(ver_str, next_digit, ver_len);
+ sprintf(flag, "%s:%s", ext_str, ver_str);
+ if (tag_len == 1) {
+ RV_FIX_CASE(flag, 0);
+ } else {
+ RV_FIX_CASE(flag, 1);
+ }
+ return skip_len + tag_len + ver_len;
+ } else {
+ strncpy(ext_str, p, tag_len);
+ sprintf(flag, "%s", ext_str);
+ if (tag_len == 1) { RV_FIX_CASE(flag, 0); }
+ return skip_len + tag_len;
+ }
+}
+
+#define FSTR_SIZE 1024
+#define RV_CHECK_FOR(e) ( strncasecmp(ps, e, 2) == 0 )
+#define ADD_EXT_FLAG(ext) el = strlen(ext); strncpy(pd, ext, el); strncpy(pd + el, " ", 1); pd += el + 1;
+char *riscv_isa_to_flags(const char *isa) {
+ char *flags = NULL, *ps = (char*)isa, *pd = NULL;
+ char flag_buf[64] = "";
+ int isa_len = 0, tl = 0, el = 0; /* el used in macro */
+
+ if (isa) {
+ isa_len = strlen(isa);
+ flags = malloc(FSTR_SIZE);
+ if (flags) {
+ memset(flags, 0, FSTR_SIZE);
+ ps = (char*)isa;
+ pd = flags;
+ if ( RV_CHECK_FOR("RV") )
+ { ps += 2; }
+ if ( RV_CHECK_FOR("32") )
+ { ADD_EXT_FLAG("RV32"); ps += 2; }
+ else if ( RV_CHECK_FOR("64") )
+ { ADD_EXT_FLAG("RV64"); ps += 2; }
+ else if ( RV_CHECK_FOR("128") )
+ { ADD_EXT_FLAG("RV128"); ps += 3; }
+
+ while( (tl = riscv_isa_next(ps, flag_buf)) ) {
+ if (flag_buf[0] == 'G') { /* G = IMAFD */
+ flag_buf[0] = 'I'; ADD_EXT_FLAG(flag_buf);
+ flag_buf[0] = 'M'; ADD_EXT_FLAG(flag_buf);
+ flag_buf[0] = 'A'; ADD_EXT_FLAG(flag_buf);
+ flag_buf[0] = 'F'; ADD_EXT_FLAG(flag_buf);
+ flag_buf[0] = 'D'; ADD_EXT_FLAG(flag_buf);
+ } else {
+ ADD_EXT_FLAG(flag_buf);
+ }
+ ps += tl;
+ if (ps - isa >= isa_len) break; /* just in case */
+ }
+ }
+ }
+ return flags;
+}
diff --git a/modules/devices/riscv/riscv_data.h b/modules/devices/riscv/riscv_data.h
new file mode 100644
index 00000000..1d3a0a48
--- /dev/null
+++ b/modules/devices/riscv/riscv_data.h
@@ -0,0 +1,33 @@
+/*
+ * rpiz - https://github.com/bp0/rpiz
+ * 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; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#ifndef _RISCVDATA_H_
+#define _RISCVDATA_H_
+
+/* convert RISC-V ISA string to flags list */
+char *riscv_isa_to_flags(const char *isa);
+
+/* all known extensions as flags list */
+const char *riscv_ext_list(void);
+
+/* get meaning of flag */
+const char *riscv_ext_meaning(const char *ext);
+
+#endif
diff --git a/modules/devices/s390/processor.c b/modules/devices/s390/processor.c
new file mode 100644
index 00000000..e4f2b303
--- /dev/null
+++ b/modules/devices/s390/processor.c
@@ -0,0 +1,180 @@
+/*
+ * HardInfo - Displays System Information
+ * Copyright (C) 2003-2006 L. A. F. Pereira <l@tia.mat.br>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, version 2 or later.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#include "hardinfo.h"
+#include "devices.h"
+#include "string.h"
+#include "cpu_util.h"
+
+GSList *
+processor_scan(void)
+{
+ GSList *procs = NULL;
+ Processor *processor = NULL;
+ FILE *cpuinfo;
+ gchar buffer[128];
+ gchar *vendor_id = NULL;
+ gint num_procs = 0;
+ gfloat bogomips = 0.0f;
+ GSList *pi = NULL;
+
+ cpuinfo = fopen(PROC_CPUINFO, "r");
+ if (!cpuinfo)
+ return NULL;
+
+#define CHECK_FOR(k) (g_str_has_prefix(tmp[0], k))
+ while (fgets(buffer, 128, cpuinfo)) {
+ gchar **tmp = g_strsplit(buffer, ":", 2);
+ if (tmp[0] && tmp[1]) {
+ tmp[0] = g_strstrip(tmp[0]);
+ tmp[1] = g_strstrip(tmp[1]);
+ } else {
+ g_strfreev(tmp);
+ continue;
+ }
+
+ get_str("vendor_id", vendor_id);
+ get_int("# processors", num_procs);
+ get_int("bogomips per cpu", bogomips);
+
+ if ( CHECK_FOR("processor") ) {
+ /* finish previous */
+ if (processor) {
+ procs = g_slist_append(procs, processor);
+ }
+
+ /* start next */
+ processor = g_new0(Processor, 1);
+ if (strlen(tmp[0]) >= 10)
+ processor->id = atol(tmp[0] + 10); /* processor n: ... */
+ else
+ processor->id = 0; /* idk */
+ processor->proc_str = g_strdup(tmp[1]);
+
+ if (vendor_id)
+ processor->model_name = g_strdup(vendor_id);
+ if (bogomips)
+ processor->bogomips = bogomips;
+
+ g_strfreev(tmp);
+ continue;
+ }
+
+ g_strfreev(tmp);
+ }
+
+ if (processor)
+ procs = g_slist_append(procs, processor);
+
+ g_free(vendor_id);
+ fclose(cpuinfo);
+
+ /* data not from /proc/cpuinfo */
+ for (pi = procs; pi; pi = pi->next) {
+ processor = (Processor *) pi->data;
+
+ /* strings can't be null or segfault later */
+ STRIFNULL(processor->model_name, _("S390 Processor") );
+ UNKIFNULL(processor->proc_str);
+
+ /* topo & freq */
+ processor->cpufreq = cpufreq_new(processor->id);
+ processor->cputopo = cputopo_new(processor->id);
+
+ if (processor->cpufreq->cpukhz_max)
+ processor->cpu_mhz = processor->cpufreq->cpukhz_max / 1000;
+ else
+ processor->cpu_mhz = 0.0f;
+
+ }
+
+ return procs;
+}
+
+gchar *processor_name(GSList * processors) {
+ return processor_name_default(processors);
+}
+
+gchar *processor_describe(GSList * processors) {
+ return processor_describe_default(processors);
+}
+
+gchar *
+processor_get_detailed_info(Processor *processor)
+{
+ gchar *tmp_cpufreq, *tmp_topology, *ret;
+ tmp_topology = cputopo_section_str(processor->cputopo);
+ tmp_cpufreq = cpufreq_section_str(processor->cpufreq);
+
+ ret = g_strdup_printf("[%s]\n"
+ "%s=%s\n" /* model */
+ "%s=%s\n" /* proc string */
+ "%s=%.2f\n" /* bogomips */
+ "%s=%s\n" /* byte order */
+ "%s" /* topology */
+ "%s", /* frequency scaling */
+ _("Processor"),
+ _("Model"), processor->model_name,
+ _("ID String"), processor->proc_str,
+ _("BogoMips"), processor->bogomips,
+ _("Byte Order"), byte_order_str(),
+ tmp_topology,
+ tmp_cpufreq
+ );
+ g_free(tmp_cpufreq);
+ g_free(tmp_topology);
+ return ret;
+}
+
+gchar *processor_get_info(GSList * processors)
+{
+ Processor *processor;
+
+ if (g_slist_length(processors) > 1) {
+ gchar *ret, *tmp, *hashkey;
+ GSList *l;
+
+ tmp = g_strdup("");
+
+ for (l = processors; l; l = l->next) {
+ processor = (Processor *) l->data;
+
+ tmp = g_strdup_printf("%s$CPU%d$%s=%.2f %s\n",
+ tmp, processor->id,
+ processor->model_name,
+ processor->cpu_mhz, _("MHz"));
+
+ hashkey = g_strdup_printf("CPU%d", processor->id);
+ moreinfo_add_with_prefix("DEV", hashkey,
+ processor_get_detailed_info(processor));
+ g_free(hashkey);
+ }
+
+ ret = g_strdup_printf("[$ShellParam$]\n"
+ "ViewType=1\n"
+ "[Processors]\n"
+ "%s", tmp);
+ g_free(tmp);
+
+ return ret;
+ }
+
+ processor = (Processor *) processors->data;
+ return processor_get_detailed_info(processor);
+}
+
diff --git a/modules/devices/sensors.c b/modules/devices/sensors.c
new file mode 100644
index 00000000..095f0bc4
--- /dev/null
+++ b/modules/devices/sensors.c
@@ -0,0 +1,770 @@
+/*
+ * HardInfo - Displays System Information
+ * Copyright (C) 2003-2006 L. A. F. Pereira <l@tia.mat.br>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, version 2 or later.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#include <string.h>
+
+#include "devices.h"
+#include "expr.h"
+#include "hardinfo.h"
+#include "socket.h"
+#include "udisks2_util.h"
+
+#if defined(HAS_LIBSENSORS) && HAS_LIBSENSORS
+#include <sensors/sensors.h>
+#endif
+
+gchar *sensors = NULL;
+gchar *sensor_icons = NULL;
+GHashTable *sensor_compute = NULL;
+GHashTable *sensor_labels = NULL;
+gboolean hwmon_first_run = TRUE;
+
+static gchar *last_group = NULL;
+
+static void read_sensor_labels(gchar *devname) {
+ FILE *conf;
+ gchar buf[256], *line, *p;
+ gboolean lock = FALSE;
+ gint i;
+
+ /* Try to open lm-sensors config file sensors3.conf */
+ conf = fopen("/etc/sensors3.conf", "r");
+
+ /* If it fails, try to open sensors.conf */
+ if (!conf)
+ conf = fopen("/etc/sensors.conf", "r");
+
+ if (!conf) {
+ /* Cannot open config file. */
+ return;
+ }
+
+ while (fgets(buf, 256, conf)) {
+ line = buf;
+
+ remove_linefeed(line);
+ strend(line, '#');
+
+ if (*line == '\0') {
+ continue;
+ } else if (lock && strstr(line, "label")) { /* label lines */
+ gchar **names = g_strsplit(strstr(line, "label") + 5, " ", 0);
+ gchar *key = NULL, *value = NULL;
+
+ for (i = 0; names[i]; i++) {
+ if (names[i][0] == '\0')
+ continue;
+
+ if (!key)
+ key = g_strdup_printf("%s/%s", devname, names[i]);
+ else if (!value)
+ value = g_strdup(names[i]);
+ else
+ value = g_strconcat(value, " ", names[i], NULL);
+ }
+
+ remove_quotes(value);
+ g_hash_table_insert(sensor_labels, key, g_strstrip(value));
+
+ g_strfreev(names);
+ } else if (lock && strstr(line, "ignore")) { /* ignore lines */
+ p = strstr(line, "ignore") + 6;
+ if (!strchr(p, ' '))
+ continue;
+
+ while (*p == ' ')
+ p++;
+ g_hash_table_insert(sensor_labels, g_strdup_printf("%s/%s", devname, p), "ignore");
+ } else if (lock && strstr(line, "compute")) { /* compute lines */
+ strend(line, ',');
+ gchar **formulas = g_strsplit(strstr(line, "compute") + 7, " ", 0);
+ gchar *key = NULL, *formula = NULL;
+
+ for (i = 0; formulas[i]; i++) {
+ if (formulas[i][0] == '\0')
+ continue;
+
+ if (!key)
+ key = g_strdup_printf("%s/%s", devname, formulas[i]);
+ else if (!formula)
+ formula = g_strdup(formulas[i]);
+ else
+ formula = g_strconcat(formula, formulas[i], NULL);
+ }
+
+ g_strfreev(formulas);
+ g_hash_table_insert(sensor_compute, key,
+ math_string_to_postfix(formula));
+ } else if (g_str_has_prefix(line,
+ "chip")) { /* chip lines (delimiter) */
+ if (lock == FALSE) {
+ gchar **chips = g_strsplit(line, " ", 0);
+
+ for (i = 1; chips[i]; i++) {
+ strend(chips[i], '*');
+
+ if (g_str_has_prefix(chips[i] + 1, devname)) {
+ lock = TRUE;
+ break;
+ }
+ }
+
+ g_strfreev(chips);
+ } else {
+ break;
+ }
+ }
+ }
+
+ fclose(conf);
+}
+
+static void add_sensor(const char *type,
+ const char *sensor,
+ const char *parent,
+ double value,
+ const char *unit,
+ const char *icon) {
+ char key[64];
+
+ snprintf(key, sizeof(key), "%s/%s", parent, sensor);
+
+ if (SENSORS_GROUP_BY_TYPE) {
+ // group by type
+ if (g_strcmp0(last_group, type) != 0) {
+ sensors = h_strdup_cprintf("[%s]\n", sensors, type);
+ g_free(last_group);
+ last_group = g_strdup(type);
+ }
+ sensors = h_strdup_cprintf("$%s$%s=%.2f%s|%s\n", sensors,
+ key, sensor, value, unit, parent);
+ }
+ else {
+ // group by device source / driver
+ if (g_strcmp0(last_group, parent) != 0) {
+ sensors = h_strdup_cprintf("[%s]\n", sensors, parent);
+ g_free(last_group);
+ last_group = g_strdup(parent);
+ }
+ sensors = h_strdup_cprintf("$%s$%s=%.2f%s|%s\n", sensors,
+ key, sensor, value, unit, type);
+ }
+
+ if (icon != NULL) {
+ sensor_icons = h_strdup_cprintf("Icon$%s$%s=%s.png\n", sensor_icons,
+ key, sensor, icon);
+ }
+
+ moreinfo_add_with_prefix("DEV", key, g_strdup_printf("%.2f%s", value, unit));
+
+ lginterval = h_strdup_cprintf("UpdateInterval$%s=1000\n", lginterval, key);
+}
+
+static gchar *get_sensor_label_from_conf(gchar *key) {
+ gchar *ret;
+ ret = g_hash_table_lookup(sensor_labels, key);
+
+ if (ret)
+ return g_strdup(ret);
+
+ return NULL;
+}
+
+static float adjust_sensor(gchar *key, float value) {
+ GSList *postfix;
+
+ postfix = g_hash_table_lookup(sensor_compute, key);
+ if (!postfix)
+ return value;
+
+ return math_postfix_eval(postfix, value);
+}
+
+static char *get_sensor_path(int number, const char *prefix) {
+ return g_strdup_printf("/sys/class/hwmon/hwmon%d/%s", number, prefix);
+}
+
+static char *determine_devname_for_hwmon_path(char *path) {
+ char *tmp, *devname = NULL;
+
+ // device name
+ tmp = g_strdup_printf("%s/name", path);
+ g_file_get_contents(tmp, &devname, NULL, NULL);
+ g_free(tmp);
+ if (devname)
+ return g_strstrip(devname);
+
+ // fallback: driver name (from driver link)
+ tmp = g_strdup_printf("%s/device/driver", path);
+ devname = g_file_read_link(tmp, NULL);
+ g_free(tmp);
+
+ if (!devname) {
+ // fallback: device folder name (from device link)
+ tmp = g_strdup_printf("%s/device", path);
+ devname = g_file_read_link(tmp, NULL);
+ g_free(tmp);
+ }
+
+ if (devname) {
+ tmp = g_path_get_basename(devname);
+ g_free(devname);
+ return tmp;
+ }
+
+ return g_strdup("unknown");
+}
+
+struct HwmonSensor {
+ const char *friendly_name;
+ const char *value_file_regex;
+ const char *value_path_format;
+ const char *label_path_format;
+ const char *key_format;
+ const char *unit;
+ const float adjust_ratio;
+ const char *icon;
+};
+
+static const struct HwmonSensor hwmon_sensors[] = {
+ {
+ "Fan Speed",
+ "^fan([0-9]+)_input$",
+ "%s/fan%d_input",
+ "%s/fan%d_label",
+ "fan%d",
+ " RPM",
+ 1.0,
+ "fan"
+ },
+ {
+ "Temperature",
+ "^temp([0-9]+)_input$",
+ "%s/temp%d_input",
+ "%s/temp%d_label",
+ "temp%d",
+ "\302\260C",
+ 1000.0,
+ "therm"
+ },
+ {
+ "Voltage",
+ "^in([0-9]+)_input$",
+ "%s/in%d_input",
+ "%s/in%d_label",
+ "in%d",
+ "V",
+ 1000.0,
+ "bolt"
+ },
+ {
+ "Current",
+ "^curr([0-9]+)_input$",
+ "%s/curr%d_input",
+ "%s/curr%d_label",
+ "curr%d",
+ " A",
+ 1000.0,
+ "bolt"
+ },
+ {
+ "Power",
+ "^power([0-9]+)_input$",
+ "%s/power%d_input",
+ "%s/power%d_label",
+ "power%d",
+ " W",
+ 1000000.0,
+ "bolt"
+ },
+ {
+ "CPU Voltage",
+ "^cpu([0-9]+)_vid$",
+ "%s/cpu%d_vid",
+ NULL,
+ "cpu%d_vid",
+ " V",
+ 1000.0,
+ "bolt"
+ },
+ { }
+};
+
+static const char *hwmon_prefix[] = {"device", "", NULL};
+
+static gboolean read_raw_hwmon_value(gchar *path_hwmon, const gchar *item_path_format, int item_id, gchar **result){
+ gchar *full_path;
+ gboolean file_result;
+
+ if (item_path_format == NULL)
+ return FALSE;
+
+ full_path = g_strdup_printf(item_path_format, path_hwmon, item_id);
+ file_result = g_file_get_contents(full_path, result, NULL, NULL);
+
+ g_free(full_path);
+
+ return file_result;
+}
+
+static void read_sensors_hwmon(void) {
+ int hwmon, count, min, max;
+ gchar *path_hwmon, *tmp, *devname, *name, *mon, *key;
+ const char **prefix, *entry;
+ GDir *dir;
+ GRegex *regex;
+ GMatchInfo *match_info;
+ GError *err = NULL;
+
+ for (prefix = hwmon_prefix; *prefix; prefix++) {
+ hwmon = 0;
+ path_hwmon = get_sensor_path(hwmon, *prefix);
+ while (path_hwmon && g_file_test(path_hwmon, G_FILE_TEST_EXISTS)) {
+ const struct HwmonSensor *sensor;
+
+ devname = determine_devname_for_hwmon_path(path_hwmon);
+ DEBUG("hwmon%d has device=%s", hwmon, devname);
+ if (hwmon_first_run) {
+ read_sensor_labels(devname);
+ }
+
+ dir = g_dir_open(path_hwmon, 0, NULL);
+ if (!dir)
+ continue;
+
+ for (sensor = hwmon_sensors; sensor->friendly_name; sensor++) {
+ DEBUG("current sensor type=%s", sensor->friendly_name);
+ regex = g_regex_new (sensor->value_file_regex, 0, 0, &err);
+ if (err != NULL){
+ g_free(err);
+ err = NULL;
+ continue;
+ }
+
+ g_dir_rewind(dir);
+ min = 999;
+ max = -1;
+
+ while ((entry = g_dir_read_name(dir))) {
+ g_regex_match(regex, entry, 0, &match_info);
+ if (g_match_info_matches(match_info)) {
+ tmp = g_match_info_fetch(match_info, 1);
+ count = atoi(tmp);
+ g_free (tmp);
+
+ if (count < min){
+ min = count;
+ }
+ if (count > max){
+ max = count;
+ }
+ }
+ g_match_info_free(match_info);
+ }
+ g_regex_unref(regex);
+
+ for (count = min; count <= max; count++) {
+ if (!read_raw_hwmon_value(path_hwmon, sensor->value_path_format, count, &tmp)) {
+ continue;
+ }
+
+ mon = g_strdup_printf(sensor->key_format, count);
+ key = g_strdup_printf("%s/%s", devname, mon);
+ name = get_sensor_label_from_conf(key);
+ if (name == NULL){
+ if (read_raw_hwmon_value(path_hwmon, sensor->label_path_format, count, &name)){
+ name = g_strchomp(name);
+ }
+ else{
+ name = g_strdup(mon);
+ }
+ }
+
+ if (!g_str_equal(name, "ignore")) {
+ float adjusted = adjust_sensor(key,
+ atof(tmp) / sensor->adjust_ratio);
+
+ add_sensor(sensor->friendly_name,
+ name,
+ devname,
+ adjusted,
+ sensor->unit,
+ sensor->icon);
+ }
+
+ g_free(tmp);
+ g_free(mon);
+ g_free(key);
+ g_free(name);
+ }
+ }
+
+ g_dir_close(dir);
+ g_free(path_hwmon);
+ g_free(devname);
+
+ path_hwmon = get_sensor_path(++hwmon, *prefix);
+ }
+ g_free(path_hwmon);
+ }
+ hwmon_first_run = FALSE;
+}
+
+static void read_sensors_acpi(void) {
+ const gchar *path_tz = "/proc/acpi/thermal_zone";
+
+ if (g_file_test(path_tz, G_FILE_TEST_EXISTS)) {
+ GDir *tz;
+
+ if ((tz = g_dir_open(path_tz, 0, NULL))) {
+ const gchar *entry;
+
+ while ((entry = g_dir_read_name(tz))) {
+ gchar *path =
+ g_strdup_printf("%s/%s/temperature", path_tz, entry);
+ gchar *contents;
+
+ if (g_file_get_contents(path, &contents, NULL, NULL)) {
+ int temperature;
+
+ sscanf(contents, "temperature: %d C", &temperature);
+
+ add_sensor("Temperature",
+ entry,
+ "ACPI Thermal Zone",
+ temperature,
+ "\302\260C",
+ "therm");
+ }
+ }
+
+ g_dir_close(tz);
+ }
+ }
+}
+
+// Sensors for Apple PowerPC devices using Windfarm driver.
+struct WindfarmSensorType {
+ const char *type;
+ const char *icon;
+ const char *file_regex;
+ const char *unit;
+ gboolean with_decimal_p;
+};
+static const struct WindfarmSensorType windfarm_sensor_types[] = {
+ {"Fan", "fan", "^[a-z-]+-fan(-[0-9]+)?$", " RPM", FALSE},
+ {"Temperature", "therm", "^[a-z-]+-temp(-[0-9]+)?$", "\302\260C", TRUE},
+ {"Power", "bolt", "^[a-z-]+-power(-[0-9]+)?$", " W", TRUE},
+ {"Current", "bolt", "^[a-z-]+-current(-[0-9]+)?$", " A", TRUE},
+ {"Voltage", "bolt", "^[a-z-]+-voltage(-[0-9]+)?$", " V", TRUE},
+ { }
+};
+static void read_sensors_windfarm(void)
+{
+ const gchar *path_wf = "/sys/devices/platform/windfarm.0";
+ GDir *wf;
+ gchar *tmp = NULL;
+ gint v1, v2;
+ double value;
+
+ wf = g_dir_open(path_wf, 0, NULL);
+ if (wf) {
+ GRegex *regex;
+ GError *err = NULL;
+ const gchar *entry;
+ const struct WindfarmSensorType *sensor;
+
+ for (sensor = windfarm_sensor_types; sensor->type; sensor++) {
+ DEBUG("current windfarm sensor type=%s", sensor->type);
+ regex = g_regex_new(sensor->file_regex, 0, 0, &err);
+ if (err != NULL) {
+ g_free(err);
+ err = NULL;
+ continue;
+ }
+
+ g_dir_rewind(wf);
+
+ while ((entry = g_dir_read_name(wf))) {
+ if (g_regex_match(regex, entry, 0, NULL)) {
+ gchar *path = g_strdup_printf("%s/%s", path_wf, entry);
+ if (g_file_get_contents(path, &tmp, NULL, NULL)) {
+
+ if (sensor->with_decimal_p) {
+ // format source
+ // https://elixir.free-electrons.com/linux/v5.14/source/drivers/macintosh/windfarm_core.c#L301
+ sscanf(tmp, "%d.%03d", &v1, &v2);
+ value = v1 + (v2 / 1000.0);
+ } else {
+ value = (double)atoi(tmp);
+ }
+ g_free(tmp);
+
+ tmp = g_strdup(entry);
+ add_sensor(sensor->type, g_strdelimit(tmp, "-", ' '),
+ "windfarm", value, sensor->unit,
+ sensor->icon);
+ g_free(tmp);
+ }
+ g_free(path);
+ }
+ }
+ g_regex_unref(regex);
+ }
+ g_dir_close(wf);
+ }
+}
+
+static void read_sensors_sys_thermal(void) {
+ const gchar *path_tz = "/sys/class/thermal";
+
+ if (g_file_test(path_tz, G_FILE_TEST_EXISTS)) {
+ GDir *tz;
+
+ if ((tz = g_dir_open(path_tz, 0, NULL))) {
+ const gchar *entry;
+ gchar *temp = g_strdup("");
+
+ while ((entry = g_dir_read_name(tz))) {
+ gchar *path = g_strdup_printf("%s/%s/temp", path_tz, entry);
+ gchar *contents;
+
+ if (g_file_get_contents(path, &contents, NULL, NULL)) {
+ int temperature;
+
+ sscanf(contents, "%d", &temperature);
+
+ add_sensor("Temperature",
+ entry,
+ "thermal",
+ temperature / 1000.0,
+ "\302\260C",
+ "therm");
+
+ g_free(contents);
+ }
+ }
+
+ g_dir_close(tz);
+ }
+ }
+}
+
+static void read_sensors_omnibook(void) {
+ const gchar *path_ob = "/proc/omnibook/temperature";
+ gchar *contents;
+
+ if (g_file_get_contents(path_ob, &contents, NULL, NULL)) {
+ int temperature;
+
+ sscanf(contents, "CPU temperature: %d C", &temperature);
+
+ add_sensor("Temperature",
+ "CPU",
+ "omnibook",
+ temperature,
+ "\302\260C",
+ "therm");
+
+ g_free(contents);
+ }
+}
+
+static void read_sensors_hddtemp(void) {
+ Socket *s;
+ gchar buffer[1024];
+ gint len = 0;
+
+ if (!(s = sock_connect("127.0.0.1", 7634)))
+ return;
+
+ while (!len)
+ len = sock_read(s, buffer, sizeof(buffer));
+ sock_close(s);
+
+ if (len > 2 && buffer[0] == '|' && buffer[1] == '/') {
+ gchar **disks;
+ int i;
+
+ disks = g_strsplit(buffer, "||", 0);
+ for (i = 0; disks[i]; i++) {
+ gchar **fields = g_strsplit(disks[i] + 1, "|", 5);
+
+ /*
+ * 0 -> /dev/hda
+ * 1 -> FUJITSU MHV2080AH
+ * 2 -> 41
+ * 3 -> C
+ */
+ const gchar *unit = strcmp(fields[3], "C")
+ ? "\302\260F" : "\302\260C";
+ add_sensor("Drive Temperature",
+ fields[1],
+ "hddtemp",
+ atoi(fields[2]),
+ unit,
+ "therm");
+
+ g_strfreev(fields);
+ }
+
+ g_strfreev(disks);
+ }
+}
+
+static void read_sensors_udisks2(void) {
+ GSList *node;
+ GSList *temps;
+ udiskt *disk;
+
+ temps = get_udisks2_temps();
+ if (temps == NULL)
+ return;
+
+ for (node = temps; node != NULL; node = node->next) {
+ disk = (udiskt *)node->data;
+ add_sensor("Drive Temperature",
+ disk->drive,
+ "udisks2",
+ disk->temperature,
+ "\302\260C",
+ "therm");
+ udiskt_free(disk);
+ }
+ g_slist_free(temps);
+}
+
+#if HAS_LIBSENSORS
+static const struct libsensors_feature_type {
+ const char *type_name;
+ const char *icon;
+ const char *unit;
+ sensors_subfeature_type input;
+} libsensors_feature_types[SENSORS_FEATURE_MAX] = {
+ [SENSORS_FEATURE_FAN] = {"Fan", "fan", "RPM",
+ SENSORS_SUBFEATURE_FAN_INPUT},
+ [SENSORS_FEATURE_TEMP] = {"Temperature", "therm", "\302\260C",
+ SENSORS_SUBFEATURE_TEMP_INPUT},
+ [SENSORS_FEATURE_POWER] = {"Power", "bolt", "W",
+ SENSORS_SUBFEATURE_POWER_INPUT},
+ [SENSORS_FEATURE_CURR] = {"Current", "bolt", "A",
+ SENSORS_SUBFEATURE_CURR_INPUT},
+ [SENSORS_FEATURE_IN] = {"Voltage", "bolt", "V",
+ SENSORS_SUBFEATURE_IN_INPUT},
+ [SENSORS_FEATURE_VID] = {"CPU Voltage", "bolt", "V",
+ SENSORS_SUBFEATURE_VID},
+};
+static gboolean libsensors_initialized;
+
+static int read_sensors_libsensors(void) {
+ char chip_name_buf[512];
+ const sensors_chip_name *name;
+ int chip_nr = 0;
+ int added_sensors = 0;
+
+ if (!libsensors_initialized)
+ return 0;
+
+ while ((name = sensors_get_detected_chips(NULL, &chip_nr))) {
+ const struct sensors_feature *feat;
+ int feat_nr = 0;
+
+ sensors_snprintf_chip_name(chip_name_buf, 512, name);
+
+ while ((feat = sensors_get_features(name, &feat_nr))) {
+ const struct libsensors_feature_type *feat_descr;
+ const struct sensors_subfeature *subfeat;
+ double value;
+
+ feat_descr = &libsensors_feature_types[feat->type];
+ if (!feat_descr->type_name)
+ continue;
+
+ subfeat = sensors_get_subfeature(name, feat, feat_descr->input);
+ if (!subfeat)
+ continue;
+
+ if (!sensors_get_value(name, subfeat->number, &value)) {
+ char *label = sensors_get_label(name, feat);
+ gchar *label_with_chip = g_strdup_printf("%s (%s)", label, chip_name_buf);
+
+ add_sensor(feat_descr->type_name,
+ label_with_chip,
+ "libsensors",
+ value,
+ feat_descr->unit,
+ feat_descr->icon);
+
+ free(label_with_chip);
+ free(label);
+
+ added_sensors++;
+ }
+ }
+ }
+
+ return added_sensors;
+}
+#else
+static int read_sensors_libsensors(void)
+{
+ return 0;
+}
+#endif
+
+void scan_sensors_do(void) {
+ g_free(sensors);
+ g_free(sensor_icons);
+ g_free(last_group);
+ last_group = NULL;
+ sensors = g_strdup("");
+ sensor_icons = g_strdup("");
+
+ g_free(lginterval);
+ lginterval = g_strdup("");
+
+ if (read_sensors_libsensors() == 0) {
+ read_sensors_hwmon();
+ read_sensors_acpi();
+ read_sensors_sys_thermal();
+ read_sensors_omnibook();
+ }
+
+ read_sensors_windfarm();
+ read_sensors_hddtemp();
+ read_sensors_udisks2();
+}
+
+void sensor_init(void) {
+#if HAS_LIBSENSORS
+ libsensors_initialized = sensors_init(NULL) == 0;
+#endif
+
+ sensor_labels =
+ g_hash_table_new_full(g_str_hash, g_str_equal, g_free, g_free);
+ sensor_compute = g_hash_table_new(g_str_hash, g_str_equal);
+}
+
+void sensor_shutdown(void) {
+#if HAS_LIBSENSORS
+ sensors_cleanup();
+#endif
+
+ g_hash_table_destroy(sensor_labels);
+ g_hash_table_destroy(sensor_compute);
+}
diff --git a/modules/devices/sh/processor.c b/modules/devices/sh/processor.c
new file mode 100644
index 00000000..a5ce3e0a
--- /dev/null
+++ b/modules/devices/sh/processor.c
@@ -0,0 +1,93 @@
+/*
+ * HardInfo - Displays System Information
+ * Copyright (C) 2003-2006 L. A. F. Pereira <l@tia.mat.br>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, version 2 or later.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#include "hardinfo.h"
+#include "devices.h"
+#include "cpu_util.h"
+
+GSList *
+processor_scan(void)
+{
+ Processor *processor;
+ FILE *cpuinfo;
+ gchar buffer[128];
+
+ cpuinfo = fopen(PROC_CPUINFO, "r");
+ if (!cpuinfo)
+ return NULL;
+
+ processor = g_new0(Processor, 1);
+ while (fgets(buffer, 128, cpuinfo)) {
+ gchar **tmp = g_strsplit(buffer, ":", 2);
+
+ if (tmp[0] && tmp[1]) {
+ tmp[0] = g_strstrip(tmp[0]);
+ tmp[1] = g_strstrip(tmp[1]);
+
+ get_str("machine", processor->vendor_id);
+ get_str("cpu type", processor->model_name);
+ get_str("cpu family", processor->family);
+ get_float("cpu clock", processor->cpu_mhz);
+ get_float("bus clock", processor->bus_mhz);
+ get_float("module clock", processor->mod_mhz);
+ get_float("bogomips", processor->bogomips);
+ }
+ g_strfreev(tmp);
+ }
+
+ fclose(cpuinfo);
+
+ STRIFNULL(processor->model_name, _("SuperH Processor"));
+ UNKIFNULL(processor->vendor_id);
+
+ return g_slist_append(NULL, processor);
+}
+
+gchar *processor_name(GSList * processors) {
+ return processor_name_default(processors);
+}
+
+gchar *processor_describe(GSList * processors) {
+ return processor_describe_default(processors);
+}
+
+gchar *
+processor_get_info(GSList *processors)
+{
+ Processor *processor = (Processor *)processors->data;
+
+ return g_strdup_printf("[%s]\n"
+ "%s=%s\n" /* cpu type */
+ "%s=%s\n" /* machine */
+ "%s=%s\n" /* family */
+ "%s=%.2f %s\n" /* frequency */
+ "%s=%.2f %s\n" /* bus frequency */
+ "%s=%.2f %s\n" /* module frequency */
+ "%s=%.2f\n" /* bogomips */
+ "%s=%s\n", /* byte order */
+ _("Processor"),
+ _("Name"), processor->model_name,
+ _("Machine"), processor->vendor_id,
+ _("Family"), processor->family,
+ _("Frequency"), processor->cpu_mhz, _("MHz"),
+ _("Bus Frequency"), processor->bus_mhz, _("MHz"),
+ _("Module Frequency"), processor->mod_mhz, _("MHz"),
+ _("BogoMips"), processor->bogomips,
+ _("Byte Order"), byte_order_str()
+ );
+}
diff --git a/modules/devices/sparc/processor.c b/modules/devices/sparc/processor.c
new file mode 100644
index 00000000..76f4b0e0
--- /dev/null
+++ b/modules/devices/sparc/processor.c
@@ -0,0 +1,80 @@
+/*
+ * HardInfo - Displays System Information
+ * Copyright (C) 2003-2006 L. A. F. Pereira <l@tia.mat.br>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, version 2 or later.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#include "hardinfo.h"
+#include "devices.h"
+#include "cpu_util.h"
+
+GSList *
+processor_scan(void)
+{
+ Processor *processor;
+ FILE *cpuinfo;
+ gchar buffer[128];
+
+ cpuinfo = fopen(PROC_CPUINFO, "r");
+ if (!cpuinfo)
+ return NULL;
+
+ processor = g_new0(Processor, 1);
+ while (fgets(buffer, 128, cpuinfo)) {
+ gchar **tmp = g_strsplit(buffer, ":", 2);
+
+ if (tmp[0] && tmp[1]) {
+ tmp[0] = g_strstrip(tmp[0]);
+ tmp[1] = g_strstrip(tmp[1]);
+
+ get_str("cpucaps", processor->cpucaps);
+ get_str("cpu", processor->model_name);
+ get_str("fpu", processor->has_fpu);
+ }
+ g_strfreev(tmp);
+ }
+
+ fclose(cpuinfo);
+
+ processor->cpu_mhz = 0.0f;
+
+ return g_slist_append(NULL, processor);
+}
+
+gchar *processor_name(GSList * processors) {
+ return processor_name_default(processors);
+}
+
+gchar *processor_describe(GSList * processors) {
+ return processor_describe_default(processors);
+}
+
+gchar *
+processor_get_info(GSList *processors)
+{
+ Processor *processor = (Processor *)processors->data;
+
+ return g_strdup_printf("[%s]\n"
+ "%s=%s\n" /* cpu */
+ "%s=%s\n" /* fpu */
+ "%s=%s\n" /* byte order */
+ "%s=%s\n", /* caps */
+ _("Processor"),
+ _("CPU"), processor->model_name,
+ _("FPU"), processor->has_fpu,
+ _("Byte Order"), byte_order_str(),
+ _("Capabilities"), processor->cpucaps
+ );
+}
diff --git a/modules/devices/spd-decode.c b/modules/devices/spd-decode.c
new file mode 100644
index 00000000..511504b2
--- /dev/null
+++ b/modules/devices/spd-decode.c
@@ -0,0 +1,1272 @@
+/*
+ * spd-decode.c, spd-vendors.c
+ * Copyright (c) 2010 L. A. F. Pereira
+ * modified by Ondrej Čerman (2019)
+ * modified by Burt P. (2019)
+ *
+ * Based on decode-dimms.pl
+ * Copyright 1998, 1999 Philip Edelbrock <phil@netroedge.com>
+ * modified by Christian Zuckschwerdt <zany@triq.net>
+ * modified by Burkart Lingner <burkart@bollchen.de>
+ *
+ * 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; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include <ctype.h>
+#include <math.h>
+#include <stdio.h>
+#include <sys/stat.h>
+
+#include "devices.h"
+#include "hardinfo.h"
+
+gboolean spd_no_driver = FALSE;
+gboolean spd_no_support = FALSE;
+gboolean spd_ddr4_partial_data = FALSE;
+int spd_ram_types = 0; /* bits using enum RamType */
+
+typedef enum {
+ UNKNOWN = 0,
+ DIRECT_RAMBUS = 1,
+ RAMBUS = 2,
+ FPM_DRAM = 3,
+ EDO = 4,
+ PIPELINED_NIBBLE = 5,
+ SDR_SDRAM = 6,
+ MULTIPLEXED_ROM = 7,
+ DDR_SGRAM = 8,
+ DDR_SDRAM = 9,
+ DDR2_SDRAM = 10,
+ DDR3_SDRAM = 11,
+ DDR4_SDRAM = 12,
+ N_RAM_TYPES = 13
+} RamType;
+
+static const char *ram_types[] = {"Unknown", "Direct Rambus", "Rambus", "FPM DRAM",
+ "EDO", "Pipelined Nibble", "SDR SDRAM", "Multiplexed ROM",
+ "DDR SGRAM", "DDR SDRAM", "DDR2 SDRAM", "DDR3 SDRAM",
+ "DDR4 SDRAM"};
+#define GET_RAM_TYPE_STR(rt) (ram_types[(rt < N_RAM_TYPES) ? rt : 0])
+
+#include "spd-vendors.c"
+
+struct dmi_mem_socket;
+typedef struct {
+ unsigned char bytes[512];
+ char dev[32]; /* %1d-%04d\0 */
+ const char *spd_driver;
+ int spd_size;
+
+ RamType type;
+
+ int vendor_bank;
+ int vendor_index;
+ const char *vendor_str;
+ const Vendor *vendor;
+
+ int dram_vendor_bank;
+ int dram_vendor_index;
+ const char *dram_vendor_str;
+ const Vendor *dram_vendor;
+
+ char partno[32];
+ const char *form_factor;
+ char type_detail[256];
+
+ dmi_mem_size size_MiB;
+
+ int spd_rev_major; // bytes[1] >> 4
+ int spd_rev_minor; // bytes[1] & 0xf
+
+ int week, year;
+
+ gboolean ddr4_no_ee1004;
+
+ struct dmi_mem_socket *dmi_socket;
+ int match_score;
+} spd_data;
+
+#define spd_data_new() g_new0(spd_data, 1)
+void spd_data_free(spd_data *s) { g_free(s); }
+
+/*
+ * We consider that no data was written to this area of the SPD EEPROM if
+ * all bytes read 0x00 or all bytes read 0xff
+ */
+static int spd_written(unsigned char *bytes, int len) {
+ do {
+ if (*bytes == 0x00 || *bytes == 0xFF) return 1;
+ } while (--len && bytes++);
+
+ return 0;
+}
+
+static int parity(int value) {
+ value ^= value >> 16;
+ value ^= value >> 8;
+ value ^= value >> 4;
+ value &= 0xf;
+
+ return (0x6996 >> value) & 1;
+}
+
+static void decode_sdr_module_size(unsigned char *bytes, dmi_mem_size *size) {
+ unsigned short i, k = 0;
+
+ i = (bytes[3] & 0x0f) + (bytes[4] & 0x0f) - 17;
+ if (bytes[5] <= 8 && bytes[17] <= 8) { k = bytes[5] * bytes[17]; }
+
+ if (i > 0 && i <= 12 && k > 0) {
+ if (size) { *size = (dmi_mem_size)k * (unsigned short)(1 << i); }
+ } else {
+ if (size) { *size = -1; }
+ }
+}
+
+static void decode_sdr_module_timings(unsigned char *bytes, float *tcl, float *trcd, float *trp,
+ float *tras) {
+ float cas[3], ctime;
+ int i, j;
+
+ for (i = 0, j = 0; j < 7; j++) {
+ if (bytes[18] & 1 << j) { cas[i++] = j + 1; }
+ }
+
+ ctime = ((bytes[9] >> 4) + (bytes[9] & 0xf)) * 0.1;
+
+ if (trcd) { *trcd = ceil(bytes[29] / ctime); }
+ if (trp) { *trp = ceil(bytes[27] / ctime); }
+ if (tras) { *tras = ceil(bytes[30] / ctime); }
+ if (tcl) { *tcl = cas[i]; }
+}
+
+static void decode_sdr_module_row_address_bits(unsigned char *bytes, char **bits) {
+ char *temp;
+
+ switch (bytes[3]) {
+ case 0: temp = "Undefined"; break;
+ case 1: temp = "1/16"; break;
+ case 2: temp = "2/27"; break;
+ case 3: temp = "3/18"; break;
+ default:
+ /* printf("%d\n", bytes[3]); */
+ temp = NULL;
+ }
+
+ if (bits) { *bits = temp; }
+}
+
+static void decode_sdr_module_col_address_bits(unsigned char *bytes, char **bits) {
+ char *temp;
+
+ switch (bytes[4]) {
+ case 0: temp = "Undefined"; break;
+ case 1: temp = "1/16"; break;
+ case 2: temp = "2/17"; break;
+ case 3: temp = "3/18"; break;
+ default:
+ /*printf("%d\n", bytes[4]); */
+ temp = NULL;
+ }
+
+ if (bits) { *bits = temp; }
+}
+
+static void decode_sdr_module_number_of_rows(unsigned char *bytes, int *rows) {
+ if (rows) { *rows = bytes[5]; }
+}
+
+static void decode_sdr_module_data_with(unsigned char *bytes, int *width) {
+ if (width) {
+ if (bytes[7] > 1) {
+ *width = 0;
+ } else {
+ *width = (bytes[7] * 0xff) + bytes[6];
+ }
+ }
+}
+
+static void decode_sdr_module_interface_signal_levels(unsigned char *bytes, char **signal_levels) {
+ char *temp;
+
+ switch (bytes[8]) {
+ case 0: temp = "5.0 Volt/TTL"; break;
+ case 1: temp = "LVTTL"; break;
+ case 2: temp = "HSTL 1.5"; break;
+ case 3: temp = "SSTL 3.3"; break;
+ case 4: temp = "SSTL 2.5"; break;
+ case 255: temp = "New Table"; break;
+ default: temp = NULL;
+ }
+
+ if (signal_levels) { *signal_levels = temp; }
+}
+
+static void decode_sdr_module_configuration_type(unsigned char *bytes, char **module_config_type) {
+ char *temp;
+
+ switch (bytes[11]) {
+ case 0: temp = "No parity"; break;
+ case 1: temp = "Parity"; break;
+ case 2: temp = "ECC"; break;
+ default: temp = NULL;
+ }
+
+ if (module_config_type) { *module_config_type = temp; }
+}
+
+static void decode_sdr_module_refresh_type(unsigned char *bytes, char **refresh_type) {
+ char *temp;
+
+ if (bytes[12] & 0x80) {
+ temp = "Self refreshing";
+ } else {
+ temp = "Not self refreshing";
+ }
+
+ if (refresh_type) { *refresh_type = temp; }
+}
+
+static void decode_sdr_module_refresh_rate(unsigned char *bytes, char **refresh_rate) {
+ char *temp;
+
+ switch (bytes[12] & 0x7f) {
+ case 0: temp = "Normal (15.625us)"; break;
+ case 1: temp = "Reduced (3.9us)"; break;
+ case 2: temp = "Reduced (7.8us)"; break;
+ case 3: temp = "Extended (31.3us)"; break;
+ case 4: temp = "Extended (62.5us)"; break;
+ case 5: temp = "Extended (125us)"; break;
+ default: temp = NULL;
+ }
+
+ if (refresh_rate) { *refresh_rate = temp; }
+}
+
+static void decode_sdr_module_detail(unsigned char *bytes, char *type_detail) {
+ bytes = bytes; /* silence unused warning */
+ if (type_detail) {
+ snprintf(type_detail, 255, "SDR");
+ }
+}
+
+static gchar *decode_sdr_sdram_extra(unsigned char *bytes) {
+ int rows, data_width;
+ float tcl, trcd, trp, tras;
+ char *row_address_bits, *col_address_bits, *signal_level;
+ char *module_config_type, *refresh_type, *refresh_rate;
+
+ decode_sdr_module_timings(bytes, &tcl, &trcd, &trp, &tras);
+ decode_sdr_module_row_address_bits(bytes, &row_address_bits);
+ decode_sdr_module_col_address_bits(bytes, &col_address_bits);
+ decode_sdr_module_number_of_rows(bytes, &rows);
+ decode_sdr_module_data_with(bytes, &data_width);
+ decode_sdr_module_interface_signal_levels(bytes, &signal_level);
+ decode_sdr_module_configuration_type(bytes, &module_config_type);
+ decode_sdr_module_refresh_type(bytes, &refresh_type);
+ decode_sdr_module_refresh_rate(bytes, &refresh_rate);
+
+ /* TODO:
+ - RAS to CAS delay
+ - Supported CAS latencies
+ - Supported CS latencies
+ - Supported WE latencies
+ - Cycle Time / Access time
+ - SDRAM module attributes
+ - SDRAM device attributes
+ - Row densities
+ - Other misc stuff
+ */
+
+ /* expected to continue an [SPD] section */
+ return g_strdup_printf("%s=%s\n"
+ "%s=%s\n"
+ "%s=%d\n"
+ "%s=%d bits\n"
+ "%s=%s\n"
+ "%s=%s\n"
+ "%s=%s (%s)\n"
+ "[%s]\n"
+ "tCL=%.2f\n"
+ "tRCD=%.2f\n"
+ "tRP=%.2f\n"
+ "tRAS=%.2f\n",
+ _("Row address bits"), row_address_bits ? row_address_bits : _("(Unknown)"),
+ _("Column address bits"), col_address_bits ? col_address_bits : _("(Unknown)"),
+ _("Number of rows"), rows,
+ _("Data width"), data_width,
+ _("Interface signal levels"), signal_level ? signal_level : _("(Unknown)"),
+ _("Configuration type"), module_config_type ? module_config_type : _("(Unknown)"),
+ _("Refresh"), refresh_type, refresh_rate ? refresh_rate : _("Unknown"),
+ _("Timings"), tcl, trcd, trp, tras);
+}
+
+static void decode_ddr_module_speed(unsigned char *bytes, float *ddrclk, int *pcclk) {
+ float temp, clk;
+ int tbits, pc;
+
+ temp = (bytes[9] >> 4) + (bytes[9] & 0xf) * 0.1;
+ clk = 2 * (1000 / temp);
+ tbits = (bytes[7] * 256) + bytes[6];
+
+ if (bytes[11] == 2 || bytes[11] == 1) { tbits -= 8; }
+
+ pc = clk * tbits / 8;
+ if (pc % 100 > 50) { pc += 100; }
+ pc -= pc % 100;
+
+ if (ddrclk) *ddrclk = (int)clk;
+
+ if (pcclk) *pcclk = pc;
+}
+
+static void decode_ddr_module_size(unsigned char *bytes, dmi_mem_size *size) {
+ unsigned short i, k;
+
+ i = (bytes[3] & 0x0f) + (bytes[4] & 0x0f) - 17;
+ k = (bytes[5] <= 8 && bytes[17] <= 8) ? bytes[5] * bytes[17] : 0;
+
+ if (i > 0 && i <= 12 && k > 0) {
+ if (size) { *size = (dmi_mem_size)k * (unsigned short)(1 << i); }
+ } else {
+ if (size) { *size = -1; }
+ }
+}
+
+static void decode_ddr_module_timings(unsigned char *bytes, float *tcl, float *trcd, float *trp,
+ float *tras) {
+ float ctime;
+ float highest_cas = 0;
+ int i;
+
+ for (i = 0; i < 7; i++) {
+ if (bytes[18] & (1 << i)) { highest_cas = 1 + i * 0.5f; }
+ }
+
+ ctime = (bytes[9] >> 4) + (bytes[9] & 0xf) * 0.1;
+
+ if (trcd) {
+ *trcd = (bytes[29] >> 2) + ((bytes[29] & 3) * 0.25);
+ *trcd = ceil(*trcd / ctime);
+ }
+
+ if (trp) {
+ *trp = (bytes[27] >> 2) + ((bytes[27] & 3) * 0.25);
+ *trp = ceil(*trp / ctime);
+ }
+
+ if (tras) {
+ *tras = bytes[30];
+ *tras = ceil(*tras / ctime);
+ }
+
+ if (tcl) { *tcl = highest_cas; }
+}
+
+static void decode_ddr_module_detail(unsigned char *bytes, char *type_detail) {
+ float ddr_clock;
+ int pc_speed;
+ if (type_detail) {
+ decode_ddr_module_speed(bytes, &ddr_clock, &pc_speed);
+ snprintf(type_detail, 255, "DDR-%.0f (PC-%d)", ddr_clock, pc_speed);
+ }
+}
+
+static gchar *decode_ddr_sdram_extra(unsigned char *bytes) {
+ float tcl, trcd, trp, tras;
+
+ decode_ddr_module_timings(bytes, &tcl, &trcd, &trp, &tras);
+
+ return g_strdup_printf("[%s]\n"
+ "tCL=%.2f\n"
+ "tRCD=%.2f\n"
+ "tRP=%.2f\n"
+ "tRAS=%.2f\n",
+ _("Timings"), tcl, trcd, trp, tras);
+}
+
+static float decode_ddr2_module_ctime(unsigned char byte) {
+ float ctime;
+
+ ctime = (byte >> 4);
+ byte &= 0xf;
+
+ if (byte <= 9) {
+ ctime += byte * 0.1;
+ } else if (byte == 10) {
+ ctime += 0.25;
+ } else if (byte == 11) {
+ ctime += 0.33;
+ } else if (byte == 12) {
+ ctime += 0.66;
+ } else if (byte == 13) {
+ ctime += 0.75;
+ }
+
+ return ctime;
+}
+
+static void decode_ddr2_module_speed(unsigned char *bytes, float *ddr_clock, int *pc2_speed) {
+ float ctime;
+ float ddrclk;
+ int tbits, pcclk;
+ ctime = decode_ddr2_module_ctime(bytes[9]);
+ ddrclk = 2 * (1000 / ctime);
+
+ tbits = (bytes[7] * 256) + bytes[6];
+ if (bytes[11] & 0x03) { tbits -= 8; }
+
+ pcclk = ddrclk * tbits / 8;
+ pcclk -= pcclk % 100;
+
+ if (ddr_clock) { *ddr_clock = (int)ddrclk; }
+ if (pc2_speed) { *pc2_speed = pcclk; }
+}
+
+static void decode_ddr2_module_size(unsigned char *bytes, dmi_mem_size *size) {
+ unsigned short i, k;
+
+ i = (bytes[3] & 0x0f) + (bytes[4] & 0x0f) - 17;
+ k = ((bytes[5] & 0x7) + 1) * bytes[17];
+
+ if (i > 0 && i <= 12 && k > 0) {
+ if (size) { *size = (dmi_mem_size)k * (unsigned short)(1 << i); }
+ } else {
+ if (size) { *size = 0; }
+ }
+}
+
+static void decode_ddr2_module_type(unsigned char *bytes, const char **type) {
+ switch (bytes[20]) {
+ case 0x01: *type = "RDIMM (Registered DIMM)"; break;
+ case 0x02: *type = "UDIMM (Unbuffered DIMM)"; break;
+ case 0x04: *type = "SO-DIMM (Small Outline DIMM)"; break;
+ case 0x06: *type = "72b-SO-CDIMM (Small Outline Clocked DIMM, 72-bit data bus)"; break;
+ case 0x07: *type = "72b-SO-RDIMM (Small Outline Registered DIMM, 72-bit data bus)"; break;
+ case 0x08: *type = "Micro-DIMM"; break;
+ case 0x10: *type = "Mini-RDIMM (Mini Registered DIMM)"; break;
+ case 0x20: *type = "Mini-UDIMM (Mini Unbuffered DIMM)"; break;
+ default: *type = NULL;
+ }
+}
+
+static void decode_ddr2_module_timings(float ctime, unsigned char *bytes, float *trcd, float *trp, float *tras) {
+
+ if (trcd) { *trcd = ceil(((bytes[29] >> 2) + ((bytes[29] & 3) * 0.25)) / ctime); }
+
+ if (trp) { *trp = ceil(((bytes[27] >> 2) + ((bytes[27] & 3) * 0.25)) / ctime); }
+
+ if (tras) { *tras = ceil(bytes[30] / ctime); }
+}
+
+static gboolean decode_ddr2_module_ctime_for_casx(int casx_minus, unsigned char *bytes, float *ctime, float *tcl){
+ int highest_cas, i, bytei;
+ float ctimev = 0;
+
+ switch (casx_minus){
+ case 0:
+ bytei = 9;
+ break;
+ case 1:
+ bytei = 23;
+ break;
+ case 2:
+ bytei = 25;
+ break;
+ default:
+ return FALSE;
+ }
+
+ for (i = 0; i < 7; i++) {
+ if (bytes[18] & (1 << i)) { highest_cas = i; }
+ }
+
+ if ((bytes[18] & (1 << (highest_cas-casx_minus))) == 0)
+ return FALSE;
+
+ ctimev = decode_ddr2_module_ctime(bytes[bytei]);
+ if (ctimev == 0)
+ return FALSE;
+
+ if (tcl) { *tcl = highest_cas-casx_minus; }
+ if (ctime) { *ctime = ctimev; }
+
+ return TRUE;
+}
+
+static void decode_ddr2_module_detail(unsigned char *bytes, char *type_detail) {
+ float ddr_clock;
+ int pc2_speed;
+ if (type_detail) {
+ decode_ddr2_module_speed(bytes, &ddr_clock, &pc2_speed);
+ snprintf(type_detail, 255, "DDR2-%.0f (PC2-%d)", ddr_clock, pc2_speed);
+ }
+}
+
+static gchar *decode_ddr2_sdram_extra(unsigned char *bytes) {
+ float trcd, trp, tras, ctime, tcl;
+ const char* voltage;
+ gchar *out;
+ int i;
+
+ switch(bytes[8]){
+ case 0x0:
+ voltage = "TTL/5 V tolerant";
+ break;
+ case 0x1:
+ voltage = "LVTTL";
+ break;
+ case 0x2:
+ voltage = "HSTL 1.5 V";
+ break;
+ case 0x3:
+ voltage = "SSTL 3.3 V";
+ break;
+ case 0x4:
+ voltage = "SSTL 2.5 V";
+ break;
+ case 0x5:
+ voltage = "SSTL 1.8 V";
+ break;
+ default:
+ voltage = _("(Unknown)");
+ }
+
+ /* expected to continue an [SPD] section */
+ out = g_strdup_printf("%s=%s\n"
+ "[%s]\n",
+ _("Voltage"), voltage,
+ _("JEDEC Timings"));
+
+ for (i = 0; i <= 2; i++) {
+ if (!decode_ddr2_module_ctime_for_casx(i, bytes, &ctime, &tcl))
+ break;
+ decode_ddr2_module_timings(ctime, bytes, &trcd, &trp, &tras);
+ out = h_strdup_cprintf("DDR2-%d=%.0f-%.0f-%.0f-%.0f\n",
+ out,
+ (int)(2 * (1000 / ctime)), tcl, trcd, trp, tras);
+ }
+
+ return out;
+}
+
+static void decode_ddr3_module_speed(unsigned char *bytes, float *ddr_clock, int *pc3_speed) {
+ float ctime;
+ float ddrclk;
+ int tbits, pcclk;
+ float mtb = 0.125;
+
+ if (bytes[10] == 1 && bytes[11] == 8) mtb = 0.125;
+ if (bytes[10] == 1 && bytes[11] == 15) mtb = 0.0625;
+ ctime = mtb * bytes[12];
+
+ ddrclk = 2 * (1000 / ctime);
+
+ tbits = 64;
+ switch (bytes[8]) {
+ case 1: tbits = 16; break;
+ case 4: tbits = 32; break;
+ case 3:
+ case 0xb: tbits = 64; break;
+ }
+
+ pcclk = ddrclk * tbits / 8;
+ pcclk -= pcclk % 100;
+
+ if (ddr_clock) { *ddr_clock = (int)ddrclk; }
+ if (pc3_speed) { *pc3_speed = pcclk; }
+}
+
+static void decode_ddr3_module_size(unsigned char *bytes, dmi_mem_size *size) {
+ unsigned int sdr_capacity = 256 << (bytes[4] & 0xF);
+ unsigned int sdr_width = 4 << (bytes[7] & 0x7);
+ unsigned int bus_width = 8 << (bytes[8] & 0x7);
+ unsigned int ranks = 1 + ((bytes[7] >> 3) & 0x7);
+
+ *size = (dmi_mem_size)sdr_capacity / 8 * bus_width / sdr_width * ranks;
+}
+
+static void decode_ddr3_module_timings(unsigned char *bytes, float *trcd, float *trp, float *tras,
+ float *tcl) {
+ float ctime;
+ float mtb = 0.125;
+ float taa;
+
+ if (bytes[10] == 1 && bytes[11] == 8) mtb = 0.125;
+ if (bytes[10] == 1 && bytes[11] == 15) mtb = 0.0625;
+ ctime = mtb * bytes[12];
+ taa = bytes[16] * mtb;
+
+ if (trcd) { *trcd = bytes[18] * mtb; }
+
+ if (trp) { *trp = bytes[20] * mtb; }
+
+ if (tras) { *tras = (bytes[22] + (bytes[21] & 0xf)) * mtb; }
+
+ if (tcl) { *tcl = ceil(taa/ctime); }
+}
+
+static void decode_ddr3_module_type(unsigned char *bytes, const char **type) {
+ switch (bytes[3]) {
+ case 0x01: *type = "RDIMM (Registered Long DIMM)"; break;
+ case 0x02: *type = "UDIMM (Unbuffered Long DIMM)"; break;
+ case 0x03: *type = "SODIMM (Small Outline DIMM)"; break;
+ default: *type = NULL;
+ }
+}
+
+static void decode_ddr3_module_detail(unsigned char *bytes, char *type_detail) {
+ float ddr_clock;
+ int pc3_speed;
+ if (type_detail) {
+ decode_ddr3_module_speed(bytes, &ddr_clock, &pc3_speed);
+ snprintf(type_detail, 255, "DDR3-%.0f (PC3-%d)", ddr_clock, pc3_speed);
+ }
+}
+
+static gchar *decode_ddr3_sdram_extra(unsigned char *bytes) {
+ float trcd, trp, tras, tcl;
+
+ decode_ddr3_module_timings(bytes, &trcd, &trp, &tras, &tcl);
+
+ int ranks = 1 + ((bytes[7] >> 3) & 0x7);
+ int pins = 4 << (bytes[7] & 0x7);
+ int die_count = (bytes[33] >> 4) & 0x7;
+ int ts = !!(bytes[32] & 0x80);
+
+ /* expected to continue an [SPD] section */
+ return g_strdup_printf("%s=%d\n"
+ "%s=%d\n"
+ "%s=%d %s\n"
+ "%s=[%02x] %s\n"
+ "%s=%s%s%s\n"
+ "%s="
+ "%s%s%s%s%s%s%s%s"
+ "%s%s%s%s%s%s%s\n"
+ "[%s]\n"
+ "tCL=%.0f\n"
+ "tRCD=%.3fns\n"
+ "tRP=%.3fns\n"
+ "tRAS=%.3fns\n",
+ _("Ranks"), ranks,
+ _("IO Pins per Chip"), pins,
+ _("Die count"), die_count, die_count ? "" : _("(Unspecified)"),
+ _("Thermal Sensor"), bytes[32], ts ? _("Present") : _("Not present"),
+ _("Supported Voltages"),
+ (bytes[6] & 4) ? "1.25V " : "",
+ (bytes[6] & 2) ? "1.35V " : "",
+ (bytes[6] & 1) ? "" : "1.5V",
+ _("Supported CAS Latencies"),
+ (bytes[15] & 0x40) ? "18 " : "",
+ (bytes[15] & 0x20) ? "17 " : "",
+ (bytes[15] & 0x10) ? "16 " : "",
+ (bytes[15] & 0x08) ? "15 " : "",
+ (bytes[15] & 0x04) ? "14 " : "",
+ (bytes[15] & 0x02) ? "13 " : "",
+ (bytes[15] & 0x01) ? "12 " : "",
+ (bytes[14] & 0x80) ? "11 " : "",
+ (bytes[14] & 0x40) ? "10 " : "",
+ (bytes[14] & 0x20) ? "9 " : "",
+ (bytes[14] & 0x10) ? "8 " : "",
+ (bytes[14] & 0x08) ? "7 " : "",
+ (bytes[14] & 0x04) ? "6 " : "",
+ (bytes[14] & 0x02) ? "5 " : "",
+ (bytes[14] & 0x01) ? "4" : "",
+ _("Timings"), tcl, trcd, trp, tras
+ );
+}
+
+static void decode_ddr3_part_number(unsigned char *bytes, char *part_number) {
+ int i;
+ if (part_number) {
+ for (i = 128; i <= 145; i++) *part_number++ = bytes[i];
+ *part_number = '\0';
+ }
+}
+
+static void decode_ddr34_manufacturer(unsigned char count, unsigned char code, char **manufacturer, int *bank, int *index) {
+ if (!manufacturer) return;
+
+ if (code == 0x00 || code == 0xFF) {
+ *manufacturer = NULL;
+ return;
+ }
+
+ if (parity(count) != 1 || parity(code) != 1) {
+ *manufacturer = _("Invalid");
+ return;
+ }
+
+ *bank = count & 0x7f;
+ *index = code & 0x7f;
+ if (*bank >= VENDORS_BANKS) {
+ *manufacturer = NULL;
+ return;
+ }
+
+ *manufacturer = (char *)JEDEC_MFG_STR(*bank, *index - 1);
+}
+
+static void decode_ddr3_manufacturer(unsigned char *bytes, char **manufacturer, int *bank, int *index) {
+ decode_ddr34_manufacturer(bytes[117], bytes[118], (char **) manufacturer, bank, index);
+}
+
+static void decode_module_manufacturer(unsigned char *bytes, char **manufacturer) {
+ char *out = "Unknown";
+ unsigned char first;
+ int ai = 0;
+ int len = 8;
+
+ if (!spd_written(bytes, 8)) {
+ out = "Undefined";
+ goto end;
+ }
+
+ do { ai++; } while ((--len && (*bytes++ == 0x7f)));
+ first = *--bytes;
+
+ if (ai == 0) {
+ out = "Invalid";
+ goto end;
+ }
+
+ if (parity(first) != 1) {
+ out = "Invalid";
+ goto end;
+ }
+
+ out = (char*)JEDEC_MFG_STR(ai - 1, (first & 0x7f) - 1);
+
+end:
+ if (manufacturer) { *manufacturer = out; }
+}
+
+static void decode_module_part_number(unsigned char *bytes, char *part_number) {
+ if (part_number) {
+ bytes += 8 + 64;
+
+ while (*++bytes && *bytes >= 32 && *bytes < 127) { *part_number++ = *bytes; }
+ *part_number = '\0';
+ }
+}
+
+static char *print_spd_timings(int speed, float cas, float trcd, float trp, float tras,
+ float ctime) {
+ return g_strdup_printf("DDR4-%d=%.0f-%.0f-%.0f-%.0f\n", speed, cas, ceil(trcd / ctime - 0.025),
+ ceil(trp / ctime - 0.025), ceil(tras / ctime - 0.025));
+}
+
+static void decode_ddr4_module_type(unsigned char *bytes, const char **type) {
+ switch (bytes[3]) {
+ case 0x01: *type = "RDIMM (Registered DIMM)"; break;
+ case 0x02: *type = "UDIMM (Unbuffered DIMM)"; break;
+ case 0x03: *type = "SODIMM (Small Outline Unbuffered DIMM)"; break;
+ case 0x04: *type = "LRDIMM (Load-Reduced DIMM)"; break;
+ case 0x05: *type = "Mini-RDIMM (Mini Registered DIMM)"; break;
+ case 0x06: *type = "Mini-UDIMM (Mini Unbuffered DIMM)"; break;
+ case 0x08: *type = "72b-SO-RDIMM (Small Outline Registered DIMM, 72-bit data bus)"; break;
+ case 0x09: *type = "72b-SO-UDIMM (Small Outline Unbuffered DIMM, 72-bit data bus)"; break;
+ case 0x0c: *type = "16b-SO-UDIMM (Small Outline Unbuffered DIMM, 16-bit data bus)"; break;
+ case 0x0d: *type = "32b-SO-UDIMM (Small Outline Unbuffered DIMM, 32-bit data bus)"; break;
+ default: *type = NULL;
+ }
+}
+
+static float ddr4_mtb_ftb_calc(unsigned char b1, signed char b2) {
+ float mtb = 0.125;
+ float ftb = 0.001;
+ return b1 * mtb + b2 * ftb;
+}
+
+static void decode_ddr4_module_speed(unsigned char *bytes, float *ddr_clock, int *pc4_speed) {
+ float ctime;
+ float ddrclk;
+ int tbits, pcclk;
+
+ ctime = ddr4_mtb_ftb_calc(bytes[18], bytes[125]);
+ ddrclk = 2 * (1000 / ctime);
+ tbits = 8 << (bytes[13] & 7);
+
+ pcclk = ddrclk * tbits / 8;
+ pcclk -= pcclk % 100;
+
+ if (ddr_clock) { *ddr_clock = (int)ddrclk; }
+ if (pc4_speed) { *pc4_speed = pcclk; }
+}
+
+static void decode_ddr4_module_spd_timings(unsigned char *bytes, float speed, char **str) {
+ float ctime, ctime_max, pctime, taa, trcd, trp, tras;
+ int pcas, best_cas, base_cas, ci, i, j;
+ unsigned char cas_support[] = {bytes[20], bytes[21], bytes[22], bytes[23] & 0x1f};
+ float possible_ctimes[] = {15 / 24.0, 15 / 22.0, 15 / 20.0, 15 / 18.0,
+ 15 / 16.0, 15 / 14.0, 15 / 12.0};
+
+ base_cas = bytes[23] & 0x80 ? 23 : 7;
+
+ ctime = ddr4_mtb_ftb_calc(bytes[18], bytes[125]);
+ ctime_max = ddr4_mtb_ftb_calc(bytes[19], bytes[124]);
+
+ taa = ddr4_mtb_ftb_calc(bytes[24], bytes[123]);
+ trcd = ddr4_mtb_ftb_calc(bytes[25], bytes[122]);
+ trp = ddr4_mtb_ftb_calc(bytes[26], bytes[121]);
+ tras = (((bytes[27] & 0x0f) << 8) + bytes[28]) * 0.125;
+
+ *str = print_spd_timings((int)speed, ceil(taa / ctime - 0.025), trcd, trp, tras, ctime);
+
+ for (ci = 0; ci < 7; ci++) {
+ best_cas = 0;
+ pctime = possible_ctimes[ci];
+
+ for (i = 3; i >= 0; i--) {
+ for (j = 7; j >= 0; j--) {
+ if ((cas_support[i] & (1 << j)) != 0) {
+ pcas = base_cas + 8 * i + j;
+ if (ceil(taa / pctime) - 0.025 <= pcas) { best_cas = pcas; }
+ }
+ }
+ }
+
+ if (best_cas > 0 && pctime <= ctime_max && pctime >= ctime) {
+ *str = h_strdup_cprintf(
+ "%s\n", *str,
+ print_spd_timings((int)(2000.0 / pctime), best_cas, trcd, trp, tras, pctime));
+ }
+ }
+}
+
+static void decode_ddr4_module_size(unsigned char *bytes, dmi_mem_size *size) {
+ int sdrcap = 256 << (bytes[4] & 15);
+ int buswidth = 8 << (bytes[13] & 7);
+ int sdrwidth = 4 << (bytes[12] & 7);
+ int signal_loading = bytes[6] & 3;
+ int lranks_per_dimm = ((bytes[12] >> 3) & 7) + 1;
+
+ if (signal_loading == 2) lranks_per_dimm *= ((bytes[6] >> 4) & 7) + 1;
+
+ *size = (dmi_mem_size)sdrcap / 8 * buswidth / sdrwidth * lranks_per_dimm;
+}
+
+
+static void decode_ddr234_module_date(unsigned char weekb, unsigned char yearb, int *week, int *year) {
+ if (yearb == 0x0 || yearb == 0xff ||
+ weekb == 0x0 || weekb == 0xff) {
+ return;
+ }
+ *week = (weekb>>4) & 0xf;
+ *week *= 10;
+ *week += weekb & 0xf;
+ *year = (yearb>>4) & 0xf;
+ *year *= 10;
+ *year += yearb & 0xf;
+ *year += 2000;
+}
+
+static void decode_ddr2_module_date(unsigned char *bytes, int *week, int *year) {
+ decode_ddr234_module_date(bytes[94], bytes[93], week, year);
+}
+
+static void decode_ddr3_module_date(unsigned char *bytes, int *week, int *year) {
+ decode_ddr234_module_date(bytes[121], bytes[120], week, year);
+}
+
+static void decode_ddr4_module_date(unsigned char *bytes, int spd_size, int *week, int *year) {
+ if (spd_size < 324)
+ return;
+ decode_ddr234_module_date(bytes[324], bytes[323], week, year);
+}
+
+static void decode_ddr3_dram_manufacturer(unsigned char *bytes,
+ char **manufacturer, int *bank, int *index) {
+ decode_ddr34_manufacturer(bytes[94], bytes[95], (char **) manufacturer, bank, index);
+}
+
+static void decode_ddr4_dram_manufacturer(unsigned char *bytes, int spd_size,
+ char **manufacturer, int *bank, int *index) {
+ if (spd_size < 351) {
+ *manufacturer = NULL;
+ return;
+ }
+
+ decode_ddr34_manufacturer(bytes[350], bytes[351], (char **) manufacturer, bank, index);
+}
+
+static void detect_ddr4_xmp(unsigned char *bytes, int spd_size, int *majv, int *minv) {
+ if (spd_size < 387)
+ return;
+
+ *majv = 0; *minv = 0;
+
+ if (bytes[384] != 0x0c || bytes[385] != 0x4a) // XMP magic number
+ return;
+
+ if (majv)
+ *majv = bytes[387] >> 4;
+ if (minv)
+ *minv = bytes[387] & 0xf;
+}
+
+static void decode_ddr4_xmp(unsigned char *bytes, int spd_size, char **str) {
+ float ctime;
+ float ddrclk, taa, trcd, trp, tras;
+
+ if (spd_size < 405)
+ return;
+
+ ctime = ddr4_mtb_ftb_calc(bytes[396], bytes[431]);
+ ddrclk = 2 * (1000 / ctime);
+ taa = ddr4_mtb_ftb_calc(bytes[401], bytes[430]);
+ trcd = ddr4_mtb_ftb_calc(bytes[402], bytes[429]);
+ trp = ddr4_mtb_ftb_calc(bytes[403], bytes[428]);
+ tras = (((bytes[404] & 0x0f) << 8) + bytes[405]) * 0.125;
+
+ *str = g_strdup_printf("[%s]\n"
+ "%s=DDR4 %d MHz\n"
+ "%s=%d.%d V\n"
+ "[%s]\n"
+ "%s",
+ _("XMP Profile"),
+ _("Speed"), (int) ddrclk,
+ _("Voltage"), bytes[393] >> 7, bytes[393] & 0x7f,
+ _("XMP Timings"),
+ print_spd_timings((int) ddrclk, ceil(taa/ctime - 0.025), trcd, trp, tras, ctime));
+}
+
+static void decode_ddr4_module_detail(unsigned char *bytes, char *type_detail) {
+ float ddr_clock;
+ int pc4_speed;
+ if (type_detail) {
+ decode_ddr4_module_speed(bytes, &ddr_clock, &pc4_speed);
+ snprintf(type_detail, 255, "DDR4-%.0f (PC4-%d)", ddr_clock, pc4_speed);
+ }
+}
+
+static gchar *decode_ddr4_sdram_extra(unsigned char *bytes, int spd_size) {
+ float ddr_clock;
+ int pc4_speed, xmp_majv = -1, xmp_minv = -1;
+ char *speed_timings = NULL, *xmp_profile = NULL, *xmp = NULL, *manf_date = NULL;
+ static gchar *out;
+
+ decode_ddr4_module_speed(bytes, &ddr_clock, &pc4_speed);
+ decode_ddr4_module_spd_timings(bytes, ddr_clock, &speed_timings);
+ detect_ddr4_xmp(bytes, spd_size, &xmp_majv, &xmp_minv);
+
+ if (xmp_majv == -1 && xmp_minv == -1) {
+ xmp = NULL;
+ }
+ else if (xmp_majv <= 0 && xmp_minv <= 0) {
+ xmp = g_strdup(_("No"));
+ }
+ else {
+ xmp = g_strdup_printf("%s (revision %d.%d)", _("Yes"), xmp_majv, xmp_minv);
+ if (xmp_majv == 2 && xmp_minv == 0)
+ decode_ddr4_xmp(bytes, spd_size, &xmp_profile);
+ }
+
+ /* expected to continue an [SPD] section */
+ out = g_strdup_printf("%s=%s\n"
+ "%s=%s\n"
+ "[%s]\n"
+ "%s\n"
+ "%s",
+ _("Voltage"), bytes[11] & 0x01 ? "1.2 V": _("(Unknown)"),
+ _("XMP"), xmp ? xmp : _("(Unknown)"),
+ _("JEDEC Timings"), speed_timings,
+ xmp_profile ? xmp_profile: "");
+
+ g_free(speed_timings);
+ g_free(manf_date);
+ g_free(xmp);
+ g_free(xmp_profile);
+
+ return out;
+}
+
+static void decode_ddr4_part_number(unsigned char *bytes, int spd_size, char *part_number) {
+ int i;
+ if (!part_number) return;
+
+ if (spd_size < 348) {
+ *part_number++ = '\0';
+ return;
+ }
+
+ for (i = 329; i <= 348; i++)
+ *part_number++ = bytes[i];
+ *part_number = '\0';
+}
+
+static void decode_ddr4_module_manufacturer(unsigned char *bytes, int spd_size,
+ char **manufacturer, int *bank, int *index) {
+ if (spd_size < 321) {
+ *manufacturer = NULL;
+ return;
+ }
+
+ decode_ddr34_manufacturer(bytes[320], bytes[321], manufacturer, bank, index);
+}
+
+static int decode_ram_type(unsigned char *bytes) {
+ if (bytes[0] < 4) {
+ switch (bytes[2]) {
+ case 1: return DIRECT_RAMBUS;
+ case 17: return RAMBUS;
+ }
+ } else {
+ switch (bytes[2]) {
+ case 1: return FPM_DRAM;
+ case 2: return EDO;
+ case 3: return PIPELINED_NIBBLE;
+ case 4: return SDR_SDRAM;
+ case 5: return MULTIPLEXED_ROM;
+ case 6: return DDR_SGRAM;
+ case 7: return DDR_SDRAM;
+ case 8: return DDR2_SDRAM;
+ case 11: return DDR3_SDRAM;
+ case 12: return DDR4_SDRAM;
+ }
+ }
+
+ return UNKNOWN;
+}
+
+static int read_spd(char *spd_path, int offset, size_t size, int use_sysfs,
+ unsigned char *bytes_out) {
+ int data_size = 0;
+ if (use_sysfs) {
+ FILE *spd;
+ gchar *temp_path;
+
+ temp_path = g_strdup_printf("%s/eeprom", spd_path);
+ if ((spd = fopen(temp_path, "rb"))) {
+ fseek(spd, offset, SEEK_SET);
+ data_size = fread(bytes_out, 1, size, spd);
+ fclose(spd);
+ }
+
+ g_free(temp_path);
+ } else {
+ int i;
+
+ for (i = 0; i <= 3; i++) {
+ FILE *spd;
+ char *temp_path;
+
+ temp_path = g_strdup_printf("%s/%02x", spd_path, offset + i * 16);
+ if ((spd = fopen(temp_path, "rb"))) {
+ data_size += fread(bytes_out + i * 16, 1, 16, spd);
+ fclose(spd);
+ }
+
+ g_free(temp_path);
+ }
+ }
+
+ return data_size;
+}
+
+static GSList *decode_dimms2(GSList *eeprom_list, const gchar *driver, gboolean use_sysfs, int max_size) {
+ GSList *eeprom, *dimm_list = NULL;
+ int count = 0;
+ int spd_size = 0;
+ unsigned char bytes[512] = {0};
+ spd_data *s = NULL;
+
+ for (eeprom = eeprom_list; eeprom; eeprom = eeprom->next, count++) {
+ gchar *spd_path = (gchar*)eeprom->data;
+ s = NULL;
+
+ RamType ram_type;
+
+ memset(bytes, 0, 512); /* clear */
+ spd_size = read_spd(spd_path, 0, max_size, use_sysfs, bytes);
+ ram_type = decode_ram_type(bytes);
+
+ switch (ram_type) {
+ case SDR_SDRAM:
+ s = spd_data_new();
+ memcpy(s->bytes, bytes, 512);
+ decode_module_part_number(bytes, s->partno);
+ decode_module_manufacturer(bytes + 64, (char**)&s->vendor_str);
+ decode_sdr_module_size(bytes, &s->size_MiB);
+ decode_sdr_module_detail(bytes, s->type_detail);
+ break;
+ case DDR_SDRAM:
+ s = spd_data_new();
+ memcpy(s->bytes, bytes, 512);
+ decode_module_part_number(bytes, s->partno);
+ decode_module_manufacturer(bytes + 64, (char**)&s->vendor_str);
+ decode_ddr_module_size(bytes, &s->size_MiB);
+ decode_ddr_module_detail(bytes, s->type_detail);
+ break;
+ case DDR2_SDRAM:
+ s = spd_data_new();
+ memcpy(s->bytes, bytes, 512);
+ decode_module_part_number(bytes, s->partno);
+ decode_module_manufacturer(bytes + 64, (char**)&s->vendor_str);
+ decode_ddr2_module_size(bytes, &s->size_MiB);
+ decode_ddr2_module_detail(bytes, s->type_detail);
+ decode_ddr2_module_type(bytes, &s->form_factor);
+ decode_ddr2_module_date(bytes, &s->week, &s->year);
+ break;
+ case DDR3_SDRAM:
+ s = spd_data_new();
+ memcpy(s->bytes, bytes, 512);
+ decode_ddr3_part_number(bytes, s->partno);
+ decode_ddr3_manufacturer(bytes, (char**)&s->vendor_str,
+ &s->vendor_bank, &s->vendor_index);
+ decode_ddr3_dram_manufacturer(bytes, (char**)&s->dram_vendor_str,
+ &s->dram_vendor_bank, &s->dram_vendor_index);
+ decode_ddr3_module_size(bytes, &s->size_MiB);
+ decode_ddr3_module_type(bytes, &s->form_factor);
+ decode_ddr3_module_detail(bytes, s->type_detail);
+ decode_ddr3_module_date(bytes, &s->week, &s->year);
+ break;
+ case DDR4_SDRAM:
+ s = spd_data_new();
+ memcpy(s->bytes, bytes, 512);
+ decode_ddr4_part_number(bytes, spd_size, s->partno);
+ decode_ddr4_module_manufacturer(bytes, spd_size, (char**)&s->vendor_str,
+ &s->vendor_bank, &s->vendor_index);
+ decode_ddr4_dram_manufacturer(bytes, spd_size, (char**)&s->dram_vendor_str,
+ &s->dram_vendor_bank, &s->dram_vendor_index);
+ decode_ddr4_module_size(bytes, &s->size_MiB);
+ decode_ddr4_module_type(bytes, &s->form_factor);
+ decode_ddr4_module_detail(bytes, s->type_detail);
+ decode_ddr4_module_date(bytes, spd_size, &s->week, &s->year);
+ s->ddr4_no_ee1004 = s->ddr4_no_ee1004 || (spd_size < 512);
+ spd_ddr4_partial_data = spd_ddr4_partial_data || s->ddr4_no_ee1004;
+ break;
+ case UNKNOWN:
+ break;
+ default:
+ DEBUG("Unsupported EEPROM type: %s for %s\n", GET_RAM_TYPE_STR(ram_type), spd_path); continue;
+ if (ram_type) {
+ /* some kind of RAM that we can't decode, but exists */
+ s = spd_data_new();
+ memcpy(s->bytes, bytes, 512);
+ }
+ }
+
+ if (s) {
+ strncpy(s->dev, g_basename(spd_path), 31);
+ s->spd_driver = driver;
+ s->spd_size = spd_size;
+ s->type = ram_type;
+ spd_ram_types |= (1 << (ram_type-1));
+ switch (ram_type) {
+ case SDR_SDRAM:
+ case DDR_SDRAM:
+ case DDR2_SDRAM:
+ s->spd_rev_major = bytes[62] >> 4;
+ s->spd_rev_minor = bytes[62] & 0xf;
+ break;
+ case DDR3_SDRAM:
+ case DDR4_SDRAM:
+ s->spd_rev_major = bytes[1] >> 4;
+ s->spd_rev_minor = bytes[1] & 0xf;
+ break;
+ }
+ s->vendor = vendor_match(s->vendor_str, NULL);
+ s->dram_vendor = vendor_match(s->dram_vendor_str, NULL);
+ dimm_list = g_slist_append(dimm_list, s);
+ }
+ }
+
+ return dimm_list;
+}
+
+struct SpdDriver {
+ const char *driver;
+ const char *dir_path;
+ const gint max_size;
+ const gboolean use_sysfs;
+ const char *spd_name;
+};
+
+static const struct SpdDriver spd_drivers[] = {
+ { "ee1004", "/sys/bus/i2c/drivers/ee1004", 512, TRUE, "ee1004"},
+ { "at24", "/sys/bus/i2c/drivers/at24" , 256, TRUE, "spd"},
+ { "eeprom", "/sys/bus/i2c/drivers/eeprom", 256, TRUE, "eeprom"},
+ { "eeprom-proc", "/proc/sys/dev/sensors" , 256, FALSE, NULL},
+ { NULL }
+};
+
+GSList *spd_scan() {
+ GDir *dir = NULL;
+ GSList *eeprom_list = NULL, *dimm_list = NULL;
+ gchar *dimm_list_entry, *dir_entry, *name_file, *name;
+ const struct SpdDriver *driver;
+ gboolean driver_found = FALSE;
+ gboolean is_spd = FALSE;
+
+ spd_ddr4_partial_data = FALSE;
+ spd_no_driver = FALSE;
+ spd_no_support = FALSE;
+
+ for (driver = spd_drivers; driver->dir_path; driver++) {
+ if (g_file_test(driver->dir_path, G_FILE_TEST_EXISTS)) {
+ dir = g_dir_open(driver->dir_path, 0, NULL);
+ if (!dir) continue;
+
+ driver_found = TRUE;
+ while ((dir_entry = (char *)g_dir_read_name(dir))) {
+ is_spd = FALSE;
+
+ if (driver->use_sysfs) {
+ name_file = NULL;
+ name = NULL;
+
+ if (isdigit(dir_entry[0])) {
+ name_file = g_build_filename(driver->dir_path, dir_entry, "name", NULL);
+ g_file_get_contents(name_file, &name, NULL, NULL);
+
+ is_spd = g_strcmp0(name, driver->spd_name);
+
+ g_free(name_file);
+ g_free(name);
+ }
+ }
+ else {
+ is_spd = g_str_has_prefix(dir_entry, "eeprom-");
+ }
+
+ if (is_spd){
+ dimm_list_entry = g_strdup_printf("%s/%s", driver->dir_path, dir_entry);
+ eeprom_list = g_slist_prepend(eeprom_list, dimm_list_entry);
+ }
+ }
+
+ g_dir_close(dir);
+
+ if (eeprom_list) {
+ dimm_list = decode_dimms2(eeprom_list, driver->driver, driver->use_sysfs, driver->max_size);
+ g_slist_free(eeprom_list);
+ eeprom_list = NULL;
+ }
+ if (dimm_list) break;
+ }
+ }
+
+ if (!driver_found) {
+ if (!g_file_test("/sys/module/eeprom", G_FILE_TEST_EXISTS) &&
+ !g_file_test("/sys/module/at24", G_FILE_TEST_EXISTS)) {
+ spd_no_driver = TRUE; /* trigger hinote for no eeprom driver */
+ } else {
+ spd_no_support = TRUE; /* trigger hinote for unsupported system */
+ }
+ }
+
+ return dimm_list;
+}
diff --git a/modules/devices/spd-vendors.c b/modules/devices/spd-vendors.c
new file mode 100644
index 00000000..80b2a6a4
--- /dev/null
+++ b/modules/devices/spd-vendors.c
@@ -0,0 +1,292 @@
+/*
+ * spd-decode.c, spd-vendors.c
+ * Copyright (c) 2010 L. A. F. Pereira
+ * modified by Ondrej Čerman (2019)
+ * modified by Burt P. (2019)
+ *
+ * Based on decode-dimms.pl
+ * Copyright 1998, 1999 Philip Edelbrock <phil@netroedge.com>
+ * modified by Christian Zuckschwerdt <zany@triq.net>
+ * modified by Burkart Lingner <burkart@bollchen.de>
+ *
+ * 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; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+/* from decode-dimms, in the i2c-tools package:
+ * https://github.com/angeloc/i2c-tools/blob/master/eeprom/decode-dimms
+ *
+ * STANDARD MANUFACTURERS IDENTIFICATION CODEs
+ * as defined in JEP106
+ *
+ * As of: 28 June 2019
+ *
+ */
+
+#define VENDORS_BANKS 8
+#define VENDORS_ITEMS 128
+#define JEDEC_MFG_STR(b,i) ( (b >= 0 && b < VENDORS_BANKS && i < VENDORS_ITEMS) ? vendors[(b)][(i)] : NULL )
+static const char* vendors[VENDORS_BANKS][VENDORS_ITEMS] =
+{
+{"AMD", "AMI", "Fairchild", "Fujitsu",
+ "GTE", "Harris", "Hitachi", "Inmos",
+ "Intel", "I.T.T.", "Intersil", "Monolithic Memories",
+ "Mostek", "Freescale (former Motorola)", "National", "NEC",
+ "RCA", "Raytheon", "Conexant (Rockwell)", "Seeq",
+ "NXP (former Signetics, Philips Semi.)", "Synertek", "Texas Instruments", "Toshiba",
+ "Xicor", "Zilog", "Eurotechnique", "Mitsubishi",
+ "Lucent (AT&T)", "Exel", "Atmel", "SGS/Thomson",
+ "Lattice Semi.", "NCR", "Wafer Scale Integration", "IBM",
+ "Tristar", "Visic", "Intl. CMOS Technology", "SSSI",
+ "MicrochipTechnology", "Ricoh Ltd.", "VLSI", "Micron Technology",
+ "SK Hynix (former Hyundai Electronics)", "OKI Semiconductor", "ACTEL", "Sharp",
+ "Catalyst", "Panasonic", "IDT", "Cypress",
+ "DEC", "LSI Logic", "Zarlink (former Plessey)", "UTMC",
+ "Thinking Machine", "Thomson CSF", "Integrated CMOS (Vertex)", "Honeywell",
+ "Tektronix", "Oracle Corporation (former Sun Microsystems)", "Silicon Storage Technology", "ProMos/Mosel Vitelic",
+ "Infineon (former Siemens)", "Macronix", "Xerox", "Plus Logic",
+ "SunDisk", "Elan Circuit Tech.", "European Silicon Str.", "Apple Computer",
+ "Xilinx", "Compaq", "Protocol Engines", "SCI",
+ "Seiko Instruments", "Samsung", "I3 Design System", "Klic",
+ "Crosspoint Solutions", "Alliance Semiconductor", "Tandem", "Hewlett-Packard",
+ "Integrated Silicon Solutions", "Brooktree", "New Media", "MHS Electronic",
+ "Performance Semi.", "Winbond Electronic", "Kawasaki Steel", "Bright Micro",
+ "TECMAR", "Exar", "PCMCIA", "LG Semi (former Goldstar)",
+ "Northern Telecom", "Sanyo", "Array Microsystems", "Crystal Semiconductor",
+ "Analog Devices", "PMC-Sierra", "Asparix", "Convex Computer",
+ "Quality Semiconductor", "Nimbus Technology", "Transwitch", "Micronas (ITT Intermetall)",
+ "Cannon", "Altera", "NEXCOM", "QUALCOMM",
+ "Sony", "Cray Research", "AMS(Austria Micro)", "Vitesse",
+ "Aster Electronics", "Bay Networks (Synoptic)", "Zentrum or ZMD", "TRW",
+ "Thesys", "Solbourne Computer", "Allied-Signal", "Dialog",
+ "Media Vision", "Numonyx Corporation (former Level One Communication)"},
+{"Cirrus Logic", "National Instruments", "ILC Data Device", "Alcatel Mietec",
+ "Micro Linear", "Univ. of NC", "JTAG Technologies", "BAE Systems",
+ "Nchip", "Galileo Tech", "Bestlink Systems", "Graychip",
+ "GENNUM", "VideoLogic", "Robert Bosch", "Chip Express",
+ "DATARAM", "United Microelec Corp.", "TCSI", "Smart Modular",
+ "Hughes Aircraft", "Lanstar Semiconductor", "Qlogic", "Kingston",
+ "Music Semi", "Ericsson Components", "SpaSE", "Eon Silicon Devices",
+ "Programmable Micro Corp", "DoD", "Integ. Memories Tech.", "Corollary Inc.",
+ "Dallas Semiconductor", "Omnivision", "EIV(Switzerland)", "Novatel Wireless",
+ "Zarlink (former Mitel)", "Clearpoint", "Cabletron", "STEC (former Silicon Technology)",
+ "Vanguard", "Hagiwara Sys-Com", "Vantis", "Celestica",
+ "Century", "Hal Computers", "Rohm Company Ltd.", "Juniper Networks",
+ "Libit Signal Processing", "Mushkin Enhanced Memory", "Tundra Semiconductor", "Adaptec Inc.",
+ "LightSpeed Semi.", "ZSP Corp.", "AMIC Technology", "Adobe Systems",
+ "Dynachip", "PNY Electronics", "Newport Digital", "MMC Networks",
+ "T Square", "Seiko Epson", "Broadcom", "Viking Components",
+ "V3 Semiconductor", "Flextronics (former Orbit)", "Suwa Electronics", "Transmeta",
+ "Micron CMS", "American Computer & Digital Components Inc", "Enhance 3000 Inc", "Tower Semiconductor",
+ "CPU Design", "Price Point", "Maxim Integrated Product", "Tellabs",
+ "Centaur Technology", "Unigen Corporation", "Transcend Information", "Memory Card Technology",
+ "CKD Corporation Ltd.", "Capital Instruments, Inc.", "Aica Kogyo, Ltd.", "Linvex Technology",
+ "MSC Vertriebs GmbH", "AKM Company, Ltd.", "Dynamem, Inc.", "NERA ASA",
+ "GSI Technology", "Dane-Elec (C Memory)", "Acorn Computers", "Lara Technology",
+ "Oak Technology, Inc.", "Itec Memory", "Tanisys Technology", "Truevision",
+ "Wintec Industries", "Super PC Memory", "MGV Memory", "Galvantech",
+ "Gadzoox Nteworks", "Multi Dimensional Cons.", "GateField", "Integrated Memory System",
+ "Triscend", "XaQti", "Goldenram", "Clear Logic",
+ "Cimaron Communications", "Nippon Steel Semi. Corp.", "Advantage Memory", "AMCC",
+ "LeCroy", "Yamaha Corporation", "Digital Microwave", "NetLogic Microsystems",
+ "MIMOS Semiconductor", "Advanced Fibre", "BF Goodrich Data.", "Epigram",
+ "Acbel Polytech Inc.", "Apacer Technology", "Admor Memory", "FOXCONN",
+ "Quadratics Superconductor", "3COM"},
+{"Camintonn Corporation", "ISOA Incorporated", "Agate Semiconductor", "ADMtek Incorporated",
+ "HYPERTEC", "Adhoc Technologies", "MOSAID Technologies", "Ardent Technologies",
+ "Switchcore", "Cisco Systems, Inc.", "Allayer Technologies", "WorkX AG (Wichman)",
+ "Oasis Semiconductor", "Novanet Semiconductor", "E-M Solutions", "Power General",
+ "Advanced Hardware Arch.", "Inova Semiconductors GmbH", "Telocity", "Delkin Devices",
+ "Symagery Microsystems", "C-Port Corporation", "SiberCore Technologies", "Southland Microsystems",
+ "Malleable Technologies", "Kendin Communications", "Great Technology Microcomputer", "Sanmina Corporation",
+ "HADCO Corporation", "Corsair", "Actrans System Inc.", "ALPHA Technologies",
+ "Silicon Laboratories, Inc. (Cygnal)", "Artesyn Technologies", "Align Manufacturing", "Peregrine Semiconductor",
+ "Chameleon Systems", "Aplus Flash Technology", "MIPS Technologies", "Chrysalis ITS",
+ "ADTEC Corporation", "Kentron Technologies", "Win Technologies", "Tachyon Semiconductor (former ASIC Designs Inc.)",
+ "Extreme Packet Devices", "RF Micro Devices", "Siemens AG", "Sarnoff Corporation",
+ "Itautec SA (former Itautec Philco SA)", "Radiata Inc.", "Benchmark Elect. (AVEX)", "Legend",
+ "SpecTek Incorporated", "Hi/fn", "Enikia Incorporated", "SwitchOn Networks",
+ "AANetcom Incorporated", "Micro Memory Bank", "ESS Technology", "Virata Corporation",
+ "Excess Bandwidth", "West Bay Semiconductor", "DSP Group", "Newport Communications",
+ "Chip2Chip Incorporated", "Phobos Corporation", "Intellitech Corporation", "Nordic VLSI ASA",
+ "Ishoni Networks", "Silicon Spice", "Alchemy Semiconductor", "Agilent Technologies",
+ "Centillium Communications", "W.L. Gore", "HanBit Electronics", "GlobeSpan",
+ "Element 14", "Pycon", "Saifun Semiconductors", "Sibyte, Incorporated",
+ "MetaLink Technologies", "Feiya Technology", "I & C Technology", "Shikatronics",
+ "Elektrobit", "Megic", "Com-Tier", "Malaysia Micro Solutions",
+ "Hyperchip", "Gemstone Communications", "Anadigm (former Anadyne)", "3ParData",
+ "Mellanox Technologies", "Tenx Technologies", "Helix AG", "Domosys",
+ "Skyup Technology", "HiNT Corporation", "Chiaro", "MDT Technologies GmbH (former MCI Computer GMBH)",
+ "Exbit Technology A/S", "Integrated Technology Express", "AVED Memory", "Legerity",
+ "Jasmine Networks", "Caspian Networks", "nCUBE", "Silicon Access Networks",
+ "FDK Corporation", "High Bandwidth Access", "MultiLink Technology", "BRECIS",
+ "World Wide Packets", "APW", "Chicory Systems", "Xstream Logic",
+ "Fast-Chip", "Zucotto Wireless", "Realchip", "Galaxy Power",
+ "eSilicon", "Morphics Technology", "Accelerant Networks", "Silicon Wave",
+ "SandCraft", "Elpida"},
+{"Solectron", "Optosys Technologies", "Buffalo (former Melco)", "TriMedia Technologies",
+ "Cyan Technologies", "Global Locate", "Optillion", "Terago Communications",
+ "Ikanos Communications", "Princeton Technology", "Nanya Technology", "Elite Flash Storage",
+ "Mysticom", "LightSand Communications", "ATI Technologies", "Agere Systems",
+ "NeoMagic", "AuroraNetics", "Golden Empire", "Mushkin",
+ "Tioga Technologies", "Netlist", "TeraLogic", "Cicada Semiconductor",
+ "Centon Electronics", "Tyco Electronics", "Magis Works", "Zettacom",
+ "Cogency Semiconductor", "Chipcon AS", "Aspex Technology", "F5 Networks",
+ "Programmable Silicon Solutions", "ChipWrights", "Acorn Networks", "Quicklogic",
+ "Kingmax Semiconductor", "BOPS", "Flasys", "BitBlitz Communications",
+ "eMemory Technology", "Procket Networks", "Purple Ray", "Trebia Networks",
+ "Delta Electronics", "Onex Communications", "Ample Communications", "Memory Experts Intl",
+ "Astute Networks", "Azanda Network Devices", "Dibcom", "Tekmos",
+ "API NetWorks", "Bay Microsystems", "Firecron Ltd", "Resonext Communications",
+ "Tachys Technologies", "Equator Technology", "Concept Computer", "SILCOM",
+ "3Dlabs", "c't Magazine", "Sanera Systems", "Silicon Packets",
+ "Viasystems Group", "Simtek", "Semicon Devices Singapore", "Satron Handelsges",
+ "Improv Systems", "INDUSYS GmbH", "Corrent", "Infrant Technologies",
+ "Ritek Corp", "empowerTel Networks", "Hypertec", "Cavium Networks",
+ "PLX Technology", "Massana Design", "Intrinsity", "Valence Semiconductor",
+ "Terawave Communications", "IceFyre Semiconductor", "Primarion", "Picochip Designs Ltd",
+ "Silverback Systems", "Jade Star Technologies", "Pijnenburg Securealink",
+ "takeMS - Ultron AG (former Memorysolution GmbH)", "Cambridge Silicon Radio",
+ "Swissbit", "Nazomi Communications", "eWave System",
+ "Rockwell Collins", "Picocel Co., Ltd.", "Alphamosaic Ltd", "Sandburst",
+ "SiCon Video", "NanoAmp Solutions", "Ericsson Technology", "PrairieComm",
+ "Mitac International", "Layer N Networks", "MtekVision", "Allegro Networks",
+ "Marvell Semiconductors", "Netergy Microelectronic", "NVIDIA", "Internet Machines",
+ "Peak Electronics", "Litchfield Communication", "Accton Technology", "Teradiant Networks",
+ "Scaleo Chip (former Europe Technologies)", "Cortina Systems", "RAM Components", "Raqia Networks",
+ "ClearSpeed", "Matsushita Battery", "Xelerated", "SimpleTech",
+ "Utron Technology", "Astec International", "AVM gmbH", "Redux Communications",
+ "Dot Hill Systems", "TeraChip"},
+{"T-RAM Incorporated", "Innovics Wireless", "Teknovus", "KeyEye Communications",
+ "Runcom Technologies", "RedSwitch", "Dotcast", "Silicon Mountain Memory",
+ "Signia Technologies", "Pixim", "Galazar Networks", "White Electronic Designs",
+ "Patriot Scientific", "Neoaxiom Corporation", "3Y Power Technology", "Scaleo Chip (former Europe Technologies)",
+ "Potentia Power Systems", "C-guys Incorporated", "Digital Communications Technology Incorporated", "Silicon-Based Technology",
+ "Fulcrum Microsystems", "Positivo Informatica Ltd", "XIOtech Corporation", "PortalPlayer",
+ "Zhiying Software", "Parker Vision, Inc. (former Direct2Data)", "Phonex Broadband", "Skyworks Solutions",
+ "Entropic Communications", "Pacific Force Technology", "Zensys A/S", "Legend Silicon Corp.",
+ "sci-worx GmbH", "SMSC (former Oasis Silicon Systems)", "Renesas Electronics (former Renesas Technology)", "Raza Microelectronics",
+ "Phyworks", "MediaTek", "Non-cents Productions", "US Modular",
+ "Wintegra Ltd", "Mathstar", "StarCore", "Oplus Technologies",
+ "Mindspeed", "Just Young Computer", "Radia Communications", "OCZ",
+ "Emuzed", "LOGIC Devices", "Inphi Corporation", "Quake Technologies",
+ "Vixel", "SolusTek", "Kongsberg Maritime", "Faraday Technology",
+ "Altium Ltd.", "Insyte", "ARM Ltd.", "DigiVision",
+ "Vativ Technologies", "Endicott Interconnect Technologies", "Pericom", "Bandspeed",
+ "LeWiz Communications", "CPU Technology", "Ramaxel Technology", "DSP Group",
+ "Axis Communications", "Legacy Electronics", "Chrontel", "Powerchip Semiconductor",
+ "MobilEye Technologies", "Excel Semiconductor", "A-DATA Technology", "VirtualDigm",
+ "G.Skill Intl", "Quanta Computer", "Yield Microelectronics", "Afa Technologies",
+ "KINGBOX Technology Co. Ltd.", "Ceva", "iStor Networks", "Advance Modules",
+ "Microsoft", "Open-Silicon", "Goal Semiconductor", "ARC International",
+ "Simmtec", "Metanoia", "Key Stream", "Lowrance Electronics",
+ "Adimos", "SiGe Semiconductor", "Fodus Communications", "Credence Systems Corp.",
+ "Genesis Microchip Inc.", "Vihana, Inc.", "WIS Technologies", "GateChange Technologies",
+ "High Density Devices AS", "Synopsys", "Gigaram", "Enigma Semiconductor Inc.",
+ "Century Micro Inc.", "Icera Semiconductor", "Mediaworks Integrated Systems", "O'Neil Product Development",
+ "Supreme Top Technology Ltd.", "MicroDisplay Corporation", "Team Group Inc.", "Sinett Corporation",
+ "Toshiba Corporation", "Tensilica", "SiRF Technology", "Bacoc Inc.",
+ "SMaL Camera Technologies", "Thomson SC", "Airgo Networks", "Wisair Ltd.",
+ "SigmaTel", "Arkados", "Compete IT gmbH Co. KG", "Eudar Technology Inc.",
+ "Focus Enhancements", "Xyratex"},
+{"Specular Networks", "Patriot Memory", "U-Chip Technology Corp.", "Silicon Optix",
+ "Greenfield Networks", "CompuRAM GmbH", "Stargen, Inc.", "NetCell Corporation",
+ "Excalibrus Technologies Ltd", "SCM Microsystems", "Xsigo Systems, Inc.", "CHIPS & Systems Inc",
+ "Tier 1 Multichip Solutions", "CWRL Labs", "Teradici", "Gigaram, Inc.",
+ "g2 Microsystems", "PowerFlash Semiconductor", "P.A. Semi, Inc.", "NovaTech Solutions, S.A.",
+ "c2 Microsystems, Inc.", "Level5 Networks", "COS Memory AG", "Innovasic Semiconductor",
+ "02IC Co. Ltd", "Tabula, Inc.", "Crucial Technology", "Chelsio Communications",
+ "Solarflare Communications", "Xambala Inc.", "EADS Astrium", "Terra Semiconductor Inc. (former ATO Semicon Co. Ltd.)",
+ "Imaging Works, Inc.", "Astute Networks, Inc.", "Tzero", "Emulex",
+ "Power-One", "Pulse~LINK Inc.", "Hon Hai Precision Industry", "White Rock Networks Inc.",
+ "Telegent Systems USA, Inc.", "Atrua Technologies, Inc.", "Acbel Polytech Inc.",
+ "eRide Inc.","ULi Electronics Inc.", "Magnum Semiconductor Inc.", "neoOne Technology, Inc.",
+ "Connex Technology, Inc.", "Stream Processors, Inc.", "Focus Enhancements", "Telecis Wireless, Inc.",
+ "uNav Microelectronics", "Tarari, Inc.", "Ambric, Inc.", "Newport Media, Inc.", "VMTS",
+ "Enuclia Semiconductor, Inc.", "Virtium Technology Inc.", "Solid State System Co., Ltd.", "Kian Tech LLC",
+ "Artimi", "Power Quotient International", "Avago Technologies", "ADTechnology", "Sigma Designs",
+ "SiCortex, Inc.", "Ventura Technology Group", "eASIC", "M.H.S. SAS", "Micro Star International",
+ "Rapport Inc.", "Makway International", "Broad Reach Engineering Co.",
+ "Semiconductor Mfg Intl Corp", "SiConnect", "FCI USA Inc.", "Validity Sensors",
+ "Coney Technology Co. Ltd.", "Spans Logic", "Neterion Inc.", "Qimonda",
+ "New Japan Radio Co. Ltd.", "Velogix", "Montalvo Systems", "iVivity Inc.", "Walton Chaintech",
+ "AENEON", "Lorom Industrial Co. Ltd.", "Radiospire Networks", "Sensio Technologies, Inc.",
+ "Nethra Imaging", "Hexon Technology Pte Ltd", "CompuStocx (CSX)", "Methode Electronics, Inc.",
+ "Connect One Ltd.", "Opulan Technologies", "Septentrio NV", "Goldenmars Technology Inc.",
+ "Kreton Corporation", "Cochlear Ltd.", "Altair Semiconductor", "NetEffect, Inc.",
+ "Spansion, Inc.", "Taiwan Semiconductor Mfg", "Emphany Systems Inc.",
+ "ApaceWave Technologies", "Mobilygen Corporation", "Tego", "Cswitch Corporation",
+ "Haier (Beijing) IC Design Co.", "MetaRAM", "Axel Electronics Co. Ltd.", "Tilera Corporation",
+ "Aquantia", "Vivace Semiconductor", "Redpine Signals", "Octalica", "InterDigital Communications",
+ "Avant Technology", "Asrock, Inc.", "Availink", "Quartics, Inc.", "Element CXI",
+ "Innovaciones Microelectronicas", "VeriSilicon Microelectronics", "W5 Networks"},
+{"MOVEKING", "Mavrix Technology, Inc.", "CellGuide Ltd.", "Faraday Technology",
+ "Diablo Technologies, Inc.", "Jennic", "Octasic", "Molex Incorporated", "3Leaf Networks",
+ "Bright Micron Technology", "Netxen", "NextWave Broadband Inc.", "DisplayLink", "ZMOS Technology",
+ "Tec-Hill", "Multigig, Inc.", "Amimon", "Euphonic Technologies, Inc.", "BRN Phoenix",
+ "InSilica", "Ember Corporation", "Avexir Technologies Corporation", "Echelon Corporation",
+ "Edgewater Computer Systems", "XMOS Semiconductor Ltd.", "GENUSION, Inc.", "Memory Corp NV",
+ "SiliconBlue Technologies", "Rambus Inc.", "Andes Technology Corporation", "Coronis Systems",
+ "Achronix Semiconductor", "Siano Mobile Silicon Ltd.", "Semtech Corporation", "Pixelworks Inc.",
+ "Gaisler Research AB", "Teranetics", "Toppan Printing Co. Ltd.", "Kingxcon",
+ "Silicon Integrated Systems", "I-O Data Device, Inc.", "NDS Americas Inc.", "Solomon Systech Limited",
+ "On Demand Microelectronics", "Amicus Wireless Inc.", "SMARDTV SNC", "Comsys Communication Ltd.",
+ "Movidia Ltd.", "Javad GNSS, Inc.", "Montage Technology Group", "Trident Microsystems", "Super Talent",
+ "Optichron, Inc.", "Future Waves UK Ltd.", "SiBEAM, Inc.", "Inicore, Inc.", "Virident Systems",
+ "M2000, Inc.", "ZeroG Wireless, Inc.", "Gingle Technology Co. Ltd.", "Space Micro Inc.", "Wilocity",
+ "Novafora, Inc.", "iKoa Corporation", "ASint Technology", "Ramtron", "Plato Networks Inc.",
+ "IPtronics AS", "Infinite-Memories", "Parade Technologies Inc.", "Dune Networks",
+ "GigaDevice Semiconductor", "Modu Ltd.", "CEITEC", "Northrop Grumman", "XRONET Corporation",
+ "Sicon Semiconductor AB", "Atla Electronics Co. Ltd.", "TOPRAM Technology", "Silego Technology Inc.",
+ "Kinglife", "Ability Industries Ltd.", "Silicon Power Computer & Communications",
+ "Augusta Technology, Inc.", "Nantronics Semiconductors", "Hilscher Gesellschaft", "Quixant Ltd.",
+ "Percello Ltd.", "NextIO Inc.", "Scanimetrics Inc.", "FS-Semi Company Ltd.", "Infinera Corporation",
+ "SandForce Inc.", "Lexar Media", "Teradyne Inc.", "Memory Exchange Corp.", "Suzhou Smartek Electronics",
+ "Avantium Corporation", "ATP Electronics Inc.", "Valens Semiconductor Ltd", "Agate Logic, Inc.",
+ "Netronome", "Zenverge, Inc.", "N-trig Ltd", "SanMax Technologies Inc.", "Contour Semiconductor Inc.",
+ "TwinMOS", "Silicon Systems, Inc.", "V-Color Technology Inc.", "Certicom Corporation", "JSC ICC Milandr",
+ "PhotoFast Global Inc.", "InnoDisk Corporation", "Muscle Power", "Energy Micro", "Innofidei",
+ "CopperGate Communications", "Holtek Semiconductor Inc.", "Myson Century, Inc.", "FIDELIX",
+ "Red Digital Cinema", "Densbits Technology", "Zempro", "MoSys", "Provigent", "Triad Semiconductor, Inc."},
+{"Siklu Communication Ltd.", "A Force Manufacturing Ltd.", "Strontium", "Abilis Systems", "Siglead, Inc.",
+ "Ubicom, Inc.", "Unifosa Corporation", "Stretch, Inc.", "Lantiq Deutschland GmbH", "Visipro",
+ "EKMemory", "Microelectronics Institute ZTE", "Cognovo Ltd.", "Carry Technology Co. Ltd.", "Nokia",
+ "King Tiger Technology", "Sierra Wireless", "HT Micron", "Albatron Technology Co. Ltd.",
+ "Leica Geosystems AG", "BroadLight", "AEXEA", "ClariPhy Communications, Inc.", "Green Plug",
+ "Design Art Networks", "Mach Xtreme Technology Ltd.", "ATO Solutions Co. Ltd.", "Ramsta",
+ "Greenliant Systems, Ltd.", "Teikon", "Antec Hadron", "NavCom Technology, Inc.",
+ "Shanghai Fudan Microelectronics", "Calxeda, Inc.", "JSC EDC Electronics", "Kandit Technology Co. Ltd.",
+ "Ramos Technology", "Goldenmars Technology", "XeL Technology Inc.", "Newzone Corporation",
+ "ShenZhen MercyPower Tech", "Nanjing Yihuo Technology", "Nethra Imaging Inc.", "SiTel Semiconductor BV",
+ "SolidGear Corporation", "Topower Computer Ind Co Ltd.", "Wilocity", "Profichip GmbH",
+ "Gerad Technologies", "Ritek Corporation", "Gomos Technology Limited", "Memoright Corporation",
+ "D-Broad, Inc.", "HiSilicon Technologies", "Syndiant Inc.", "Enverv Inc.", "Cognex",
+ "Xinnova Technology Inc.", "Ultron AG", "Concord Idea Corporation", "AIM Corporation",
+ "Lifetime Memory Products", "Ramsway", "Recore Systems BV", "Haotian Jinshibo Science Tech",
+ "Being Advanced Memory", "Adesto Technologies", "Giantec Semiconductor, Inc.", "HMD Electronics AG",
+ "Gloway International (HK)", "Kingcore", "Anucell Technology Holding",
+ "Accord Software & Systems Pvt. Ltd.", "Active-Semi Inc.", "Denso Corporation", "TLSI Inc.",
+ "Shenzhen Daling Electronic Co. Ltd.", "Mustang", "Orca Systems", "Passif Semiconductor",
+ "GigaDevice Semiconductor (Beijing) Inc.", "Memphis Electronic", "Beckhoff Automation GmbH",
+ "Harmony Semiconductor Corp (former ProPlus Design Solutions)", "Air Computers SRL", "TMT Memory",
+ "Eorex Corporation", "Xingtera", "Netsol", "Bestdon Technology Co. Ltd.", "Baysand Inc.",
+ "Uroad Technology Co. Ltd. (former Triple Grow Industrial Ltd.)", "Wilk Elektronik S.A.",
+ "AAI", "Harman", "Berg Microelectronics Inc.", "ASSIA, Inc.", "Visiontek Products LLC",
+ "OCMEMORY", "Welink Solution Inc.", "Shark Gaming", "Avalanche Technology",
+ "R&D Center ELVEES OJSC", "KingboMars Technology Co. Ltd.",
+ "High Bridge Solutions Industria Eletronica", "Transcend Technology Co. Ltd.",
+ "Everspin Technologies", "Hon-Hai Precision", "Smart Storage Systems", "Toumaz Group",
+ "Zentel Electronics Corporation", "Panram International Corporation",
+ "Silicon Space Technology"}
+};
diff --git a/modules/devices/storage.c b/modules/devices/storage.c
new file mode 100644
index 00000000..bbf9b195
--- /dev/null
+++ b/modules/devices/storage.c
@@ -0,0 +1,820 @@
+/*
+ * HardInfo - Displays System Information
+ * Copyright (C) 2003-2006 L. A. F. Pereira <l@tia.mat.br>
+ * modified by Ondrej Čerman (2019-2021)
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, version 2 or later.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#include <string.h>
+
+#include "hardinfo.h"
+#include "devices.h"
+#include "udisks2_util.h"
+#include "storage_util.h"
+
+#define UNKIFNULL_AC(f) (f != NULL) ? f : _("(Unknown)");
+
+gchar *storage_icons = NULL;
+
+gchar *nvme_pci_sections(pcid *p) {
+ const gchar *vendor, *svendor, *product, *sproduct;
+
+ if (!p) return NULL;
+
+ vendor = UNKIFNULL_AC(p->vendor_id_str);
+ svendor = UNKIFNULL_AC(p->sub_vendor_id_str);
+ product = UNKIFNULL_AC(p->device_id_str);
+ sproduct = UNKIFNULL_AC(p->sub_device_id_str);
+
+ gchar *vendor_device_str;
+ if (p->vendor_id == p->sub_vendor_id && p->device_id == p->sub_device_id) {
+ vendor_device_str = g_strdup_printf("[%s]\n"
+ /* Vendor */ "$^$%s=[%04x] %s\n"
+ /* Device */ "%s=[%04x] %s\n",
+ _("NVMe Controller"),
+ _("Vendor"), p->vendor_id, vendor,
+ _("Device"), p->device_id, product);
+ } else {
+ vendor_device_str = g_strdup_printf("[%s]\n"
+ /* Vendor */ "$^$%s=[%04x] %s\n"
+ /* Device */ "%s=[%04x] %s\n"
+ /* Sub-device vendor */ "$^$%s=[%04x] %s\n"
+ /* Sub-device */ "%s=[%04x] %s\n",
+ _("NVMe Controller"),
+ _("Vendor"), p->vendor_id, vendor,
+ _("Device"), p->device_id, product,
+ _("SVendor"), p->sub_vendor_id, svendor,
+ _("SDevice"), p->sub_device_id, sproduct);
+ }
+
+ gchar *pcie_str;
+ if (p->pcie_width_curr) {
+ pcie_str = g_strdup_printf("[%s]\n"
+ /* Addy */ "%s=PCI/%s\n"
+ /* Width (max) */ "%s=x%u\n"
+ /* Speed (max) */ "%s=%0.1f %s\n",
+ _("PCI Express"),
+ _("Location"), p->slot_str,
+ _("Maximum Link Width"), p->pcie_width_max,
+ _("Maximum Link Speed"), p->pcie_speed_max, _("GT/s") );
+ } else
+ pcie_str = strdup("");
+
+ gchar *ret = g_strdup_printf("%s%s", vendor_device_str, pcie_str);
+ g_free(vendor_device_str);
+ g_free(pcie_str);
+ return ret;
+}
+
+gboolean __scan_udisks2_devices(void) {
+ GSList *node, *drives;
+ u2driveext *ext;
+ udiskd *disk;
+ udiskp *part;
+ udisksa *attrib;
+ gchar *udisks2_storage_list = NULL, *features = NULL, *moreinfo = NULL;
+ gchar *devid, *size, *tmp = NULL, *media_comp = NULL, *ven_tag = NULL;
+ const gchar *url, *media_label, *alabel, *icon, *media_curr = NULL;
+ int n = 0, i, j, m;
+
+ // http://storaged.org/doc/udisks2-api/latest/gdbus-org.freedesktop.UDisks2.Drive.html#gdbus-property-org-freedesktop-UDisks2-Drive.MediaCompatibility
+ static struct {
+ char *media;
+ char *label;
+ char *icon;
+ } media_info[] = {
+ { "thumb", "Thumb-drive", "usbfldisk" },
+ { "flash", "Flash Card", "usbfldisk" },
+ { "flash_cf", "CompactFlash", "usbfldisk" },
+ { "flash_ms", "MemoryStick", "usbfldisk" },
+ { "flash_sm", "SmartMedia", "usbfldisk" },
+ { "flash_sd", "SD", "usbfldisk" },
+ { "flash_sdhc", "SDHC", "usbfldisk" },
+ { "flash_sdxc", "SDXC", "usbfldisk" },
+ { "flash_mmc", "MMC", "usbfldisk" },
+ { "floppy", "Floppy Disk", "media-floppy" },
+ { "floppy_zip", "Zip Disk", "media-floppy" },
+ { "floppy_jaz", "Jaz Disk", "media-floppy" },
+ { "optical", "Optical Disc", "cdrom" },
+ { "optical_cd", "CD-ROM", "cdrom" },
+ { "optical_cd_r", "CD-R", "cdrom" },
+ { "optical_cd_rw", "CD-RW", "cdrom" },
+ { "optical_dvd", "DVD-ROM", "cdrom" },
+ { "optical_dvd_r", "DVD-R", "cdrom" },
+ { "optical_dvd_rw", "DVD-RW", "cdrom" },
+ { "optical_dvd_ram", "DVD-RAM", "cdrom" },
+ { "optical_dvd_plus_r", "DVD+R" , "cdrom" },
+ { "optical_dvd_plus_rw", "DVD+RW" , "cdrom" },
+ { "optical_dvd_plus_r_dl", "DVD+R DL", "cdrom" },
+ { "optical_dvd_plus_rw_dl", "DVD+RW DL", "cdrom" },
+ { "optical_bd", "BD-ROM", "cdrom" },
+ { "optical_bd_r", "BD-R", "cdrom" },
+ { "optical_bd_re", "BD-RE", "cdrom" },
+ { "optical_hddvd", "HD DVD-ROM", "cdrom" },
+ { "optical_hddvd_r", "HD DVD-R", "cdrom" },
+ { "optical_hddvd_rw", "HD DVD-RW", "cdrom" },
+ { "optical_mo", "MO Disc", "cdrom" },
+ { "optical_mrw", "MRW Media", "cdrom" },
+ { "optical_mrw_w", "MRW Media (write)", "cdrom" },
+ { NULL, NULL }
+ };
+
+ struct {
+ char *identifier;
+ char *label;
+ } smart_attrib_info[] = {
+ { "raw-read-error-rate", _("Read Error Rate" ) },
+ { "throughput-performance", _("Throughput Performance") },
+ { "spin-up-time", _("Spin-Up Time") },
+ { "start-stop-count", _("Start/Stop Count") },
+ { "reallocated-sector-count", _("Reallocated Sector Count") },
+ { "read-channel-margin", _("Read Channel Margin") },
+ { "seek-error-rate", _("Seek Error Rate") },
+ { "seek-time-performance", _("Seek Timer Performance") },
+ { "power-on-hours", _("Power-On Hours") },
+ { "spin-retry-count", _("Spin Retry Count") },
+ { "calibration-retry-count", _("Calibration Retry Count") },
+ { "power-cycle-count", _("Power Cycle Count") },
+ { "read-soft-error-rate", _("Soft Read Error Rate") },
+ { "runtime-bad-block-total", _("Runtime Bad Block") },
+ { "end-to-end-error", _("End-to-End error") },
+ { "reported-uncorrect", _("Reported Uncorrectable Errors") },
+ { "command-timeout", _("Command Timeout") },
+ { "high-fly-writes", _("High Fly Writes") },
+ { "airflow-temperature-celsius", _("Airflow Temperature") },
+ { "g-sense-error-rate", _("G-sense Error Rate") },
+ { "power-off-retract-count", _("Power-off Retract Count") },
+ { "load-cycle-count", _("Load Cycle Count") },
+ { "temperature-celsius-2", _("Temperature") },
+ { "hardware-ecc-recovered", _("Hardware ECC Recovered") },
+ { "reallocated-event-count", _("Reallocation Event Count") },
+ { "current-pending-sector", _("Current Pending Sector Count") },
+ { "offline-uncorrectable", _("Uncorrectable Sector Count") },
+ { "udma-crc-error-count", _("UltraDMA CRC Error Count") },
+ { "multi-zone-error-rate", _("Multi-Zone Error Rate") },
+ { "soft-read-error-rate", _("Soft Read Error Rate") },
+ { "run-out-cancel", _("Run Out Cancel") },
+ { "flying-height", _("Flying Height") },
+ { "spin-high-current", _("Spin High Current") },
+ { "spin-buzz", _("Spin Buzz") },
+ { "offline-seek-performance", _("Offline Seek Performance") },
+ { "disk-shift", _("Disk Shift") },
+ { "g-sense-error-rate-2", _("G-Sense Error Rate") },
+ { "loaded-hours", _("Loaded Hours") },
+ { "load-retry-count", _("Load/Unload Retry Count") },
+ { "load-friction", _("Load Friction") },
+ { "load-cycle-count-2", _("Load/Unload Cycle Count") },
+ { "load-in-time", _("Load-in time") },
+ { "torq-amp-count", _("Torque Amplification Count") },
+ { "power-off-retract-count-2", _("Power-Off Retract Count") },
+ { "head-amplitude", _("GMR Head Amplitude") },
+ { "temperature-celsius", _("Temperature") },
+ { "endurance-remaining", _("Endurance Remaining") },
+ { "power-on-seconds-2", _("Power-On Hours") },
+ { "good-block-rate", _("Good Block Rate") },
+ { "head-flying-hours", _("Head Flying Hours") },
+ { "read-error-retry-rate", _("Read Error Retry Rate") },
+ { "total-lbas-written", _("Total LBAs Written") },
+ { "total-lbas-read", _("Total LBAs Read") },
+ { "wear-leveling-count", _("Wear leveling Count") },
+ { "used-reserved-blocks-total", _("Total Used Reserved Block Count") },
+ { "program-fail-count-total", _("Total Program Fail Count") },
+ { "erase-fail-count-total", _("Total Erase Fail Count") },
+ { "available-reserved-space", _("Available Reserved Space") },
+ { "program-fail-count", _("Program Fail Count") },
+ { "erase-fail-count", _("Erase Fail Count") },
+ { "ta-increase-count", _("TA Counter Increased") },
+ { "unused-reserved-blocks", _("Total Unused Reserved Block Count") },
+ { NULL, NULL }
+ };
+
+ moreinfo_del_with_prefix("DEV:UDISKS");
+ udisks2_storage_list = g_strdup(_("\n[UDisks2]\n"));
+
+ drives = get_udisks2_drives_ext();
+ for (node = drives; node != NULL; node = node->next) {
+ ext = (u2driveext *)node->data;
+ disk = ext->d;
+ devid = g_strdup_printf("UDISKS%d", n++);
+
+ icon = NULL;
+
+ media_curr = disk->media;
+ if (disk->media){
+ for (j = 0; media_info[j].media != NULL; j++) {
+ if (g_strcmp0(disk->media, media_info[j].media) == 0) {
+ media_curr = media_info[j].label;
+ break;
+ }
+ }
+ }
+
+ if (disk->media_compatibility){
+ for (i = 0; disk->media_compatibility[i] != NULL; i++){
+ media_label = disk->media_compatibility[i];
+
+ for (j = 0; media_info[j].media != NULL; j++) {
+ if (g_strcmp0(disk->media_compatibility[i], media_info[j].media) == 0) {
+ media_label = media_info[j].label;
+ if (icon == NULL)
+ icon = media_info[j].icon;
+ break;
+ }
+ }
+
+ if (media_comp == NULL){
+ media_comp = g_strdup(media_label);
+ }
+ else{
+ media_comp = h_strdup_cprintf(", %s", media_comp, media_label);
+ }
+ }
+ }
+ if (icon == NULL && disk->ejectable && g_strcmp0(disk->connection_bus, "usb") == 0) {
+ icon = "usbfldisk";
+ }
+ if (icon == NULL){
+ icon = "hdd";
+ }
+
+ size = size_human_readable((gfloat) disk->size);
+ ven_tag = vendor_list_ribbon(ext->vendors, params.fmt_opts);
+
+ udisks2_storage_list = h_strdup_cprintf("$%s$%s=%s|%s %s\n", udisks2_storage_list, devid, disk->block_dev, size, ven_tag ? ven_tag : "", disk->model);
+ storage_icons = h_strdup_cprintf("Icon$%s$%s=%s.png\n", storage_icons, devid, disk->model, icon);
+ features = h_strdup_cprintf("%s", features, disk->removable ? _("Removable"): _("Fixed"));
+
+ if (disk->ejectable) {
+ features = h_strdup_cprintf(", %s", features, _("Ejectable"));
+ }
+ if (disk->smart_supported) {
+ features = h_strdup_cprintf(", %s", features, _("Self-monitoring (S.M.A.R.T.)"));
+ }
+ if (disk->pm_supported) {
+ features = h_strdup_cprintf(", %s", features, _("Power Management"));
+ }
+ if (disk->apm_supported) {
+ features = h_strdup_cprintf(", %s", features, _("Advanced Power Management"));
+ }
+ if (disk->aam_supported) {
+ features = h_strdup_cprintf(", %s", features, _("Automatic Acoustic Management"));
+ }
+
+ moreinfo = g_strdup_printf(_("[Drive Information]\n"
+ "Model=%s\n"),
+ disk->model);
+
+ if (disk->vendor && *disk->vendor) {
+ moreinfo = h_strdup_cprintf("$^$%s=%s\n",
+ moreinfo,
+ _("Vendor"), disk->vendor);
+ }
+
+ moreinfo = h_strdup_cprintf(_("Revision=%s\n"
+ "Block Device=%s\n"
+ "Serial=%s\n"
+ "Size=%s\n"
+ "Features=%s\n"),
+ moreinfo,
+ disk->revision,
+ disk->block_dev,
+ disk->serial,
+ size,
+ features);
+ g_free(size);
+ g_free(ven_tag);
+
+ if (disk->rotation_rate > 0) {
+ moreinfo = h_strdup_cprintf(_("Rotation Rate=%d RPM\n"), moreinfo, disk->rotation_rate);
+ }
+ if (media_comp || media_curr) {
+ moreinfo = h_strdup_cprintf(_("Media=%s\n"
+ "Media compatibility=%s\n"),
+ moreinfo,
+ media_curr ? media_curr : _("(None)"),
+ media_comp ? media_comp : _("(Unknown)"));
+ }
+ if (disk->connection_bus && strlen(disk->connection_bus) > 0) {
+ moreinfo = h_strdup_cprintf(_("Connection bus=%s\n"), moreinfo, disk->connection_bus);
+ }
+
+ tmp = NULL;
+ if (disk->wwid) {
+ m = strlen(disk->wwid);
+ if (m > 2 && m % 2 == 0){
+ for (j = 4; j < m; j = j + 2) {
+ tmp = h_strdup_cprintf("%s%c%c", tmp, j > 4 ? "-": "", disk->wwid[j], disk->wwid[j+1]);
+ }
+ }
+ moreinfo = h_strdup_cprintf("%s=%s\n", moreinfo,
+ g_str_has_prefix(disk->wwid, "nna.") ? _("WWN"):
+ (g_str_has_prefix(disk->wwid, "eui.") ? _("EUI "): "Unknown ID"),
+ tmp);
+ g_free(tmp);
+ }
+ else{
+ moreinfo = h_strdup_cprintf("%s=%s\n", moreinfo, _("WWN / EUI"), _("(None)"));
+ }
+
+ if (ext->wwid_oui.oui) {
+ moreinfo = h_strdup_cprintf(_("$^$%s=[%s] %s\n"),
+ moreinfo,
+ _("IEEE OUI"), ext->wwid_oui.oui,
+ ext->wwid_oui.vendor ?
+ ext->wwid_oui.vendor : _("(Unknown)"));
+ }
+
+ if (ext->nvme_controller) {
+ gchar *nvme = nvme_pci_sections(ext->nvme_controller);
+ if (nvme)
+ moreinfo = h_strdup_cprintf("%s", moreinfo, nvme);
+ g_free(nvme);
+ }
+ if (disk->smart_enabled) {
+ moreinfo = h_strdup_cprintf(_("[Self-monitoring (S.M.A.R.T.)]\n"
+ "Status=%s\n"
+ "Bad Sectors=%" G_GINT64_FORMAT "\n"
+ "Power on time=%" G_GUINT64_FORMAT " days %" G_GUINT64_FORMAT " hours\n"
+ "Temperature=%d°C\n"),
+ moreinfo,
+ disk->smart_failing ? _("Failing"): _("OK"),
+ disk->smart_bad_sectors,
+ disk->smart_poweron/(60*60*24), (disk->smart_poweron/60/60) % 24,
+ disk->smart_temperature);
+
+ if (disk->smart_attributes != NULL) {
+ moreinfo = h_strdup_cprintf(_("[S.M.A.R.T. Attributes]\n"
+ "Attribute=<tt>Value / Normalized / Worst / Threshold</tt>\n"),
+ moreinfo);
+
+ attrib = disk->smart_attributes;
+
+ while (attrib != NULL){
+ tmp = g_strdup("");
+
+ switch (attrib->interpreted_unit){
+ case UDSK_INTPVAL_SKIP:
+ tmp = h_strdup_cprintf("-", tmp);
+ break;
+ case UDSK_INTPVAL_MILISECONDS:
+ tmp = h_strdup_cprintf("%" G_GINT64_FORMAT " ms", tmp, attrib->interpreted);
+ break;
+ case UDSK_INTPVAL_HOURS:
+ tmp = h_strdup_cprintf("%" G_GINT64_FORMAT " h", tmp, attrib->interpreted);
+ break;
+ case UDSK_INTPVAL_CELSIUS:
+ tmp = h_strdup_cprintf("%" G_GINT64_FORMAT "°C", tmp, attrib->interpreted);
+ break;
+ case UDSK_INTPVAL_SECTORS:
+ tmp = h_strdup_cprintf(ngettext("%" G_GINT64_FORMAT " sector",
+ "%" G_GINT64_FORMAT " sectors", attrib->interpreted),
+ tmp, attrib->interpreted);
+ break;
+ case UDSK_INTPVAL_DIMENSIONLESS:
+ default:
+ tmp = h_strdup_cprintf("%" G_GINT64_FORMAT, tmp, attrib->interpreted);
+ break;
+ }
+
+ // pad spaces to next col
+ j = g_utf8_strlen(tmp, -1);
+ if (j < 13) tmp = h_strdup_cprintf("%*c", tmp, 13 - j, ' ');
+
+ if (attrib->value != -1)
+ tmp = h_strdup_cprintf("%-13d", tmp, attrib->value);
+ else
+ tmp = h_strdup_cprintf("%-13s", tmp, "???");
+
+ if (attrib->worst != -1)
+ tmp = h_strdup_cprintf("%-8d", tmp, attrib->worst);
+ else
+ tmp = h_strdup_cprintf("%-8s", tmp, "???");
+
+ if (attrib->threshold != -1)
+ tmp = h_strdup_cprintf("%d", tmp, attrib->threshold);
+ else
+ tmp = h_strdup_cprintf("???", tmp);
+
+
+ alabel = attrib->identifier;
+ for (i = 0; smart_attrib_info[i].identifier != NULL; i++) {
+ if (g_strcmp0(attrib->identifier, smart_attrib_info[i].identifier) == 0) {
+ alabel = smart_attrib_info[i].label;
+ break;
+ }
+ }
+
+ moreinfo = h_strdup_cprintf(_("(%d) %s=<tt>%s</tt>\n"),
+ moreinfo,
+ attrib->id, alabel, tmp);
+ g_free(tmp);
+ attrib = attrib->next;
+ }
+ }
+ }
+ if (disk->partition_table || disk->partitions) {
+ moreinfo = h_strdup_cprintf(_("[Partition table]\n"
+ "Type=%s\n"),
+ moreinfo,
+ disk->partition_table ? disk->partition_table : _("(Unknown)"));
+
+ if (disk->partitions != NULL) {
+ part = disk->partitions;
+ while (part != NULL){
+
+ tmp = size_human_readable((gfloat) part->size);
+ if (part->label) {
+ tmp = h_strdup_cprintf(" - %s", tmp, part->label);
+ }
+ if (part->type && part->version) {
+ tmp = h_strdup_cprintf(" (%s %s)", tmp, part->type, part->version);
+ }
+ else if (part->type) {
+ tmp = h_strdup_cprintf(" (%s)", tmp, part->type);
+ }
+ moreinfo = h_strdup_cprintf(_("Partition %s=%s\n"),
+ moreinfo,
+ part->block, tmp);
+ g_free(tmp);
+ part = part->next;
+ }
+ }
+ }
+
+ moreinfo_add_with_prefix("DEV", devid, moreinfo);
+ g_free(devid);
+ g_free(features);
+ g_free(media_comp);
+ media_comp = NULL;
+
+ features = NULL;
+ moreinfo = NULL;
+ devid = NULL;
+
+ u2driveext_free(ext);
+ }
+ g_slist_free(drives);
+
+ if (n) {
+ storage_list = h_strconcat(storage_list, udisks2_storage_list, NULL);
+ g_free(udisks2_storage_list);
+ return TRUE;
+ }
+
+ g_free(udisks2_storage_list);
+ return FALSE;
+}
+
+/* SCSI support by Pascal F.Martin <pascalmartin@earthlink.net> */
+void __scan_scsi_devices(void)
+{
+ FILE *proc_scsi;
+ gchar buffer[256], *buf;
+ gint n = 0;
+ gint scsi_controller = 0;
+ gint scsi_channel = 0;
+ gint scsi_id = 0 ;
+ gint scsi_lun = 0;
+ gchar *vendor = NULL, *revision = NULL, *model = NULL;
+ gchar *scsi_storage_list;
+
+ /* remove old devices from global device table */
+ moreinfo_del_with_prefix("DEV:SCSI");
+
+ scsi_storage_list = g_strdup(_("\n[SCSI Disks]\n"));
+
+ int otype = 0;
+ if (proc_scsi = fopen("/proc/scsi/scsi", "r")) {
+ otype = 1;
+ } else if (proc_scsi = popen("lsscsi -c", "r")) {
+ otype = 2;
+ }
+
+ if (otype) {
+ while (fgets(buffer, 256, proc_scsi)) {
+ buf = g_strstrip(buffer);
+ if (!strncmp(buf, "Host: scsi", 10)) {
+ sscanf(buf,
+ "Host: scsi%d Channel: %d Id: %d Lun: %d",
+ &scsi_controller, &scsi_channel, &scsi_id, &scsi_lun);
+
+ n++;
+ } else if (!strncmp(buf, "Vendor: ", 8)) {
+ buf[17] = '\0';
+ buf[41] = '\0';
+ buf[53] = '\0';
+
+ vendor = g_strdup(g_strstrip(buf + 8));
+ model = g_strdup_printf("%s %s", vendor, g_strstrip(buf + 24));
+ revision = g_strdup(g_strstrip(buf + 46));
+ } else if (!strncmp(buf, "Type: ", 8)) {
+ char *p;
+ gchar *type = NULL, *icon = NULL;
+
+ if (!(p = strstr(buf, "ANSI SCSI revision"))) {
+ p = strstr(buf, "ANSI SCSI revision");
+ }
+
+ if (p != NULL) {
+ while (*(--p) == ' ');
+ *(++p) = 0;
+
+ static struct {
+ char *type;
+ char *label;
+ char *icon;
+ } type2icon[] = {
+ { "Direct-Access", "Disk", "hdd"},
+ { "Sequential-Access", "Tape", "tape"},
+ { "Printer", "Printer", "lpr"},
+ { "WORM", "CD-ROM", "cdrom"},
+ { "CD-ROM", "CD-ROM", "cdrom"},
+ { "Scanner", "Scanner", "scanner"},
+ { "Flash Disk", "USB Flash Disk", "usbfldisk" },
+ { NULL, "Generic", "scsi"}
+ };
+ int i;
+
+ if (model && strstr(model, "Flash Disk")) {
+ type = "Flash Disk";
+ icon = "usbfldisk";
+ } else {
+ for (i = 0; type2icon[i].type != NULL; i++)
+ if (g_str_equal(buf + 8, type2icon[i].type))
+ break;
+
+ type = type2icon[i].label;
+ icon = type2icon[i].icon;
+ }
+ }
+
+ gchar *devid = g_strdup_printf("SCSI%d", n);
+ scsi_storage_list = h_strdup_cprintf("$%s$scsi%d=|%s\n", scsi_storage_list, devid, scsi_controller, model);
+ storage_icons = h_strdup_cprintf("Icon$%s$%s=%s.png\n", storage_icons, devid, model, icon);
+
+ gchar *strhash = g_strdup_printf(_("[Device Information]\n"
+ "Model=%s\n"), model);
+
+ strhash = h_strdup_cprintf("$^$%s=%s\n",
+ strhash,
+ _("Vendor"), model);
+
+ strhash = h_strdup_cprintf(_("Type=%s\n"
+ "Revision=%s\n"
+ "[SCSI Controller]\n"
+ "Controller=scsi%d\n"
+ "Channel=%d\n"
+ "ID=%d\n" "LUN=%d\n"),
+ strhash,
+ type,
+ revision,
+ scsi_controller,
+ scsi_channel,
+ scsi_id,
+ scsi_lun);
+
+ moreinfo_add_with_prefix("DEV", devid, strhash);
+ g_free(devid);
+
+ g_free(model);
+ g_free(revision);
+ g_free(vendor);
+
+ scsi_controller = scsi_channel = scsi_id = scsi_lun = 0;
+ }
+ }
+ if (otype == 1)
+ fclose(proc_scsi);
+ else if (otype == 2)
+ pclose(proc_scsi);
+ }
+
+ if (n) {
+ storage_list = h_strconcat(storage_list, scsi_storage_list, NULL);
+ g_free(scsi_storage_list);
+ }
+}
+
+void __scan_ide_devices(void)
+{
+ FILE *proc_ide;
+ gchar *device, *model, *media, *pgeometry = NULL, *lgeometry = NULL;
+ gchar iface;
+ gint n = 0, i = 0, cache, nn = 0;
+ gchar *capab = NULL, *speed = NULL, *driver = NULL, *ide_storage_list;
+
+ /* remove old devices from global device table */
+ moreinfo_del_with_prefix("DEV:IDE");
+
+ ide_storage_list = g_strdup(_("\n[IDE Disks]\n"));
+
+ iface = 'a';
+ for (i = 0; i <= 16; i++) {
+ device = g_strdup_printf("/proc/ide/hd%c/model", iface);
+ if (g_file_test(device, G_FILE_TEST_EXISTS)) {
+ gchar buf[128];
+
+ cache = 0;
+
+ proc_ide = fopen(device, "r");
+ if (!proc_ide)
+ continue;
+
+ (void) fgets(buf, 128, proc_ide);
+ fclose(proc_ide);
+
+ buf[strlen(buf) - 1] = 0;
+
+ model = g_strdup(buf);
+
+ g_free(device);
+
+ device = g_strdup_printf("/proc/ide/hd%c/media", iface);
+ proc_ide = fopen(device, "r");
+ if (!proc_ide) {
+ free(model);
+ continue;
+ }
+
+ (void) fgets(buf, 128, proc_ide);
+ fclose(proc_ide);
+ buf[strlen(buf) - 1] = 0;
+
+ media = g_strdup(buf);
+ if (g_str_equal(media, "cdrom")) {
+ /* obtain cd-rom drive information from cdrecord */
+ GTimer *timer;
+ gchar *tmp = g_strdup_printf("cdrecord dev=/dev/hd%c -prcap 2>/dev/stdout", iface);
+ FILE *prcap;
+
+ if ((prcap = popen(tmp, "r"))) {
+ /* we need a timeout so cdrecord does not try to get information on cd drives
+ with inserted media, which is not possible currently. half second should be
+ enough. */
+ timer = g_timer_new();
+ g_timer_start(timer);
+
+ while (fgets(buf, 128, prcap)
+ && g_timer_elapsed(timer, NULL) < 0.5) {
+ if (g_str_has_prefix(buf, " Does")) {
+ if (g_str_has_suffix(buf, "media\n")
+ && !strstr(buf, "speed")) {
+ gchar *media_type = g_strstrip(strstr(buf, "Does "));
+ gchar **ttmp = g_strsplit(media_type, " ", 0);
+
+ capab = h_strdup_cprintf("\nCan %s#%d=%s\n", capab, ttmp[1], ++nn, ttmp[2]);
+
+ g_strfreev(ttmp);
+ } else if (strstr(buf, "Buffer-Underrun-Free")) {
+ capab =
+ h_strdup_cprintf
+ ("\nSupports BurnProof=%s\n", capab, strstr(buf, "Does not") ? "No" : "Yes");
+ } else if (strstr(buf, "multi-session")) {
+ capab =
+ h_strdup_cprintf
+ ("\nCan read multi-session CDs=%s\n",
+ capab, strstr(buf, "Does not") ? "No" : "Yes");
+ } else if (strstr(buf, "audio CDs")) {
+ capab =
+ h_strdup_cprintf
+ ("\nCan play audio CDs=%s\n", capab, strstr(buf, "Does not") ? "No" : "Yes");
+ } else if (strstr(buf, "PREVENT/ALLOW")) {
+ capab =
+ h_strdup_cprintf
+ ("\nCan lock media=%s\n", capab, strstr(buf, "Does not") ? "No" : "Yes");
+ }
+ } else if ((strstr(buf, "read")
+ || strstr(buf, "write"))
+ && strstr(buf, "kB/s")) {
+ speed =
+ g_strconcat(speed ? speed : "", strreplacechr(g_strstrip(buf), ":", '='), "\n", NULL);
+ } else if (strstr(buf, "Device seems to be")) {
+ driver = g_strdup_printf(_("Driver=%s\n"), strchr(buf, ':') + 1);
+ }
+ }
+
+ pclose(prcap);
+ g_timer_destroy(timer);
+ }
+
+ g_free(tmp);
+ }
+ g_free(device);
+
+ device = g_strdup_printf("/proc/ide/hd%c/cache", iface);
+ if (g_file_test(device, G_FILE_TEST_EXISTS)) {
+ proc_ide = fopen(device, "r");
+ if (proc_ide) {
+ (void) fscanf(proc_ide, "%d", &cache);
+ fclose(proc_ide);
+ } else {
+ cache = 0;
+ }
+ }
+ g_free(device);
+
+ device = g_strdup_printf("/proc/ide/hd%c/geometry", iface);
+ if (g_file_test(device, G_FILE_TEST_EXISTS)) {
+ gchar *tmp;
+
+ proc_ide = fopen(device, "r");
+ if (proc_ide) {
+ (void) fgets(buf, 64, proc_ide);
+ for (tmp = buf; *tmp; tmp++) {
+ if (*tmp >= '0' && *tmp <= '9')
+ break;
+ }
+
+ pgeometry = g_strdup(g_strstrip(tmp));
+
+ (void) fgets(buf, 64, proc_ide);
+ for (tmp = buf; *tmp; tmp++) {
+ if (*tmp >= '0' && *tmp <= '9')
+ break;
+ }
+ lgeometry = g_strdup(g_strstrip(tmp));
+
+ fclose(proc_ide);
+ } else {
+ pgeometry = g_strdup("Unknown");
+ lgeometry = g_strdup("Unknown");
+ }
+
+ }
+ g_free(device);
+
+ n++;
+
+ gchar *devid = g_strdup_printf("IDE%d", n);
+
+ ide_storage_list = h_strdup_cprintf("$%s$hd%c=|%s\n", ide_storage_list, devid, iface, model);
+ storage_icons =
+ h_strdup_cprintf("Icon$%s$%s=%s.png\n", storage_icons,
+ devid, model, g_str_equal(media, "cdrom") ? "cdrom" : "hdd");
+
+ gchar *strhash = g_strdup_printf(_("[Device Information]\n" "Model=%s\n"),
+ model);
+
+ strhash = h_strdup_cprintf("$^$%s=%s\n",
+ strhash, _("Vendor"), model);
+
+ strhash = h_strdup_cprintf(_("Device Name=hd%c\n"
+ "Media=%s\n" "Cache=%dkb\n"), strhash, iface, media, cache);
+ if (driver) {
+ strhash = h_strdup_cprintf("%s\n", strhash, driver);
+
+ g_free(driver);
+ driver = NULL;
+ }
+
+ if (pgeometry && lgeometry) {
+ strhash = h_strdup_cprintf(_("[Geometry]\n"
+ "Physical=%s\n" "Logical=%s\n"), strhash, pgeometry, lgeometry);
+
+ g_free(pgeometry);
+ pgeometry = NULL;
+ g_free(lgeometry);
+ lgeometry = NULL;
+ }
+
+ if (capab) {
+ strhash = h_strdup_cprintf(_("[Capabilities]\n%s"), strhash, capab);
+
+ g_free(capab);
+ capab = NULL;
+ }
+
+ if (speed) {
+ strhash = h_strdup_cprintf(_("[Speeds]\n%s"), strhash, speed);
+
+ g_free(speed);
+ speed = NULL;
+ }
+
+ moreinfo_add_with_prefix("DEV", devid, strhash);
+ g_free(devid);
+ g_free(model);
+ } else {
+ g_free(device);
+ }
+
+ iface++;
+ }
+
+ if (n) {
+ storage_list = h_strconcat(storage_list, ide_storage_list, NULL);
+ g_free(ide_storage_list);
+ }
+}
diff --git a/modules/devices/usb.c b/modules/devices/usb.c
new file mode 100644
index 00000000..c6a5ab39
--- /dev/null
+++ b/modules/devices/usb.c
@@ -0,0 +1,224 @@
+/*
+ * HardInfo - Displays System Information
+ * Copyright (C) 2003-2008 L. A. F. Pereira <l@tia.mat.br>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, version 2 or later.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#include <string.h>
+
+#include "cpu_util.h"
+#include "hardinfo.h"
+#include "devices.h"
+#include "usb_util.h"
+
+gchar *usb_list = NULL;
+gchar *usb_icons = NULL;
+
+#define UNKIFNULL_AC(f) (f != NULL) ? f : _("(Unknown)")
+#define IARR_END -2
+#define IARR_ANY -1
+
+static struct {
+ int class;
+ char *icon;
+} usb_class_icons[] = {
+ { 0x1, "audio"}, /* Audio */
+ { 0x2, "modem"}, /* Communications and CDC Control */
+ { 0x3, "inputdevices"}, /* HID (Human Interface Device) */
+ { 0x6, "camera-photo"}, /* Still Imaging */
+ { 0x7, "printer"}, /* Printer */
+ { 0x8, "media-removable"}, /* Mass storage */
+ { 0x9, "usb"}, /* Hub */
+ { 0xe, "camera-web"}, /* Video */
+ {IARR_END, NULL}
+};
+
+static struct {
+ int class, subclass, protocol;
+ char *icon;
+} usb_type_icons[] = {
+ { 0x2, 0x6, IARR_ANY, "network-interface"}, /* Ethernet Networking Control Model */
+ { 0x3, 0x1, 0x1, "keyboard"}, /* Keyboard */
+ { 0x3, 0x1, 0x2, "mouse"}, /* Mouse */
+ {0xe0, 0x1, 0x1, "bluetooth"}, /* Bluetooth Programming Interface */
+ {IARR_END, IARR_END, IARR_END, NULL},
+};
+
+static const char* get_class_icon(int class){
+ int i = 0;
+ while (usb_class_icons[i].class != IARR_END) {
+ if (usb_class_icons[i].class == class) {
+ return usb_class_icons[i].icon;
+ }
+ i++;
+ }
+ return NULL;
+}
+
+static const char* get_usbif_icon(const usbi *usbif) {
+ int i = 0;
+ while (usb_type_icons[i].class != IARR_END) {
+ if (usb_type_icons[i].class == usbif->if_class && usb_type_icons[i].subclass == usbif->if_subclass &&
+ (usb_type_icons[i].protocol == IARR_ANY || usb_type_icons[i].protocol == usbif->if_protocol)) {
+
+ return usb_type_icons[i].icon;
+ }
+ i++;
+ }
+
+ return get_class_icon(usbif->if_class);
+}
+
+static const char* get_usbdev_icon(const usbd *u) {
+ const char * icon = NULL;
+ usbi *curr_if;
+
+ curr_if = u->if_list;
+ while (icon == NULL && curr_if != NULL){
+ icon = get_usbif_icon(curr_if);
+ curr_if = curr_if->next;
+ }
+
+ if (icon == NULL){
+ icon = get_class_icon(u->dev_class);
+ }
+
+ return icon;
+}
+
+static void _usb_dev(const usbd *u) {
+ gchar *name, *key, *label, *str, *speed;
+ gchar *product, *vendor, *manufacturer, *device; /* don't free */
+ gchar *interfaces = strdup("");
+ usbi *i;
+ const char* icon;
+
+ vendor = UNKIFNULL_AC(u->vendor);
+ product = UNKIFNULL_AC(u->product);
+ manufacturer = UNKIFNULL_AC(u->manufacturer);
+ device = UNKIFNULL_AC(u->device);
+
+ if (u->vendors) {
+ gchar *ribbon = vendor_list_ribbon(u->vendors, params.fmt_opts);
+ name = g_strdup_printf("%s %s", ribbon, u->product? product: device);
+ } else {
+ name = g_strdup_printf("%s %s", u->vendor? vendor: manufacturer, u->product? product: device);
+ }
+ key = g_strdup_printf("USB%03d:%03d:%03d", u->bus, u->dev, 0);
+ label = g_strdup_printf("%03d:%03d", u->bus, u->dev);
+ icon = get_usbdev_icon(u);
+
+ usb_list = h_strdup_cprintf("$%s$%s=%s\n", usb_list, key, label, name);
+ usb_icons = h_strdup_cprintf("Icon$%s$%s=%s.png\n", usb_icons, key, label, icon ? icon: "usb");
+
+ if (u->if_list != NULL) {
+ i = u->if_list;
+ while (i != NULL){
+ interfaces = h_strdup_cprintf("[%s %d %s]\n"
+ /* Class */ "%s=[%d] %s\n"
+ /* Sub-class */ "%s=[%d] %s\n"
+ /* Protocol */ "%s=[%d] %s\n"
+ /* Driver */ "%s=%s\n",
+ interfaces,
+ _("Interface"), i->if_number, i->if_label? i->if_label: "",
+ _("Class"), i->if_class, UNKIFNULL_AC(i->if_class_str),
+ _("Sub-class"), i->if_subclass, UNKIFNULL_AC(i->if_subclass_str),
+ _("Protocol"), i->if_protocol, UNKIFNULL_AC(i->if_protocol_str),
+ _("Driver"), UNKIFNULL_AC(i->driver)
+ );
+ i = i->next;
+ }
+ }
+
+ if (u->speed_mbs > 0){
+ speed = g_strdup_printf("%d %s", u->speed_mbs, _("Mb/s"));
+ }
+ else{
+ speed = g_strdup(_("Unknown"));
+ }
+
+ str = g_strdup_printf("[%s]\n"
+ /* Product */ "%s=[0x%04x] %s\n"
+ /* Vendor */ "$^$%s=[0x%04x] %s\n"
+ /* Device */ "%s=%s\n"
+ /* Manufacturer */ "$^$%s=%s\n"
+ /* Max Current */ "%s=%d %s\n"
+ /* USB Version */ "%s=%s\n"
+ /* Speed */ "%s=%s\n"
+ /* Class */ "%s=[%d] %s\n"
+ /* Sub-class */ "%s=[%d] %s\n"
+ /* Protocol */ "%s=[%d] %s\n"
+ /* Dev Version */ "%s=%s\n"
+ /* Serial */ "%s=%s\n"
+ "[%s]\n"
+ /* Bus */ "%s=%03d\n"
+ /* Device */ "%s=%03d\n"
+ /* Interfaces */ "%s",
+ _("Device Information"),
+ _("Product"), u->product_id, product,
+ _("Vendor"), u->vendor_id, vendor,
+ _("Device"), device,
+ _("Manufacturer"), manufacturer,
+ _("Max Current"), u->max_curr_ma, _("mA"),
+ _("USB Version"), u->usb_version,
+ _("Speed"), speed,
+ _("Class"), u->dev_class, UNKIFNULL_AC(u->dev_class_str),
+ _("Sub-class"), u->dev_subclass, UNKIFNULL_AC(u->dev_subclass_str),
+ _("Protocol"), u->dev_protocol, UNKIFNULL_AC(u->dev_protocol_str),
+ _("Device Version"), UNKIFNULL_AC(u->device_version),
+ _("Serial Number"), UNKIFNULL_AC(u->serial),
+ _("Connection"),
+ _("Bus"), u->bus,
+ _("Device"), u->dev,
+ interfaces
+ );
+
+ moreinfo_add_with_prefix("DEV", key, str); /* str now owned by morinfo */
+
+ g_free(speed);
+ g_free(name);
+ g_free(key);
+ g_free(label);
+ g_free(interfaces);
+}
+
+void __scan_usb(void) {
+ usbd *list = usb_get_device_list();
+ usbd *curr = list;
+
+ int c = usbd_list_count(list);
+
+ if (usb_list) {
+ moreinfo_del_with_prefix("DEV:USB");
+ g_free(usb_list);
+ }
+ if (usb_icons){
+ g_free(usb_icons);
+ usb_icons = NULL;
+ }
+ usb_list = g_strdup_printf("[%s]\n", _("USB Devices"));
+
+ if (c > 0) {
+ while(curr) {
+ _usb_dev(curr);
+ curr=curr->next;
+ }
+
+ usbd_list_free(list);
+ } else {
+ /* No USB? */
+ usb_list = g_strconcat(usb_list, _("No USB devices found."), "=\n", NULL);
+ }
+}
diff --git a/modules/devices/x86/processor.c b/modules/devices/x86/processor.c
new file mode 100644
index 00000000..4141f051
--- /dev/null
+++ b/modules/devices/x86/processor.c
@@ -0,0 +1,823 @@
+/*
+ * HardInfo - Displays System Information
+ * Copyright (C) 2003-2006 L. A. F. Pereira <l@tia.mat.br>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, version 2 or later.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#include "hardinfo.h"
+#include "devices.h"
+#include "cpu_util.h"
+#include "nice_name.h"
+#include "x86_data.h"
+#include "x86_data.c"
+
+/*
+ * This function is partly based on x86cpucaps
+ * by Osamu Kayasono <jacobi@jcom.home.ne.jp>
+ */
+void get_processor_strfamily(Processor * processor)
+{
+ gint family = processor->family;
+ gint model = processor->model;
+
+ if (g_str_equal(processor->vendor_id, "GenuineIntel")) {
+ if (family == 4) {
+ processor->strmodel = g_strdup("i486 series");
+ } else if (family == 5) {
+ if (model < 4) {
+ processor->strmodel = g_strdup("Pentium Classic");
+ } else {
+ processor->strmodel = g_strdup("Pentium MMX");
+ }
+ } else if (family == 6) {
+ if (model <= 1) {
+ processor->strmodel = g_strdup("Pentium Pro");
+ } else if (model < 7) {
+ processor->strmodel = g_strdup("Pentium II/Pentium II Xeon/Celeron");
+ } else if (model == 9) {
+ processor->strmodel = g_strdup("Pentium M");
+ } else {
+ processor->strmodel = g_strdup("Pentium III/Pentium III Xeon/Celeron/Core Duo/Core Duo 2");
+ }
+ } else if (family > 6) {
+ processor->strmodel = g_strdup("Pentium 4");
+ } else {
+ processor->strmodel = g_strdup("i386 class");
+ }
+ } else if (g_str_equal(processor->vendor_id, "AuthenticAMD")) {
+ if (family == 4) {
+ if (model <= 9) {
+ processor->strmodel = g_strdup("AMD i80486 series");
+ } else {
+ processor->strmodel = g_strdup("AMD 5x86");
+ }
+ } else if (family == 5) {
+ if (model <= 3) {
+ processor->strmodel = g_strdup("AMD K5");
+ } else if (model <= 7) {
+ processor->strmodel = g_strdup("AMD K6");
+ } else if (model == 8) {
+ processor->strmodel = g_strdup("AMD K6-2");
+ } else if (model == 9) {
+ processor->strmodel = g_strdup("AMD K6-III");
+ } else {
+ processor->strmodel = g_strdup("AMD K6-2+/III+");
+ }
+ } else if (family == 6) {
+ if (model == 1) {
+ processor->strmodel = g_strdup("AMD Athlon (K7)");
+ } else if (model == 2) {
+ processor->strmodel = g_strdup("AMD Athlon (K75)");
+ } else if (model == 3) {
+ processor->strmodel = g_strdup("AMD Duron (Spitfire)");
+ } else if (model == 4) {
+ processor->strmodel = g_strdup("AMD Athlon (Thunderbird)");
+ } else if (model == 6) {
+ processor->strmodel = g_strdup("AMD Athlon XP/MP/4 (Palomino)");
+ } else if (model == 7) {
+ processor->strmodel = g_strdup("AMD Duron (Morgan)");
+ } else if (model == 8) {
+ processor->strmodel = g_strdup("AMD Athlon XP/MP (Thoroughbred)");
+ } else if (model == 10) {
+ processor->strmodel = g_strdup("AMD Athlon XP/MP (Barton)");
+ } else {
+ processor->strmodel = g_strdup("AMD Athlon (unknown)");
+ }
+ } else if (family > 6) {
+ processor->strmodel = g_strdup("AMD Opteron/Athlon64/FX");
+ } else {
+ processor->strmodel = g_strdup("AMD i386 class");
+ }
+ } else if (g_str_equal(processor->vendor_id, "CyrixInstead")) {
+ if (family == 4) {
+ processor->strmodel = g_strdup("Cyrix 5x86");
+ } else if (family == 5) {
+ processor->strmodel = g_strdup("Cyrix M1 (6x86)");
+ } else if (family == 6) {
+ if (model == 0) {
+ processor->strmodel = g_strdup("Cyrix M2 (6x86MX)");
+ } else if (model <= 5) {
+ processor->strmodel = g_strdup("VIA Cyrix III (M2 core)");
+ } else if (model == 6) {
+ processor->strmodel = g_strdup("VIA Cyrix III (WinChip C5A)");
+ } else if (model == 7) {
+ processor->strmodel = g_strdup("VIA Cyrix III (WinChip C5B/C)");
+ } else {
+ processor->strmodel = g_strdup("VIA Cyrix III (WinChip C5C-T)");
+ }
+ } else {
+ processor->strmodel = g_strdup("Cyrix i386 class");
+ }
+ } else if (g_str_equal(processor->vendor_id, "CentaurHauls")) {
+ if (family == 5) {
+ if (model <= 4) {
+ processor->strmodel = g_strdup("Centaur WinChip C6");
+ } else if (model <= 8) {
+ processor->strmodel = g_strdup("Centaur WinChip 2");
+ } else {
+ processor->strmodel = g_strdup("Centaur WinChip 2A");
+ }
+ } else {
+ processor->strmodel = g_strdup("Centaur i386 class");
+ }
+ } else if (g_str_equal(processor->vendor_id, "GenuineTMx86")) {
+ processor->strmodel = g_strdup("Transmeta Crusoe TM3x00/5x00");
+ } else {
+ processor->strmodel = g_strdup("Unknown");
+ }
+}
+
+static gchar *__cache_get_info_as_string(Processor *processor)
+{
+ gchar *result = g_strdup("");
+ GSList *cache_list;
+ ProcessorCache *cache;
+
+ if (!processor->cache) {
+ return g_strdup(_("Cache information not available=\n"));
+ }
+
+ for (cache_list = processor->cache; cache_list; cache_list = cache_list->next) {
+ cache = (ProcessorCache *)cache_list->data;
+
+ result = h_strdup_cprintf(_("Level %d (%s)=%d-way set-associative, %d sets, %dKB size\n"),
+ result,
+ cache->level,
+ C_("cache-type", cache->type),
+ cache->ways_of_associativity,
+ cache->number_of_sets,
+ cache->size);
+ }
+
+ return result;
+}
+
+/* This is not used directly, but creates translatable strings for
+ * the type string returned from /sys/.../cache */
+static const char* cache_types[] = {
+ NC_("cache-type", /*/cache type, as appears in: Level 1 (Data)*/ "Data"),
+ NC_("cache-type", /*/cache type, as appears in: Level 1 (Instruction)*/ "Instruction"),
+ NC_("cache-type", /*/cache type, as appears in: Level 2 (Unified)*/ "Unified")
+};
+
+static void __cache_obtain_info(Processor *processor)
+{
+ ProcessorCache *cache;
+ gchar *endpoint, *entry, *index;
+ gchar *uref = NULL;
+ gint i;
+ gint processor_number = processor->id;
+
+ endpoint = g_strdup_printf("/sys/devices/system/cpu/cpu%d/cache", processor_number);
+
+ for (i = 0; ; i++) {
+ cache = g_new0(ProcessorCache, 1);
+
+ index = g_strdup_printf("index%d/", i);
+
+ entry = g_strconcat(index, "type", NULL);
+ cache->type = h_sysfs_read_string(endpoint, entry);
+ g_free(entry);
+
+ if (!cache->type) {
+ g_free(cache);
+ g_free(index);
+ goto fail;
+ }
+
+ entry = g_strconcat(index, "level", NULL);
+ cache->level = h_sysfs_read_int(endpoint, entry);
+ g_free(entry);
+
+ entry = g_strconcat(index, "number_of_sets", NULL);
+ cache->number_of_sets = h_sysfs_read_int(endpoint, entry);
+ g_free(entry);
+
+ entry = g_strconcat(index, "physical_line_partition", NULL);
+ cache->physical_line_partition = h_sysfs_read_int(endpoint, entry);
+ g_free(entry);
+
+ entry = g_strconcat(index, "size", NULL);
+ cache->size = h_sysfs_read_int(endpoint, entry);
+ g_free(entry);
+
+ entry = g_strconcat(index, "ways_of_associativity", NULL);
+ cache->ways_of_associativity = h_sysfs_read_int(endpoint, entry);
+ g_free(entry);
+
+ /* unique cache references: id is nice, but share_cpu_list can be
+ * used if it is not available. */
+ entry = g_strconcat(index, "id", NULL);
+ uref = h_sysfs_read_string(endpoint, entry);
+ g_free(entry);
+ if (uref != NULL && *uref != 0 )
+ cache->uid = atoi(uref);
+ else
+ cache->uid = -1;
+ g_free(uref);
+ entry = g_strconcat(index, "shared_cpu_list", NULL);
+ cache->shared_cpu_list = h_sysfs_read_string(endpoint, entry);
+ g_free(entry);
+
+ /* reacharound */
+ entry = g_strconcat(index, "../../topology/physical_package_id", NULL);
+ cache->phy_sock = h_sysfs_read_int(endpoint, entry);
+ g_free(entry);
+
+ g_free(index);
+
+ processor->cache = g_slist_append(processor->cache, cache);
+ }
+
+fail:
+ g_free(endpoint);
+}
+
+#define khzint_to_mhzdouble(k) (((double)k)/1000)
+#define cmp_clocks_test(f) if (a->f < b->f) return -1; if (a->f > b->f) return 1;
+
+static gint cmp_cpufreq_data(cpufreq_data *a, cpufreq_data *b) {
+ gint i = 0;
+ i = g_strcmp0(a->shared_list, b->shared_list); if (i!=0) return i;
+ cmp_clocks_test(cpukhz_max);
+ cmp_clocks_test(cpukhz_min);
+ return 0;
+}
+
+static gint cmp_cpufreq_data_ignore_affected(cpufreq_data *a, cpufreq_data *b) {
+ gint i = 0;
+ cmp_clocks_test(cpukhz_max);
+ cmp_clocks_test(cpukhz_min);
+ return 0;
+}
+
+gchar *clocks_summary(GSList * processors)
+{
+ gchar *ret = g_strdup_printf("[%s]\n", _("Clocks"));
+ GSList *all_clocks = NULL, *uniq_clocks = NULL;
+ GSList *tmp, *l;
+ Processor *p;
+ cpufreq_data *c, *cur = NULL;
+ gint cur_count = 0, i = 0;
+
+ /* create list of all clock references */
+ for (l = processors; l; l = l->next) {
+ p = (Processor*)l->data;
+ if (p->cpufreq && p->cpufreq->cpukhz_max > 0) {
+ all_clocks = g_slist_prepend(all_clocks, p->cpufreq);
+ }
+ }
+
+ if (g_slist_length(all_clocks) == 0) {
+ ret = h_strdup_cprintf("%s=\n", ret, _("(Not Available)") );
+ g_slist_free(all_clocks);
+ return ret;
+ }
+
+ /* ignore duplicate references */
+ all_clocks = g_slist_sort(all_clocks, (GCompareFunc)cmp_cpufreq_data);
+ for (l = all_clocks; l; l = l->next) {
+ c = (cpufreq_data*)l->data;
+ if (!cur) {
+ cur = c;
+ } else {
+ if (cmp_cpufreq_data(cur, c) != 0) {
+ uniq_clocks = g_slist_prepend(uniq_clocks, cur);
+ cur = c;
+ }
+ }
+ }
+ uniq_clocks = g_slist_prepend(uniq_clocks, cur);
+ uniq_clocks = g_slist_reverse(uniq_clocks);
+ cur = 0, cur_count = 0;
+
+ /* count and list clocks */
+ for (l = uniq_clocks; l; l = l->next) {
+ c = (cpufreq_data*)l->data;
+ if (!cur) {
+ cur = c;
+ cur_count = 1;
+ } else {
+ if (cmp_cpufreq_data_ignore_affected(cur, c) != 0) {
+ ret = h_strdup_cprintf(_("%.2f-%.2f %s=%dx\n"),
+ ret,
+ khzint_to_mhzdouble(cur->cpukhz_min),
+ khzint_to_mhzdouble(cur->cpukhz_max),
+ _("MHz"),
+ cur_count);
+ cur = c;
+ cur_count = 1;
+ } else {
+ cur_count++;
+ }
+ }
+ }
+ ret = h_strdup_cprintf(_("%.2f-%.2f %s=%dx\n"),
+ ret,
+ khzint_to_mhzdouble(cur->cpukhz_min),
+ khzint_to_mhzdouble(cur->cpukhz_max),
+ _("MHz"),
+ cur_count);
+
+ g_slist_free(all_clocks);
+ g_slist_free(uniq_clocks);
+ return ret;
+}
+
+#define cmp_cache_test(f) if (a->f < b->f) return -1; if (a->f > b->f) return 1;
+
+static gint cmp_cache(ProcessorCache *a, ProcessorCache *b) {
+ gint i = 0;
+ cmp_cache_test(phy_sock);
+ i = g_strcmp0(a->type, b->type); if (i!=0) return i;
+ cmp_cache_test(level);
+ cmp_cache_test(size);
+ cmp_cache_test(uid); /* uid is unique among caches with the same (type, level) */
+ if (a->uid == -1) {
+ /* if id wasn't available, use shared_cpu_list as a unique ref */
+ i = g_strcmp0(a->shared_cpu_list, b->shared_cpu_list); if (i!=0)
+ return i;
+ }
+ return 0;
+}
+
+static gint cmp_cache_ignore_id(ProcessorCache *a, ProcessorCache *b) {
+ gint i = 0;
+ cmp_cache_test(phy_sock);
+ i = g_strcmp0(a->type, b->type); if (i!=0) return i;
+ cmp_cache_test(level);
+ cmp_cache_test(size);
+ return 0;
+}
+
+gchar *caches_summary(GSList * processors)
+{
+ gchar *ret = g_strdup_printf("[%s]\n", _("Caches"));
+ GSList *all_cache = NULL, *uniq_cache = NULL;
+ GSList *tmp, *l;
+ Processor *p;
+ ProcessorCache *c, *cur = NULL;
+ gint cur_count = 0, i = 0;
+
+ /* create list of all cache references */
+ for (l = processors; l; l = l->next) {
+ p = (Processor*)l->data;
+ if (p->cache) {
+ tmp = g_slist_copy(p->cache);
+ if (all_cache) {
+ all_cache = g_slist_concat(all_cache, tmp);
+ } else {
+ all_cache = tmp;
+ }
+ }
+ }
+
+ if (g_slist_length(all_cache) == 0) {
+ ret = h_strdup_cprintf("%s=\n", ret, _("(Not Available)") );
+ g_slist_free(all_cache);
+ return ret;
+ }
+
+ /* ignore duplicate references */
+ all_cache = g_slist_sort(all_cache, (GCompareFunc)cmp_cache);
+ for (l = all_cache; l; l = l->next) {
+ c = (ProcessorCache*)l->data;
+ if (!cur) {
+ cur = c;
+ } else {
+ if (cmp_cache(cur, c) != 0) {
+ uniq_cache = g_slist_prepend(uniq_cache, cur);
+ cur = c;
+ }
+ }
+ }
+ uniq_cache = g_slist_prepend(uniq_cache, cur);
+ uniq_cache = g_slist_reverse(uniq_cache);
+ cur = 0, cur_count = 0;
+
+ /* count and list caches */
+ for (l = uniq_cache; l; l = l->next) {
+ c = (ProcessorCache*)l->data;
+ if (!cur) {
+ cur = c;
+ cur_count = 1;
+ } else {
+ if (cmp_cache_ignore_id(cur, c) != 0) {
+ ret = h_strdup_cprintf(_("Level %d (%s)#%d=%dx %dKB (%dKB), %d-way set-associative, %d sets\n"),
+ ret,
+ cur->level,
+ C_("cache-type", cur->type),
+ cur->phy_sock,
+ cur_count,
+ cur->size,
+ cur->size * cur_count,
+ cur->ways_of_associativity,
+ cur->number_of_sets);
+ cur = c;
+ cur_count = 1;
+ } else {
+ cur_count++;
+ }
+ }
+ }
+ ret = h_strdup_cprintf(_("Level %d (%s)#%d=%dx %dKB (%dKB), %d-way set-associative, %d sets\n"),
+ ret,
+ cur->level,
+ C_("cache-type", cur->type),
+ cur->phy_sock,
+ cur_count,
+ cur->size,
+ cur->size * cur_count,
+ cur->ways_of_associativity,
+ cur->number_of_sets);
+
+ g_slist_free(all_cache);
+ g_slist_free(uniq_cache);
+ return ret;
+}
+
+#define PROC_SCAN_READ_BUFFER_SIZE 1024
+GSList *processor_scan(void)
+{
+ GSList *procs = NULL, *l = NULL;
+ Processor *processor = NULL;
+ FILE *cpuinfo;
+ gchar *buffer;
+
+ buffer = g_malloc(PROC_SCAN_READ_BUFFER_SIZE);
+ cpuinfo = fopen(PROC_CPUINFO, "r");
+ if (!cpuinfo)
+ return NULL;
+
+ while (fgets(buffer, PROC_SCAN_READ_BUFFER_SIZE, cpuinfo)) {
+ int rlen = strlen(buffer);
+ if (rlen >= PROC_SCAN_READ_BUFFER_SIZE - 1) {
+ fprintf(stderr, "Warning: truncated a line (probably flags list) longer than %d bytes while reading %s.\n", PROC_SCAN_READ_BUFFER_SIZE, PROC_CPUINFO);
+ }
+ gchar **tmp = g_strsplit(buffer, ":", 2);
+ if (!tmp[1] || !tmp[0]) {
+ g_strfreev(tmp);
+ continue;
+ }
+
+ tmp[0] = g_strstrip(tmp[0]);
+ tmp[1] = g_strstrip(tmp[1]);
+
+ if (g_str_has_prefix(tmp[0], "processor")) {
+ /* finish previous */
+ if (processor)
+ procs = g_slist_append(procs, processor);
+
+ /* start next */
+ processor = g_new0(Processor, 1);
+ processor->id = atol(tmp[1]);
+ g_strfreev(tmp);
+ continue;
+ }
+
+ if (processor) {
+ get_str("model name", processor->model_name);
+ get_str("vendor_id", processor->vendor_id);
+ get_str("flags", processor->flags);
+ get_str("bugs", processor->bugs);
+ get_str("power management", processor->pm);
+ get_str("microcode", processor->microcode);
+ get_int("cache size", processor->cache_size);
+ get_float("cpu MHz", processor->cpu_mhz);
+ get_float("bogomips", processor->bogomips);
+
+ get_str("fpu", processor->has_fpu);
+
+ get_str("fdiv_bug", processor->bug_fdiv);
+ get_str("hlt_bug", processor->bug_hlt);
+ get_str("f00f_bug", processor->bug_f00f);
+ get_str("coma_bug", processor->bug_coma);
+ /* sep_bug? */
+
+ get_int("model", processor->model);
+ get_int("cpu family", processor->family);
+ get_int("stepping", processor->stepping);
+ }
+ g_strfreev(tmp);
+ }
+
+ fclose(cpuinfo);
+ g_free(buffer);
+
+ /* finish last */
+ if (processor)
+ procs = g_slist_append(procs, processor);
+
+ for (l = procs; l; l = l->next) {
+ processor = (Processor *) l->data;
+
+ STRIFNULL(processor->microcode, _("(Not Available)") );
+
+ get_processor_strfamily(processor);
+ __cache_obtain_info(processor);
+
+#define NULLIFNOTYES(f) if (processor->f) if (strcmp(processor->f, "yes") != 0) { g_free(processor->f); processor->f = NULL; }
+ NULLIFNOTYES(bug_fdiv);
+ NULLIFNOTYES(bug_hlt);
+ NULLIFNOTYES(bug_f00f);
+ NULLIFNOTYES(bug_coma);
+
+ if (processor->bugs == NULL || g_strcmp0(processor->bugs, "") == 0) {
+ g_free(processor->bugs);
+ /* make bugs list on old kernels that don't offer one */
+ processor->bugs = g_strdup_printf("%s%s%s%s%s%s%s%s%s%s",
+ /* the oldest bug workarounds indicated in /proc/cpuinfo */
+ processor->bug_fdiv ? " fdiv" : "",
+ processor->bug_hlt ? " _hlt" : "",
+ processor->bug_f00f ? " f00f" : "",
+ processor->bug_coma ? " coma" : "",
+ /* these bug workarounds were reported as "features" in older kernels */
+ processor_has_flag(processor->flags, "fxsave_leak") ? " fxsave_leak" : "",
+ processor_has_flag(processor->flags, "clflush_monitor") ? " clflush_monitor" : "",
+ processor_has_flag(processor->flags, "11ap") ? " 11ap" : "",
+ processor_has_flag(processor->flags, "tlb_mmatch") ? " tlb_mmatch" : "",
+ processor_has_flag(processor->flags, "apic_c1e") ? " apic_c1e" : "",
+ ""); /* just to make adding lines easier */
+ g_strchug(processor->bugs);
+ }
+
+ if (processor->pm == NULL || g_strcmp0(processor->pm, "") == 0) {
+ g_free(processor->pm);
+ /* make power management list on old kernels that don't offer one */
+ processor->pm = g_strdup_printf("%s%s",
+ /* "hw_pstate" -> "hwpstate" */
+ processor_has_flag(processor->flags, "hw_pstate") ? " hwpstate" : "",
+ ""); /* just to make adding lines easier */
+ g_strchug(processor->pm);
+ }
+
+ /* topo & freq */
+ processor->cpufreq = cpufreq_new(processor->id);
+ processor->cputopo = cputopo_new(processor->id);
+
+ if (processor->cpufreq->cpukhz_max)
+ processor->cpu_mhz = processor->cpufreq->cpukhz_max / 1000;
+
+ nice_name_x86_cpuid_model_string(processor->model_name);
+ }
+
+ return procs;
+}
+
+gchar *processor_get_capabilities_from_flags(gchar *strflags, gchar *lookup_prefix)
+{
+ gchar **flags, **old;
+ gchar tmp_flag[64] = "";
+ const gchar *meaning;
+ gchar *tmp = NULL;
+ gint j = 0, i = 0;
+
+ flags = g_strsplit(strflags, " ", 0);
+ old = flags;
+
+ while (flags[j]) {
+ if ( sscanf(flags[j], "[%d]", &i)==1 ) {
+ /* Some flags are indexes, like [13], and that looks like
+ * a new section to hardinfo shell */
+ tmp = h_strdup_cprintf("(%s%d)=\n", tmp,
+ (lookup_prefix) ? lookup_prefix : "",
+ i );
+ } else {
+ sprintf(tmp_flag, "%s%s", lookup_prefix, flags[j]);
+ meaning = x86_flag_meaning(tmp_flag);
+
+ if (meaning) {
+ tmp = h_strdup_cprintf("%s=%s\n", tmp, flags[j], meaning);
+ } else {
+ tmp = h_strdup_cprintf("%s=\n", tmp, flags[j]);
+ }
+ }
+ j++;
+ }
+ if (tmp == NULL || g_strcmp0(tmp, "") == 0)
+ tmp = g_strdup_printf("%s=%s\n", "empty", _("Empty List"));
+
+ g_strfreev(old);
+ return tmp;
+}
+
+gchar *processor_get_detailed_info(Processor * processor)
+{
+ gchar *tmp_flags, *tmp_bugs, *tmp_pm, *tmp_cpufreq, *tmp_topology, *ret, *cache_info;
+
+ tmp_flags = processor_get_capabilities_from_flags(processor->flags, "");
+ tmp_bugs = processor_get_capabilities_from_flags(processor->bugs, "bug:");
+ tmp_pm = processor_get_capabilities_from_flags(processor->pm, "pm:");
+ cache_info = __cache_get_info_as_string(processor);
+
+ tmp_topology = cputopo_section_str(processor->cputopo);
+ tmp_cpufreq = cpufreq_section_str(processor->cpufreq);
+
+ ret = g_strdup_printf("[%s]\n"
+ "%s=%s\n"
+ "%s=%d, %d, %d (%s)\n" /* family, model, stepping (decoded name) */
+ "$^$%s=%s\n" /* vendor */
+ "%s=%s\n" /* microcode */
+ "[%s]\n" /* configuration */
+ "%s=%d %s\n" /* cache size (from cpuinfo) */
+ "%s=%.2f %s\n" /* frequency */
+ "%s=%.2f\n" /* bogomips */
+ "%s=%s\n" /* byte order */
+ "%s" /* topology */
+ "%s" /* frequency scaling */
+ "[%s]\n" /* cache */
+ "%s\n"
+ "[%s]\n" /* pm */
+ "%s"
+ "[%s]\n" /* bugs */
+ "%s"
+ "[%s]\n" /* flags */
+ "%s",
+ _("Processor"),
+ _("Model Name"), processor->model_name,
+ _("Family, model, stepping"),
+ processor->family,
+ processor->model,
+ processor->stepping,
+ processor->strmodel,
+ _("Vendor"), processor->vendor_id,
+ _("Microcode Version"), processor->microcode,
+ _("Configuration"),
+ _("Cache Size"), processor->cache_size, _("kb"),
+ _("Frequency"), processor->cpu_mhz, _("MHz"),
+ _("BogoMips"), processor->bogomips,
+ _("Byte Order"), byte_order_str(),
+ tmp_topology,
+ tmp_cpufreq,
+ _("Cache"), cache_info,
+ _("Power Management"), tmp_pm,
+ _("Bug Workarounds"), tmp_bugs,
+ _("Capabilities"), tmp_flags );
+ g_free(tmp_flags);
+ g_free(tmp_bugs);
+ g_free(tmp_pm);
+ g_free(cache_info);
+ g_free(tmp_cpufreq);
+ g_free(tmp_topology);
+ return ret;
+}
+
+gchar *processor_name(GSList * processors) {
+ return processor_name_default(processors);
+}
+
+gchar *processor_describe(GSList * processors) {
+ return processor_describe_default(processors);
+}
+
+gchar *dmi_socket_info() {
+ gchar *ret;
+ dmi_type dt = 4;
+ guint i;
+ dmi_handle_list *hl = dmidecode_handles(&dt);
+
+ if (!hl) {
+ ret = g_strdup_printf("[%s]\n%s=%s\n",
+ _("Socket Information"), _("Result"),
+ (getuid() == 0)
+ ? _("(Not available)")
+ : _("(Not available; Perhaps try running HardInfo as root.)") );
+ } else {
+ ret = g_strdup("");
+ for(i = 0; i < hl->count; i++) {
+ dmi_handle h = hl->handles[i];
+ gchar *upgrade = dmidecode_match("Upgrade", &dt, &h);
+ gchar *socket = dmidecode_match("Socket Designation", &dt, &h);
+ gchar *bus_clock_str = dmidecode_match("External Clock", &dt, &h);
+ gchar *voltage_str = dmidecode_match("Voltage", &dt, &h);
+ gchar *max_speed_str = dmidecode_match("Max Speed", &dt, &h);
+
+ ret = h_strdup_cprintf("[%s (%d) %s]\n"
+ "%s=0x%x\n"
+ "%s=%s\n"
+ "%s=%s\n"
+ "%s=%s\n"
+ "%s=%s\n",
+ ret,
+ _("CPU Socket"), i, socket,
+ _("DMI Handle"), h,
+ _("Type"), upgrade,
+ _("Voltage"), voltage_str,
+ _("External Clock"), bus_clock_str,
+ _("Max Frequency"), max_speed_str
+ );
+ g_free(upgrade);
+ g_free(socket);
+ g_free(bus_clock_str);
+ g_free(voltage_str);
+ g_free(max_speed_str);
+ }
+
+ dmi_handle_list_free(hl);
+ }
+
+ return ret;
+}
+
+gchar *processor_meta(GSList * processors) {
+ gchar *meta_cpu_name = processor_name(processors);
+ gchar *meta_cpu_desc = processor_describe(processors);
+ gchar *meta_freq_desc = processor_frequency_desc(processors);
+ gchar *meta_clocks = clocks_summary(processors);
+ gchar *meta_caches = caches_summary(processors);
+ gchar *meta_dmi = dmi_socket_info();
+ gchar *ret = NULL;
+ UNKIFNULL(meta_cpu_desc);
+ ret = g_strdup_printf("[%s]\n"
+ "%s=%s\n"
+ "%s=%s\n"
+ "%s=%s\n"
+ "%s"
+ "%s"
+ "%s",
+ _("Package Information"),
+ _("Name"), meta_cpu_name,
+ _("Topology"), meta_cpu_desc,
+ _("Logical CPU Config"), meta_freq_desc,
+ meta_clocks,
+ meta_caches,
+ meta_dmi);
+ g_free(meta_cpu_desc);
+ g_free(meta_freq_desc);
+ g_free(meta_clocks);
+ g_free(meta_caches);
+ return ret;
+}
+
+gchar *processor_get_info(GSList * processors)
+{
+ Processor *processor;
+ gchar *ret, *tmp, *hashkey;
+ gchar *meta; /* becomes owned by more_info? no need to free? */
+ GSList *l;
+ gchar *icons=g_strdup("");
+
+ tmp = g_strdup_printf("$!CPU_META$%s=|Summary\n", "all");
+
+ meta = processor_meta(processors);
+ moreinfo_add_with_prefix("DEV", "CPU_META", meta);
+
+ for (l = processors; l; l = l->next) {
+ processor = (Processor *) l->data;
+
+ gchar *model_name = g_strdup(processor->model_name);
+ const Vendor *v = vendor_match(processor->vendor_id, NULL);
+ if (v)
+ tag_vendor(&model_name, 0, v->name_short ? v->name_short : v->name, v->ansi_color, params.fmt_opts);
+
+ // bp: not convinced it looks good, but here's how it would be done...
+ //icons = h_strdup_cprintf("Icon$CPU%d$cpu%d=processor.png\n", icons, processor->id, processor->id);
+
+ tmp = g_strdup_printf("%s$CPU%d$cpu%d=%.2f %s|%s|%d:%d\n",
+ tmp, processor->id,
+ processor->id,
+ processor->cpu_mhz, _("MHz"),
+ model_name,
+ processor->cputopo->socket_id,
+ processor->cputopo->core_id);
+
+ hashkey = g_strdup_printf("CPU%d", processor->id);
+ moreinfo_add_with_prefix("DEV", hashkey,
+ processor_get_detailed_info(processor));
+ g_free(hashkey);
+ g_free(model_name);
+ }
+
+ ret = g_strdup_printf("[$ShellParam$]\n"
+ "ViewType=1\n"
+ "ColumnTitle$TextValue=%s\n"
+ "ColumnTitle$Value=%s\n"
+ "ColumnTitle$Extra1=%s\n"
+ "ColumnTitle$Extra2=%s\n"
+ "ShowColumnHeaders=true\n"
+ "%s"
+ "[Processors]\n"
+ "%s", _("Device"), _("Frequency"), _("Model"), _("Socket:Core"), icons, tmp);
+ g_free(tmp);
+ g_free(icons);
+
+ // now here's something fun...
+ struct Info *i = info_unflatten(ret);
+ g_free(ret);
+ ret = info_flatten(i);
+
+ return ret;
+}
+
diff --git a/modules/devices/x86/x86_data.c b/modules/devices/x86/x86_data.c
new file mode 100644
index 00000000..f56e8668
--- /dev/null
+++ b/modules/devices/x86/x86_data.c
@@ -0,0 +1,400 @@
+/*
+ * rpiz - https://github.com/bp0/rpiz
+ * 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; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#include <json-glib/json-glib.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include "x86_data.h"
+
+#ifndef C_
+#define C_(Ctx, String) String
+#endif
+#ifndef NC_
+#define NC_(Ctx, String) String
+#endif
+
+/* sources:
+ * https://unix.stackexchange.com/a/43540
+ * https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux-stable.git/tree/arch/x86/include/asm/cpufeatures.h?id=refs/tags/v4.9
+ * hardinfo: modules/devices/x86/processor.c
+ */
+
+struct flag_to_meaning {
+ char *name;
+ char *meaning;
+};
+
+static const struct flag_to_meaning builtin_tab_flag_meaning[] = {
+/* Intel-defined CPU features, CPUID level 0x00000001 (edx)
+ * See also Wikipedia and table 2-27 in Intel Advanced Vector Extensions Programming Reference */
+ { "fpu", NC_("x86-flag", /*/flag:fpu*/ "Onboard FPU (floating point support)") },
+ { "vme", NC_("x86-flag", /*/flag:vme*/ "Virtual 8086 mode enhancements") },
+ { "de", NC_("x86-flag", /*/flag:de*/ "Debugging Extensions (CR4.DE)") },
+ { "pse", NC_("x86-flag", /*/flag:pse*/ "Page Size Extensions (4MB memory pages)") },
+ { "tsc", NC_("x86-flag", /*/flag:tsc*/ "Time Stamp Counter (RDTSC)") },
+ { "msr", NC_("x86-flag", /*/flag:msr*/ "Model-Specific Registers (RDMSR, WRMSR)") },
+ { "pae", NC_("x86-flag", /*/flag:pae*/ "Physical Address Extensions (support for more than 4GB of RAM)") },
+ { "mce", NC_("x86-flag", /*/flag:mce*/ "Machine Check Exception") },
+ { "cx8", NC_("x86-flag", /*/flag:cx8*/ "CMPXCHG8 instruction (64-bit compare-and-swap)") },
+ { "apic", NC_("x86-flag", /*/flag:apic*/ "Onboard APIC") },
+ { "sep", NC_("x86-flag", /*/flag:sep*/ "SYSENTER/SYSEXIT") },
+ { "mtrr", NC_("x86-flag", /*/flag:mtrr*/ "Memory Type Range Registers") },
+ { "pge", NC_("x86-flag", /*/flag:pge*/ "Page Global Enable (global bit in PDEs and PTEs)") },
+ { "mca", NC_("x86-flag", /*/flag:mca*/ "Machine Check Architecture") },
+ { "cmov", NC_("x86-flag", /*/flag:cmov*/ "CMOV instructions (conditional move) (also FCMOV)") },
+ { "pat", NC_("x86-flag", /*/flag:pat*/ "Page Attribute Table") },
+ { "pse36", NC_("x86-flag", /*/flag:pse36*/ "36-bit PSEs (huge pages)") },
+ { "pn", NC_("x86-flag", /*/flag:pn*/ "Processor serial number") },
+ { "clflush", NC_("x86-flag", /*/flag:clflush*/ "Cache Line Flush instruction") },
+ { "dts", NC_("x86-flag", /*/flag:dts*/ "Debug Store (buffer for debugging and profiling instructions), or alternately: digital thermal sensor") },
+ { "acpi", NC_("x86-flag", /*/flag:acpi*/ "ACPI via MSR (temperature monitoring and clock speed modulation)") },
+ { "mmx", NC_("x86-flag", /*/flag:mmx*/ "Multimedia Extensions") },
+ { "fxsr", NC_("x86-flag", /*/flag:fxsr*/ "FXSAVE/FXRSTOR, CR4.OSFXSR") },
+ { "sse", NC_("x86-flag", /*/flag:sse*/ "Intel SSE vector instructions") },
+ { "sse2", NC_("x86-flag", /*/flag:sse2*/ "SSE2") },
+ { "ss", NC_("x86-flag", /*/flag:ss*/ "CPU self snoop") },
+ { "ht", NC_("x86-flag", /*/flag:ht*/ "Hyper-Threading") },
+ { "tm", NC_("x86-flag", /*/flag:tm*/ "Automatic clock control (Thermal Monitor)") },
+ { "ia64", NC_("x86-flag", /*/flag:ia64*/ "Intel Itanium Architecture 64-bit (not to be confused with Intel's 64-bit x86 architecture with flag x86-64 or \"AMD64\" bit indicated by flag lm)") },
+ { "pbe", NC_("x86-flag", /*/flag:pbe*/ "Pending Break Enable (PBE# pin) wakeup support") },
+/* AMD-defined CPU features, CPUID level 0x80000001
+ * See also Wikipedia and table 2-23 in Intel Advanced Vector Extensions Programming Reference */
+ { "syscall", NC_("x86-flag", /*/flag:syscall*/ "SYSCALL (Fast System Call) and SYSRET (Return From Fast System Call)") },
+ { "mp", NC_("x86-flag", /*/flag:mp*/ "Multiprocessing Capable.") },
+ { "nx", NC_("x86-flag", /*/flag:nx*/ "Execute Disable") },
+ { "mmxext", NC_("x86-flag", /*/flag:mmxext*/ "AMD MMX extensions") },
+ { "fxsr_opt", NC_("x86-flag", /*/flag:fxsr_opt*/ "FXSAVE/FXRSTOR optimizations") },
+ { "pdpe1gb", NC_("x86-flag", /*/flag:pdpe1gb*/ "One GB pages (allows hugepagesz=1G)") },
+ { "rdtscp", NC_("x86-flag", /*/flag:rdtscp*/ "Read Time-Stamp Counter and Processor ID") },
+ { "lm", NC_("x86-flag", /*/flag:lm*/ "Long Mode (x86-64: amd64, also known as Intel 64, i.e. 64-bit capable)") },
+ { "3dnow", NC_("x86-flag", /*/flag:3dnow*/ "3DNow! (AMD vector instructions, competing with Intel's SSE1)") },
+ { "3dnowext", NC_("x86-flag", /*/flag:3dnowext*/ "AMD 3DNow! extensions") },
+/* Transmeta-defined CPU features, CPUID level 0x80860001 */
+ { "recovery", NC_("x86-flag", /*/flag:recovery*/ "CPU in recovery mode") },
+ { "longrun", NC_("x86-flag", /*/flag:longrun*/ "Longrun power control") },
+ { "lrti", NC_("x86-flag", /*/flag:lrti*/ "LongRun table interface") },
+/* Other features, Linux-defined mapping */
+ { "cxmmx", NC_("x86-flag", /*/flag:cxmmx*/ "Cyrix MMX extensions") },
+ { "k6_mtrr", NC_("x86-flag", /*/flag:k6_mtrr*/ "AMD K6 nonstandard MTRRs") },
+ { "cyrix_arr", NC_("x86-flag", /*/flag:cyrix_arr*/ "Cyrix ARRs (= MTRRs)") },
+ { "centaur_mcr", NC_("x86-flag", /*/flag:centaur_mcr*/ "Centaur MCRs (= MTRRs)") },
+ { "constant_tsc", NC_("x86-flag", /*/flag:constant_tsc*/ "TSC ticks at a constant rate") },
+ { "up", NC_("x86-flag", /*/flag:up*/ "SMP kernel running on UP") },
+ { "art", NC_("x86-flag", /*/flag:art*/ "Always-Running Timer") },
+ { "arch_perfmon", NC_("x86-flag", /*/flag:arch_perfmon*/ "Intel Architectural PerfMon") },
+ { "pebs", NC_("x86-flag", /*/flag:pebs*/ "Precise-Event Based Sampling") },
+ { "bts", NC_("x86-flag", /*/flag:bts*/ "Branch Trace Store") },
+ { "rep_good", NC_("x86-flag", /*/flag:rep_good*/ "rep microcode works well") },
+ { "acc_power", NC_("x86-flag", /*/flag:acc_power*/ "AMD accumulated power mechanism") },
+ { "nopl", NC_("x86-flag", /*/flag:nopl*/ "The NOPL (0F 1F) instructions") },
+ { "xtopology", NC_("x86-flag", /*/flag:xtopology*/ "cpu topology enum extensions") },
+ { "tsc_reliable", NC_("x86-flag", /*/flag:tsc_reliable*/ "TSC is known to be reliable") },
+ { "nonstop_tsc", NC_("x86-flag", /*/flag:nonstop_tsc*/ "TSC does not stop in C states") },
+ { "extd_apicid", NC_("x86-flag", /*/flag:extd_apicid*/ "has extended APICID (8 bits)") },
+ { "amd_dcm", NC_("x86-flag", /*/flag:amd_dcm*/ "multi-node processor") },
+ { "aperfmperf", NC_("x86-flag", /*/flag:aperfmperf*/ "APERFMPERF") },
+ { "eagerfpu", NC_("x86-flag", /*/flag:eagerfpu*/ "Non lazy FPU restore") },
+ { "nonstop_tsc_s3", NC_("x86-flag", /*/flag:nonstop_tsc_s3*/ "TSC doesn't stop in S3 state") },
+ { "mce_recovery", NC_("x86-flag", /*/flag:mce_recovery*/ "CPU has recoverable machine checks") },
+/* Intel-defined CPU features, CPUID level 0x00000001 (ecx)
+ * See also Wikipedia and table 2-26 in Intel Advanced Vector Extensions Programming Reference */
+ { "pni", NC_("x86-flag", /*/flag:pni*/ "SSE-3 (\"Prescott New Instructions\")") },
+ { "pclmulqdq", NC_("x86-flag", /*/flag:pclmulqdq*/ "Perform a Carry-Less Multiplication of Quadword instruction - accelerator for GCM)") },
+ { "dtes64", NC_("x86-flag", /*/flag:dtes64*/ "64-bit Debug Store") },
+ { "monitor", NC_("x86-flag", /*/flag:monitor*/ "Monitor/Mwait support (Intel SSE3 supplements)") },
+ { "ds_cpl", NC_("x86-flag", /*/flag:ds_cpl*/ "CPL Qual. Debug Store") },
+ { "vmx", NC_("x86-flag", /*/flag:vmx*/ "Hardware virtualization, Intel VMX") },
+ { "smx", NC_("x86-flag", /*/flag:smx*/ "Safer mode TXT (TPM support)") },
+ { "est", NC_("x86-flag", /*/flag:est*/ "Enhanced SpeedStep") },
+ { "tm2", NC_("x86-flag", /*/flag:tm2*/ "Thermal Monitor 2") },
+ { "ssse3", NC_("x86-flag", /*/flag:ssse3*/ "Supplemental SSE-3") },
+ { "cid", NC_("x86-flag", /*/flag:cid*/ "Context ID") },
+ { "sdbg", NC_("x86-flag", /*/flag:sdbg*/ "silicon debug") },
+ { "fma", NC_("x86-flag", /*/flag:fma*/ "Fused multiply-add") },
+ { "cx16", NC_("x86-flag", /*/flag:cx16*/ "CMPXCHG16B") },
+ { "xtpr", NC_("x86-flag", /*/flag:xtpr*/ "Send Task Priority Messages") },
+ { "pdcm", NC_("x86-flag", /*/flag:pdcm*/ "Performance Capabilities") },
+ { "pcid", NC_("x86-flag", /*/flag:pcid*/ "Process Context Identifiers") },
+ { "dca", NC_("x86-flag", /*/flag:dca*/ "Direct Cache Access") },
+ { "sse4_1", NC_("x86-flag", /*/flag:sse4_1*/ "SSE-4.1") },
+ { "sse4_2", NC_("x86-flag", /*/flag:sse4_2*/ "SSE-4.2") },
+ { "x2apic", NC_("x86-flag", /*/flag:x2apic*/ "x2APIC") },
+ { "movbe", NC_("x86-flag", /*/flag:movbe*/ "Move Data After Swapping Bytes instruction") },
+ { "popcnt", NC_("x86-flag", /*/flag:popcnt*/ "Return the Count of Number of Bits Set to 1 instruction (Hamming weight, i.e. bit count)") },
+ { "tsc_deadline_timer", NC_("x86-flag", /*/flag:tsc_deadline_timer*/ "Tsc deadline timer") },
+ { "aes/aes-ni", NC_("x86-flag", /*/flag:aes/aes-ni*/ "Advanced Encryption Standard (New Instructions)") },
+ { "xsave", NC_("x86-flag", /*/flag:xsave*/ "Save Processor Extended States: also provides XGETBY,XRSTOR,XSETBY") },
+ { "avx", NC_("x86-flag", /*/flag:avx*/ "Advanced Vector Extensions") },
+ { "f16c", NC_("x86-flag", /*/flag:f16c*/ "16-bit fp conversions (CVT16)") },
+ { "rdrand", NC_("x86-flag", /*/flag:rdrand*/ "Read Random Number from hardware random number generator instruction") },
+ { "hypervisor", NC_("x86-flag", /*/flag:hypervisor*/ "Running on a hypervisor") },
+/* VIA/Cyrix/Centaur-defined CPU features, CPUID level 0xC0000001 */
+ { "rng", NC_("x86-flag", /*/flag:rng*/ "Random Number Generator present (xstore)") },
+ { "rng_en", NC_("x86-flag", /*/flag:rng_en*/ "Random Number Generator enabled") },
+ { "ace", NC_("x86-flag", /*/flag:ace*/ "on-CPU crypto (xcrypt)") },
+ { "ace_en", NC_("x86-flag", /*/flag:ace_en*/ "on-CPU crypto enabled") },
+ { "ace2", NC_("x86-flag", /*/flag:ace2*/ "Advanced Cryptography Engine v2") },
+ { "ace2_en", NC_("x86-flag", /*/flag:ace2_en*/ "ACE v2 enabled") },
+ { "phe", NC_("x86-flag", /*/flag:phe*/ "PadLock Hash Engine") },
+ { "phe_en", NC_("x86-flag", /*/flag:phe_en*/ "PHE enabled") },
+ { "pmm", NC_("x86-flag", /*/flag:pmm*/ "PadLock Montgomery Multiplier") },
+ { "pmm_en", NC_("x86-flag", /*/flag:pmm_en*/ "PMM enabled") },
+/* More extended AMD flags: CPUID level 0x80000001, ecx */
+ { "lahf_lm", NC_("x86-flag", /*/flag:lahf_lm*/ "Load AH from Flags (LAHF) and Store AH into Flags (SAHF) in long mode") },
+ { "cmp_legacy", NC_("x86-flag", /*/flag:cmp_legacy*/ "If yes HyperThreading not valid") },
+ { "svm", NC_("x86-flag", /*/flag:svm*/ "\"Secure virtual machine\": AMD-V") },
+ { "extapic", NC_("x86-flag", /*/flag:extapic*/ "Extended APIC space") },
+ { "cr8_legacy", NC_("x86-flag", /*/flag:cr8_legacy*/ "CR8 in 32-bit mode") },
+ { "abm", NC_("x86-flag", /*/flag:abm*/ "Advanced Bit Manipulation") },
+ { "sse4a", NC_("x86-flag", /*/flag:sse4a*/ "SSE-4A") },
+ { "misalignsse", NC_("x86-flag", /*/flag:misalignsse*/ "indicates if a general-protection exception (#GP) is generated when some legacy SSE instructions operate on unaligned data. Also depends on CR0 and Alignment Checking bit") },
+ { "3dnowprefetch", NC_("x86-flag", /*/flag:3dnowprefetch*/ "3DNow prefetch instructions") },
+ { "osvw", NC_("x86-flag", /*/flag:osvw*/ "indicates OS Visible Workaround, which allows the OS to work around processor errata.") },
+ { "ibs", NC_("x86-flag", /*/flag:ibs*/ "Instruction Based Sampling") },
+ { "xop", NC_("x86-flag", /*/flag:xop*/ "extended AVX instructions") },
+ { "skinit", NC_("x86-flag", /*/flag:skinit*/ "SKINIT/STGI instructions") },
+ { "wdt", NC_("x86-flag", /*/flag:wdt*/ "Watchdog timer") },
+ { "lwp", NC_("x86-flag", /*/flag:lwp*/ "Light Weight Profiling") },
+ { "fma4", NC_("x86-flag", /*/flag:fma4*/ "4 operands MAC instructions") },
+ { "tce", NC_("x86-flag", /*/flag:tce*/ "translation cache extension") },
+ { "nodeid_msr", NC_("x86-flag", /*/flag:nodeid_msr*/ "NodeId MSR") },
+ { "tbm", NC_("x86-flag", /*/flag:tbm*/ "Trailing Bit Manipulation") },
+ { "topoext", NC_("x86-flag", /*/flag:topoext*/ "Topology Extensions CPUID leafs") },
+ { "perfctr_core", NC_("x86-flag", /*/flag:perfctr_core*/ "Core Performance Counter Extensions") },
+ { "perfctr_nb", NC_("x86-flag", /*/flag:perfctr_nb*/ "NB Performance Counter Extensions") },
+ { "bpext", NC_("x86-flag", /*/flag:bpext*/ "data breakpoint extension") },
+ { "ptsc", NC_("x86-flag", /*/flag:ptsc*/ "performance time-stamp counter") },
+ { "perfctr_l2", NC_("x86-flag", /*/flag:perfctr_l2*/ "L2 Performance Counter Extensions") },
+ { "mwaitx", NC_("x86-flag", /*/flag:mwaitx*/ "MWAIT extension (MONITORX/MWAITX)") },
+/* Auxiliary flags: Linux defined - For features scattered in various CPUID levels */
+ { "cpb", NC_("x86-flag", /*/flag:cpb*/ "AMD Core Performance Boost") },
+ { "epb", NC_("x86-flag", /*/flag:epb*/ "IA32_ENERGY_PERF_BIAS support") },
+ { "hw_pstate", NC_("x86-flag", /*/flag:hw_pstate*/ "AMD HW-PState") },
+ { "proc_feedback", NC_("x86-flag", /*/flag:proc_feedback*/ "AMD ProcFeedbackInterface") },
+ { "intel_pt", NC_("x86-flag", /*/flag:intel_pt*/ "Intel Processor Tracing") },
+/* Virtualization flags: Linux defined */
+ { "tpr_shadow", NC_("x86-flag", /*/flag:tpr_shadow*/ "Intel TPR Shadow") },
+ { "vnmi", NC_("x86-flag", /*/flag:vnmi*/ "Intel Virtual NMI") },
+ { "flexpriority", NC_("x86-flag", /*/flag:flexpriority*/ "Intel FlexPriority") },
+ { "ept", NC_("x86-flag", /*/flag:ept*/ "Intel Extended Page Table") },
+ { "vpid", NC_("x86-flag", /*/flag:vpid*/ "Intel Virtual Processor ID") },
+ { "vmmcall", NC_("x86-flag", /*/flag:vmmcall*/ "prefer VMMCALL to VMCALL") },
+/* Intel-defined CPU features, CPUID level 0x00000007:0 (ebx) */
+ { "fsgsbase", NC_("x86-flag", /*/flag:fsgsbase*/ "{RD/WR}{FS/GS}BASE instructions") },
+ { "tsc_adjust", NC_("x86-flag", /*/flag:tsc_adjust*/ "TSC adjustment MSR") },
+ { "bmi1", NC_("x86-flag", /*/flag:bmi1*/ "1st group bit manipulation extensions") },
+ { "hle", NC_("x86-flag", /*/flag:hle*/ "Hardware Lock Elision") },
+ { "avx2", NC_("x86-flag", /*/flag:avx2*/ "AVX2 instructions") },
+ { "smep", NC_("x86-flag", /*/flag:smep*/ "Supervisor Mode Execution Protection") },
+ { "bmi2", NC_("x86-flag", /*/flag:bmi2*/ "2nd group bit manipulation extensions") },
+ { "erms", NC_("x86-flag", /*/flag:erms*/ "Enhanced REP MOVSB/STOSB") },
+ { "invpcid", NC_("x86-flag", /*/flag:invpcid*/ "Invalidate Processor Context ID") },
+ { "rtm", NC_("x86-flag", /*/flag:rtm*/ "Restricted Transactional Memory") },
+ { "cqm", NC_("x86-flag", /*/flag:cqm*/ "Cache QoS Monitoring") },
+ { "mpx", NC_("x86-flag", /*/flag:mpx*/ "Memory Protection Extension") },
+ { "avx512f", NC_("x86-flag", /*/flag:avx512f*/ "AVX-512 foundation") },
+ { "avx512dq", NC_("x86-flag", /*/flag:avx512dq*/ "AVX-512 Double/Quad instructions") },
+ { "rdseed", NC_("x86-flag", /*/flag:rdseed*/ "The RDSEED instruction") },
+ { "adx", NC_("x86-flag", /*/flag:adx*/ "The ADCX and ADOX instructions") },
+ { "smap", NC_("x86-flag", /*/flag:smap*/ "Supervisor Mode Access Prevention") },
+ { "clflushopt", NC_("x86-flag", /*/flag:clflushopt*/ "CLFLUSHOPT instruction") },
+ { "clwb", NC_("x86-flag", /*/flag:clwb*/ "CLWB instruction") },
+ { "avx512pf", NC_("x86-flag", /*/flag:avx512pf*/ "AVX-512 Prefetch") },
+ { "avx512er", NC_("x86-flag", /*/flag:avx512er*/ "AVX-512 Exponential and Reciprocal") },
+ { "avx512cd", NC_("x86-flag", /*/flag:avx512cd*/ "AVX-512 Conflict Detection") },
+ { "sha_ni", NC_("x86-flag", /*/flag:sha_ni*/ "SHA1/SHA256 Instruction Extensions") },
+ { "avx512bw", NC_("x86-flag", /*/flag:avx512bw*/ "AVX-512 Byte/Word instructions") },
+ { "avx512vl", NC_("x86-flag", /*/flag:avx512vl*/ "AVX-512 128/256 Vector Length extensions") },
+/* Extended state features, CPUID level 0x0000000d:1 (eax) */
+ { "xsaveopt", NC_("x86-flag", /*/flag:xsaveopt*/ "Optimized XSAVE") },
+ { "xsavec", NC_("x86-flag", /*/flag:xsavec*/ "XSAVEC") },
+ { "xgetbv1", NC_("x86-flag", /*/flag:xgetbv1*/ "XGETBV with ECX = 1") },
+ { "xsaves", NC_("x86-flag", /*/flag:xsaves*/ "XSAVES/XRSTORS") },
+/* Intel-defined CPU QoS sub-leaf, CPUID level 0x0000000F:0 (edx) */
+ { "cqm_llc", NC_("x86-flag", /*/flag:cqm_llc*/ "LLC QoS") },
+/* Intel-defined CPU QoS sub-leaf, CPUID level 0x0000000F:1 (edx) */
+ { "cqm_occup_llc", NC_("x86-flag", /*/flag:cqm_occup_llc*/ "LLC occupancy monitoring") },
+ { "cqm_mbm_total", NC_("x86-flag", /*/flag:cqm_mbm_total*/ "LLC total MBM monitoring") },
+ { "cqm_mbm_local", NC_("x86-flag", /*/flag:cqm_mbm_local*/ "LLC local MBM monitoring") },
+/* AMD-defined CPU features, CPUID level 0x80000008 (ebx) */
+ { "clzero", NC_("x86-flag", /*/flag:clzero*/ "CLZERO instruction") },
+ { "irperf", NC_("x86-flag", /*/flag:irperf*/ "instructions retired performance counter") },
+/* Thermal and Power Management leaf, CPUID level 0x00000006 (eax) */
+ { "dtherm", NC_("x86-flag", /*/flag:dtherm*/ "digital thermal sensor") }, /* formerly dts */
+ { "ida", NC_("x86-flag", /*/flag:ida*/ "Intel Dynamic Acceleration") },
+ { "arat", NC_("x86-flag", /*/flag:arat*/ "Always Running APIC Timer") },
+ { "pln", NC_("x86-flag", /*/flag:pln*/ "Intel Power Limit Notification") },
+ { "pts", NC_("x86-flag", /*/flag:pts*/ "Intel Package Thermal Status") },
+ { "hwp", NC_("x86-flag", /*/flag:hwp*/ "Intel Hardware P-states") },
+ { "hwp_notify", NC_("x86-flag", /*/flag:hwp_notify*/ "HWP notification") },
+ { "hwp_act_window", NC_("x86-flag", /*/flag:hwp_act_window*/ "HWP Activity Window") },
+ { "hwp_epp", NC_("x86-flag", /*/flag:hwp_epp*/ "HWP Energy Performance Preference") },
+ { "hwp_pkg_req", NC_("x86-flag", /*/flag:hwp_pkg_req*/ "HWP package-level request") },
+/* AMD SVM Feature Identification, CPUID level 0x8000000a (edx) */
+ { "npt", NC_("x86-flag", /*/flag:npt*/ "AMD Nested Page Table support") },
+ { "lbrv", NC_("x86-flag", /*/flag:lbrv*/ "AMD LBR Virtualization support") },
+ { "svm_lock", NC_("x86-flag", /*/flag:svm_lock*/ "AMD SVM locking MSR") },
+ { "nrip_save", NC_("x86-flag", /*/flag:nrip_save*/ "AMD SVM next_rip save") },
+ { "tsc_scale", NC_("x86-flag", /*/flag:tsc_scale*/ "AMD TSC scaling support") },
+ { "vmcb_clean", NC_("x86-flag", /*/flag:vmcb_clean*/ "AMD VMCB clean bits support") },
+ { "flushbyasid", NC_("x86-flag", /*/flag:flushbyasid*/ "AMD flush-by-ASID support") },
+ { "decodeassists", NC_("x86-flag", /*/flag:decodeassists*/ "AMD Decode Assists support") },
+ { "pausefilter", NC_("x86-flag", /*/flag:pausefilter*/ "AMD filtered pause intercept") },
+ { "pfthreshold", NC_("x86-flag", /*/flag:pfthreshold*/ "AMD pause filter threshold") },
+ { "avic", NC_("x86-flag", /*/flag:avic*/ "Virtual Interrupt Controller") },
+/* Intel-defined CPU features, CPUID level 0x00000007:0 (ecx) */
+ { "pku", NC_("x86-flag", /*/flag:pku*/ "Protection Keys for Userspace") },
+ { "ospke", NC_("x86-flag", /*/flag:ospke*/ "OS Protection Keys Enable") },
+/* AMD-defined CPU features, CPUID level 0x80000007 (ebx) */
+ { "overflow_recov", NC_("x86-flag", /*/flag:overflow_recov*/ "MCA overflow recovery support") },
+ { "succor", NC_("x86-flag", /*/flag:succor*/ "uncorrectable error containment and recovery") },
+ { "smca", NC_("x86-flag", /*/flag:smca*/ "Scalable MCA") },
+
+/* bug workarounds */
+ { "bug:f00f", NC_("x86-flag", /*/bug:f00f*/ "Intel F00F bug") },
+ { "bug:fdiv", NC_("x86-flag", /*/bug:fdiv*/ "FPU FDIV") },
+ { "bug:coma", NC_("x86-flag", /*/bug:coma*/ "Cyrix 6x86 coma") },
+ { "bug:tlb_mmatch", NC_("x86-flag", /*/bug:tlb_mmatch*/ "AMD Erratum 383") },
+ { "bug:apic_c1e", NC_("x86-flag", /*/bug:apic_c1e*/ "AMD Erratum 400") },
+ { "bug:11ap", NC_("x86-flag", /*/bug:11ap*/ "Bad local APIC aka 11AP") },
+ { "bug:fxsave_leak", NC_("x86-flag", /*/bug:fxsave_leak*/ "FXSAVE leaks FOP/FIP/FOP") },
+ { "bug:clflush_monitor", NC_("x86-flag", /*/bug:clflush_monitor*/ "AAI65, CLFLUSH required before MONITOR") },
+ { "bug:sysret_ss_attrs", NC_("x86-flag", /*/bug:sysret_ss_attrs*/ "SYSRET doesn't fix up SS attrs") },
+ { "bug:espfix", NC_("x86-flag", /*/bug:espfix*/ "IRET to 16-bit SS corrupts ESP/RSP high bits") },
+ { "bug:null_seg", NC_("x86-flag", /*/bug:null_seg*/ "Nulling a selector preserves the base") }, /* see: detect_null_seg_behavior() */
+ { "bug:swapgs_fence", NC_("x86-flag", /*/bug:swapgs_fence*/ "SWAPGS without input dep on GS") },
+ { "bug:monitor", NC_("x86-flag", /*/bug:monitor*/ "IPI required to wake up remote CPU") },
+ { "bug:amd_e400", NC_("x86-flag", /*/bug:amd_e400*/ "AMD Erratum 400") },
+ { "bug:cpu_insecure", NC_("x86-flag", /*/bug:cpu_insecure & bug:cpu_meltdown*/ "CPU is affected by meltdown attack and needs kernel page table isolation") },
+ { "bug:cpu_meltdown", NC_("x86-flag", /*/bug:cpu_insecure & bug:cpu_meltdown*/ "CPU is affected by meltdown attack and needs kernel page table isolation") },
+ { "bug:spectre_v1", NC_("x86-flag", /*/bug:spectre_v1*/ "CPU is affected by Spectre variant 1 attack with conditional branches") },
+ { "bug:spectre_v2", NC_("x86-flag", /*/bug:spectre_v2*/ "CPU is affected by Spectre variant 2 attack with indirect branches") },
+ { "bug:spec_store_bypass", NC_("x86-flag", /*/bug:spec_store_bypass*/ "CPU is affected by speculative store bypass attack") },
+ { "bug:l1tf", NC_("x86-flag", /*/bug:l1tf*/ "CPU is affected by L1 Terminal Fault") },
+/* power management
+ * ... from arch/x86/kernel/cpu/powerflags.h */
+ { "pm:ts", NC_("x86-flag", /*/flag:pm:ts*/ "temperature sensor") },
+ { "pm:fid", NC_("x86-flag", /*/flag:pm:fid*/ "frequency id control") },
+ { "pm:vid", NC_("x86-flag", /*/flag:pm:vid*/ "voltage id control") },
+ { "pm:ttp", NC_("x86-flag", /*/flag:pm:ttp*/ "thermal trip") },
+ { "pm:tm", NC_("x86-flag", /*/flag:pm:tm*/ "hardware thermal control") },
+ { "pm:stc", NC_("x86-flag", /*/flag:pm:stc*/ "software thermal control") },
+ { "pm:100mhzsteps", NC_("x86-flag", /*/flag:pm:100mhzsteps*/ "100 MHz multiplier control") },
+ { "pm:hwpstate", NC_("x86-flag", /*/flag:pm:hwpstate*/ "hardware P-state control") },
+ { "pm:cpb", NC_("x86-flag", /*/flag:pm:cpb*/ "core performance boost") },
+ { "pm:eff_freq_ro", NC_("x86-flag", /*/flag:pm:eff_freq_ro*/ "Readonly aperf/mperf") },
+ { "pm:proc_feedback", NC_("x86-flag", /*/flag:pm:proc_feedback*/ "processor feedback interface") },
+ { "pm:acc_power", NC_("x86-flag", /*/flag:pm:acc_power*/ "accumulated power mechanism") },
+ { NULL, NULL},
+};
+
+static struct flag_to_meaning *tab_flag_meaning;
+
+static char all_flags[4096] = "";
+
+static void build_meaning_table_iter(JsonObject *object,
+ const gchar *member_name,
+ JsonNode *member_node,
+ gpointer user_data)
+{
+ int *i = user_data;
+
+ tab_flag_meaning[*i] = (struct flag_to_meaning) {
+ .name = g_strdup(member_name),
+ .meaning = g_strdup(json_node_get_string(member_node)),
+ };
+
+ (*i)++;
+}
+
+void cpuflags_x86_init(void)
+{
+ gchar *flag_json = g_build_filename(g_get_user_config_dir(), "hardinfo2",
+ "cpuflags.json", NULL);
+ gboolean use_builtin_table = TRUE;
+
+ if (!g_file_test(flag_json, G_FILE_TEST_EXISTS))
+ goto use_builtin_table;
+
+ JsonParser *parser = json_parser_new();
+ if (!json_parser_load_from_file(parser, flag_json, NULL))
+ goto use_builtin_table_with_json;
+
+ JsonNode *root = json_parser_get_root(parser);
+ if (json_node_get_node_type(root) != JSON_NODE_OBJECT)
+ goto use_builtin_table_with_json;
+
+ JsonObject *x86_flags =
+ json_object_get_object_member(json_node_get_object(root), "x86");
+ if (!x86_flags)
+ goto use_builtin_table_with_json;
+
+ tab_flag_meaning =
+ g_new(struct flag_to_meaning, json_object_get_size(x86_flags) + 1);
+ int i = 0;
+ json_object_foreach_member(x86_flags, build_meaning_table_iter, &i);
+ tab_flag_meaning[i] = (struct flag_to_meaning){NULL, NULL};
+ use_builtin_table = FALSE;
+
+use_builtin_table_with_json:
+ g_object_unref(parser);
+use_builtin_table:
+ g_free(flag_json);
+
+ if (use_builtin_table)
+ tab_flag_meaning = (struct flag_to_meaning *)builtin_tab_flag_meaning;
+}
+
+const char *x86_flag_meaning(const char *flag) {
+ int i;
+
+ if (!flag)
+ return NULL;
+
+ for (i = 0; tab_flag_meaning[i].name; i++) {
+ if (strcmp(tab_flag_meaning[i].name, flag) == 0) {
+ if (tab_flag_meaning[i].meaning != NULL)
+ return C_("x86-flag", tab_flag_meaning[i].meaning);
+ else return NULL;
+ }
+ }
+
+ return NULL;
+}
+
+static void x86_flag_find_dups(void) {
+ int t, i;
+
+ t = 0;
+ while(tab_flag_meaning[t].name != NULL) {
+ i = t+1;
+ while(tab_flag_meaning[i].name != NULL) {
+ if (strcmp(tab_flag_meaning[t].name, tab_flag_meaning[i].name) == 0) {
+ printf("x86-flag duplicate definition: %s\n ... %d: %s\n ... %d: %s\n",
+ tab_flag_meaning[i].name,
+ t, tab_flag_meaning[t].meaning,
+ i, tab_flag_meaning[i].meaning);
+ }
+ i++;
+ }
+ t++;
+ }
+}
diff --git a/modules/devices/x86/x86_data.h b/modules/devices/x86/x86_data.h
new file mode 100644
index 00000000..66a4c80f
--- /dev/null
+++ b/modules/devices/x86/x86_data.h
@@ -0,0 +1,28 @@
+/*
+ * rpiz - https://github.com/bp0/rpiz
+ * 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; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#ifndef _X86DATA_H_
+#define _X86DATA_H_
+
+/* cpu flags from /proc/cpuinfo */
+const char *x86_flag_list(void); /* list of all known flags */
+const char *x86_flag_meaning(const char *flag); /* lookup flag meaning */
+
+#endif
diff --git a/modules/network.c b/modules/network.c
new file mode 100644
index 00000000..4b65e0aa
--- /dev/null
+++ b/modules/network.c
@@ -0,0 +1,447 @@
+/*
+ * HardInfo - Displays System Information
+ * Copyright (C) 2003-2008 L. A. F. Pereira <l@tia.mat.br>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, version 2 or later.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#include <ctype.h>
+#include <stdlib.h>
+#include <string.h>
+#include <gtk/gtk.h>
+#include <config.h>
+#include <time.h>
+#include <string.h>
+#include <sys/utsname.h>
+#include <sys/stat.h>
+
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <netdb.h>
+
+#include <hardinfo.h>
+#include <iconcache.h>
+#include <shell.h>
+
+#include <vendor.h>
+
+#include "network.h"
+
+/* Callbacks */
+gchar *callback_network();
+gchar *callback_route();
+gchar *callback_dns();
+gchar *callback_connections();
+gchar *callback_shares();
+gchar *callback_arp();
+gchar *callback_statistics();
+
+/* Scan callbacks */
+void scan_network(gboolean reload);
+void scan_route(gboolean reload);
+void scan_dns(gboolean reload);
+void scan_connections(gboolean reload);
+void scan_shares(gboolean reload);
+void scan_arp(gboolean reload);
+void scan_statistics(gboolean reload);
+
+static ModuleEntry entries[] = {
+ {N_("Interfaces"), "network-interface.png", callback_network, scan_network, MODULE_FLAG_NONE},
+ {N_("IP Connections"), "network-connections.png", callback_connections, scan_connections, MODULE_FLAG_NONE},
+ {N_("Routing Table"), "network.png", callback_route, scan_route, MODULE_FLAG_NONE},
+ {N_("ARP Table"), "module.png", callback_arp, scan_arp, MODULE_FLAG_NONE},
+ {N_("DNS Servers"), "dns.png", callback_dns, scan_dns, MODULE_FLAG_NONE},
+ {N_("Statistics"), "network-statistics.png", callback_statistics, scan_statistics, MODULE_FLAG_NONE},
+ {N_("Shared Directories"), "shares.png", callback_shares, scan_shares, MODULE_FLAG_NONE},
+ {NULL},
+};
+
+void scan_shares(gboolean reload)
+{
+ SCAN_START();
+ scan_samba();
+ scan_nfs_shared_directories();
+ SCAN_END();
+}
+
+static gchar *__statistics = NULL;
+void scan_statistics(gboolean reload)
+{
+ FILE *netstat;
+ gchar buffer[256];
+ gchar *netstat_path;
+ int line = 0;
+
+ SCAN_START();
+
+ g_free(__statistics);
+ __statistics = g_strdup("");
+
+ if ((netstat_path = find_program("netstat"))) {
+ gchar *command_line = g_strdup_printf("%s -s", netstat_path);
+
+ if ((netstat = popen(command_line, "r"))) {
+ while (fgets(buffer, 256, netstat)) {
+ if (!isspace(buffer[0]) && strchr(buffer, ':')) {
+ gchar *tmp;
+
+ tmp = g_ascii_strup(strend(buffer, ':'), -1);
+
+ __statistics = h_strdup_cprintf("[%s]\n",
+ __statistics,
+ tmp);
+ g_free(tmp);
+
+ } else {
+ gchar *tmp = buffer;
+
+ while (*tmp && isspace(*tmp)) tmp++;
+ /* the bolded-space/dot used here is a hardinfo shell hack */
+ if (params.markup_ok)
+ __statistics = h_strdup_cprintf("<b> </b>#%d=%s\n",
+ __statistics,
+ line++, tmp);
+ else
+ __statistics = h_strdup_cprintf(">#%d=%s\n",
+ __statistics,
+ line++, tmp);
+ }
+ }
+
+ pclose(netstat);
+ }
+
+ g_free(command_line);
+ g_free(netstat_path);
+ }
+
+ SCAN_END();
+}
+
+static gchar *__nameservers = NULL;
+void scan_dns(gboolean reload)
+{
+ FILE *resolv;
+ gchar buffer[256];
+
+ SCAN_START();
+
+ g_free(__nameservers);
+ __nameservers = g_strdup("");
+
+ if ((resolv = fopen("/etc/resolv.conf", "r"))) {
+ while (fgets(buffer, 256, resolv)) {
+ if (g_str_has_prefix(buffer, "nameserver")) {
+ gchar *ip;
+ struct sockaddr_in sa;
+ char hbuf[NI_MAXHOST];
+
+ ip = g_strstrip(buffer + sizeof("nameserver"));
+
+ sa.sin_family = AF_INET;
+ sa.sin_addr.s_addr = inet_addr(ip);
+
+ if (getnameinfo((struct sockaddr *)&sa, sizeof(sa), hbuf, sizeof(hbuf), NULL, 0, NI_NAMEREQD)) {
+ __nameservers = h_strdup_cprintf("%s=\n",
+ __nameservers,
+ ip);
+ } else {
+ __nameservers = h_strdup_cprintf("%s=%s\n",
+ __nameservers,
+ ip, hbuf);
+ }
+
+ shell_status_pulse();
+ }
+ }
+ fclose(resolv);
+ }
+
+ SCAN_END();
+}
+
+void scan_network(gboolean reload)
+{
+ SCAN_START();
+ scan_net_interfaces();
+ SCAN_END();
+}
+
+static gchar *__routing_table = NULL;
+void scan_route(gboolean reload)
+{
+ FILE *route;
+ gchar buffer[256];
+ gchar *route_path;
+
+ SCAN_START();
+
+ g_free(__routing_table);
+ __routing_table = g_strdup("");
+
+ if ((route_path = find_program("route"))) {
+ gchar *command_line = g_strdup_printf("%s -n", route_path);
+
+ if ((route = popen(command_line, "r"))) {
+ /* eat first two lines */
+ (void)fgets(buffer, 256, route);
+ (void)fgets(buffer, 256, route);
+
+ while (fgets(buffer, 256, route)) {
+ buffer[15] = '\0';
+ buffer[31] = '\0';
+ buffer[47] = '\0';
+ buffer[53] = '\0';
+
+ __routing_table = h_strdup_cprintf("%s / %s=%s|%s|%s\n",
+ __routing_table,
+ g_strstrip(buffer), g_strstrip(buffer + 16),
+ g_strstrip(buffer + 72),
+ g_strstrip(buffer + 48),
+ g_strstrip(buffer + 32));
+ }
+
+ pclose(route);
+ }
+
+ g_free(command_line);
+ g_free(route_path);
+ }
+
+ SCAN_END();
+}
+
+static gchar *__arp_table = NULL;
+void scan_arp(gboolean reload)
+{
+ FILE *arp;
+ gchar buffer[256];
+
+ SCAN_START();
+
+ g_free(__arp_table);
+ __arp_table = g_strdup("");
+
+ if ((arp = fopen("/proc/net/arp", "r"))) {
+ /* eat first line */
+ (void)fgets(buffer, 256, arp);
+
+ while (fgets(buffer, 256, arp)) {
+ buffer[15] = '\0';
+ buffer[58] = '\0';
+
+ __arp_table = h_strdup_cprintf("%s=%s|%s\n",
+ __arp_table,
+ g_strstrip(buffer),
+ g_strstrip(buffer + 72),
+ g_strstrip(buffer + 41));
+ }
+
+ fclose(arp);
+ }
+
+ SCAN_END();
+}
+
+static gchar *__connections = NULL;
+void scan_connections(gboolean reload)
+{
+ FILE *netstat;
+ gchar buffer[256];
+ gchar *netstat_path;
+
+ SCAN_START();
+
+ g_free(__connections);
+ __connections = g_strdup("");
+
+ if ((netstat_path = find_program("netstat"))) {
+ gchar *command_line = g_strdup_printf("%s -an", netstat_path);
+
+ if ((netstat = popen("netstat -an", "r"))) {
+ while (fgets(buffer, 256, netstat)) {
+ buffer[6] = '\0';
+ buffer[43] = '\0';
+ buffer[67] = '\0';
+
+ if (g_str_has_prefix(buffer, "tcp") || g_str_has_prefix(buffer, "udp")) {
+ __connections = h_strdup_cprintf("%s=%s|%s|%s\n",
+ __connections,
+ g_strstrip(buffer + 20), /* local address */
+ g_strstrip(buffer), /* protocol */
+ g_strstrip(buffer + 44), /* foreign address */
+ g_strstrip(buffer + 68)); /* state */
+ }
+ }
+
+ pclose(netstat);
+ }
+
+ g_free(command_line);
+ g_free(netstat_path);
+ }
+
+ SCAN_END();
+}
+
+gchar *callback_arp()
+{
+ return g_strdup_printf("[%s]\n"
+ "%s\n"
+ "[$ShellParam$]\n"
+ "ReloadInterval=3000\n"
+ "ColumnTitle$TextValue=%s\n" /* IP Address */
+ "ColumnTitle$Value=%s\n" /* Interface */
+ "ColumnTitle$Extra1=%s\n" /* MAC Address */
+ "ShowColumnHeaders=true\n",
+ _("ARP Table"), __arp_table,
+ _("IP Address"), _("Interface"), _("MAC Address") );
+}
+
+gchar *callback_shares()
+{
+ return g_strdup_printf("[%s]\n"
+ "%s\n"
+ "[%s]\n"
+ "%s",
+ _("SAMBA"), smb_shares_list,
+ _("NFS"), nfs_shares_list);
+}
+
+gchar *callback_dns()
+{
+ return g_strdup_printf("[%s]\n"
+ "%s\n"
+ "[$ShellParam$]\n"
+ "ColumnTitle$TextValue=%s\n" /* IP Address */
+ "ColumnTitle$Value=%s\n" /* Name */
+ "ShowColumnHeaders=true\n",
+ _("Name Servers"), __nameservers,
+ _("IP Address"), _("Name") );
+}
+
+gchar *callback_connections()
+{
+ return g_strdup_printf("[%s]\n"
+ "%s\n"
+ "[$ShellParam$]\n"
+ "ReloadInterval=3000\n"
+ "ColumnTitle$TextValue=%s\n" /* Local Address */
+ "ColumnTitle$Value=%s\n" /* Protocol */
+ "ColumnTitle$Extra1=%s\n" /* Foreign Address */
+ "ColumnTitle$Extra2=%s\n" /* State */
+ "ShowColumnHeaders=true\n",
+ _("Connections"), __connections,
+ _("Local Address"), _("Protocol"), _("Foreign Address"), _("State") );
+}
+
+gchar *callback_network()
+{
+ return g_strdup_printf("%s\n"
+ "[$ShellParam$]\n"
+ "ReloadInterval=3000\n"
+ "ViewType=1\n"
+ "ColumnTitle$TextValue=%s\n" /* Interface */
+ "ColumnTitle$Value=%s\n" /* IP Address */
+ "ColumnTitle$Extra1=%s\n" /* Sent */
+ "ColumnTitle$Extra2=%s\n" /* Received */
+ "ShowColumnHeaders=true\n"
+ "%s",
+ network_interfaces,
+ _("Interface"), _("IP Address"), _("Sent"), _("Received"),
+ network_icons);
+}
+
+gchar *callback_route()
+{
+ return g_strdup_printf("[%s]\n"
+ "%s\n"
+ "[$ShellParam$]\n"
+ "ViewType=0\n"
+ "ReloadInterval=3000\n"
+ "ColumnTitle$TextValue=%s\n" /* Destination / Gateway */
+ "ColumnTitle$Value=%s\n" /* Interface */
+ "ColumnTitle$Extra1=%s\n" /* Flags */
+ "ColumnTitle$Extra2=%s\n" /* Mask */
+ "ShowColumnHeaders=true\n",
+ _("IP routing table"), __routing_table,
+ _("Destination/Gateway"), _("Interface"), _("Flags"), _("Mask") );
+}
+
+gchar *callback_statistics()
+{
+ return g_strdup_printf("%s\n"
+ "[$ShellParam$]\n"
+ "ReloadInterval=3000\n",
+ __statistics);
+}
+
+gchar *hi_more_info(gchar * entry)
+{
+ gchar *info = moreinfo_lookup_with_prefix("NET", entry);
+
+ if (info)
+ return g_strdup(info);
+
+ return g_strdup_printf("[%s]", entry);
+}
+
+ModuleEntry *hi_module_get_entries(void)
+{
+ return entries;
+}
+
+gchar *hi_module_get_name(void)
+{
+ return g_strdup(_("Network"));
+}
+
+guchar hi_module_get_weight(void)
+{
+ return 160;
+}
+
+void hi_module_init(void)
+{
+}
+
+void hi_module_deinit(void)
+{
+ moreinfo_del_with_prefix("NET");
+
+ g_free(smb_shares_list);
+ g_free(nfs_shares_list);
+ g_free(network_interfaces);
+ g_free(network_icons);
+
+ g_free(__statistics);
+ g_free(__nameservers);
+ g_free(__arp_table);
+ g_free(__routing_table);
+ g_free(__connections);
+}
+
+const ModuleAbout *hi_module_get_about(void)
+{
+ static const ModuleAbout ma = {
+ .author = "L. A. F. Pereira",
+ .description =
+ N_("Gathers information about this computer's network connection"),
+ .version = VERSION,
+ .license = "GNU GPL version 2 or later.",
+ };
+
+ return &ma;
+}
diff --git a/modules/network/net.c b/modules/network/net.c
new file mode 100644
index 00000000..9a5cb5f1
--- /dev/null
+++ b/modules/network/net.c
@@ -0,0 +1,494 @@
+/*
+ * HardInfo - Displays System Information
+ * Copyright (C) 2003-2008 L. A. F. Pereira <l@tia.mat.br>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, version 2 or later.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+/*
+ * Wireless Extension Example
+ * http://www.krugle.org/examples/p-OZYzuisV6gyQIaTu/iwconfig.c
+ */
+
+#include "config.h"
+
+#include <stdio.h>
+#include <unistd.h>
+#include <string.h>
+
+#include <sys/ioctl.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+
+#include <netinet/in.h>
+#include <linux/sockios.h>
+
+#include <arpa/inet.h>
+
+#ifdef HAS_LINUX_WE
+#include <linux/if.h>
+#include <linux/wireless.h>
+#else
+#include <net/if.h>
+#endif /* HAS_LINUX_WE */
+
+#include "hardinfo.h"
+#include "network.h"
+
+gchar *network_interfaces = NULL, *network_icons = NULL;
+
+typedef struct _NetInfo NetInfo;
+struct _NetInfo {
+ char name[16];
+ int mtu;
+ unsigned char mac[8];
+ char ip[16];
+ char mask[16];
+ char broadcast[16];
+
+#ifdef HAS_LINUX_WE
+ char wi_essid[IW_ESSID_MAX_SIZE + 1];
+ int wi_rate;
+ int wi_mode, wi_status;
+ gboolean wi_has_txpower;
+ struct iw_param wi_txpower;
+ int wi_quality_level, wi_signal_level, wi_noise_level;
+ gboolean is_wireless;
+#endif
+};
+
+#ifdef HAS_LINUX_WE
+const gchar *wi_operation_modes[] = {
+ NC_("wi-op-mode", "Auto"),
+ NC_("wi-op-mode", "Ad-Hoc"),
+ NC_("wi-op-mode", "Managed"),
+ NC_("wi-op-mode", "Master"),
+ NC_("wi-op-mode", "Repeater"),
+ NC_("wi-op-mode", "Secondary"),
+ NC_("wi-op-mode", "(Unknown)")
+};
+
+void get_wireless_info(int fd, NetInfo *netinfo)
+{
+ FILE *wrls;
+ char wbuf[256];
+ struct iwreq wi_req;
+ int r, trash;
+
+ netinfo->is_wireless = FALSE;
+
+ if ((wrls = fopen("/proc/net/wireless", "r"))) {
+ while (fgets(wbuf, 256, wrls)) {
+ if (strchr(wbuf, ':') && strstr(wbuf, netinfo->name)) {
+ gchar *buf1 = wbuf;
+
+ netinfo->is_wireless = TRUE;
+
+ buf1 = strchr(buf1, ':') + 1;
+
+ if (strchr(buf1, '.')) {
+ sscanf(buf1, "%d %d. %d. %d %d %d %d %d %d %d",
+ &(netinfo->wi_status),
+ &(netinfo->wi_quality_level),
+ &(netinfo->wi_signal_level),
+ &(netinfo->wi_noise_level),
+ &trash, &trash, &trash, &trash, &trash, &trash);
+ } else {
+ sscanf(buf1, "%d %d %d %d %d %d %d %d %d %d",
+ &(netinfo->wi_status),
+ &(netinfo->wi_quality_level),
+ &(netinfo->wi_signal_level),
+ &(netinfo->wi_noise_level),
+ &trash, &trash, &trash, &trash, &trash,
+ &trash);
+ }
+
+ break;
+ }
+ }
+ fclose(wrls);
+ }
+
+ if (!netinfo->is_wireless)
+ return;
+
+ strncpy(wi_req.ifr_name, netinfo->name, 16);
+
+ /* obtain essid */
+ wi_req.u.essid.pointer = netinfo->wi_essid;
+ wi_req.u.essid.length = IW_ESSID_MAX_SIZE + 1;
+ wi_req.u.essid.flags = 0;
+
+ if (ioctl(fd, SIOCGIWESSID, &wi_req) < 0) {
+ strcpy(netinfo->wi_essid, "");
+ } else {
+ netinfo->wi_essid[wi_req.u.essid.length] = '\0';
+ }
+
+ /* obtain bit rate */
+ if (ioctl(fd, SIOCGIWRATE, &wi_req) < 0) {
+ netinfo->wi_rate = 0;
+ } else {
+ netinfo->wi_rate = wi_req.u.bitrate.value;
+ }
+
+ /* obtain operation mode */
+ if (ioctl(fd, SIOCGIWMODE, &wi_req) < 0) {
+ netinfo->wi_mode = 0;
+ } else {
+ if (wi_req.u.mode < 6) {
+ netinfo->wi_mode = wi_req.u.mode;
+ } else {
+ netinfo->wi_mode = 6;
+ }
+ }
+
+#if WIRELESS_EXT >= 10
+ /* obtain txpower */
+ if (ioctl(fd, SIOCGIWTXPOW, &wi_req) < 0) {
+ netinfo->wi_has_txpower = FALSE;
+ } else {
+ netinfo->wi_has_txpower = TRUE;
+
+ memcpy(&netinfo->wi_txpower, &wi_req.u.txpower, sizeof(struct iw_param));
+ }
+#else
+ netinfo->wi_has_txpower = FALSE;
+#endif /* WIRELESS_EXT >= 10 */
+}
+#endif /* HAS_LINUX_WE */
+
+void get_net_info(char *if_name, NetInfo * netinfo)
+{
+ struct ifreq ifr;
+ int fd;
+
+ fd = socket(PF_INET, SOCK_DGRAM, IPPROTO_IP);
+
+ /* IPv4 */
+ ifr.ifr_addr.sa_family = AF_INET;
+ strncpy(netinfo->name, if_name, sizeof(netinfo->name));
+
+ /* MTU */
+ strcpy(ifr.ifr_name, if_name);
+ if (ioctl(fd, SIOCGIFMTU, &ifr) < 0) {
+ netinfo->mtu = 0;
+ } else {
+ netinfo->mtu = ifr.ifr_mtu;
+ }
+
+ /* HW Address */
+ strcpy(ifr.ifr_name, if_name);
+ if (ioctl(fd, SIOCGIFHWADDR, &ifr) < 0) {
+ memset(netinfo->mac, 0, 8);
+ } else {
+ memcpy(netinfo->mac, ifr.ifr_ifru.ifru_hwaddr.sa_data, 8);
+ }
+
+ /* IP Address */
+ strcpy(ifr.ifr_name, if_name);
+ if (ioctl(fd, SIOCGIFADDR, &ifr) < 0) {
+ netinfo->ip[0] = 0;
+ } else {
+ snprintf(netinfo->ip, sizeof(netinfo->ip), "%s",
+ inet_ntoa(((struct sockaddr_in *) &ifr.ifr_addr)->
+ sin_addr));
+ }
+
+ /* Mask Address */
+ strcpy(ifr.ifr_name, if_name);
+ if (ioctl(fd, SIOCGIFNETMASK, &ifr) < 0) {
+ netinfo->mask[0] = 0;
+ } else {
+ snprintf(netinfo->mask, sizeof(netinfo->mask), "%s",
+ inet_ntoa(((struct sockaddr_in *) &ifr.ifr_addr)->
+ sin_addr));
+ }
+
+ /* Broadcast Address */
+ strcpy(ifr.ifr_name, if_name);
+ if (ioctl(fd, SIOCGIFBRDADDR, &ifr) < 0) {
+ netinfo->broadcast[0] = 0;
+ } else {
+ snprintf(netinfo->broadcast, sizeof(netinfo->broadcast), "%s",
+ inet_ntoa(((struct sockaddr_in *) &ifr.ifr_addr)->
+ sin_addr));
+ }
+
+#ifdef HAS_LINUX_WE
+ get_wireless_info(fd, netinfo);
+#endif
+
+ shutdown(fd, 0);
+ close(fd);
+}
+
+static struct {
+ char *type;
+ char *label;
+ char *icon;
+} netdev2type[] = {
+ /* Classic */
+ { "eth", NC_("net-if-type", "Ethernet"), "network-interface" },
+ { "lo", NC_("net-if-type", "Loopback"), "network" },
+ { "ppp", NC_("net-if-type", "Point-to-Point"), "modem" },
+ { "ath", NC_("net-if-type", "Wireless"), "wireless" },
+ { "wlan", NC_("net-if-type", "Wireless"), "wireless" },
+ { "ra", NC_("net-if-type", "Wireless"), "wireless" },
+ { "wmaster", NC_("net-if-type", "Wireless"), "wireless" },
+ { "tun", NC_("net-if-type", "Virtual Point-to-Point (TUN)"), "network" },
+ { "tap", NC_("net-if-type", "Ethernet (TAP)"), "network" },
+ { "plip", NC_("net-if-type", "Parallel Line Internet Protocol"), "network" },
+ { "irlan", NC_("net-if-type", "Infrared"), "network" },
+ { "slip", NC_("net-if-type", "Serial Line Internet Protocol"), "network" },
+ { "isdn", NC_("net-if-type", "Integrated Services Digital Network"), "modem" },
+ { "sit", NC_("net-if-type", "IPv6-over-IPv4 Tunnel"), "network" },
+ { "vmnet8", NC_("net-if-type", "VMWare Virtual Network Interface (NAT)"), "computer" },
+ { "vmnet", NC_("net-if-type", "VMWare Virtual Network Interface"), "computer" },
+ { "pan", NC_("net-if-type", "Personal Area Network (PAN)"), "bluetooth" },
+ { "bnep", NC_("net-if-type", "Bluetooth"), "bluetooth" },
+ { "br", NC_("net-if-type", "Bridge Interface"), "network" },
+ { "ham", NC_("net-if-type", "Hamachi Virtual Personal Network"), "network"},
+ { "net", NC_("net-if-type", "Ethernet"), "network-interface" },
+ { "ifb", NC_("net-if-type", "Intermediate Functional Block"), "network" },
+ { "gre", NC_("net-if-type", "GRE Network Tunnel"), "network" },
+ { "msh", NC_("net-if-type", "Mesh Network"), "wireless" },
+ { "wmaster", NC_("net-if-type", "Wireless Master Interface"), "wireless" },
+ { "vboxnet", NC_("net-if-type", "VirtualBox Virtual Network Interface"), "network" },
+
+ /* Predictable network interface device names (systemd) */
+ { "en", NC_("net-if-type", "Ethernet"), "network-interface" },
+ { "sl", NC_("net-if-type", "Serial Line Internet Protocol"), "network" },
+ { "wl", NC_("net-if-type", "Wireless"), "wireless" },
+ { "ww", NC_("net-if-type", "Wireless (WAN)"), "wireless" },
+
+ { NULL, NC_("net-if-type", "(Unknown)"), "network" },
+};
+
+static void net_get_iface_type(gchar * name, gchar ** type, gchar ** icon, NetInfo *ni)
+{
+ int i;
+
+#ifdef HAS_LINUX_WE
+ if (ni->is_wireless) {
+ *type = "Wireless"; /* translated when used */
+ *icon = "wireless";
+
+ return;
+ }
+#endif
+
+ for (i = 0; netdev2type[i].type; i++) {
+ if (g_str_has_prefix(name, netdev2type[i].type))
+ break;
+ }
+
+ *type = netdev2type[i].label; /* translated when used */
+ *icon = netdev2type[i].icon;
+}
+
+static gboolean
+remove_net_devices(gpointer key, gpointer value, gpointer data)
+{
+ return g_str_has_prefix(key, "NET");
+}
+
+#ifdef HAS_LINUX_WE
+const char *wifi_bars(int signal, int noise)
+{
+ signal = -signal;
+
+ if (signal > 80)
+ return "▰▰▰▰▰";
+ if (signal > 55)
+ return "▰▰▰▰▱";
+ if (signal > 30)
+ return "▰▰▰▱▱";
+ if (signal > 15)
+ return "▰▰▱▱▱";
+ if (signal > 5)
+ return "▰▱▱▱▱";
+ return "▱▱▱▱▱";
+}
+#endif
+
+static void scan_net_interfaces_24(void)
+{
+ FILE *proc_net;
+ NetInfo ni;
+ gchar buffer[256];
+ gchar *devid, *detailed;
+ gdouble recv_bytes;
+ gdouble recv_errors;
+ gdouble recv_packets;
+
+ gdouble trans_bytes;
+ gdouble trans_errors;
+ gdouble trans_packets;
+
+ if (!g_file_test("/proc/net/dev", G_FILE_TEST_EXISTS)) {
+ if (network_interfaces) {
+ g_free(network_interfaces);
+ network_interfaces = g_strdup_printf("[%s]]\n%s=\n",
+ _("Network Interfaces"), _("None Found") );
+ }
+
+ return;
+ }
+
+ g_free(network_interfaces);
+
+ g_free(network_icons);
+
+ network_interfaces = g_strdup_printf("[%s]\n", _("Network Interfaces"));
+ network_icons = g_strdup("");
+
+ proc_net = fopen("/proc/net/dev", "r");
+ if (!proc_net)
+ return;
+
+ while (fgets(buffer, 256, proc_net)) {
+ if (strchr(buffer, ':')) {
+ gint trash;
+ gchar ifacename[16];
+ gchar *buf = buffer;
+ gchar *iface_type, *iface_icon;
+ gint i;
+
+ buf = g_strstrip(buf);
+
+ memset(ifacename, 0, 16);
+
+ for (i = 0; buffer[i] != ':' && i < 16; i++) {
+ ifacename[i] = buffer[i];
+ }
+
+ buf = strchr(buf, ':') + 1;
+
+ /* iface: bytes packets errs drop fifo frame compressed multicast */
+ sscanf(buf, "%lf %lf %lf %d %d %d %d %d %lf %lf %lf",
+ &recv_bytes, &recv_packets,
+ &recv_errors, &trash, &trash, &trash, &trash,
+ &trash, &trans_bytes, &trans_packets, &trans_errors);
+
+ gdouble recv_mb = recv_bytes / 1048576.0;
+ gdouble trans_mb = trans_bytes / 1048576.0;
+
+ get_net_info(ifacename, &ni);
+
+ devid = g_strdup_printf("NET%s", ifacename);
+
+ network_interfaces =
+ h_strdup_cprintf
+ ("$%s$%s=%s|%.2lf%s|%.2lf%s\n",
+ network_interfaces, devid, ifacename, ni.ip[0] ? ni.ip : "",
+ trans_mb, _("MiB"), recv_mb, _("MiB"));
+ net_get_iface_type(ifacename, &iface_type, &iface_icon, &ni);
+
+ network_icons = h_strdup_cprintf("Icon$%s$%s=%s.png\n",
+ network_icons, devid,
+ ifacename, iface_icon);
+
+ detailed = g_strdup_printf("[%s]\n"
+ "%s=%s\n" /* Interface Type */
+ "%s=%02x:%02x:%02x:%02x:%02x:%02x\n" /* MAC */
+ "%s=%d\n" /* MTU */
+ "[%s]\n" /*Transfer Details*/
+ "%s=%.0lf (%.2f%s)\n" /* Bytes Received */
+ "%s=%.0lf (%.2f%s)\n" /* Bytes Sent */,
+ _("Network Adapter Properties"),
+ _("Interface Type"), C_("net-if-type", iface_type),
+ _("Hardware Address (MAC)"),
+ ni.mac[0], ni.mac[1],
+ ni.mac[2], ni.mac[3],
+ ni.mac[4], ni.mac[5],
+ _("MTU"), ni.mtu,
+ _("Transfer Details"),
+ _("Bytes Received"), recv_bytes, recv_mb, _("MiB"),
+ _("Bytes Sent"), trans_bytes, trans_mb, _("MiB"));
+
+#ifdef HAS_LINUX_WE
+ if (ni.is_wireless) {
+ gchar *txpower;
+
+ if (ni.wi_has_txpower) {
+ gint mw, dbm;
+
+ if (ni.wi_txpower.flags & IW_TXPOW_MWATT) {
+ mw = ni.wi_txpower.value;
+ dbm = (int) ceil(10.0 * log10((double) ni.wi_txpower.value));
+ } else {
+ dbm = ni.wi_txpower.value;
+ mw = (int) floor(pow(10.0, ((double) dbm / 10.0)));
+ }
+
+ txpower = g_strdup_printf("%d%s (%d%s)", dbm, _("dBm"), mw, _("mW"));
+ } else {
+ txpower = g_strdup(_("(Unknown)"));
+ }
+
+ detailed = h_strdup_cprintf("\n[%s]\n"
+ "%s=%s\n" /* Network Name (SSID) */
+ "%s=%d%s\n" /* Bit Rate */
+ "%s=%s\n" /* Transmission Power */
+ "%s=%s\n" /* Mode */
+ "%s=%d\n" /* Status */
+ "%s=%d\n" /* Link Quality */
+ "%s=%d %s / %d %s (%s)\n",
+ detailed,
+ _("Wireless Properties"),
+ _("Network Name (SSID)"), ni.wi_essid,
+ _("Bit Rate"), ni.wi_rate / 1000000, _("Mb/s"),
+ _("Transmission Power"), txpower,
+ _("Mode"), C_("wi-op-mode", wi_operation_modes[ni.wi_mode]),
+ _("Status"), ni.wi_status,
+ _("Link Quality"), ni.wi_quality_level,
+ _("Signal / Noise"),
+ ni.wi_signal_level, _("dBm"),
+ ni.wi_noise_level, _("dBm"),
+ wifi_bars(ni.wi_signal_level, ni.wi_noise_level));
+
+ g_free(txpower);
+ }
+#endif
+
+ if (ni.ip[0] || ni.mask[0] || ni.broadcast[0]) {
+ detailed =
+ h_strdup_cprintf("\n[%s]\n"
+ "%s=%s\n"
+ "%s=%s\n"
+ "%s=%s\n", detailed,
+ _("Internet Protocol (IPv4)"),
+ _("IP Address"), ni.ip[0] ? ni.ip : _("(Not set)"),
+ _("Mask"), ni.mask[0] ? ni.mask : _("(Not set)"),
+ _("Broadcast Address"),
+ ni.broadcast[0] ? ni.broadcast : _("(Not set)") );
+ }
+
+ moreinfo_add_with_prefix("NET", devid, detailed);
+ g_free(devid);
+ }
+ }
+ fclose(proc_net);
+}
+
+void scan_net_interfaces(void)
+{
+ /* FIXME: See if we're running Linux 2.6 and if /sys is mounted, then use
+ that instead of /proc/net/dev */
+
+ /* remove old devices from global device table */
+ moreinfo_del_with_prefix("NET");
+
+ scan_net_interfaces_24();
+}
diff --git a/modules/network/nfs.c b/modules/network/nfs.c
new file mode 100644
index 00000000..67095f92
--- /dev/null
+++ b/modules/network/nfs.c
@@ -0,0 +1,59 @@
+/*
+ * HardInfo - Displays System Information
+ * Copyright (C) 2003-2009 L. A. F. Pereira <l@tia.mat.br>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, version 2 or later.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#include <string.h>
+
+#include "hardinfo.h"
+#include "network.h"
+
+gchar *nfs_shares_list = NULL;
+
+void
+scan_nfs_shared_directories(void)
+{
+ FILE *exports;
+ gint count = 0;
+ gchar buf[512];
+
+ g_free(nfs_shares_list);
+
+ nfs_shares_list = g_strdup("");
+
+ if ((exports = fopen("/etc/exports", "r"))) {
+ while (fgets(buf, 512, exports)) {
+ if (buf[0] != '/')
+ continue;
+
+ strend(buf, ' ');
+ strend(buf, '\t');
+
+ nfs_shares_list = h_strdup_cprintf("%s=\n",
+ nfs_shares_list, buf);
+ count++;
+ }
+
+ fclose(exports);
+ }
+
+ if (!count) {
+ g_free(nfs_shares_list);
+
+ nfs_shares_list = g_strdup("No NFS exports=\n");
+ }
+}
+
diff --git a/modules/network/samba.c b/modules/network/samba.c
new file mode 100644
index 00000000..e14742ea
--- /dev/null
+++ b/modules/network/samba.c
@@ -0,0 +1,123 @@
+/*
+ * HardInfo - Displays System Information
+ * Copyright (C) 2003-2009 L. A. F. Pereira <l@tia.mat.br>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, version 2 or later.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#include <string.h>
+
+#include "hardinfo.h"
+#include "network.h"
+
+gchar *smb_shares_list = NULL;
+
+void scan_samba_from_string(gchar *str, gsize length);
+void scan_samba_usershares(void);
+
+void
+scan_samba(void)
+{
+ gchar *str;
+ gsize length;
+
+ if (smb_shares_list) {
+ g_free(smb_shares_list);
+ smb_shares_list = g_strdup("");
+ }
+
+ if (g_file_get_contents("/etc/samba/smb.conf",
+ &str, &length, NULL)) {
+ shell_status_update("Scanning SAMBA shares...");
+ scan_samba_from_string(str, length);
+ g_free(str);
+ }
+
+ scan_samba_usershares();
+}
+
+void
+scan_samba_usershares(void)
+{
+ FILE *usershare_list;
+ gboolean spawned;
+ int status;
+ gchar *out, *err, *p, *next_nl;
+ gchar *usershare, *cmdline;
+ gsize length;
+
+ spawned = hardinfo_spawn_command_line_sync("net usershare list",
+ &out, &err, &status, NULL);
+
+ if (spawned && status == 0 && out != NULL) {
+ shell_status_update("Scanning SAMBA user shares...");
+ p = out;
+ while(next_nl = strchr(p, '\n')) {
+ cmdline = g_strdup_printf("net usershare info '%s'",
+ strend(p, '\n'));
+ if (hardinfo_spawn_command_line_sync(cmdline,
+ &usershare, NULL, NULL, NULL)) {
+ length = strlen(usershare);
+ scan_samba_from_string(usershare, length);
+ g_free(usershare);
+ }
+ g_free(cmdline);
+
+ p = next_nl + 1;
+ }
+ g_free(out);
+ g_free(err);
+ }
+}
+
+void
+scan_samba_from_string(gchar *str, gsize length)
+{
+ GKeyFile *keyfile;
+ GError *error = NULL;
+ gchar **groups;
+ gint i = 0;
+
+ keyfile = g_key_file_new();
+
+ gchar *_smbconf = str;
+ for (; *_smbconf; _smbconf++)
+ if (*_smbconf == ';') *_smbconf = '\0';
+
+ if (!g_key_file_load_from_data(keyfile, str, length, 0, &error)) {
+ smb_shares_list = g_strdup("Cannot parse smb.conf=\n");
+ if (error)
+ g_error_free(error);
+ goto cleanup;
+ }
+
+ groups = g_key_file_get_groups(keyfile, NULL);
+ while (groups[i]) {
+ if (g_key_file_has_key(keyfile, groups[i], "path", NULL)) {
+ gchar *path = g_key_file_get_string(keyfile, groups[i], "path", NULL);
+ smb_shares_list = h_strdup_cprintf("%s=%s\n",
+ smb_shares_list,
+ groups[i], path);
+ g_free(path);
+ }
+
+ i++;
+ }
+
+ g_strfreev(groups);
+
+ cleanup:
+ g_key_file_free(keyfile);
+}
+