]> git.ipfire.org Git - thirdparty/util-linux.git/blobdiff - sys-utils/lscpu.c
Merge branch 'patch-23' of https://github.com/mariobl/util-linux
[thirdparty/util-linux.git] / sys-utils / lscpu.c
index cd7475bd173316199cdaefc1ddc15face5fbc1fe..a65ccad5e435b93686c478aa197adb7dc71fa28c 100644 (file)
@@ -1,24 +1,16 @@
 /*
- * lscpu - CPU architecture information helper
- *
- * Copyright (C) 2008 Cai Qian <qcai@redhat.com>
- * Copyright (C) 2008 Karel Zak <kzak@redhat.com>
+ * SPDX-License-Identifier: GPL-2.0-or-later
  *
  * 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 would 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.
+ * Copyright (C) 2008 Cai Qian <qcai@redhat.com>
+ * Copyright (C) 2008-2023 Karel Zak <kzak@redhat.com>
  *
- * 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.
+ * lscpu - CPU architecture information helper
  */
-
 #include <assert.h>
 #include <ctype.h>
 #include <dirent.h>
@@ -39,6 +31,8 @@
 
 #include "closestream.h"
 #include "optutils.h"
+#include "c_strtod.h"
+#include "sysfs.h"
 
 #include "lscpu.h"
 
