/* SPDX-License-Identifier: GPL-2.0 */ /** * \file stat.c Functions used for sending/receiving the status of para_server * and para_audiod. */ #include "para.h" #include "error.h" #include "string.h" /** The minimal length of a status item buffer. */ #define MIN_STAT_ITEM_LEN 9 /* 5 + 2 + 2, e.g. '0005 00:\n' */ /* * Read the size prefix of a status item buffer and return its value. * * Each status item sent by para_server is prefixed with its length, a four * digit hex number encoded in ascii format. */ static int read_size_header(const char *buf) { int i, len = 0; for (i = 0; i < 4; i++) { unsigned char c = buf[i]; len <<= 4; if (c >= '0' && c <= '9') { len += c - '0'; continue; } if (c >= 'a' && c <= 'f') { len += c - 'a' + 10; continue; } return -1; } if (buf[4] != ' ') return -1; return len; } /** * Parse the status output of para_server or para_audiod. * * The item buffer is expected to contain status items in the format produced by * parser-friendly output mode of the stat command. * * \param item_buf The source buffer. * \param num_bytes The buffer length in bytes. * \param items Will be updated accordingly. Old values are freed. * * \return Mask of status items that have changed. Parse errors are logged * but are otherwise ignored. */ uint64_t parse_status_items(char *item_buf, size_t num_bytes, char **items) { uint64_t mask = 0ULL; char *buf = item_buf; int len = num_bytes; for (;;) { int i, ret, item_len, item_num = 0; char *old, *new; if (len < MIN_STAT_ITEM_LEN) break; ret = read_size_header(buf); if (ret < 0) goto parse_error; item_len = ret; if (item_len > len - 5) /* item not complete */ break; for (i = 0; i < 2; i++) { unsigned char c = buf[5 + i]; item_num <<= 4; if (c >= '0' && c <= '9') { item_num += c - '0'; continue; } if (c >= 'a' && c <= 'f') { item_num += c - 'a' + 10; continue; } goto parse_error; } if (buf[7] != ':' || buf[5 + item_len - 1] != '\n') goto parse_error; buf[5 + item_len - 1] = '\0'; old = items[item_num]; new = buf + 8; if (item_num >= NUM_STAT_ITEMS) PARA_WARNING_LOG("unknown status item %d: %s\n", item_num, new); else if (!old || strcmp(old, new)) { free(old); items[item_num] = para_strdup(new); mask |= 1ULL << item_num; } buf += 5 + item_len; len -= 5 + item_len; assert(len >= 0 && buf <= item_buf + num_bytes); } if (len != 0) { PARA_WARNING_LOG("len=%d, num_bytes=%zu\n", len, num_bytes); PARA_WARNING_LOG("buf: %s\n", buf); PARA_WARNING_LOG("mask: %" PRIu64 "\n", mask); } return mask; parse_error: PARA_ERROR_LOG("status item parse error\n"); return mask; }