/* * Copyright (C) 2013 Red Hat, Inc. All rights reserved. * * 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * * Authors: Peter Schiffer */ #include "dmidecode.h" /****************************************************************************** * DmiProcessor */ /* * Initialize DmiProcessor attributes. * @param cpu */ void init_dmiprocessor_struct(DmiProcessor *cpu) { cpu->id = NULL; cpu->family = NULL; cpu->status = NULL; cpu->current_speed = 0; cpu->max_speed = 0; cpu->external_clock = 0; cpu->name = NULL; cpu->cores = 1; cpu->enabled_cores = 1; cpu->threads = 1; cpu->type = NULL; cpu->stepping = NULL; cpu->upgrade = NULL; cpu->charact_nb = 0; cpu->characteristics = NULL; cpu->l1_cache_handle = NULL; cpu->l2_cache_handle = NULL; cpu->l3_cache_handle = NULL; cpu->manufacturer = NULL; cpu->serial_number = NULL; cpu->part_number = NULL; } /* * Check attributes of cpu structure and fill in defaults if needed. * @param cpu * @return 0 if success, negative value otherwise */ short check_dmiprocessor_attributes(DmiProcessor *cpu) { short ret = -1; if (!cpu->id) { if (!(cpu->id = strdup(""))) { ret = -2; goto done; } } if (!cpu->family) { if (!(cpu->family = strdup("Unknown"))) { ret = -3; goto done; } } if (!cpu->status) { if (!(cpu->status = strdup("Unknown"))) { ret = -4; goto done; } } if (!cpu->name) { if (!(cpu->name = strdup(""))) { ret = -5; goto done; } } if (!cpu->type) { if (!(cpu->type = strdup(""))) { ret = -6; goto done; } } if (!cpu->stepping) { if (!(cpu->stepping = strdup(""))) { ret = -7; goto done; } } if (!cpu->upgrade) { if (!(cpu->upgrade = strdup("Unknown"))) { ret = -8; goto done; } } if (!cpu->l1_cache_handle) { if (!(cpu->l1_cache_handle = strdup(""))) { ret = -9; goto done; } } if (!cpu->l2_cache_handle) { if (!(cpu->l2_cache_handle = strdup(""))) { ret = -10; goto done; } } if (!cpu->l3_cache_handle) { if (!(cpu->l3_cache_handle = strdup(""))) { ret = -11; goto done; } } if (!cpu->manufacturer) { if (!(cpu->manufacturer = strdup(""))) { ret = -12; goto done; } } if (!cpu->serial_number) { if (!(cpu->serial_number = strdup(""))) { ret = -13; goto done; } } if (!cpu->part_number) { if (!(cpu->part_number = strdup(""))) { ret = -14; goto done; } } ret = 0; done: if (ret != 0) { warn("Failed to allocate memory."); } return ret; } short dmi_get_processors(DmiProcessor **cpus, unsigned *cpus_nb) { short ret = -1; int curr_cpu = -1; unsigned i, buffer_size = 0; char **buffer = NULL, *buf; *cpus_nb = 0; /* get dmidecode output */ if (run_command("dmidecode -t 4", &buffer, &buffer_size) != 0) { ret = -2; goto done; } /* count processors */ for (i = 0; i < buffer_size; i++) { if (strncmp(buffer[i], "Handle 0x", 9) == 0) { (*cpus_nb)++; } } /* if no processor was found */ if (*cpus_nb < 1) { warn("Dmidecode didn't recognize any processor."); ret = -3; goto done; } /* allocate memory for processors */ *cpus = (DmiProcessor *)calloc(*cpus_nb, sizeof(DmiProcessor)); if (!(*cpus)) { warn("Failed to allocate memory."); ret = -4; goto done; } /* parse information about processors */ for (i = 0; i < buffer_size; i++) { if (strncmp(buffer[i], "Handle 0x", 9) == 0) { curr_cpu++; init_dmiprocessor_struct(&(*cpus)[curr_cpu]); continue; } /* ignore first useless lines */ if (curr_cpu < 0) { continue; } /* ID */ buf = copy_string_part_after_delim(buffer[i], "ID: "); if (buf) { (*cpus)[curr_cpu].id = buf; buf = NULL; continue; } /* Family */ buf = copy_string_part_after_delim(buffer[i], "Family: "); if (buf) { (*cpus)[curr_cpu].family = buf; buf = NULL; continue; } /* Manufacturer */ buf = copy_string_part_after_delim(buffer[i], "Manufacturer: "); if (buf) { (*cpus)[curr_cpu].manufacturer = buf; buf = NULL; continue; } /* Status */ buf = copy_string_part_after_delim(buffer[i], "Status: Populated, "); if (buf) { (*cpus)[curr_cpu].status = buf; buf = NULL; continue; } /* Current Speed */ buf = copy_string_part_after_delim(buffer[i], "Current Speed: "); if (buf && strcmp(buf, "Unknown") != 0) { sscanf(buf, "%u", &(*cpus)[curr_cpu].current_speed); free(buf); buf = NULL; continue; } /* Max Speed */ buf = copy_string_part_after_delim(buffer[i], "Max Speed: "); if (buf && strcmp(buf, "Unknown") != 0) { sscanf(buf, "%u", &(*cpus)[curr_cpu].max_speed); free(buf); buf = NULL; continue; } /* External Clock Speed */ buf = copy_string_part_after_delim(buffer[i], "External Clock: "); if (buf && strcmp(buf, "Unknown") != 0) { sscanf(buf, "%u", &(*cpus)[curr_cpu].external_clock); free(buf); buf = NULL; continue; } /* CPU Name */ buf = copy_string_part_after_delim(buffer[i], "Version: "); if (buf) { (*cpus)[curr_cpu].name = buf; buf = NULL; continue; } /* Cores */ buf = copy_string_part_after_delim(buffer[i], "Core Count: "); if (buf) { sscanf(buf, "%u", &(*cpus)[curr_cpu].cores); free(buf); buf = NULL; continue; } /* Enabled Cores */ buf = copy_string_part_after_delim(buffer[i], "Core Enabled: "); if (buf) { sscanf(buf, "%u", &(*cpus)[curr_cpu].enabled_cores); free(buf); buf = NULL; continue; } /* Threads */ buf = copy_string_part_after_delim(buffer[i], "Thread Count: "); if (buf) { sscanf(buf, "%u", &(*cpus)[curr_cpu].threads); free(buf); buf = NULL; continue; } /* CPU Type/Role */ buf = copy_string_part_after_delim(buffer[i], "Type: "); if (buf) { (*cpus)[curr_cpu].type = buf; buf = NULL; continue; } /* Stepping */ buf = copy_string_part_after_delim(buffer[i], ", Stepping "); if (buf) { (*cpus)[curr_cpu].stepping = buf; buf = NULL; continue; } /* Upgrade */ buf = copy_string_part_after_delim(buffer[i], "Upgrade: "); if (buf) { (*cpus)[curr_cpu].upgrade = buf; buf = NULL; continue; } /* Level 1 Cache Handle */ buf = copy_string_part_after_delim(buffer[i], "L1 Cache Handle: "); if (buf) { (*cpus)[curr_cpu].l1_cache_handle = buf; buf = NULL; continue; } /* Level 2 Cache Handle */ buf = copy_string_part_after_delim(buffer[i], "L2 Cache Handle: "); if (buf) { (*cpus)[curr_cpu].l2_cache_handle = buf; buf = NULL; continue; } /* Level 3 Cache Handle */ buf = copy_string_part_after_delim(buffer[i], "L3 Cache Handle: "); if (buf) { (*cpus)[curr_cpu].l3_cache_handle = buf; buf = NULL; continue; } /* Serial Number */ buf = copy_string_part_after_delim(buffer[i], "Serial Number: "); if (buf) { (*cpus)[curr_cpu].serial_number = buf; buf = NULL; continue; } /* Part Number */ buf = copy_string_part_after_delim(buffer[i], "Part Number: "); if (buf) { (*cpus)[curr_cpu].part_number = buf; buf = NULL; continue; } /* CPU Characteristics */ if (strstr(buffer[i], "Characteristics:") && !strstr(buffer[i], "Characteristics: ")) { /* count characteristics */ (*cpus)[curr_cpu].charact_nb = 0; while (strlen(buffer[i + (*cpus)[curr_cpu].charact_nb + 1])) { (*cpus)[curr_cpu].charact_nb += 1; } /* allocate memory */ (*cpus)[curr_cpu].characteristics = (char **)calloc((*cpus)[curr_cpu].charact_nb, sizeof(char *)); if (!(*cpus)[curr_cpu].characteristics) { warn("Failed to allocate memory."); ret = -5; goto done; } unsigned j; char *tmp_line; for (j = 0; j < (*cpus)[curr_cpu].charact_nb; j++) { tmp_line = trim(buffer[i + j + 1], NULL); if (tmp_line) { (*cpus)[curr_cpu].characteristics[j] = tmp_line; } else { (*cpus)[curr_cpu].characteristics[j] = strdup(""); if (!(*cpus)[curr_cpu].characteristics[j]) { warn("Failed to allocate memory."); ret = -6; goto done; } } } /* skip characteristics and newline after them */ i += (*cpus)[curr_cpu].charact_nb + 1; } } /* fill in default attributes if needed */ for (i = 0; i < *cpus_nb; i++) { if (check_dmiprocessor_attributes(&(*cpus)[i]) != 0) { ret = -7; goto done; } } ret = 0; done: free_2d_buffer(&buffer, &buffer_size); if (ret != 0) { dmi_free_processors(cpus, cpus_nb); } return ret; } void dmi_free_processors(DmiProcessor **cpus, unsigned *cpus_nb) { unsigned i, j; if (*cpus_nb > 0) { for (i = 0; i < *cpus_nb; i++) { free((*cpus)[i].id); (*cpus)[i].id = NULL; free((*cpus)[i].family); (*cpus)[i].family = NULL; free((*cpus)[i].status); (*cpus)[i].status = NULL; free((*cpus)[i].name); (*cpus)[i].name = NULL; free((*cpus)[i].type); (*cpus)[i].type = NULL; free((*cpus)[i].stepping); (*cpus)[i].stepping = NULL; free((*cpus)[i].upgrade); (*cpus)[i].upgrade = NULL; if ((*cpus)[i].charact_nb > 0) { for (j = 0; j < (*cpus)[i].charact_nb; j++) { free((*cpus)[i].characteristics[j]); (*cpus)[i].characteristics[j] = NULL; } free((*cpus)[i].characteristics); } (*cpus)[i].charact_nb = 0; (*cpus)[i].characteristics = NULL; free((*cpus)[i].l1_cache_handle); (*cpus)[i].l1_cache_handle = NULL; free((*cpus)[i].l2_cache_handle); (*cpus)[i].l2_cache_handle = NULL; free((*cpus)[i].l3_cache_handle); (*cpus)[i].l3_cache_handle = NULL; free((*cpus)[i].manufacturer); (*cpus)[i].manufacturer = NULL; free((*cpus)[i].serial_number); (*cpus)[i].serial_number = NULL; free((*cpus)[i].part_number); (*cpus)[i].part_number = NULL; } free (*cpus); } *cpus_nb = 0; *cpus = NULL; } /****************************************************************************** * DmiCpuCache */ /* * Initialize DmiCpuCache attributes. * @param cache */ void init_dmi_cpu_cache_struct(DmiCpuCache *cache) { cache->id = NULL; cache->size = 0; cache->name = NULL; cache->status = NULL; cache->level = 0; cache->op_mode = NULL; cache->type = NULL; cache->associativity = NULL; } /* * Check attributes of cache structure and fill in defaults if needed. * @param cache * @return 0 if success, negative value otherwise */ short check_dmi_cpu_cache_attributes(DmiCpuCache *cache) { short ret = -1; if (!cache->id) { if (!(cache->id = strdup(""))) { ret = -2; goto done; } } if (!cache->name) { if (!(cache->name = strdup(""))) { ret = -3; goto done; } } if (!cache->status) { if (!(cache->status = strdup(""))) { ret = -4; goto done; } } if (!cache->op_mode) { if (!(cache->op_mode = strdup("Unknown"))) { ret = -5; goto done; } } if (!cache->type) { if (!(cache->type = strdup("Unknown"))) { ret = -6; goto done; } } if (!cache->associativity) { if (!(cache->associativity = strdup("Unknown"))) { ret = -7; goto done; } } ret = 0; done: if (ret != 0) { warn("Failed to allocate memory."); } return ret; } short dmi_get_cpu_caches(DmiCpuCache **caches, unsigned *caches_nb) { short ret = -1; int curr_cache = -1; unsigned i, buffer_size = 0; char **buffer = NULL, *buf; *caches_nb = 0; /* get dmidecode output */ if (run_command("dmidecode -t 7", &buffer, &buffer_size) != 0) { ret = -2; goto done; } /* count caches */ for (i = 0; i < buffer_size; i++) { if (strncmp(buffer[i], "Handle 0x", 9) == 0) { (*caches_nb)++; } } /* if no cache was found */ if (*caches_nb < 1) { warn("Dmidecode didn't recognize any processor cache memory."); ret = -3; goto done; } /* allocate memory for caches */ *caches = (DmiCpuCache *)calloc(*caches_nb, sizeof(DmiCpuCache)); if (!(*caches)) { warn("Failed to allocate memory."); ret = -4; goto done; } /* parse information about cache */ for (i = 0; i < buffer_size; i++) { if (strncmp(buffer[i], "Handle 0x", 9) == 0) { curr_cache++; init_dmi_cpu_cache_struct(&(*caches)[curr_cache]); /* Cache ID is it's handle */ char *id_start = buffer[i] + 7; char *id_end = strchr(buffer[i], ','); if (!id_end) { warn("Unrecognized output from dmidecode program."); ret = -5; goto done; } (*caches)[curr_cache].id = strndup(id_start, id_end - id_start); if (!(*caches)[curr_cache].id) { warn("Failed to allocate memory."); ret = -6; goto done; } continue; } /* ignore first useless lines */ if (curr_cache < 0) { continue; } /* Cache Name */ buf = copy_string_part_after_delim(buffer[i], "Socket Designation: "); if (buf) { (*caches)[curr_cache].name = buf; buf = NULL; continue; } /* Cache Status and Level */ buf = copy_string_part_after_delim(buffer[i], "Configuration: "); if (buf) { char **confs = NULL; unsigned confs_len = 0; if (explode(buf, ",", &confs, &confs_len) != 0 || confs_len < 3) { free_2d_buffer(&confs, &confs_len); free(buf); buf = NULL; continue; } (*caches)[curr_cache].status = trim(confs[0], NULL); sscanf(confs[2], "%*s %u", &(*caches)[curr_cache].level); free_2d_buffer(&confs, &confs_len); free(buf); buf = NULL; continue; } /* Cache Operational Mode */ buf = copy_string_part_after_delim(buffer[i], "Operational Mode: "); if (buf) { (*caches)[curr_cache].op_mode = buf; buf = NULL; continue; } /* Cache Size */ buf = copy_string_part_after_delim(buffer[i], "Installed Size: "); if (buf) { sscanf(buf, "%u", &(*caches)[curr_cache].size); (*caches)[curr_cache].size *= 1024; /* It's in kB, we want B */ free(buf); buf = NULL; continue; } /* Cache Type */ buf = copy_string_part_after_delim(buffer[i], "System Type: "); if (buf) { (*caches)[curr_cache].type = buf; buf = NULL; continue; } /* Cache Associativity */ buf = copy_string_part_after_delim(buffer[i], "Associativity: "); if (buf) { (*caches)[curr_cache].associativity = buf; buf = NULL; continue; } } /* fill in default attributes if needed */ for (i = 0; i < *caches_nb; i++) { if (check_dmi_cpu_cache_attributes(&(*caches)[i]) != 0) { ret = -7; goto done; } } ret = 0; done: free_2d_buffer(&buffer, &buffer_size); if (ret != 0) { dmi_free_cpu_caches(caches, caches_nb); } return ret; } void dmi_free_cpu_caches(DmiCpuCache **caches, unsigned *caches_nb) { unsigned i; if (*caches_nb > 0) { for (i = 0; i < *caches_nb; i++) { free((*caches)[i].id); (*caches)[i].id = NULL; free((*caches)[i].name); (*caches)[i].name = NULL; free((*caches)[i].status); (*caches)[i].status = NULL; free((*caches)[i].op_mode); (*caches)[i].op_mode = NULL; free((*caches)[i].type); (*caches)[i].type = NULL; free((*caches)[i].associativity); (*caches)[i].associativity = NULL; } free (*caches); } *caches_nb = 0; *caches = NULL; }