@@ -54,13 +48,13 @@ static const char *hv_vendors[] = {
        [VIRT_VENDOR_XEN]       = "Xen",
        [VIRT_VENDOR_KVM]       = "KVM",
        [VIRT_VENDOR_MSHV]      = "Microsoft",
-       [VIRT_VENDOR_VMWARE]  = "VMware",
+       [VIRT_VENDOR_VMWARE]    = "VMware",
        [VIRT_VENDOR_IBM]       = "IBM",
        [VIRT_VENDOR_VSERVER]   = "Linux-VServer",
        [VIRT_VENDOR_UML]       = "User-mode Linux",
        [VIRT_VENDOR_INNOTEK]   = "Innotek GmbH",
        [VIRT_VENDOR_HITACHI]   = "Hitachi",
-       [VIRT_VENDOR_PARALLELS] = "Parallels",
+       [VIRT_VENDOR_PARALLELS] = "Parallels",
        [VIRT_VENDOR_VBOX]      = "Oracle",
        [VIRT_VENDOR_OS400]     = "OS/400",
        [VIRT_VENDOR_PHYP]      = "pHyp",
@@ -80,10 +74,10 @@ struct polarization_modes {
 };
 
 static struct polarization_modes polar_modes[] = {
-       [POLAR_UNKNOWN]    = {"U",  "-"},
-       [POLAR_VLOW]       = {"VL", "vert-low"},
-       [POLAR_VMEDIUM]    = {"VM", "vert-medium"},
-       [POLAR_VHIGH]      = {"VH", "vert-high"},
+       [POLAR_UNKNOWN]    = {"U",  "-"},
+       [POLAR_VLOW]       = {"VL", "vert-low"},
+       [POLAR_VMEDIUM]    = {"VM", "vert-medium"},
+       [POLAR_VHIGH]      = {"VH", "vert-high"},
        [POLAR_HORIZONTAL] = {"H",  "horizontal"},
 };
 
@@ -108,6 +102,7 @@ enum {
        COL_CPU_SCALMHZ,
        COL_CPU_MAXMHZ,
        COL_CPU_MINMHZ,
+       COL_CPU_MODELNAME,
 };
 
 enum {
@@ -151,10 +146,11 @@ static struct lscpu_coldesc coldescs_cpu[] =
        [COL_CPU_ADDRESS]      = { "ADDRESS", N_("physical address of a CPU") },
        [COL_CPU_CONFIGURED]   = { "CONFIGURED", N_("shows if the hypervisor has allocated the CPU"), 0, 0, SCOLS_JSON_BOOLEAN_OPTIONAL },
        [COL_CPU_ONLINE]       = { "ONLINE", N_("shows if Linux currently makes use of the CPU"), SCOLS_FL_RIGHT, 0, SCOLS_JSON_BOOLEAN_OPTIONAL },
-       [COL_CPU_MHZ]          = { "MHZ", N_("shows the currently MHz of the CPU"), SCOLS_FL_RIGHT, 0, SCOLS_JSON_NUMBER },
+       [COL_CPU_MHZ]          = { "MHZ", N_("shows the currently MHz of the CPU"), SCOLS_FL_RIGHT, 0, SCOLS_JSON_NUMBER },
        [COL_CPU_SCALMHZ]      = { "SCALMHZ%", N_("shows scaling percentage of the CPU frequency"), SCOLS_FL_RIGHT, SCOLS_JSON_NUMBER },
        [COL_CPU_MAXMHZ]       = { "MAXMHZ", N_("shows the maximum MHz of the CPU"), SCOLS_FL_RIGHT, 0, SCOLS_JSON_NUMBER },
-       [COL_CPU_MINMHZ]       = { "MINMHZ", N_("shows the minimum MHz of the CPU"), SCOLS_FL_RIGHT, 0, SCOLS_JSON_NUMBER }
+       [COL_CPU_MINMHZ]       = { "MINMHZ", N_("shows the minimum MHz of the CPU"), SCOLS_FL_RIGHT, 0, SCOLS_JSON_NUMBER },
+       [COL_CPU_MODELNAME]    = { "MODELNAME", N_("shows CPU model name"), 0, 0, SCOLS_JSON_STRING }
 };
 
 static struct lscpu_coldesc coldescs_cache[] =
@@ -167,12 +163,12 @@ static struct lscpu_coldesc coldescs_cache[] =
        [COL_CACHE_WAYS]       = { "WAYS", N_("ways of associativity"), SCOLS_FL_RIGHT, 0, SCOLS_JSON_NUMBER },
        [COL_CACHE_ALLOCPOL]   = { "ALLOC-POLICY", N_("allocation policy") },
        [COL_CACHE_WRITEPOL]   = { "WRITE-POLICY", N_("write policy") },
-       [COL_CACHE_PHYLINE]    = { "PHY-LINE", N_("number of physical cache line per cache t"), SCOLS_FL_RIGHT, 0, SCOLS_JSON_NUMBER },
+       [COL_CACHE_PHYLINE]    = { "PHY-LINE", N_("number of physical cache line per cache tag"), SCOLS_FL_RIGHT, 0, SCOLS_JSON_NUMBER },
        [COL_CACHE_SETS]       = { "SETS", N_("number of sets in the cache; set lines has the same cache index"), SCOLS_FL_RIGHT, 0, SCOLS_JSON_NUMBER },
        [COL_CACHE_COHERENCYSIZE] = { "COHERENCY-SIZE", N_("minimum amount of data in bytes transferred from memory to cache"), SCOLS_FL_RIGHT, 0, SCOLS_JSON_NUMBER }
 };
 
-static int is_term = 0;
+static int hierarchic = -1;
 
 UL_DEBUG_DEFINE_MASK(lscpu);
 UL_DEBUG_DEFINE_MASKNAMES(lscpu) = UL_DEBUG_EMPTY_MASKNAMES;
@@ -217,6 +213,15 @@ static void lscpu_context_init_paths(struct lscpu_cxt *cxt)
        DBG(MISC, ul_debugobj(cxt, "initialize paths"));
        ul_path_init_debug();
 
+       /* / */
+       cxt->rootfs = NULL;
+       if (cxt->prefix) {
+               cxt->rootfs = ul_new_path("/");
+               if (!cxt->rootfs)
+                       err(EXIT_FAILURE, _("failed to initialize rootfs handler"));
+               ul_path_set_prefix(cxt->rootfs, cxt->prefix);
+       }
+
        /* /sys/devices/system/cpu */
        cxt->syscpu = ul_new_path(_PATH_SYS_CPU);
        if (!cxt->syscpu)
