|
#include <stdbool.h> |
|
#include <stdint.h> |
|
#include <stdlib.h> |
|
#include <string.h> |
|
#include <errno.h> |
|
|
|
#include <sys/types.h> |
|
#include <sys/stat.h> |
|
#include <unistd.h> |
|
#include <fcntl.h> |
|
#include <sched.h> |
|
|
|
#if CPUINFO_MOCK |
|
#include <cpuinfo-mock.h> |
|
#endif |
|
#include <linux/api.h> |
|
#include <cpuinfo/log.h> |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
#define BUFFER_SIZE 256 |
|
|
|
|
|
|
|
inline static bool is_whitespace(char c) { |
|
switch (c) { |
|
case ' ': |
|
case '\t': |
|
case '\n': |
|
case '\r': |
|
return true; |
|
default: |
|
return false; |
|
} |
|
} |
|
|
|
inline static const char* parse_number(const char* string, const char* end, uint32_t number_ptr[restrict static 1]) { |
|
uint32_t number = 0; |
|
while (string != end) { |
|
const uint32_t digit = (uint32_t) (*string) - (uint32_t) '0'; |
|
if (digit >= 10) { |
|
break; |
|
} |
|
number = number * UINT32_C(10) + digit; |
|
string += 1; |
|
} |
|
*number_ptr = number; |
|
return string; |
|
} |
|
|
|
inline static bool parse_entry(const char* entry_start, const char* entry_end, cpuinfo_cpulist_callback callback, void* context) { |
|
|
|
for (; entry_start != entry_end; entry_start++) { |
|
if (!is_whitespace(*entry_start)) { |
|
break; |
|
} |
|
} |
|
|
|
for (; entry_end != entry_start; entry_end--) { |
|
if (!is_whitespace(entry_end[-1])) { |
|
break; |
|
} |
|
} |
|
|
|
const size_t entry_length = (size_t) (entry_end - entry_start); |
|
if (entry_length == 0) { |
|
cpuinfo_log_warning("unexpected zero-length cpu list entry ignored"); |
|
return false; |
|
} |
|
|
|
#if CPUINFO_LOG_DEBUG_PARSERS |
|
cpuinfo_log_debug("parse cpu list entry \"%.*s\" (%zu chars)", (int) entry_length, entry_start, entry_length); |
|
#endif |
|
uint32_t first_cpu, last_cpu; |
|
|
|
const char* number_end = parse_number(entry_start, entry_end, &first_cpu); |
|
if (number_end == entry_start) { |
|
|
|
cpuinfo_log_warning("invalid character '%c' in the cpu list entry \"%.*s\": entry is ignored", |
|
entry_start[0], (int) entry_length, entry_start); |
|
return false; |
|
} else if (number_end == entry_end) { |
|
|
|
#if CPUINFO_LOG_DEBUG_PARSERS |
|
cpuinfo_log_debug("cpulist: call callback with list_start = %"PRIu32", list_end = %"PRIu32, |
|
first_cpu, first_cpu + 1); |
|
#endif |
|
return callback(first_cpu, first_cpu + 1, context); |
|
} |
|
|
|
|
|
if (*number_end != '-') { |
|
cpuinfo_log_warning("invalid character '%c' in the cpu list entry \"%.*s\": entry is ignored", |
|
*number_end, (int) entry_length, entry_start); |
|
return false; |
|
} |
|
|
|
const char* number_start = number_end + 1; |
|
number_end = parse_number(number_start, entry_end, &last_cpu); |
|
if (number_end == number_start) { |
|
|
|
cpuinfo_log_warning("invalid character '%c' in the cpu list entry \"%.*s\": entry is ignored", |
|
*number_start, (int) entry_length, entry_start); |
|
return false; |
|
} |
|
|
|
if (number_end != entry_end) { |
|
|
|
cpuinfo_log_warning("ignored invalid characters \"%.*s\" at the end of cpu list entry \"%.*s\"", |
|
(int) (entry_end - number_end), number_start, (int) entry_length, entry_start); |
|
} |
|
|
|
if (last_cpu < first_cpu) { |
|
cpuinfo_log_warning("ignored cpu list entry \"%.*s\": invalid range %"PRIu32"-%"PRIu32, |
|
(int) entry_length, entry_start, first_cpu, last_cpu); |
|
return false; |
|
} |
|
|
|
|
|
#if CPUINFO_LOG_DEBUG_PARSERS |
|
cpuinfo_log_debug("cpulist: call callback with list_start = %"PRIu32", list_end = %"PRIu32, |
|
first_cpu, last_cpu + 1); |
|
#endif |
|
return callback(first_cpu, last_cpu + 1, context); |
|
} |
|
|
|
bool cpuinfo_linux_parse_cpulist(const char* filename, cpuinfo_cpulist_callback callback, void* context) { |
|
bool status = true; |
|
int file = -1; |
|
char buffer[BUFFER_SIZE]; |
|
#if CPUINFO_LOG_DEBUG_PARSERS |
|
cpuinfo_log_debug("parsing cpu list from file %s", filename); |
|
#endif |
|
|
|
#if CPUINFO_MOCK |
|
file = cpuinfo_mock_open(filename, O_RDONLY); |
|
#else |
|
file = open(filename, O_RDONLY); |
|
#endif |
|
if (file == -1) { |
|
cpuinfo_log_info("failed to open %s: %s", filename, strerror(errno)); |
|
status = false; |
|
goto cleanup; |
|
} |
|
|
|
size_t position = 0; |
|
const char* buffer_end = &buffer[BUFFER_SIZE]; |
|
char* data_start = buffer; |
|
ssize_t bytes_read; |
|
do { |
|
#if CPUINFO_MOCK |
|
bytes_read = cpuinfo_mock_read(file, data_start, (size_t) (buffer_end - data_start)); |
|
#else |
|
bytes_read = read(file, data_start, (size_t) (buffer_end - data_start)); |
|
#endif |
|
if (bytes_read < 0) { |
|
cpuinfo_log_info("failed to read file %s at position %zu: %s", filename, position, strerror(errno)); |
|
status = false; |
|
goto cleanup; |
|
} |
|
|
|
position += (size_t) bytes_read; |
|
const char* data_end = data_start + (size_t) bytes_read; |
|
const char* entry_start = buffer; |
|
|
|
if (bytes_read == 0) { |
|
|
|
const char* entry_end = data_end; |
|
const bool entry_status = parse_entry(entry_start, entry_end, callback, context); |
|
status &= entry_status; |
|
} else { |
|
const char* entry_end; |
|
do { |
|
|
|
for (entry_end = entry_start; entry_end != data_end; entry_end++) { |
|
if (*entry_end == ',') { |
|
break; |
|
} |
|
} |
|
|
|
|
|
|
|
|
|
|
|
if (entry_end != data_end) { |
|
const bool entry_status = parse_entry(entry_start, entry_end, callback, context); |
|
status &= entry_status; |
|
entry_start = entry_end + 1; |
|
} |
|
} while (entry_end != data_end); |
|
|
|
|
|
const size_t entry_length = (size_t) (entry_end - entry_start); |
|
memmove(buffer, entry_start, entry_length); |
|
data_start = &buffer[entry_length]; |
|
} |
|
} while (bytes_read != 0); |
|
|
|
cleanup: |
|
if (file != -1) { |
|
#if CPUINFO_MOCK |
|
cpuinfo_mock_close(file); |
|
#else |
|
close(file); |
|
#endif |
|
file = -1; |
|
} |
|
return status; |
|
} |
|
|