@@ -249,6 +254,7 @@ static void lscpu_free_context(struct lscpu_cxt *cxt)
        DBG(MISC, ul_debugobj(cxt, " de-initialize paths"));
        ul_unref_path(cxt->syscpu);
        ul_unref_path(cxt->procfs);
+       ul_unref_path(cxt->rootfs);
 
        DBG(MISC, ul_debugobj(cxt, " freeing cpus"));
        for (i = 0; i < cxt->npossibles; i++) {
@@ -349,10 +355,10 @@ static char *get_cell_data(
                snprintf(buf, bufsz, "%d", cpu->logical_id);
                break;
        case COL_CPU_BOGOMIPS:
-               if (cpu->bogomips)
-                       xstrncpy(buf, cpu->bogomips, bufsz);
-               else if (cpu->type->bogomips)
-                       xstrncpy(buf, cpu->type->bogomips, bufsz);
+               if (!cpu->bogomips && !cpu->type->bogomips)
+                       break;
+               snprintf(buf, bufsz, "%.2f", (float) c_strtod(
+                               cpu->bogomips ? : cpu->type->bogomips, NULL));
                break;
        case COL_CPU_CORE:
                fill_id(cxt, cpu, core, buf, bufsz);
@@ -442,6 +448,10 @@ static char *get_cell_data(
                if (cpu->mhz_min_freq)
                        snprintf(buf, bufsz, "%.4f", cpu->mhz_min_freq);
                break;
+       case COL_CPU_MODELNAME:
+               if (cpu->type && cpu->type->modelname)
+                       xstrncpy(buf, cpu->type->modelname, bufsz);
+               break;
        }
        return buf;
 }
@@ -594,14 +604,10 @@ static void print_caches_readable(struct lscpu_cxt *cxt, int cols[], size_t ncol
                struct libscols_column *cl;
 
                cl = scols_table_new_column(tb, cd->name, 0, cd->flags);
-
                if (cl == NULL)
                        err(EXIT_FAILURE, _("failed to allocate output column"));
-
                if (cxt->json)
                        scols_column_set_json_type(cl, cd->json_type);
-
-
        }
 
        /* standard caches */
@@ -756,10 +762,8 @@ static void print_cpus_readable(struct lscpu_cxt *cxt, int cols[], size_t ncols)
                cl = scols_table_new_column(tb, data, 0, cd->flags);
                if (cl == NULL)
                        err(EXIT_FAILURE, _("failed to allocate output column"));
-
                if (cxt->json)
                        scols_column_set_json_type(cl, cd->json_type);
-
        }
 
        for (i = 0; i < cxt->npossibles; i++) {
@@ -805,8 +809,8 @@ static struct libscols_line *
        struct libscols_line *ln;
        va_list args;
 
-       /* Don't print section lines without data on non-terminal output */
-       if (!is_term && fmt == NULL)
+       /* Don't print section lines without data */
+       if (!hierarchic && fmt == NULL)
                return NULL;
 
        ln = scols_table_new_line(tb, sec);
@@ -843,13 +847,15 @@ print_cpuset(struct lscpu_cxt *cxt,
             const char *key, cpu_set_t *set)
 {
        size_t setbuflen = 7 * cxt->maxcpus;
-       char setbuf[setbuflen], *p;
+       char *setbuf, *p;
 
        assert(set);
        assert(key);
        assert(tb);
        assert(cxt);
 
+       setbuf = xmalloc(setbuflen);
+
        if (cxt->hex) {
                p = cpumask_create(setbuf, setbuflen, set, cxt->setsize);
                add_summary_s(tb, sec, key, p);
@@ -857,6 +863,8 @@ print_cpuset(struct lscpu_cxt *cxt,
                p = cpulist_create(setbuf, setbuflen, set, cxt->setsize);
                add_summary_s(tb, sec, key, p);
        }
+
+       free(setbuf);
 }
 
 static void
@@ -865,10 +873,11 @@ print_summary_cputype(struct lscpu_cxt *cxt,
                     struct libscols_table *tb,
                     struct libscols_line *sec)
 {
-       if (ct->modelname)
-               sec = add_summary_s(tb, sec, _("Model name:"), ct->modelname);
+       sec = add_summary_s(tb, sec, _("Model name:"), ct->modelname ? ct->modelname : "-");
        if (ct->bios_modelname)
                add_summary_s(tb, sec, _("BIOS Model name:"), ct->bios_modelname);
+       if (ct->bios_family)
+               add_summary_s(tb, sec, _("BIOS CPU family:"), ct->bios_family);
        if (ct->machinetype)
                add_summary_s(tb, sec, _("Machine type:"), ct->machinetype);
        if (ct->family)
@@ -923,7 +932,7 @@ print_summary_cputype(struct lscpu_cxt *cxt,
                add_summary_x(tb, sec, _("CPU min MHz:"), "%.4f", lsblk_cputype_get_minmhz(cxt, ct));
        }
        if (ct->bogomips)
-               add_summary_s(tb, sec, _("BogoMIPS:"), ct->bogomips);
+               add_summary_x(tb, sec, _("BogoMIPS:"), "%.2f",  (float) c_strtod(ct->bogomips, NULL));
 
        if (ct->dispatching >= 0)
                add_summary_s(tb, sec, _("Dispatching mode:"), _(disp_modes[ct->dispatching]));
@@ -947,7 +956,7 @@ static void print_summary(struct lscpu_cxt *cxt)
        char field[256];
        size_t i = 0;
        struct libscols_table *tb;
-       struct libscols_line *sec = NULL;
+       struct libscols_line *sec;
        int hdr_caches = 0;
 
        scols_init_debug(0);
@@ -960,7 +969,7 @@ static void print_summary(struct lscpu_cxt *cxt)
        if (cxt->json) {
                scols_table_enable_json(tb, 1);
                scols_table_set_name(tb, "lscpu");
-       } else if (is_term) {
+       } else if (hierarchic) {
                struct libscols_symbols *sy = scols_new_symbols();
 
                if (!sy)
@@ -972,16 +981,15 @@ static void print_summary(struct lscpu_cxt *cxt)
                scols_unref_symbols(sy);
        }
 
-       if (scols_table_new_column(tb, "field", 0, is_term ? SCOLS_FL_TREE : 0) == NULL ||
+       if (scols_table_new_column(tb, "field", 0, hierarchic ? SCOLS_FL_TREE : 0) == NULL ||
            scols_table_new_column(tb, "data", 0, SCOLS_FL_NOEXTREMES | SCOLS_FL_WRAP) == NULL)
                err(EXIT_FAILURE, _("failed to initialize output column"));
 
        ct = lscpu_cputype_get_default(cxt);
 
        /* Section: architecture */
-       if (cxt->arch)
-               sec = add_summary_s(tb, NULL, _("Architecture:"), cxt->arch->name);
-       if (cxt->arch && (cxt->arch->bit32 || cxt->arch->bit64)) {
+       sec = add_summary_s(tb, NULL, _("Architecture:"), cxt->arch->name);
+       if (cxt->arch->bit32 || cxt->arch->bit64) {
                char buf[32], *p = buf;
 
                if (cxt->arch->bit32) {
@@ -995,13 +1003,13 @@ static void print_summary(struct lscpu_cxt *cxt)
                *(p - 2) = '\0';
                add_summary_s(tb, sec, _("CPU op-mode(s):"), buf);
        }
-       if (ct->addrsz)
+       if (ct && ct->addrsz)
                add_summary_s(tb, sec, _("Address sizes:"), ct->addrsz);
-#if !defined(WORDS_BIGENDIAN)
-       add_summary_s(tb, sec, _("Byte Order:"), "Little Endian");
-#else
-       add_summary_s(tb, sec, _("Byte Order:"), "Big Endian");
-#endif
+
+       if (sysfs_get_byteorder(cxt->rootfs) == SYSFS_BYTEORDER_LITTLE)
+               add_summary_s(tb, sec, _("Byte Order:"), "Little Endian");
+       else
+               add_summary_s(tb, sec, _("Byte Order:"), "Big Endian");
 
        /* Section: CPU lists */
        sec = add_summary_n(tb, NULL, _("CPU(s):"), cxt->npresents);
@@ -1037,9 +1045,9 @@ static void print_summary(struct lscpu_cxt *cxt)
        sec = NULL;
 
        /* Section: cpu type description */
-       if (ct->vendor)
+       if (ct && ct->vendor)
                sec = add_summary_s(tb, NULL, _("Vendor ID:"), ct->vendor);
-       if (ct->bios_vendor)
+       if (ct && ct->bios_vendor)
                add_summary_s(tb, sec, _("BIOS Vendor ID:"), ct->bios_vendor);
 
        for (i = 0; i < cxt->ncputypes; i++)
@@ -1085,7 +1093,7 @@ static void print_summary(struct lscpu_cxt *cxt)
                                hdr_caches = 1;
                        }
 
-                       snprintf(field, sizeof(field), is_term ? _("%s:") : _("%s cache:"), name);
+                       snprintf(field, sizeof(field), hierarchic ? _("%s:") : _("%s cache:"), name);
                        if (cxt->bytes)
                                add_summary_sprint(tb, sec, field,
                                                P_("%" PRIu64 " (%d instance)",
@@ -1115,7 +1123,7 @@ static void print_summary(struct lscpu_cxt *cxt)
                        sec = add_summary_e(tb, NULL, _("Caches:"));
                        hdr_caches = 1;
                }
-               snprintf(field, sizeof(field), is_term ? _("%s:") : _("%s cache:"), ca->name);
+               snprintf(field, sizeof(field), hierarchic ? _("%s:") : _("%s cache:"), ca->name);
                if (cxt->bytes)
                        add_summary_x(tb, sec, field, "%" PRIu64, ca->size);
                else {
@@ -1146,7 +1154,7 @@ static void print_summary(struct lscpu_cxt *cxt)
                sec = add_summary_e(tb, NULL, _("Vulnerabilities:"));
 
                for (i = 0; i < cxt->nvuls; i++) {
-                       snprintf(field, sizeof(field), is_term ?
+                       snprintf(field, sizeof(field), hierarchic ?
                                        _("%s:") : _("Vulnerability %s:"), cxt->vuls[i].name);
                        add_summary_s(tb, sec, field, cxt->vuls[i].text);
                }
@@ -1179,9 +1187,10 @@ static void __attribute__((__noreturn__)) usage(void)
        fputs(_(" -s, --sysroot <dir>     use specified directory as system root\n"), out);
        fputs(_(" -x, --hex               print hexadecimal masks rather than lists of CPUs\n"), out);
        fputs(_(" -y, --physical          print physical instead of logical IDs\n"), out);
+       fputs(_("     --hierarchic[=when] use subsections in summary (auto, never, always)\n"), out);
        fputs(_("     --output-all        print all available columns for -e, -p or -C\n"), out);
        fputs(USAGE_SEPARATOR, out);
-       printf(USAGE_HELP_OPTIONS(25));
+       fprintf(out, USAGE_HELP_OPTIONS(25));
 
        fputs(_("\nAvailable output columns for -e or -p:\n"), out);
        for (i = 0; i < ARRAY_SIZE(coldescs_cpu); i++)
@@ -1191,7 +1200,7 @@ static void __attribute__((__noreturn__)) usage(void)
        for (i = 0; i < ARRAY_SIZE(coldescs_cache); i++)
                fprintf(out, " %13s  %s\n", coldescs_cache[i].name, _(coldescs_cache[i].help));
 
-       printf(USAGE_MAN_TAIL("lscpu(1)"));
+       fprintf(out, USAGE_MAN_TAIL("lscpu(1)"));
 
        exit(EXIT_SUCCESS);
 }
@@ -1206,6 +1215,7 @@ int main(int argc, char *argv[])
        size_t i, ncolumns = 0;
        enum {
                OPT_OUTPUT_ALL = CHAR_MAX + 1,
+               OPT_HIERARCHIC,
        };
        static const struct option longopts[] = {
                { "all",        no_argument,       NULL, 'a' },
@@ -1222,6 +1232,7 @@ int main(int argc, char *argv[])
                { "hex",        no_argument,       NULL, 'x' },
                { "version",    no_argument,       NULL, 'V' },
                { "output-all", no_argument,       NULL, OPT_OUTPUT_ALL },
+               { "hierarchic", optional_argument, NULL, OPT_HIERARCHIC },
                { NULL,         0, NULL, 0 }
        };
 
@@ -1292,7 +1303,19 @@ int main(int argc, char *argv[])
                case OPT_OUTPUT_ALL:
                        all = 1;
                        break;
-
+               case OPT_HIERARCHIC:
+                       if (optarg) {
+                               if (strcmp(optarg, "auto") == 0)
+                                       hierarchic = -1;
+                               else if (strcmp(optarg, "never") == 0)
+                                       hierarchic = 0;
+                               else if (strcmp(optarg, "always") == 0)
+                                       hierarchic = 1;
+                               else
+                                       errx(EXIT_FAILURE, _("unsupported --flat argument"));
+                       } else
+                               hierarchic = 1;
+                       break;
                case 'h':
                        usage();
                case 'V':
@@ -1330,7 +1353,6 @@ int main(int argc, char *argv[])
                cxt->show_offline = cxt->mode == LSCPU_OUTPUT_READABLE ? 1 : 0;
        }
 
-       is_term = isatty(STDOUT_FILENO);        /* global variable */
 
        lscpu_init_debug();
 
@@ -1349,6 +1371,9 @@ int main(int argc, char *argv[])
 
        cxt->virt = lscpu_read_virtualization(cxt);
 
+       if (hierarchic == -1)
+               hierarchic = isatty(STDOUT_FILENO);     /* default */
+
        switch(cxt->mode) {
        case LSCPU_OUTPUT_SUMMARY:
                print_summary(cxt);
@@ -1415,6 +1440,7 @@ int main(int argc, char *argv[])
                print_cpus_readable(cxt, columns, ncolumns);
                break;
        case LSCPU_OUTPUT_PARSABLE:
+               cxt->show_compatible = 1;
                if (!ncolumns) {
                        columns[ncolumns++] = COL_CPU_CPU;
                        columns[ncolumns++] = COL_CPU_CORE;
@@ -1424,12 +1450,14 @@ int main(int argc, char *argv[])
                                columns[ncolumns++] = COL_CPU_SOCKET;
                        columns[ncolumns++] = COL_CPU_NODE;
                        columns[ncolumns++] = COL_CPU_CACHE;
-                       cxt->show_compatible = 1;
                }
-               if (outarg && string_add_to_idarray(outarg, columns,
+               if (outarg) {
+                       if (string_add_to_idarray(outarg, columns,
                                        ARRAY_SIZE(columns),
                                        &ncolumns, cpu_column_name_to_id) < 0)
-                       return EXIT_FAILURE;
+                               return EXIT_FAILURE;
+                       cxt->show_compatible = 0;
+               }
 
                print_cpus_parsable(cxt, columns, ncolumns);
                break;