From aaf37a3203b5ad30714cde34f4a6b40c3195eebf Mon Sep 17 00:00:00 2001 From: Jon Medhurst Date: Tue, 11 Jun 2013 12:10:56 +0100 Subject: gator: Version 5.15 Signed-off-by: Jon Medhurst --- tools/gator/daemon/Android.mk | 52 + tools/gator/daemon/Buffer.cpp | 227 ++ tools/gator/daemon/Buffer.h | 66 + tools/gator/daemon/CapturedXML.cpp | 126 + tools/gator/daemon/CapturedXML.h | 26 + tools/gator/daemon/Child.cpp | 405 +++ tools/gator/daemon/Child.h | 31 + tools/gator/daemon/Collector.cpp | 221 ++ tools/gator/daemon/Collector.h | 38 + tools/gator/daemon/ConfigurationXML.cpp | 199 ++ tools/gator/daemon/ConfigurationXML.h | 32 + tools/gator/daemon/Counter.h | 61 + tools/gator/daemon/Driver.cpp | 15 + tools/gator/daemon/Driver.h | 44 + tools/gator/daemon/EventsXML.cpp | 70 + tools/gator/daemon/EventsXML.h | 18 + tools/gator/daemon/Fifo.cpp | 130 + tools/gator/daemon/Fifo.h | 44 + tools/gator/daemon/Hwmon.cpp | 326 +++ tools/gator/daemon/Hwmon.h | 39 + tools/gator/daemon/KMod.cpp | 100 + tools/gator/daemon/KMod.h | 27 + tools/gator/daemon/LocalCapture.cpp | 129 + tools/gator/daemon/LocalCapture.h | 26 + tools/gator/daemon/Logging.cpp | 78 + tools/gator/daemon/Logging.h | 47 + tools/gator/daemon/Makefile | 25 + tools/gator/daemon/Makefile_aarch64 | 15 + tools/gator/daemon/OlySocket.cpp | 261 ++ tools/gator/daemon/OlySocket.h | 36 + tools/gator/daemon/OlyUtility.cpp | 228 ++ tools/gator/daemon/OlyUtility.h | 42 + tools/gator/daemon/Sender.cpp | 130 + tools/gator/daemon/Sender.h | 38 + tools/gator/daemon/SessionData.cpp | 148 ++ tools/gator/daemon/SessionData.h | 71 + tools/gator/daemon/SessionXML.cpp | 109 + tools/gator/daemon/SessionXML.h | 38 + tools/gator/daemon/StreamlineSetup.cpp | 277 ++ tools/gator/daemon/StreamlineSetup.h | 44 + tools/gator/daemon/common.mk | 50 + tools/gator/daemon/configuration.xml | 57 + tools/gator/daemon/escape.c | 75 + tools/gator/daemon/events-ARM11.xml | 39 + tools/gator/daemon/events-ARM11MPCore.xml | 26 + tools/gator/daemon/events-CCI-400.xml | 47 + tools/gator/daemon/events-Cortex-A15.xml | 68 + tools/gator/daemon/events-Cortex-A5.xml | 36 + tools/gator/daemon/events-Cortex-A53.xml | 171 ++ tools/gator/daemon/events-Cortex-A57.xml | 171 ++ tools/gator/daemon/events-Cortex-A7.xml | 43 + tools/gator/daemon/events-Cortex-A8.xml | 52 + tools/gator/daemon/events-Cortex-A9.xml | 65 + tools/gator/daemon/events-Krait-architected.xml | 22 + tools/gator/daemon/events-L2C-310.xml | 18 + tools/gator/daemon/events-Linux.xml | 17 + tools/gator/daemon/events-Mali-400.xml | 180 ++ tools/gator/daemon/events-Mali-T6xx.xml | 38 + tools/gator/daemon/events-Mali-T6xx_hw.xml | 113 + tools/gator/daemon/events-Scorpion.xml | 107 + tools/gator/daemon/events-ScorpionMP.xml | 90 + tools/gator/daemon/events_footer.xml | 1 + tools/gator/daemon/events_header.xml | 2 + tools/gator/daemon/libsensors/COPYING.LGPL | 502 ++++ tools/gator/daemon/libsensors/access.c | 561 +++++ tools/gator/daemon/libsensors/access.h | 33 + tools/gator/daemon/libsensors/conf-lex.c | 2881 +++++++++++++++++++++ tools/gator/daemon/libsensors/conf-lex.l | 372 +++ tools/gator/daemon/libsensors/conf-parse.c | 2042 +++++++++++++++ tools/gator/daemon/libsensors/conf-parse.h | 84 + tools/gator/daemon/libsensors/conf-parse.y | 347 +++ tools/gator/daemon/libsensors/conf.h | 34 + tools/gator/daemon/libsensors/data.c | 278 ++ tools/gator/daemon/libsensors/data.h | 184 ++ tools/gator/daemon/libsensors/error.c | 92 + tools/gator/daemon/libsensors/error.h | 74 + tools/gator/daemon/libsensors/general.c | 85 + tools/gator/daemon/libsensors/general.h | 39 + tools/gator/daemon/libsensors/init.c | 341 +++ tools/gator/daemon/libsensors/init.h | 28 + tools/gator/daemon/libsensors/scanner.h | 32 + tools/gator/daemon/libsensors/sensors.h | 311 +++ tools/gator/daemon/libsensors/sysfs.c | 926 +++++++ tools/gator/daemon/libsensors/sysfs.h | 43 + tools/gator/daemon/libsensors/version.h | 1 + tools/gator/daemon/main.cpp | 385 +++ tools/gator/daemon/mxml/COPYING | 507 ++++ tools/gator/daemon/mxml/config.h | 96 + tools/gator/daemon/mxml/mxml-attr.c | 319 +++ tools/gator/daemon/mxml/mxml-entity.c | 460 ++++ tools/gator/daemon/mxml/mxml-file.c | 3082 +++++++++++++++++++++++ tools/gator/daemon/mxml/mxml-get.c | 471 ++++ tools/gator/daemon/mxml/mxml-index.c | 662 +++++ tools/gator/daemon/mxml/mxml-node.c | 807 ++++++ tools/gator/daemon/mxml/mxml-private.c | 331 +++ tools/gator/daemon/mxml/mxml-private.h | 50 + tools/gator/daemon/mxml/mxml-search.c | 287 +++ tools/gator/daemon/mxml/mxml-set.c | 349 +++ tools/gator/daemon/mxml/mxml-string.c | 476 ++++ tools/gator/daemon/mxml/mxml.h | 329 +++ 100 files changed, 23428 insertions(+) create mode 100644 tools/gator/daemon/Android.mk create mode 100644 tools/gator/daemon/Buffer.cpp create mode 100644 tools/gator/daemon/Buffer.h create mode 100644 tools/gator/daemon/CapturedXML.cpp create mode 100644 tools/gator/daemon/CapturedXML.h create mode 100644 tools/gator/daemon/Child.cpp create mode 100644 tools/gator/daemon/Child.h create mode 100644 tools/gator/daemon/Collector.cpp create mode 100644 tools/gator/daemon/Collector.h create mode 100644 tools/gator/daemon/ConfigurationXML.cpp create mode 100644 tools/gator/daemon/ConfigurationXML.h create mode 100644 tools/gator/daemon/Counter.h create mode 100644 tools/gator/daemon/Driver.cpp create mode 100644 tools/gator/daemon/Driver.h create mode 100644 tools/gator/daemon/EventsXML.cpp create mode 100644 tools/gator/daemon/EventsXML.h create mode 100644 tools/gator/daemon/Fifo.cpp create mode 100644 tools/gator/daemon/Fifo.h create mode 100644 tools/gator/daemon/Hwmon.cpp create mode 100644 tools/gator/daemon/Hwmon.h create mode 100644 tools/gator/daemon/KMod.cpp create mode 100644 tools/gator/daemon/KMod.h create mode 100644 tools/gator/daemon/LocalCapture.cpp create mode 100644 tools/gator/daemon/LocalCapture.h create mode 100644 tools/gator/daemon/Logging.cpp create mode 100644 tools/gator/daemon/Logging.h create mode 100644 tools/gator/daemon/Makefile create mode 100644 tools/gator/daemon/Makefile_aarch64 create mode 100644 tools/gator/daemon/OlySocket.cpp create mode 100644 tools/gator/daemon/OlySocket.h create mode 100644 tools/gator/daemon/OlyUtility.cpp create mode 100644 tools/gator/daemon/OlyUtility.h create mode 100644 tools/gator/daemon/Sender.cpp create mode 100644 tools/gator/daemon/Sender.h create mode 100644 tools/gator/daemon/SessionData.cpp create mode 100644 tools/gator/daemon/SessionData.h create mode 100644 tools/gator/daemon/SessionXML.cpp create mode 100644 tools/gator/daemon/SessionXML.h create mode 100644 tools/gator/daemon/StreamlineSetup.cpp create mode 100644 tools/gator/daemon/StreamlineSetup.h create mode 100644 tools/gator/daemon/common.mk create mode 100644 tools/gator/daemon/configuration.xml create mode 100644 tools/gator/daemon/escape.c create mode 100644 tools/gator/daemon/events-ARM11.xml create mode 100644 tools/gator/daemon/events-ARM11MPCore.xml create mode 100644 tools/gator/daemon/events-CCI-400.xml create mode 100644 tools/gator/daemon/events-Cortex-A15.xml create mode 100644 tools/gator/daemon/events-Cortex-A5.xml create mode 100644 tools/gator/daemon/events-Cortex-A53.xml create mode 100644 tools/gator/daemon/events-Cortex-A57.xml create mode 100644 tools/gator/daemon/events-Cortex-A7.xml create mode 100644 tools/gator/daemon/events-Cortex-A8.xml create mode 100644 tools/gator/daemon/events-Cortex-A9.xml create mode 100644 tools/gator/daemon/events-Krait-architected.xml create mode 100644 tools/gator/daemon/events-L2C-310.xml create mode 100644 tools/gator/daemon/events-Linux.xml create mode 100644 tools/gator/daemon/events-Mali-400.xml create mode 100644 tools/gator/daemon/events-Mali-T6xx.xml create mode 100644 tools/gator/daemon/events-Mali-T6xx_hw.xml create mode 100644 tools/gator/daemon/events-Scorpion.xml create mode 100644 tools/gator/daemon/events-ScorpionMP.xml create mode 100644 tools/gator/daemon/events_footer.xml create mode 100644 tools/gator/daemon/events_header.xml create mode 100644 tools/gator/daemon/libsensors/COPYING.LGPL create mode 100644 tools/gator/daemon/libsensors/access.c create mode 100644 tools/gator/daemon/libsensors/access.h create mode 100644 tools/gator/daemon/libsensors/conf-lex.c create mode 100644 tools/gator/daemon/libsensors/conf-lex.l create mode 100644 tools/gator/daemon/libsensors/conf-parse.c create mode 100644 tools/gator/daemon/libsensors/conf-parse.h create mode 100644 tools/gator/daemon/libsensors/conf-parse.y create mode 100644 tools/gator/daemon/libsensors/conf.h create mode 100644 tools/gator/daemon/libsensors/data.c create mode 100644 tools/gator/daemon/libsensors/data.h create mode 100644 tools/gator/daemon/libsensors/error.c create mode 100644 tools/gator/daemon/libsensors/error.h create mode 100644 tools/gator/daemon/libsensors/general.c create mode 100644 tools/gator/daemon/libsensors/general.h create mode 100644 tools/gator/daemon/libsensors/init.c create mode 100644 tools/gator/daemon/libsensors/init.h create mode 100644 tools/gator/daemon/libsensors/scanner.h create mode 100644 tools/gator/daemon/libsensors/sensors.h create mode 100644 tools/gator/daemon/libsensors/sysfs.c create mode 100644 tools/gator/daemon/libsensors/sysfs.h create mode 100644 tools/gator/daemon/libsensors/version.h create mode 100644 tools/gator/daemon/main.cpp create mode 100644 tools/gator/daemon/mxml/COPYING create mode 100644 tools/gator/daemon/mxml/config.h create mode 100644 tools/gator/daemon/mxml/mxml-attr.c create mode 100644 tools/gator/daemon/mxml/mxml-entity.c create mode 100644 tools/gator/daemon/mxml/mxml-file.c create mode 100644 tools/gator/daemon/mxml/mxml-get.c create mode 100644 tools/gator/daemon/mxml/mxml-index.c create mode 100644 tools/gator/daemon/mxml/mxml-node.c create mode 100644 tools/gator/daemon/mxml/mxml-private.c create mode 100644 tools/gator/daemon/mxml/mxml-private.h create mode 100644 tools/gator/daemon/mxml/mxml-search.c create mode 100644 tools/gator/daemon/mxml/mxml-set.c create mode 100644 tools/gator/daemon/mxml/mxml-string.c create mode 100644 tools/gator/daemon/mxml/mxml.h (limited to 'tools/gator/daemon') diff --git a/tools/gator/daemon/Android.mk b/tools/gator/daemon/Android.mk new file mode 100644 index 00000000000..4798a0a9181 --- /dev/null +++ b/tools/gator/daemon/Android.mk @@ -0,0 +1,52 @@ +LOCAL_PATH := $(call my-dir) +include $(CLEAR_VARS) + +XML_H := $(shell cd $(LOCAL_PATH) && make events_xml.h configuration_xml.h) + +LOCAL_CFLAGS += -Wall -O3 -mthumb-interwork -fno-exceptions -DETCDIR=\"/etc\" -Ilibsensors + +LOCAL_SRC_FILES := \ + Buffer.cpp \ + CapturedXML.cpp \ + Child.cpp \ + Collector.cpp \ + ConfigurationXML.cpp \ + Driver.cpp \ + EventsXML.cpp \ + Fifo.cpp \ + Hwmon.cpp \ + KMod.cpp \ + LocalCapture.cpp \ + Logging.cpp \ + main.cpp \ + OlySocket.cpp \ + OlyUtility.cpp \ + Sender.cpp \ + SessionData.cpp \ + SessionXML.cpp \ + StreamlineSetup.cpp \ + libsensors/access.c \ + libsensors/conf-lex.c \ + libsensors/conf-parse.c \ + libsensors/data.c \ + libsensors/error.c \ + libsensors/general.c \ + libsensors/init.c \ + libsensors/sysfs.c \ + mxml/mxml-attr.c \ + mxml/mxml-entity.c \ + mxml/mxml-file.c \ + mxml/mxml-get.c \ + mxml/mxml-index.c \ + mxml/mxml-node.c \ + mxml/mxml-private.c \ + mxml/mxml-search.c \ + mxml/mxml-set.c \ + mxml/mxml-string.c + +LOCAL_C_INCLUDES := $(LOCAL_PATH) + +LOCAL_MODULE := gatord +LOCAL_MODULE_TAGS := optional + +include $(BUILD_EXECUTABLE) diff --git a/tools/gator/daemon/Buffer.cpp b/tools/gator/daemon/Buffer.cpp new file mode 100644 index 00000000000..56cf42cb774 --- /dev/null +++ b/tools/gator/daemon/Buffer.cpp @@ -0,0 +1,227 @@ +/** + * Copyright (C) ARM Limited 2013. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#include "Buffer.h" + +#include "Logging.h" +#include "Sender.h" +#include "SessionData.h" + +#define mask (size - 1) + +Buffer::Buffer (const int32_t core, const int32_t buftype, const int size, sem_t *const readerSem) : core(core), buftype(buftype), size(size), readPos(0), writePos(0), commitPos(0), available(true), done(false), buf(new char[size]), +#ifdef GATOR_LIVE + commitTime(gSessionData->mLiveRate), +#endif + readerSem(readerSem) { + if ((size & mask) != 0) { + logg->logError(__FILE__, __LINE__, "Buffer size is not a power of 2"); + handleException(); + } + frame(); +} + +Buffer::~Buffer () { + delete [] buf; +} + +void Buffer::write (Sender * const sender) { + if (!commitReady()) { + return; + } + + // determine the size of two halves + int length1 = commitPos - readPos; + char * buffer1 = buf + readPos; + int length2 = 0; + char * buffer2 = buf; + if (length1 < 0) { + length1 = size - readPos; + length2 = commitPos; + } + + logg->logMessage("Sending data length1: %i length2: %i", length1, length2); + + // start, middle or end + if (length1 > 0) { + sender->writeData(buffer1, length1, RESPONSE_APC_DATA); + } + + // possible wrap around + if (length2 > 0) { + sender->writeData(buffer2, length2, RESPONSE_APC_DATA); + } + + readPos = commitPos; +} + +bool Buffer::commitReady () const { + return commitPos != readPos; +} + +int Buffer::bytesAvailable () const { + int filled = writePos - readPos; + if (filled < 0) { + filled += size; + } + + int remaining = size - filled; + + if (available) { + // Give some extra room; also allows space to insert the overflow error packet + remaining -= 200; + } else { + // Hysteresis, prevents multiple overflow messages + remaining -= 2000; + } + + return remaining; +} + +bool Buffer::checkSpace (const int bytes) { + const int remaining = bytesAvailable(); + + if (remaining < bytes) { + available = false; + } else { + available = true; + } + + return available; +} + +void Buffer::commit (const uint64_t time) { + // post-populate the length, which does not include the response type length nor the length itself, i.e. only the length of the payload + const int typeLength = gSessionData->mLocalCapture ? 0 : 1; + int length = writePos - commitPos; + if (length < 0) { + length += size; + } + length = length - typeLength - sizeof(int32_t); + for (size_t byte = 0; byte < sizeof(int32_t); byte++) { + buf[(commitPos + typeLength + byte) & mask] = (length >> byte * 8) & 0xFF; + } + + logg->logMessage("Committing data readPos: %i writePos: %i commitPos: %i", readPos, writePos, commitPos); + commitPos = writePos; + +#ifdef GATOR_LIVE + if (gSessionData->mLiveRate > 0) { + while (time > commitTime) { + commitTime += gSessionData->mLiveRate; + } + } +#endif + + if (!done) { + frame(); + } + + // send a notification that data is ready + sem_post(readerSem); +} + +void Buffer::check (const uint64_t time) { + int filled = writePos - commitPos; + if (filled < 0) { + filled += size; + } + if (filled >= ((size * 3) / 4) +#ifdef GATOR_LIVE + || (gSessionData->mLiveRate > 0 && time >= commitTime) +#endif + ) { + commit(time); + } +} + +void Buffer::packInt (int32_t x) { + int packedBytes = 0; + int more = true; + while (more) { + // low order 7 bits of x + char b = x & 0x7f; + x >>= 7; + + if ((x == 0 && (b & 0x40) == 0) || (x == -1 && (b & 0x40) != 0)) { + more = false; + } else { + b |= 0x80; + } + + buf[(writePos + packedBytes) & mask] = b; + packedBytes++; + } + + writePos = (writePos + packedBytes) & mask; +} + +void Buffer::packInt64 (int64_t x) { + int packedBytes = 0; + int more = true; + while (more) { + // low order 7 bits of x + char b = x & 0x7f; + x >>= 7; + + if ((x == 0 && (b & 0x40) == 0) || (x == -1 && (b & 0x40) != 0)) { + more = false; + } else { + b |= 0x80; + } + + buf[(writePos + packedBytes) & mask] = b; + packedBytes++; + } + + writePos = (writePos + packedBytes) & mask; +} + +void Buffer::frame () { + if (!gSessionData->mLocalCapture) { + packInt(RESPONSE_APC_DATA); + } + // Reserve space for the length + writePos += sizeof(int32_t); + packInt(buftype); + packInt(core); +} + +bool Buffer::eventHeader (const uint64_t curr_time) { + bool retval = false; + if (checkSpace(MAXSIZE_PACK32 + MAXSIZE_PACK64)) { + packInt(0); // key of zero indicates a timestamp + packInt64(curr_time); + retval = true; + } + + return retval; +} + +void Buffer::event (const int32_t key, const int32_t value) { + if (checkSpace(2 * MAXSIZE_PACK32)) { + packInt(key); + packInt(value); + } +} + +void Buffer::event64 (const int64_t key, const int64_t value) { + if (checkSpace(2 * MAXSIZE_PACK64)) { + packInt64(key); + packInt64(value); + } +} + +void Buffer::setDone () { + done = true; + commit(0); +} + +bool Buffer::isDone () const { + return done && readPos == commitPos && commitPos == writePos; +} diff --git a/tools/gator/daemon/Buffer.h b/tools/gator/daemon/Buffer.h new file mode 100644 index 00000000000..c460fb7854a --- /dev/null +++ b/tools/gator/daemon/Buffer.h @@ -0,0 +1,66 @@ +/** + * Copyright (C) ARM Limited 2013. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#ifndef BUFFER_H +#define BUFFER_H + +#include +#include +#include + +#define GATOR_LIVE + +class Sender; + +class Buffer { +public: + static const size_t MAXSIZE_PACK32 = 5; + static const size_t MAXSIZE_PACK64 = 10; + + Buffer (int32_t core, int32_t buftype, const int size, sem_t *const readerSem); + ~Buffer (); + + void write (Sender * sender); + + int bytesAvailable () const; + void commit (const uint64_t time); + void check (const uint64_t time); + + void frame (); + + bool eventHeader (uint64_t curr_time); + void event (int32_t key, int32_t value); + void event64 (int64_t key, int64_t value); + + void setDone (); + bool isDone () const; + +private: + bool commitReady () const; + bool checkSpace (int bytes); + + void packInt (int32_t x); + void packInt64 (int64_t x); + + const int32_t core; + const int32_t buftype; + const int size; + int readPos; + int writePos; + int commitPos; + bool available; + bool done; + char *const buf; +#ifdef GATOR_LIVE + uint64_t commitTime; +#endif + + sem_t *const readerSem; +}; + +#endif // BUFFER_H diff --git a/tools/gator/daemon/CapturedXML.cpp b/tools/gator/daemon/CapturedXML.cpp new file mode 100644 index 00000000000..af726df71d5 --- /dev/null +++ b/tools/gator/daemon/CapturedXML.cpp @@ -0,0 +1,126 @@ +/** + * Copyright (C) ARM Limited 2010-2013. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#include +#include +#include +#include "SessionData.h" +#include "CapturedXML.h" +#include "Logging.h" +#include "OlyUtility.h" + +CapturedXML::CapturedXML() { +} + +CapturedXML::~CapturedXML() { +} + +mxml_node_t* CapturedXML::getTree(bool includeTime) { + mxml_node_t *xml; + mxml_node_t *captured; + mxml_node_t *target; + int x; + + xml = mxmlNewXML("1.0"); + + captured = mxmlNewElement(xml, "captured"); + mxmlElementSetAttr(captured, "version", "1"); + mxmlElementSetAttrf(captured, "protocol", "%d", PROTOCOL_VERSION); + if (includeTime) { // Send the following only after the capture is complete + if (time(NULL) > 1267000000) { // If the time is reasonable (after Feb 23, 2010) + mxmlElementSetAttrf(captured, "created", "%lu", time(NULL)); // Valid until the year 2038 + } + } + + target = mxmlNewElement(captured, "target"); + mxmlElementSetAttr(target, "name", gSessionData->mCoreName); + mxmlElementSetAttrf(target, "sample_rate", "%d", gSessionData->mSampleRate); + mxmlElementSetAttrf(target, "cores", "%d", gSessionData->mCores); + mxmlElementSetAttrf(target, "cpuid", "0x%x", gSessionData->mCpuId); + + mxml_node_t *counters = NULL; + for (x = 0; x < MAX_PERFORMANCE_COUNTERS; x++) { + const Counter & counter = gSessionData->mCounters[x]; + if (counter.isEnabled()) { + if (counters == NULL) { + counters = mxmlNewElement(captured, "counters"); + } + mxml_node_t *const node = mxmlNewElement(counters, "counter"); + mxmlElementSetAttrf(node, "key", "0x%x", counter.getKey()); + mxmlElementSetAttr(node, "type", counter.getType()); + mxmlElementSetAttrf(node, "event", "0x%x", counter.getEvent()); + if (counter.getCount() > 0) { + mxmlElementSetAttrf(node, "count", "%d", counter.getCount()); + } + } + } + + return xml; +} + +char* CapturedXML::getXML(bool includeTime) { + char* xml_string; + mxml_node_t *xml = getTree(includeTime); + xml_string = mxmlSaveAllocString(xml, mxmlWhitespaceCB); + mxmlDelete(xml); + return xml_string; +} + +void CapturedXML::write(char* path) { + char file[PATH_MAX]; + + // Set full path + snprintf(file, PATH_MAX, "%s/captured.xml", path); + + char* xml = getXML(true); + if (util->writeToDisk(file, xml) < 0) { + logg->logError(__FILE__, __LINE__, "Error writing %s\nPlease verify the path.", file); + handleException(); + } + + free(xml); +} + +// whitespace callback utility function used with mini-xml +const char * mxmlWhitespaceCB(mxml_node_t *node, int loc) { + const char *name; + + name = mxmlGetElement(node); + + if (loc == MXML_WS_BEFORE_OPEN) { + // Single indentation + if (!strcmp(name, "target") || !strcmp(name, "counters")) + return("\n "); + + // Double indentation + if (!strcmp(name, "counter")) + return("\n "); + + // Avoid a carriage return on the first line of the xml file + if (!strncmp(name, "?xml", 4)) + return(NULL); + + // Default - no indentation + return("\n"); + } + + if (loc == MXML_WS_BEFORE_CLOSE) { + // No indentation + if (!strcmp(name, "captured")) + return("\n"); + + // Single indentation + if (!strcmp(name, "counters")) + return("\n "); + + // Default - no carriage return + return(NULL); + } + + return(NULL); +} diff --git a/tools/gator/daemon/CapturedXML.h b/tools/gator/daemon/CapturedXML.h new file mode 100644 index 00000000000..b0482f593c6 --- /dev/null +++ b/tools/gator/daemon/CapturedXML.h @@ -0,0 +1,26 @@ +/** + * Copyright (C) ARM Limited 2010-2013. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#ifndef __CAPTURED_XML_H__ +#define __CAPTURED_XML_H__ + +#include "mxml/mxml.h" + +class CapturedXML { +public: + CapturedXML(); + ~CapturedXML(); + char* getXML(bool includeTime); // the string should be freed by the caller + void write(char* path); +private: + mxml_node_t* getTree(bool includeTime); +}; + +const char * mxmlWhitespaceCB(mxml_node_t *node, int where); + +#endif //__CAPTURED_XML_H__ diff --git a/tools/gator/daemon/Child.cpp b/tools/gator/daemon/Child.cpp new file mode 100644 index 00000000000..286c7e7ba39 --- /dev/null +++ b/tools/gator/daemon/Child.cpp @@ -0,0 +1,405 @@ +/** + * Copyright (C) ARM Limited 2010-2013. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#include +#include +#include +#include +#include +#include "Logging.h" +#include "CapturedXML.h" +#include "SessionData.h" +#include "Child.h" +#include "LocalCapture.h" +#include "Collector.h" +#include "Sender.h" +#include "OlyUtility.h" +#include "StreamlineSetup.h" +#include "ConfigurationXML.h" +#include "Driver.h" +#include "Fifo.h" +#include "Buffer.h" + +#define NS_PER_S ((uint64_t)1000000000) +#define NS_PER_US 1000 + +static sem_t haltPipeline, senderThreadStarted, startProfile, senderSem; // Shared by Child and spawned threads +static Fifo* collectorFifo = NULL; // Shared by Child.cpp and spawned threads +static Buffer* buffer = NULL; +static Sender* sender = NULL; // Shared by Child.cpp and spawned threads +static Collector* collector = NULL; +Child* child = NULL; // shared by Child.cpp and main.cpp + +extern void cleanUp(); +void handleException() { + if (child && child->numExceptions++ > 0) { + // it is possible one of the below functions itself can cause an exception, thus allow only one exception + logg->logMessage("Received multiple exceptions, terminating the child"); + exit(1); + } + fprintf(stderr, "%s", logg->getLastError()); + + if (child && child->socket) { + if (sender) { + // send the error, regardless of the command sent by Streamline + sender->writeData(logg->getLastError(), strlen(logg->getLastError()), RESPONSE_ERROR); + + // cannot close the socket before Streamline issues the command, so wait for the command before exiting + if (gSessionData->mWaitingOnCommand) { + char discard; + child->socket->receiveNBytes(&discard, 1); + } + + // this indirectly calls close socket which will ensure the data has been sent + delete sender; + } + } + + if (gSessionData->mLocalCapture) + cleanUp(); + + exit(1); +} + +// CTRL C Signal Handler for child process +static void child_handler(int signum) { + static bool beenHere = false; + if (beenHere == true) { + logg->logMessage("Gator is being forced to shut down."); + exit(1); + } + beenHere = true; + logg->logMessage("Gator is shutting down."); + if (signum == SIGALRM || !collector) { + exit(1); + } else { + child->endSession(); + alarm(5); // Safety net in case endSession does not complete within 5 seconds + } +} + +static void* durationThread(void* pVoid) { + prctl(PR_SET_NAME, (unsigned long)&"gatord-duration", 0, 0, 0); + sem_wait(&startProfile); + if (gSessionData->mSessionIsActive) { + // Time out after duration seconds + // Add a second for host-side filtering + sleep(gSessionData->mDuration + 1); + if (gSessionData->mSessionIsActive) { + logg->logMessage("Duration expired."); + child->endSession(); + } + } + logg->logMessage("Exit duration thread"); + return 0; +} + +static void* stopThread(void* pVoid) { + OlySocket* socket = child->socket; + + prctl(PR_SET_NAME, (unsigned long)&"gatord-stopper", 0, 0, 0); + while (gSessionData->mSessionIsActive) { + // This thread will stall until the APC_STOP or PING command is received over the socket or the socket is disconnected + unsigned char header[5]; + const int result = socket->receiveNBytes((char*)&header, sizeof(header)); + const char type = header[0]; + const int length = (header[1] << 0) | (header[2] << 8) | (header[3] << 16) | (header[4] << 24); + if (result == -1) { + child->endSession(); + } else if (result > 0) { + if ((type != COMMAND_APC_STOP) && (type != COMMAND_PING)) { + logg->logMessage("INVESTIGATE: Received unknown command type %d", type); + } else { + // verify a length of zero + if (length == 0) { + if (type == COMMAND_APC_STOP) { + logg->logMessage("Stop command received."); + child->endSession(); + } else { + // Ping is used to make sure gator is alive and requires an ACK as the response + logg->logMessage("Ping command received."); + sender->writeData(NULL, 0, RESPONSE_ACK); + } + } else { + logg->logMessage("INVESTIGATE: Received stop command but with length = %d", length); + } + } + } + } + + logg->logMessage("Exit stop thread"); + return 0; +} + +void* countersThread(void* pVoid) { + prctl(PR_SET_NAME, (unsigned long)&"gatord-counters", 0, 0, 0); + + gSessionData->hwmon.start(); + + int64_t monotonic_started = 0; + while (monotonic_started <= 0) { + usleep(10); + + if (Collector::readInt64Driver("/dev/gator/started", &monotonic_started) == -1) { + logg->logError(__FILE__, __LINE__, "Error reading gator driver start time"); + handleException(); + } + } + + uint64_t next_time = 0; + while (gSessionData->mSessionIsActive) { + struct timespec ts; +#ifndef CLOCK_MONOTONIC_RAW + // Android doesn't have this defined but it was added in Linux 2.6.28 +#define CLOCK_MONOTONIC_RAW 4 +#endif + if (clock_gettime(CLOCK_MONOTONIC_RAW, &ts) != 0) { + logg->logError(__FILE__, __LINE__, "Failed to get uptime"); + handleException(); + } + const uint64_t curr_time = (NS_PER_S*ts.tv_sec + ts.tv_nsec) - monotonic_started; + // Sample ten times a second ignoring gSessionData->mSampleRate + next_time += NS_PER_S/10;//gSessionData->mSampleRate; + if (next_time < curr_time) { + logg->logMessage("Too slow, curr_time: %lli next_time: %lli", curr_time, next_time); + next_time = curr_time; + } + + if (buffer->eventHeader(curr_time)) { + gSessionData->hwmon.read(buffer); + // Only check after writing all counters so that time and corresponding counters appear in the same frame + buffer->check(curr_time); + } + + if (buffer->bytesAvailable() <= 0) { + logg->logMessage("One shot (counters)"); + child->endSession(); + } + + usleep((next_time - curr_time)/NS_PER_US); + } + + buffer->setDone(); + + return NULL; +} + +static void* senderThread(void* pVoid) { + int length = 1; + char* data; + char end_sequence[] = {RESPONSE_APC_DATA, 0, 0, 0, 0}; + + sem_post(&senderThreadStarted); + prctl(PR_SET_NAME, (unsigned long)&"gatord-sender", 0, 0, 0); + sem_wait(&haltPipeline); + + while (length > 0 || !buffer->isDone()) { + sem_wait(&senderSem); + data = collectorFifo->read(&length); + if (data != NULL) { + sender->writeData(data, length, RESPONSE_APC_DATA); + collectorFifo->release(); + } + if (!buffer->isDone()) { + buffer->write(sender); + } + } + + // write end-of-capture sequence + if (!gSessionData->mLocalCapture) { + sender->writeData(end_sequence, sizeof(end_sequence), RESPONSE_APC_DATA); + } + + logg->logMessage("Exit sender thread"); + return 0; +} + +Child::Child() { + initialization(); + gSessionData->mLocalCapture = true; +} + +Child::Child(OlySocket* sock, int conn) { + initialization(); + socket = sock; + mNumConnections = conn; +} + +Child::~Child() { +} + +void Child::initialization() { + // Set up different handlers for signals + gSessionData->mSessionIsActive = true; + signal(SIGINT, child_handler); + signal(SIGTERM, child_handler); + signal(SIGABRT, child_handler); + signal(SIGALRM, child_handler); + socket = NULL; + numExceptions = 0; + mNumConnections = 0; + + // Initialize semaphores + sem_init(&senderThreadStarted, 0, 0); + sem_init(&startProfile, 0, 0); + sem_init(&senderSem, 0, 0); +} + +void Child::endSession() { + gSessionData->mSessionIsActive = false; + collector->stop(); + sem_post(&haltPipeline); +} + +void Child::run() { + char* collectBuffer; + int bytesCollected = 0; + LocalCapture* localCapture = NULL; + pthread_t durationThreadID, stopThreadID, senderThreadID, countersThreadID; + + prctl(PR_SET_NAME, (unsigned long)&"gatord-child", 0, 0, 0); + + // Disable line wrapping when generating xml files; carriage returns and indentation to be added manually + mxmlSetWrapMargin(0); + + // Instantiate the Sender - must be done first, after which error messages can be sent + sender = new Sender(socket); + + if (mNumConnections > 1) { + logg->logError(__FILE__, __LINE__, "Session already in progress"); + handleException(); + } + + // Populate gSessionData with the configuration + { ConfigurationXML configuration; } + + // Set up the driver; must be done after gSessionData->mPerfCounterType[] is populated + collector = new Collector(); + + // Initialize all drivers + for (Driver *driver = Driver::getHead(); driver != NULL; driver = driver->getNext()) { + driver->resetCounters(); + } + + // Set up counters using the associated driver's setup function + for (int i = 0; i < MAX_PERFORMANCE_COUNTERS; i++) { + Counter & counter = gSessionData->mCounters[i]; + if (counter.isEnabled()) { + counter.getDriver()->setupCounter(counter); + } + } + + // Start up and parse session xml + if (socket) { + // Respond to Streamline requests + StreamlineSetup ss(socket); + } else { + char* xmlString; + xmlString = util->readFromDisk(gSessionData->mSessionXMLPath); + if (xmlString == 0) { + logg->logError(__FILE__, __LINE__, "Unable to read session xml file: %s", gSessionData->mSessionXMLPath); + handleException(); + } + gSessionData->parseSessionXML(xmlString); + localCapture = new LocalCapture(); + localCapture->createAPCDirectory(gSessionData->mTargetPath); + localCapture->copyImages(gSessionData->mImages); + localCapture->write(xmlString); + sender->createDataFile(gSessionData->mAPCDir); + free(xmlString); + } + + // Create user-space buffers, add 5 to the size to account for the 1-byte type and 4-byte length + logg->logMessage("Created %d MB collector buffer with a %d-byte ragged end", gSessionData->mTotalBufferSize, collector->getBufferSize()); + collectorFifo = new Fifo(collector->getBufferSize() + 5, gSessionData->mTotalBufferSize*1024*1024, &senderSem); + + // Get the initial pointer to the collect buffer + collectBuffer = collectorFifo->start(); + + // Create a new Block Counter Buffer + buffer = new Buffer(0, 5, gSessionData->mTotalBufferSize*1024*1024, &senderSem); + + // Sender thread shall be halted until it is signaled for one shot mode + sem_init(&haltPipeline, 0, gSessionData->mOneShot ? 0 : 2); + + // Create the duration, stop, and sender threads + bool thread_creation_success = true; + if (gSessionData->mDuration > 0 && pthread_create(&durationThreadID, NULL, durationThread, NULL)) { + thread_creation_success = false; + } else if (socket && pthread_create(&stopThreadID, NULL, stopThread, NULL)) { + thread_creation_success = false; + } else if (pthread_create(&senderThreadID, NULL, senderThread, NULL)){ + thread_creation_success = false; + } + + if (gSessionData->hwmon.countersEnabled()) { + if (pthread_create(&countersThreadID, NULL, countersThread, this)) { + thread_creation_success = false; + } + } else { + // Let senderThread know there is no buffer data to send + buffer->setDone(); + } + + if (!thread_creation_success) { + logg->logError(__FILE__, __LINE__, "Failed to create gator threads"); + handleException(); + } + + // Wait until thread has started + sem_wait(&senderThreadStarted); + + // Start profiling + logg->logMessage("********** Profiling started **********"); + collector->start(); + sem_post(&startProfile); + + // Collect Data + do { + // This command will stall until data is received from the driver + bytesCollected = collector->collect(collectBuffer); + + // In one shot mode, stop collection once all the buffers are filled + if (gSessionData->mOneShot && gSessionData->mSessionIsActive) { + if (bytesCollected == -1 || collectorFifo->willFill(bytesCollected)) { + logg->logMessage("One shot"); + endSession(); + } + } + collectBuffer = collectorFifo->write(bytesCollected); + } while (bytesCollected > 0); + logg->logMessage("Exit collect data loop"); + + if (gSessionData->hwmon.countersEnabled()) { + pthread_join(countersThreadID, NULL); + } + + // Wait for the other threads to exit + pthread_join(senderThreadID, NULL); + + // Shutting down the connection should break the stop thread which is stalling on the socket recv() function + if (socket) { + logg->logMessage("Waiting on stop thread"); + socket->shutdownConnection(); + pthread_join(stopThreadID, NULL); + } + + // Write the captured xml file + if (gSessionData->mLocalCapture) { + CapturedXML capturedXML; + capturedXML.write(gSessionData->mAPCDir); + } + + logg->logMessage("Profiling ended."); + + delete buffer; + delete collectorFifo; + delete sender; + delete collector; + delete localCapture; +} diff --git a/tools/gator/daemon/Child.h b/tools/gator/daemon/Child.h new file mode 100644 index 00000000000..e39d1827640 --- /dev/null +++ b/tools/gator/daemon/Child.h @@ -0,0 +1,31 @@ +/** + * Copyright (C) ARM Limited 2010-2013. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#ifndef __CHILD_H__ +#define __CHILD_H__ + +#include + +class OlySocket; + +class Child { +public: + Child(); + Child(OlySocket* sock, int numConnections); + ~Child(); + void run(); + OlySocket *socket; + void endSession(); + int numExceptions; +private: + int mNumConnections; + + void initialization(); +}; + +#endif //__CHILD_H__ diff --git a/tools/gator/daemon/Collector.cpp b/tools/gator/daemon/Collector.cpp new file mode 100644 index 00000000000..bf73534692a --- /dev/null +++ b/tools/gator/daemon/Collector.cpp @@ -0,0 +1,221 @@ +/** + * Copyright (C) ARM Limited 2010-2013. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#define __STDC_FORMAT_MACROS + +#include +#include +#include +#include +#include +#include +#include +#include "Collector.h" +#include "SessionData.h" +#include "Logging.h" +#include "Sender.h" + +// Driver initialization independent of session settings +Collector::Collector() { + mBufferFD = 0; + + checkVersion(); + + int enable = -1; + if (readIntDriver("/dev/gator/enable", &enable) != 0 || enable != 0) { + logg->logError(__FILE__, __LINE__, "Driver already enabled, possibly a session is already in progress."); + handleException(); + } + + readIntDriver("/dev/gator/cpu_cores", &gSessionData->mCores); + if (gSessionData->mCores == 0) { + gSessionData->mCores = 1; + } + + mBufferSize = 0; + if (readIntDriver("/dev/gator/buffer_size", &mBufferSize) || mBufferSize <= 0) { + logg->logError(__FILE__, __LINE__, "Unable to read the driver buffer size"); + handleException(); + } +} + +Collector::~Collector() { + // Write zero for safety, as a zero should have already been written + writeDriver("/dev/gator/enable", "0"); + + // Calls event_buffer_release in the driver + if (mBufferFD) { + close(mBufferFD); + } +} + +void Collector::checkVersion() { + int driver_version = 0; + + if (readIntDriver("/dev/gator/version", &driver_version) == -1) { + logg->logError(__FILE__, __LINE__, "Error reading gator driver version"); + handleException(); + } + + // Verify the driver version matches the daemon version + if (driver_version != PROTOCOL_VERSION) { + if ((driver_version > PROTOCOL_DEV) || (PROTOCOL_VERSION > PROTOCOL_DEV)) { + // One of the mismatched versions is development version + logg->logError(__FILE__, __LINE__, + "DEVELOPMENT BUILD MISMATCH: gator driver version \"%d\" is not in sync with gator daemon version \"%d\".\n" + ">> The following must be synchronized from engineering repository:\n" + ">> * gator driver\n" + ">> * gator daemon\n" + ">> * Streamline", driver_version, PROTOCOL_VERSION); + handleException(); + } else { + // Release version mismatch + logg->logError(__FILE__, __LINE__, + "gator driver version \"%d\" is different than gator daemon version \"%d\".\n" + ">> Please upgrade the driver and daemon to the latest versions.", driver_version, PROTOCOL_VERSION); + handleException(); + } + } +} + +void Collector::start() { + // Set the maximum backtrace depth + if (writeReadDriver("/dev/gator/backtrace_depth", &gSessionData->mBacktraceDepth)) { + logg->logError(__FILE__, __LINE__, "Unable to set the driver backtrace depth"); + handleException(); + } + + // open the buffer which calls userspace_buffer_open() in the driver + mBufferFD = open("/dev/gator/buffer", O_RDONLY); + if (mBufferFD < 0) { + logg->logError(__FILE__, __LINE__, "The gator driver did not set up properly. Please view the linux console or dmesg log for more information on the failure."); + handleException(); + } + + // set the tick rate of the profiling timer + if (writeReadDriver("/dev/gator/tick", &gSessionData->mSampleRate) != 0) { + logg->logError(__FILE__, __LINE__, "Unable to set the driver tick"); + handleException(); + } + + // notify the kernel of the response type + int response_type = gSessionData->mLocalCapture ? 0 : RESPONSE_APC_DATA; + if (writeDriver("/dev/gator/response_type", response_type)) { + logg->logError(__FILE__, __LINE__, "Unable to write the response type"); + handleException(); + } + + // Set the live rate + if (writeReadDriver("/dev/gator/live_rate", &gSessionData->mLiveRate)) { + logg->logError(__FILE__, __LINE__, "Unable to set the driver live rate"); + handleException(); + } + + logg->logMessage("Start the driver"); + + // This command makes the driver start profiling by calling gator_op_start() in the driver + if (writeDriver("/dev/gator/enable", "1") != 0) { + logg->logError(__FILE__, __LINE__, "The gator driver did not start properly. Please view the linux console or dmesg log for more information on the failure."); + handleException(); + } + + lseek(mBufferFD, 0, SEEK_SET); +} + +// These commands should cause the read() function in collect() to return +void Collector::stop() { + // This will stop the driver from profiling + if (writeDriver("/dev/gator/enable", "0") != 0) { + logg->logMessage("Stopping kernel failed"); + } +} + +int Collector::collect(char* buffer) { + // Calls event_buffer_read in the driver + int bytesRead; + + errno = 0; + bytesRead = read(mBufferFD, buffer, mBufferSize); + + // If read() returned due to an interrupt signal, re-read to obtain the last bit of collected data + if (bytesRead == -1 && errno == EINTR) { + bytesRead = read(mBufferFD, buffer, mBufferSize); + } + + // return the total bytes written + logg->logMessage("Driver read of %d bytes", bytesRead); + return bytesRead; +} + +int Collector::readIntDriver(const char* fullpath, int* value) { + FILE* file = fopen(fullpath, "r"); + if (file == NULL) { + return -1; + } + if (fscanf(file, "%u", value) != 1) { + fclose(file); + logg->logMessage("Invalid value in file %s", fullpath); + return -1; + } + fclose(file); + return 0; +} + +int Collector::readInt64Driver(const char* fullpath, int64_t* value) { + FILE* file = fopen(fullpath, "r"); + if (file == NULL) { + return -1; + } + if (fscanf(file, "%" SCNi64, value) != 1) { + fclose(file); + logg->logMessage("Invalid value in file %s", fullpath); + return -1; + } + fclose(file); + return 0; +} + +int Collector::writeDriver(const char* path, int value) { + char data[40]; // Sufficiently large to hold any integer + snprintf(data, sizeof(data), "%d", value); + return writeDriver(path, data); +} + +int Collector::writeDriver(const char* path, int64_t value) { + char data[40]; // Sufficiently large to hold any integer + snprintf(data, sizeof(data), "%" PRIi64, value); + return writeDriver(path, data); +} + +int Collector::writeDriver(const char* fullpath, const char* data) { + int fd = open(fullpath, O_WRONLY); + if (fd < 0) { + return -1; + } + if (write(fd, data, strlen(data)) < 0) { + close(fd); + logg->logMessage("Opened but could not write to %s", fullpath); + return -1; + } + close(fd); + return 0; +} + +int Collector::writeReadDriver(const char* path, int* value) { + if (writeDriver(path, *value) || readIntDriver(path, value)) { + return -1; + } + return 0; +} + +int Collector::writeReadDriver(const char* path, int64_t* value) { + if (writeDriver(path, *value) || readInt64Driver(path, value)) { + return -1; + } + return 0; +} diff --git a/tools/gator/daemon/Collector.h b/tools/gator/daemon/Collector.h new file mode 100644 index 00000000000..c5e9eac573d --- /dev/null +++ b/tools/gator/daemon/Collector.h @@ -0,0 +1,38 @@ +/** + * Copyright (C) ARM Limited 2010-2013. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#ifndef __COLLECTOR_H__ +#define __COLLECTOR_H__ + +#include + +class Collector { +public: + Collector(); + ~Collector(); + void start(); + void stop(); + int collect(char* buffer); + int getBufferSize() {return mBufferSize;} + + static int readIntDriver(const char* path, int* value); + static int readInt64Driver(const char* path, int64_t* value); + static int writeDriver(const char* path, int value); + static int writeDriver(const char* path, int64_t value); + static int writeDriver(const char* path, const char* data); + static int writeReadDriver(const char* path, int* value); + static int writeReadDriver(const char* path, int64_t* value); + +private: + int mBufferSize; + int mBufferFD; + + void checkVersion(); +}; + +#endif //__COLLECTOR_H__ diff --git a/tools/gator/daemon/ConfigurationXML.cpp b/tools/gator/daemon/ConfigurationXML.cpp new file mode 100644 index 00000000000..fb00202f656 --- /dev/null +++ b/tools/gator/daemon/ConfigurationXML.cpp @@ -0,0 +1,199 @@ +/** + * Copyright (C) ARM Limited 2010-2013. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#include +#include +#include +#include "ConfigurationXML.h" +#include "Driver.h" +#include "Logging.h" +#include "OlyUtility.h" +#include "SessionData.h" + +static const char* ATTR_COUNTER = "counter"; +static const char* ATTR_REVISION = "revision"; +static const char* ATTR_EVENT = "event"; +static const char* ATTR_COUNT = "count"; + +ConfigurationXML::ConfigurationXML() { + const char * configuration_xml; + unsigned int configuration_xml_len; + getDefaultConfigurationXml(configuration_xml, configuration_xml_len); + + char path[PATH_MAX]; + + if (gSessionData->mConfigurationXMLPath) { + strncpy(path, gSessionData->mConfigurationXMLPath, PATH_MAX); + } else { + if (util->getApplicationFullPath(path, PATH_MAX) != 0) { + logg->logMessage("Unable to determine the full path of gatord, the cwd will be used"); + } + strncat(path, "configuration.xml", PATH_MAX - strlen(path) - 1); + } + mConfigurationXML = util->readFromDisk(path); + + for (int retryCount = 0; retryCount < 2; ++retryCount) { + if (mConfigurationXML == NULL) { + logg->logMessage("Unable to locate configuration.xml, using default in binary"); + // null-terminate configuration_xml + mConfigurationXML = (char*)malloc(configuration_xml_len + 1); + memcpy(mConfigurationXML, (const void*)configuration_xml, configuration_xml_len); + mConfigurationXML[configuration_xml_len] = 0; + } + + int ret = parse(mConfigurationXML); + if (ret == 1) { + // remove configuration.xml on disk to use the default + if (remove(path) != 0) { + logg->logError(__FILE__, __LINE__, "Invalid configuration.xml file detected and unable to delete it. To resolve, delete configuration.xml on disk"); + handleException(); + } + logg->logMessage("Invalid configuration.xml file detected and removed"); + + // Free the current configuration and reload + free((void*)mConfigurationXML); + mConfigurationXML = NULL; + continue; + } + + break; + } + + validate(); +} + +ConfigurationXML::~ConfigurationXML() { + if (mConfigurationXML) { + free((void*)mConfigurationXML); + } +} + +int ConfigurationXML::parse(const char* configurationXML) { + mxml_node_t *tree, *node; + int ret; + + // clear counter overflow + gSessionData->mCounterOverflow = 0; + mIndex = 0; + + // disable all counters prior to parsing the configuration xml + for (int i = 0; i < MAX_PERFORMANCE_COUNTERS; i++) { + gSessionData->mCounters[i].setEnabled(false); + } + + tree = mxmlLoadString(NULL, configurationXML, MXML_NO_CALLBACK); + + node = mxmlGetFirstChild(tree); + while (node && mxmlGetType(node) != MXML_ELEMENT) + node = mxmlWalkNext(node, tree, MXML_NO_DESCEND); + + ret = configurationsTag(node); + + node = mxmlGetFirstChild(node); + while (node) { + if (mxmlGetType(node) != MXML_ELEMENT) { + node = mxmlWalkNext(node, tree, MXML_NO_DESCEND); + continue; + } + configurationTag(node); + node = mxmlWalkNext(node, tree, MXML_NO_DESCEND); + } + + mxmlDelete(tree); + + return ret; +} + +void ConfigurationXML::validate(void) { + for (int i = 0; i < MAX_PERFORMANCE_COUNTERS; i++) { + const Counter & counter = gSessionData->mCounters[i]; + if (counter.isEnabled()) { + if (strcmp(counter.getType(), "") == 0) { + logg->logError(__FILE__, __LINE__, "Invalid required attribute in configuration.xml:\n counter=\"%s\"\n event=%d\n", counter.getType(), counter.getEvent()); + handleException(); + } + + // iterate through the remaining enabled performance counters + for (int j = i + 1; j < MAX_PERFORMANCE_COUNTERS; j++) { + const Counter & counter2 = gSessionData->mCounters[j]; + if (counter2.isEnabled()) { + // check if the types are the same + if (strcmp(counter.getType(), counter2.getType()) == 0) { + logg->logError(__FILE__, __LINE__, "Duplicate performance counter type in configuration.xml: %s", counter.getType()); + handleException(); + } + } + } + } + } +} + +#define CONFIGURATION_REVISION 3 +int ConfigurationXML::configurationsTag(mxml_node_t *node) { + const char* revision_string; + + revision_string = mxmlElementGetAttr(node, ATTR_REVISION); + if (!revision_string) { + return 1; //revision issue; + } + + int revision = strtol(revision_string, NULL, 10); + if (revision < CONFIGURATION_REVISION) { + return 1; // revision issue + } + + // A revision >= CONFIGURATION_REVISION is okay + // Greater than can occur when Streamline is newer than gator + + return 0; +} + +void ConfigurationXML::configurationTag(mxml_node_t *node) { + // handle all other performance counters + if (mIndex >= MAX_PERFORMANCE_COUNTERS) { + mIndex++; + gSessionData->mCounterOverflow = mIndex; + return; + } + + // read attributes + Counter & counter = gSessionData->mCounters[mIndex]; + counter.clear(); + if (mxmlElementGetAttr(node, ATTR_COUNTER)) counter.setType(mxmlElementGetAttr(node, ATTR_COUNTER)); + if (mxmlElementGetAttr(node, ATTR_EVENT)) counter.setEvent(strtol(mxmlElementGetAttr(node, ATTR_EVENT), NULL, 16)); + if (mxmlElementGetAttr(node, ATTR_COUNT)) counter.setCount(strtol(mxmlElementGetAttr(node, ATTR_COUNT), NULL, 10)); + counter.setEnabled(true); + + // Associate a driver with each counter + for (Driver *driver = Driver::getHead(); driver != NULL; driver = driver->getNext()) { + if (driver->claimCounter(counter)) { + if (counter.getDriver() != NULL) { + logg->logError(__FILE__, __LINE__, "More than one driver has claimed %s:%i", counter.getType(), counter.getEvent()); + handleException(); + } + counter.setDriver(driver); + } + } + + // If no driver is associated with the counter, disable it + if (counter.getDriver() == NULL) { + logg->logMessage("No driver has claimed %s:%i", counter.getType(), counter.getEvent()); + counter.setEnabled(false); + } + + if (counter.isEnabled()) { + // update counter index + mIndex++; + } +} + +void ConfigurationXML::getDefaultConfigurationXml(const char * & xml, unsigned int & len) { +#include "configuration_xml.h" // defines and initializes char configuration_xml[] and int configuration_xml_len + xml = (const char *)configuration_xml; + len = configuration_xml_len; +} diff --git a/tools/gator/daemon/ConfigurationXML.h b/tools/gator/daemon/ConfigurationXML.h new file mode 100644 index 00000000000..f709ad16825 --- /dev/null +++ b/tools/gator/daemon/ConfigurationXML.h @@ -0,0 +1,32 @@ +/** + * Copyright (C) ARM Limited 2010-2013. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#ifndef COUNTERS_H +#define COUNTERS_H + +#include "mxml/mxml.h" + +class ConfigurationXML { +public: + static void getDefaultConfigurationXml(const char * & xml, unsigned int & len); + + ConfigurationXML(); + ~ConfigurationXML(); + const char* getConfigurationXML() {return mConfigurationXML;} + void validate(void); + +private: + char* mConfigurationXML; + int mIndex; + + int parse(const char* xmlFile); + int configurationsTag(mxml_node_t *node); + void configurationTag(mxml_node_t *node); +}; + +#endif // COUNTERS_H diff --git a/tools/gator/daemon/Counter.h b/tools/gator/daemon/Counter.h new file mode 100644 index 00000000000..231a85d6e3b --- /dev/null +++ b/tools/gator/daemon/Counter.h @@ -0,0 +1,61 @@ +/** + * Copyright (C) ARM Limited 2013. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#ifndef COUNTER_H +#define COUNTER_H + +#include + +class Driver; + +class Counter { +public: + static const size_t MAX_STRING_LEN = 80; + static const size_t MAX_DESCRIPTION_LEN = 400; + + Counter () { + clear(); + } + + void clear () { + mType[0] = '\0'; + mEnabled = false; + mEvent = 0; + mCount = 0; + mKey = 0; + mDriver = NULL; + } + + void setType(const char *const type) { strncpy(mType, type, sizeof(mType)); mType[sizeof(mType) - 1] = '\0'; } + void setEnabled(const bool enabled) { mEnabled = enabled; } + void setEvent(const int event) { mEvent = event; } + void setCount(const int count) { mCount = count; } + void setKey(const int key) { mKey = key; } + void setDriver(Driver *const driver) { mDriver = driver; } + + const char *getType() const { return mType;} + bool isEnabled() const { return mEnabled; } + int getEvent() const { return mEvent; } + int getCount() const { return mCount; } + int getKey() const { return mKey; } + Driver *getDriver() const { return mDriver; } + +private: + // Intentionally unimplemented + Counter(const Counter &); + Counter & operator=(const Counter &); + + char mType[MAX_STRING_LEN]; + bool mEnabled; + int mEvent; + int mCount; + int mKey; + Driver *mDriver; +}; + +#endif // COUNTER_H diff --git a/tools/gator/daemon/Driver.cpp b/tools/gator/daemon/Driver.cpp new file mode 100644 index 00000000000..c262467dc21 --- /dev/null +++ b/tools/gator/daemon/Driver.cpp @@ -0,0 +1,15 @@ +/** + * Copyright (C) ARM Limited 2013. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#include "Driver.h" + +Driver *Driver::head = NULL; + +Driver::Driver() : next(head) { + head = this; +} diff --git a/tools/gator/daemon/Driver.h b/tools/gator/daemon/Driver.h new file mode 100644 index 00000000000..dd1dc27d1cd --- /dev/null +++ b/tools/gator/daemon/Driver.h @@ -0,0 +1,44 @@ +/** + * Copyright (C) ARM Limited 2013. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#ifndef DRIVER_H +#define DRIVER_H + +#include "mxml/mxml.h" + +class Counter; + +class Driver { +public: + static Driver *getHead() { return head; } + + virtual ~Driver() {} + + // Returns true if this driver can manage the counter + virtual bool claimCounter(const Counter &counter) const = 0; + // Clears and disables all counters + virtual void resetCounters() = 0; + // Enables and prepares the counter for capture + virtual void setupCounter(Counter &counter) = 0; + + // Emits available counters + virtual void writeCounters(mxml_node_t *root) const = 0; + // Emits possible dynamically generated events/counters + virtual void writeEvents(mxml_node_t *root) const {} + + Driver *getNext() const { return next; } + +protected: + Driver (); + +private: + static Driver *head; + Driver *next; +}; + +#endif // DRIVER_H diff --git a/tools/gator/daemon/EventsXML.cpp b/tools/gator/daemon/EventsXML.cpp new file mode 100644 index 00000000000..2a80482e0b8 --- /dev/null +++ b/tools/gator/daemon/EventsXML.cpp @@ -0,0 +1,70 @@ +/** + * Copyright (C) ARM Limited 2013. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#include "EventsXML.h" + +#include "CapturedXML.h" +#include "Logging.h" +#include "OlyUtility.h" +#include "SessionData.h" + +char* EventsXML::getXML() { +#include "events_xml.h" // defines and initializes char events_xml[] and int events_xml_len + char path[PATH_MAX]; + mxml_node_t *xml; + FILE *fl; + + // Avoid unused variable warning + (void)events_xml_len; + + // Load the provided or default events xml + if (gSessionData->mEventsXMLPath) { + strncpy(path, gSessionData->mEventsXMLPath, PATH_MAX); + } else { + util->getApplicationFullPath(path, PATH_MAX); + strncat(path, "events.xml", PATH_MAX - strlen(path) - 1); + } + fl = fopen(path, "r"); + if (fl) { + xml = mxmlLoadFile(NULL, fl, MXML_NO_CALLBACK); + fclose(fl); + } else { + logg->logMessage("Unable to locate events.xml, using default"); + xml = mxmlLoadString(NULL, (char *)events_xml, MXML_NO_CALLBACK); + } + + // Add dynamic events from the drivers + mxml_node_t *events = mxmlFindElement(xml, xml, "events", NULL, NULL, MXML_DESCEND); + if (!events) { + logg->logMessage("Unable to find node in the events.xml"); + handleException(); + } + for (Driver *driver = Driver::getHead(); driver != NULL; driver = driver->getNext()) { + driver->writeEvents(events); + } + + char* string = mxmlSaveAllocString(xml, mxmlWhitespaceCB); + mxmlDelete(xml); + + return string; +} + +void EventsXML::write(const char* path) { + char file[PATH_MAX]; + + // Set full path + snprintf(file, PATH_MAX, "%s/events.xml", path); + + char* buf = getXML(); + if (util->writeToDisk(file, buf) < 0) { + logg->logError(__FILE__, __LINE__, "Error writing %s\nPlease verify the path.", file); + handleException(); + } + + free(buf); +} diff --git a/tools/gator/daemon/EventsXML.h b/tools/gator/daemon/EventsXML.h new file mode 100644 index 00000000000..8e693efab20 --- /dev/null +++ b/tools/gator/daemon/EventsXML.h @@ -0,0 +1,18 @@ +/** + * Copyright (C) ARM Limited 2013. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#ifndef EVENTS_XML +#define EVENTS_XML + +class EventsXML { +public: + char* getXML(); + void write(const char* path); +}; + +#endif // EVENTS_XML diff --git a/tools/gator/daemon/Fifo.cpp b/tools/gator/daemon/Fifo.cpp new file mode 100644 index 00000000000..250a4d023bf --- /dev/null +++ b/tools/gator/daemon/Fifo.cpp @@ -0,0 +1,130 @@ +/** + * Copyright (C) ARM Limited 2010-2013. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#include "Fifo.h" + +#include +#ifdef WIN32 +#define valloc malloc +#endif + +#include "Logging.h" + +// bufferSize is the amount of data to be filled +// singleBufferSize is the maximum size that may be filled during a single write +// (bufferSize + singleBufferSize) will be allocated +Fifo::Fifo(int singleBufferSize, int bufferSize, sem_t* readerSem) { + mWrite = mRead = mReadCommit = mRaggedEnd = 0; + mWrapThreshold = bufferSize; + mSingleBufferSize = singleBufferSize; + mReaderSem = readerSem; + mBuffer = (char*)valloc(bufferSize + singleBufferSize); + mEnd = false; + + if (mBuffer == NULL) { + logg->logError(__FILE__, __LINE__, "failed to allocate %d bytes", bufferSize + singleBufferSize); + handleException(); + } + + if (sem_init(&mWaitForSpaceSem, 0, 0)) { + logg->logError(__FILE__, __LINE__, "sem_init() failed"); + handleException(); + } +} + +Fifo::~Fifo() { + free(mBuffer); + sem_destroy(&mWaitForSpaceSem); +} + +int Fifo::numBytesFilled() const { + return mWrite - mRead + mRaggedEnd; +} + +char* Fifo::start() const { + return mBuffer; +} + +bool Fifo::isEmpty() const { + return mRead == mWrite && mRaggedEnd == 0; +} + +bool Fifo::isFull() const { + return willFill(0); +} + +// Determines if the buffer will fill assuming 'additional' bytes will be added to the buffer +// 'full' means there is less than singleBufferSize bytes available contiguously; it does not mean there are zero bytes available +bool Fifo::willFill(int additional) const { + if (mWrite > mRead) { + if (numBytesFilled() + additional < mWrapThreshold) { + return false; + } + } else { + if (numBytesFilled() + additional < mWrapThreshold - mSingleBufferSize) { + return false; + } + } + return true; +} + +// This function will stall until contiguous singleBufferSize bytes are available +char* Fifo::write(int length) { + if (length <= 0) { + length = 0; + mEnd = true; + } + + // update the write pointer + mWrite += length; + + // handle the wrap-around + if (mWrite >= mWrapThreshold) { + mRaggedEnd = mWrite; + mWrite = 0; + } + + // send a notification that data is ready + sem_post(mReaderSem); + + // wait for space + while (isFull()) { + sem_wait(&mWaitForSpaceSem); + } + + return &mBuffer[mWrite]; +} + +void Fifo::release() { + // update the read pointer now that the data has been handled + mRead = mReadCommit; + + // handle the wrap-around + if (mRead >= mWrapThreshold) { + mRaggedEnd = mRead = mReadCommit = 0; + } + + // send a notification that data is free (space is available) + sem_post(&mWaitForSpaceSem); +} + +// This function will return null if no data is available +char* Fifo::read(int *const length) { + // wait for data + if (isEmpty() && !mEnd) { + return NULL; + } + + // obtain the length + do { + mReadCommit = mRaggedEnd ? mRaggedEnd : mWrite; + *length = mReadCommit - mRead; + } while (*length < 0); // plugs race condition without using semaphores + + return &mBuffer[mRead]; +} diff --git a/tools/gator/daemon/Fifo.h b/tools/gator/daemon/Fifo.h new file mode 100644 index 00000000000..ada42b9fb58 --- /dev/null +++ b/tools/gator/daemon/Fifo.h @@ -0,0 +1,44 @@ +/** + * Copyright (C) ARM Limited 2010-2013. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#ifndef __FIFO_H__ +#define __FIFO_H__ + +#ifdef WIN32 +#include +#define sem_t HANDLE +#define sem_init(sem, pshared, value) ((*(sem) = CreateSemaphore(NULL, value, INFINITE, NULL)) == NULL) +#define sem_wait(sem) WaitForSingleObject(*(sem), INFINITE) +#define sem_post(sem) ReleaseSemaphore(*(sem), 1, NULL) +#define sem_destroy(sem) CloseHandle(*(sem)) +#else +#include +#endif + +class Fifo { +public: + Fifo(int singleBufferSize, int totalBufferSize, sem_t* readerSem); + ~Fifo(); + int numBytesFilled() const; + bool isEmpty() const; + bool isFull() const; + bool willFill(int additional) const; + char* start() const; + char* write(int length); + void release(); + char* read(int *const length); + +private: + int mSingleBufferSize, mWrite, mRead, mReadCommit, mRaggedEnd, mWrapThreshold; + sem_t mWaitForSpaceSem; + sem_t* mReaderSem; + char* mBuffer; + bool mEnd; +}; + +#endif //__FIFO_H__ diff --git a/tools/gator/daemon/Hwmon.cpp b/tools/gator/daemon/Hwmon.cpp new file mode 100644 index 00000000000..94752158436 --- /dev/null +++ b/tools/gator/daemon/Hwmon.cpp @@ -0,0 +1,326 @@ +/** + * Copyright (C) ARM Limited 2013. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#include "Hwmon.h" + +#include "libsensors/sensors.h" + +#include "Buffer.h" +#include "Counter.h" +#include "Logging.h" +#include "SessionData.h" + +class HwmonCounter { +public: + HwmonCounter(HwmonCounter *next, int key, const sensors_chip_name *chip, const sensors_feature *feature); + ~HwmonCounter(); + + HwmonCounter *getNext() const { return next; } + int getKey() const { return key; } + bool isEnabled() const { return enabled; } + const char *getName() const { return name; } + const char *getLabel() const { return label; } + const char *getTitle() const { return title; } + bool isDuplicate() const { return duplicate; } + const char *getDisplay() const { return display; } + const char *getUnit() const { return unit; } + int getModifier() const { return modifier; } + + void setEnabled(const bool enabled) { + this->enabled = enabled; + // canRead will clear enabled if the counter is not readable + canRead(); + } + + double read(); + bool canRead(); + +private: + void init(const sensors_chip_name *chip, const sensors_feature *feature); + + HwmonCounter *const next; + const int key; + int polled : 1, + readable : 1, + enabled : 1, + monotonic: 1, + duplicate : 1; + + const sensors_chip_name *chip; + const sensors_feature *feature; + + char *name; + char *label; + const char *title; + const char *display; + const char *unit; + int modifier; + double previous_value; + + sensors_subfeature_type input; +}; + +HwmonCounter::HwmonCounter(HwmonCounter *next, int key, const sensors_chip_name *chip, const sensors_feature *feature) : next(next), key(key), polled(false), readable(false), enabled(false), duplicate(false), chip(chip), feature(feature) { + + int len = sensors_snprintf_chip_name(NULL, 0, chip) + 1; + char *chip_name = new char[len]; + sensors_snprintf_chip_name(chip_name, len, chip); + + len = snprintf(NULL, 0, "hwmon_%s_%d", chip_name, feature->number) + 1; + name = new char[len]; + len = snprintf(name, len, "hwmon_%s_%d", chip_name, feature->number); + + delete [] chip_name; + + label = sensors_get_label(chip, feature); + + switch (feature->type) { + case SENSORS_FEATURE_IN: + title = "Voltage"; + input = SENSORS_SUBFEATURE_IN_INPUT; + display = "average"; + unit = "V"; + modifier = 1000; + monotonic = false; + break; + case SENSORS_FEATURE_FAN: + title = "Fan"; + input = SENSORS_SUBFEATURE_FAN_INPUT; + display = "average"; + unit = "RPM"; + modifier = 1; + monotonic = false; + break; + case SENSORS_FEATURE_TEMP: + title = "Temperature"; + input = SENSORS_SUBFEATURE_TEMP_INPUT; + display = "maximum"; + unit = "°C"; + modifier = 1000; + monotonic = false; + break; + case SENSORS_FEATURE_POWER: + title = "Power"; + input = SENSORS_SUBFEATURE_POWER_INPUT; + display = "average"; + unit = "W"; + modifier = 1000000; + monotonic = false; + break; + case SENSORS_FEATURE_ENERGY: + title = "Energy"; + input = SENSORS_SUBFEATURE_ENERGY_INPUT; + display = "accumulate"; + unit = "J"; + modifier = 1000000; + monotonic = true; + break; + case SENSORS_FEATURE_CURR: + title = "Current"; + input = SENSORS_SUBFEATURE_CURR_INPUT; + display = "average"; + unit = "A"; + modifier = 1000; + monotonic = false; + break; + case SENSORS_FEATURE_HUMIDITY: + title = "Humidity"; + input = SENSORS_SUBFEATURE_HUMIDITY_INPUT; + display = "average"; + unit = "%"; + modifier = 1000; + monotonic = false; + break; + default: + logg->logError(__FILE__, __LINE__, "Unsupported hwmon feature %i", feature->type); + handleException(); + } + + for (HwmonCounter * counter = next; counter != NULL; counter = counter->getNext()) { + if (strcmp(label, counter->getLabel()) == 0 && strcmp(title, counter->getTitle()) == 0) { + duplicate = true; + counter->duplicate = true; + break; + } + } +} + +HwmonCounter::~HwmonCounter() { + free((void *)label); + delete [] name; +} + +double HwmonCounter::read() { + double value; + double result; + const sensors_subfeature *subfeature; + + // Keep in sync with canRead + subfeature = sensors_get_subfeature(chip, feature, input); + if (!subfeature) { + logg->logError(__FILE__, __LINE__, "No input value for hwmon sensor %s", label); + handleException(); + } + + if (sensors_get_value(chip, subfeature->number, &value) != 0) { + logg->logError(__FILE__, __LINE__, "Can't get input value for hwmon sensor %s", label); + handleException(); + } + + result = (monotonic ? value - previous_value : value); + previous_value = value; + + return result; +} + +bool HwmonCounter::canRead() { + if (!polled) { + double value; + const sensors_subfeature *subfeature; + bool result = true; + + subfeature = sensors_get_subfeature(chip, feature, input); + if (!subfeature) { + result = false; + } else { + result = sensors_get_value(chip, subfeature->number, &value) == 0; + } + + polled = true; + readable = result; + } + + enabled &= readable; + + return readable; +} + +Hwmon::Hwmon() : counters(NULL) { + int err = sensors_init(NULL); + if (err) { + logg->logMessage("Failed to initialize libsensors! (%d)", err); + return; + } + sensors_sysfs_no_scaling = 1; + + int chip_nr = 0; + const sensors_chip_name *chip; + while ((chip = sensors_get_detected_chips(NULL, &chip_nr))) { + int feature_nr = 0; + const sensors_feature *feature; + while ((feature = sensors_get_features(chip, &feature_nr))) { + counters = new HwmonCounter(counters, getEventKey(), chip, feature); + } + } +} + +Hwmon::~Hwmon() { + while (counters != NULL) { + HwmonCounter * counter = counters; + counters = counter->getNext(); + delete counter; + } + sensors_cleanup(); +} + +HwmonCounter *Hwmon::findCounter(const Counter &counter) const { + for (HwmonCounter * hwmonCounter = counters; hwmonCounter != NULL; hwmonCounter = hwmonCounter->getNext()) { + if (hwmonCounter->canRead() && strcmp(hwmonCounter->getName(), counter.getType()) == 0) { + return hwmonCounter; + } + } + + return NULL; +} + +bool Hwmon::claimCounter(const Counter &counter) const { + return findCounter(counter) != NULL; +} + +bool Hwmon::countersEnabled() const { + for (HwmonCounter * counter = counters; counter != NULL; counter = counter->getNext()) { + if (counter->isEnabled()) { + return true; + } + } + return false; +} + +void Hwmon::resetCounters() { + for (HwmonCounter * counter = counters; counter != NULL; counter = counter->getNext()) { + counter->setEnabled(false); + } +} + +void Hwmon::setupCounter(Counter &counter) { + HwmonCounter *const hwmonCounter = findCounter(counter); + if (hwmonCounter == NULL) { + counter.setEnabled(false); + return; + } + hwmonCounter->setEnabled(true); + counter.setKey(hwmonCounter->getKey()); +} + +void Hwmon::writeCounters(mxml_node_t *root) const { + for (HwmonCounter * counter = counters; counter != NULL; counter = counter->getNext()) { + if (!counter->canRead()) { + continue; + } + mxml_node_t *node = mxmlNewElement(root, "counter"); + mxmlElementSetAttr(node, "name", counter->getName()); + } +} + +void Hwmon::writeEvents(mxml_node_t *root) const { + root = mxmlNewElement(root, "category"); + mxmlElementSetAttr(root, "name", "hwmon"); + + char buf[1024]; + for (HwmonCounter * counter = counters; counter != NULL; counter = counter->getNext()) { + if (!counter->canRead()) { + continue; + } + mxml_node_t *node = mxmlNewElement(root, "event"); + mxmlElementSetAttr(node, "counter", counter->getName()); + mxmlElementSetAttr(node, "title", counter->getTitle()); + if (counter->isDuplicate()) { + mxmlElementSetAttrf(node, "name", "%s (0x%x)", counter->getLabel(), counter->getKey()); + } else { + mxmlElementSetAttr(node, "name", counter->getLabel()); + } + mxmlElementSetAttr(node, "display", counter->getDisplay()); + mxmlElementSetAttr(node, "units", counter->getUnit()); + if (counter->getModifier() != 1) { + mxmlElementSetAttrf(node, "modifier", "%d", counter->getModifier()); + } + if (strcmp(counter->getDisplay(), "average") == 0 || strcmp(counter->getDisplay(), "maximum") == 0) { + mxmlElementSetAttr(node, "average_selection", "yes"); + } + snprintf(buf, sizeof(buf), "libsensors %s sensor %s (%s)", counter->getTitle(), counter->getLabel(), counter->getName()); + mxmlElementSetAttr(node, "description", buf); + } +} + +void Hwmon::start() { + for (HwmonCounter * counter = counters; counter != NULL; counter = counter->getNext()) { + if (!counter->isEnabled()) { + continue; + } + counter->read(); + } +} + +void Hwmon::read(Buffer * const buffer) { + for (HwmonCounter * counter = counters; counter != NULL; counter = counter->getNext()) { + if (!counter->isEnabled()) { + continue; + } + buffer->event(counter->getKey(), counter->read()); + } +} diff --git a/tools/gator/daemon/Hwmon.h b/tools/gator/daemon/Hwmon.h new file mode 100644 index 00000000000..35981dc3d9a --- /dev/null +++ b/tools/gator/daemon/Hwmon.h @@ -0,0 +1,39 @@ +/** + * Copyright (C) ARM Limited 2013. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#ifndef HWMON_H +#define HWMON_H + +#include "Driver.h" + +class Buffer; +class HwmonCounter; + +class Hwmon : public Driver { +public: + Hwmon(); + ~Hwmon(); + + bool claimCounter(const Counter &counter) const; + bool countersEnabled() const; + void resetCounters(); + void setupCounter(Counter &counter); + + void writeCounters(mxml_node_t *root) const; + void writeEvents(mxml_node_t *root) const; + + void start(); + void read(Buffer * buffer); + +private: + HwmonCounter *findCounter(const Counter &counter) const; + + HwmonCounter *counters; +}; + +#endif // HWMON_H diff --git a/tools/gator/daemon/KMod.cpp b/tools/gator/daemon/KMod.cpp new file mode 100644 index 00000000000..04f33306487 --- /dev/null +++ b/tools/gator/daemon/KMod.cpp @@ -0,0 +1,100 @@ +/** + * Copyright (C) ARM Limited 2013. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#include "KMod.h" + +#include +#include +#include + +#include "Collector.h" +#include "Counter.h" +#include "Logging.h" + +// Claim all the counters in /dev/gator/events +bool KMod::claimCounter(const Counter &counter) const { + char text[128]; + snprintf(text, sizeof(text), "/dev/gator/events/%s", counter.getType()); + return access(text, F_OK) == 0; +} + +void KMod::resetCounters() { + char base[128]; + char text[128]; + + // Initialize all perf counters in the driver, i.e. set enabled to zero + struct dirent *ent; + DIR* dir = opendir("/dev/gator/events"); + if (dir) { + while ((ent = readdir(dir)) != NULL) { + // skip hidden files, current dir, and parent dir + if (ent->d_name[0] == '.') + continue; + snprintf(base, sizeof(base), "/dev/gator/events/%s", ent->d_name); + snprintf(text, sizeof(text), "%s/enabled", base); + Collector::writeDriver(text, 0); + snprintf(text, sizeof(text), "%s/count", base); + Collector::writeDriver(text, 0); + } + closedir(dir); + } +} + +void KMod::setupCounter(Counter &counter) { + char base[128]; + char text[128]; + snprintf(base, sizeof(base), "/dev/gator/events/%s", counter.getType()); + + snprintf(text, sizeof(text), "%s/enabled", base); + int enabled = true; + if (Collector::writeReadDriver(text, &enabled) || !enabled) { + counter.setEnabled(false); + return; + } + + snprintf(text, sizeof(text), "%s/key", base); + int key = 0; + Collector::readIntDriver(text, &key); + counter.setKey(key); + + snprintf(text, sizeof(text), "%s/event", base); + Collector::writeDriver(text, counter.getEvent()); + snprintf(text, sizeof(text), "%s/count", base); + if (access(text, F_OK) == 0) { + int count = counter.getCount(); + if (Collector::writeReadDriver(text, &count) && counter.getCount() > 0) { + logg->logError(__FILE__, __LINE__, "Cannot enable EBS for %s:%i with a count of %d\n", counter.getType(), counter.getEvent(), counter.getCount()); + handleException(); + } + counter.setCount(count); + } else if (counter.getCount() > 0) { + logg->logError(__FILE__, __LINE__, "Event Based Sampling is only supported with kernel versions 3.0.0 and higher with CONFIG_PERF_EVENTS=y, and CONFIG_HW_PERF_EVENTS=y\n"); + handleException(); + } +} + +void KMod::writeCounters(mxml_node_t *root) const { + struct dirent *ent; + mxml_node_t *counter; + + // counters.xml is simply a file listing of /dev/gator/events + DIR* dir = opendir("/dev/gator/events"); + if (dir == NULL) { + logg->logError(__FILE__, __LINE__, "Cannot create counters.xml since unable to read /dev/gator/events"); + handleException(); + } + + while ((ent = readdir(dir)) != NULL) { + // skip hidden files, current dir, and parent dir + if (ent->d_name[0] == '.') + continue; + counter = mxmlNewElement(root, "counter"); + mxmlElementSetAttr(counter, "name", ent->d_name); + } + closedir(dir); +} diff --git a/tools/gator/daemon/KMod.h b/tools/gator/daemon/KMod.h new file mode 100644 index 00000000000..797426290df --- /dev/null +++ b/tools/gator/daemon/KMod.h @@ -0,0 +1,27 @@ +/** + * Copyright (C) ARM Limited 2013. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#ifndef KMOD_H +#define KMOD_H + +#include "Driver.h" + +// Driver for the gator kernel module +class KMod : public Driver { +public: + KMod() {} + ~KMod() {} + + bool claimCounter(const Counter &counter) const; + void resetCounters(); + void setupCounter(Counter &counter); + + void writeCounters(mxml_node_t *root) const; +}; + +#endif // KMOD_H diff --git a/tools/gator/daemon/LocalCapture.cpp b/tools/gator/daemon/LocalCapture.cpp new file mode 100644 index 00000000000..3235a34ae9c --- /dev/null +++ b/tools/gator/daemon/LocalCapture.cpp @@ -0,0 +1,129 @@ +/** + * Copyright (C) ARM Limited 2010-2013. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#include +#include +#include +#include +#include +#include +#include "LocalCapture.h" +#include "SessionData.h" +#include "Logging.h" +#include "OlyUtility.h" +#include "EventsXML.h" + +LocalCapture::LocalCapture() {} + +LocalCapture::~LocalCapture() {} + +void LocalCapture::createAPCDirectory(char* target_path) { + gSessionData->mAPCDir = createUniqueDirectory(target_path, ".apc"); + if ((removeDirAndAllContents(gSessionData->mAPCDir) != 0 || mkdir(gSessionData->mAPCDir, S_IRWXU | S_IRWXG | S_IROTH | S_IXOTH) != 0)) { + logg->logError(__FILE__, __LINE__, "Unable to create directory %s", gSessionData->mAPCDir); + handleException(); + } +} + +void LocalCapture::write(char* string) { + char file[PATH_MAX]; + + // Set full path + snprintf(file, PATH_MAX, "%s/session.xml", gSessionData->mAPCDir); + + // Write the file + if (util->writeToDisk(file, string) < 0) { + logg->logError(__FILE__, __LINE__, "Error writing %s\nPlease verify the path.", file); + handleException(); + } + + // Write events XML + EventsXML eventsXML; + eventsXML.write(gSessionData->mAPCDir); +} + +char* LocalCapture::createUniqueDirectory(const char* initialPath, const char* ending) { + char* output; + char path[PATH_MAX]; + + // Ensure the path is an absolute path, i.e. starts with a slash + if (initialPath == 0 || strlen(initialPath) == 0) { + logg->logError(__FILE__, __LINE__, "Missing -o command line option required for a local capture."); + handleException(); + } else if (initialPath[0] != '/') { + if (getcwd(path, PATH_MAX) == 0) { + logg->logMessage("Unable to retrieve the current working directory"); + } + strncat(path, "/", PATH_MAX - strlen(path) - 1); + strncat(path, initialPath, PATH_MAX - strlen(path) - 1); + } else { + strncpy(path, initialPath, PATH_MAX); + path[PATH_MAX - 1] = 0; // strncpy does not guarantee a null-terminated string + } + + // Add ending if it is not already there + if (strcmp(&path[strlen(path) - strlen(ending)], ending) != 0) { + strncat(path, ending, PATH_MAX - strlen(path) - 1); + } + + output = strdup(path); + + return output; +} + +int LocalCapture::removeDirAndAllContents(char* path) { + int error = 0; + struct stat mFileInfo; + // Does the path exist? + if (stat(path, &mFileInfo) == 0) { + // Is it a directory? + if (mFileInfo.st_mode & S_IFDIR) { + DIR * dir = opendir(path); + dirent* entry = readdir(dir); + while (entry) { + if (strcmp(entry->d_name, ".") != 0 && strcmp(entry->d_name, "..") != 0) { + char* newpath = (char*)malloc(strlen(path) + strlen(entry->d_name) + 2); + sprintf(newpath, "%s/%s", path, entry->d_name); + error = removeDirAndAllContents(newpath); + free(newpath); + if (error) { + break; + } + } + entry = readdir(dir); + } + closedir(dir); + if (error == 0) { + error = rmdir(path); + } + } else { + error = remove(path); + } + } + return error; +} + +void LocalCapture::copyImages(ImageLinkList* ptr) { + char dstfilename[PATH_MAX]; + + while (ptr) { + strncpy(dstfilename, gSessionData->mAPCDir, PATH_MAX); + dstfilename[PATH_MAX - 1] = 0; // strncpy does not guarantee a null-terminated string + if (gSessionData->mAPCDir[strlen(gSessionData->mAPCDir) - 1] != '/') { + strncat(dstfilename, "/", PATH_MAX - strlen(dstfilename) - 1); + } + strncat(dstfilename, util->getFilePart(ptr->path), PATH_MAX - strlen(dstfilename) - 1); + if (util->copyFile(ptr->path, dstfilename)) { + logg->logMessage("copied file %s to %s", ptr->path, dstfilename); + } else { + logg->logMessage("copy of file %s to %s failed", ptr->path, dstfilename); + } + + ptr = ptr->next; + } +} diff --git a/tools/gator/daemon/LocalCapture.h b/tools/gator/daemon/LocalCapture.h new file mode 100644 index 00000000000..8042d6a8dc3 --- /dev/null +++ b/tools/gator/daemon/LocalCapture.h @@ -0,0 +1,26 @@ +/** + * Copyright (C) ARM Limited 2010-2013. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#ifndef __LOCAL_CAPTURE_H__ +#define __LOCAL_CAPTURE_H__ + +struct ImageLinkList; + +class LocalCapture { +public: + LocalCapture(); + ~LocalCapture(); + void write(char* string); + void copyImages(ImageLinkList* ptr); + void createAPCDirectory(char* target_path); +private: + char* createUniqueDirectory(const char* path, const char* ending); + int removeDirAndAllContents(char* path); +}; + +#endif //__LOCAL_CAPTURE_H__ diff --git a/tools/gator/daemon/Logging.cpp b/tools/gator/daemon/Logging.cpp new file mode 100644 index 00000000000..5fd45b54f90 --- /dev/null +++ b/tools/gator/daemon/Logging.cpp @@ -0,0 +1,78 @@ +/** + * Copyright (C) ARM Limited 2010-2013. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#include +#include +#include +#include + +#ifdef WIN32 +#define MUTEX_INIT() mLoggingMutex = CreateMutex(NULL, false, NULL); +#define MUTEX_LOCK() WaitForSingleObject(mLoggingMutex, 0xFFFFFFFF); +#define MUTEX_UNLOCK() ReleaseMutex(mLoggingMutex); +#define snprintf _snprintf +#else +#include +#define MUTEX_INIT() pthread_mutex_init(&mLoggingMutex, NULL) +#define MUTEX_LOCK() pthread_mutex_lock(&mLoggingMutex) +#define MUTEX_UNLOCK() pthread_mutex_unlock(&mLoggingMutex) +#endif + +#include "Logging.h" + +// Global thread-safe logging +Logging* logg = NULL; + +Logging::Logging(bool debug) { + mDebug = debug; + MUTEX_INIT(); + + strcpy(mErrBuf, "Unknown Error"); + strcpy(mLogBuf, "Unknown Message"); +} + +Logging::~Logging() { +} + +void Logging::logError(const char* file, int line, const char* fmt, ...) { + va_list args; + + MUTEX_LOCK(); + if (mDebug) { + snprintf(mErrBuf, sizeof(mErrBuf), "ERROR[%s:%d]: ", file, line); + } else { + mErrBuf[0] = 0; + } + + va_start(args, fmt); + vsnprintf(mErrBuf + strlen(mErrBuf), sizeof(mErrBuf) - 2 - strlen(mErrBuf), fmt, args); // subtract 2 for \n and \0 + va_end(args); + + if (strlen(mErrBuf) > 0) { + strcat(mErrBuf, "\n"); + } + MUTEX_UNLOCK(); +} + +void Logging::logMessage(const char* fmt, ...) { + if (mDebug) { + va_list args; + + MUTEX_LOCK(); + strcpy(mLogBuf, "INFO: "); + + va_start(args, fmt); + vsnprintf(mLogBuf + strlen(mLogBuf), sizeof(mLogBuf) - 2 - strlen(mLogBuf), fmt, args); // subtract 2 for \n and \0 + va_end(args); + strcat(mLogBuf, "\n"); + + fprintf(stdout, "%s", mLogBuf); + fflush(stdout); + MUTEX_UNLOCK(); + } +} diff --git a/tools/gator/daemon/Logging.h b/tools/gator/daemon/Logging.h new file mode 100644 index 00000000000..8f960de27bf --- /dev/null +++ b/tools/gator/daemon/Logging.h @@ -0,0 +1,47 @@ +/** + * Copyright (C) ARM Limited 2010-2013. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#ifndef __LOGGING_H__ +#define __LOGGING_H__ + +#include +#include +#include +#ifdef WIN32 +#include +#else +#include +#endif + +#define DRIVER_ERROR "\n Driver issue:\n >> gator.ko must be built against the current kernel version & configuration\n >> gator.ko should be co-located with gatord in the same directory\n >> OR insmod gator.ko prior to launching gatord" + +class Logging { +public: + Logging(bool debug); + ~Logging(); + void logError(const char* file, int line, const char* fmt, ...); + void logMessage(const char* fmt, ...); + char* getLastError() {return mErrBuf;} + char* getLastMessage() {return mLogBuf;} + +private: + char mErrBuf[4096]; // Arbitrarily large buffer to hold a string + char mLogBuf[4096]; // Arbitrarily large buffer to hold a string + bool mDebug; +#ifdef WIN32 + HANDLE mLoggingMutex; +#else + pthread_mutex_t mLoggingMutex; +#endif +}; + +extern Logging* logg; + +extern void handleException() __attribute__ ((noreturn)); + +#endif //__LOGGING_H__ diff --git a/tools/gator/daemon/Makefile b/tools/gator/daemon/Makefile new file mode 100644 index 00000000000..24ee9404547 --- /dev/null +++ b/tools/gator/daemon/Makefile @@ -0,0 +1,25 @@ +# +# Makefile for ARM Streamline - Gator Daemon +# + +# Uncomment and define CROSS_COMPILE if it is not already defined +# CROSS_COMPILE=/path/to/cross-compiler/arm-linux-gnueabihf- +# NOTE: This toolchain uses the hardfloat abi by default. For non-hardfloat +# targets run 'make SOFTFLOAT=1 SYSROOT=/path/to/sysroot', see +# README_Streamline.txt for more details + +CPP = $(CROSS_COMPILE)g++ +GCC = $(CROSS_COMPILE)gcc + +# -mthumb-interwork is required for interworking to ARM or Thumb stdlibc +CFLAGS += -mthumb-interwork + +ifeq ($(SOFTFLOAT),1) + CFLAGS += -marm -march=armv4t -mfloat-abi=soft + LDFLAGS += -marm -march=armv4t -mfloat-abi=soft +endif +ifneq ($(SYSROOT),) + LDFLAGS += --sysroot=$(SYSROOT) +endif + +include common.mk diff --git a/tools/gator/daemon/Makefile_aarch64 b/tools/gator/daemon/Makefile_aarch64 new file mode 100644 index 00000000000..10b4b4a71ab --- /dev/null +++ b/tools/gator/daemon/Makefile_aarch64 @@ -0,0 +1,15 @@ +# +# Makefile for ARM Streamline - Gator Daemon +# make -f Makefile_aarch64 +# + +# Uncomment and define CROSS_COMPILE if it is not already defined +# CROSS_COMPILE=/path/to/cross-compiler/arm-linux-gnueabihf- +# NOTE: This toolchain uses the hardfloat abi by default. For non-hardfloat +# targets it is necessary to add options +# '-marm -march=armv4t -mfloat-abi=soft'. + +CPP = $(CROSS_COMPILE)g++ +GCC = $(CROSS_COMPILE)gcc + +include common.mk diff --git a/tools/gator/daemon/OlySocket.cpp b/tools/gator/daemon/OlySocket.cpp new file mode 100644 index 00000000000..132510df584 --- /dev/null +++ b/tools/gator/daemon/OlySocket.cpp @@ -0,0 +1,261 @@ +/** + * Copyright (C) ARM Limited 2010-2013. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#include "OlySocket.h" + +#include +#ifdef WIN32 +#include +#else +#include +#include +#include +#include +#endif + +#include "Logging.h" + +#ifdef WIN32 +#define CLOSE_SOCKET(x) closesocket(x) +#define SHUTDOWN_RX_TX SD_BOTH +#define snprintf _snprintf +#else +#define CLOSE_SOCKET(x) close(x) +#define SHUTDOWN_RX_TX SHUT_RDWR +#endif + +OlySocket::OlySocket(int port, bool multiple) { +#ifdef WIN32 + WSADATA wsaData; + if (WSAStartup(0x0202, &wsaData) != 0) { + logg->logError(__FILE__, __LINE__, "Windows socket initialization failed"); + handleException(); + } +#endif + + if (multiple) { + createServerSocket(port); + } else { + createSingleServerConnection(port); + } +} + +OlySocket::OlySocket(int port, char* host) { + mFDServer = 0; + createClientSocket(host, port); +} + +OlySocket::~OlySocket() { + if (mSocketID > 0) { + CLOSE_SOCKET(mSocketID); + } +} + +void OlySocket::shutdownConnection() { + // Shutdown is primarily used to unblock other threads that are blocking on send/receive functions + shutdown(mSocketID, SHUTDOWN_RX_TX); +} + +void OlySocket::closeSocket() { + // Used for closing an accepted socket but keeping the server socket active + if (mSocketID > 0) { + CLOSE_SOCKET(mSocketID); + mSocketID = -1; + } +} + +void OlySocket::closeServerSocket() { + if (CLOSE_SOCKET(mFDServer) != 0) { + logg->logError(__FILE__, __LINE__, "Failed to close server socket."); + handleException(); + } + mFDServer = 0; +} + +void OlySocket::createClientSocket(char* hostname, int portno) { +#ifdef WIN32 + // TODO: Implement for Windows +#else + char buf[32]; + struct addrinfo hints, *res, *res0; + + snprintf(buf, sizeof(buf), "%d", portno); + mSocketID = -1; + memset((void*)&hints, 0, sizeof(hints)); + hints.ai_family = PF_UNSPEC; + hints.ai_socktype = SOCK_STREAM; + + if (getaddrinfo(hostname, buf, &hints, &res0)) { + logg->logError(__FILE__, __LINE__, "Client socket failed to get address info for %s", hostname); + handleException(); + } + for (res=res0; res!=NULL; res = res->ai_next) { + if ( res->ai_family != PF_INET || res->ai_socktype != SOCK_STREAM ) { + continue; + } + mSocketID = socket(res->ai_family, res->ai_socktype, res->ai_protocol); + if (mSocketID < 0) { + continue; + } + if (connect(mSocketID, res->ai_addr, res->ai_addrlen) < 0) { + close(mSocketID); + mSocketID = -1; + } + if (mSocketID > 0) { + break; + } + } + freeaddrinfo(res0); + if (mSocketID <= 0) { + logg->logError(__FILE__, __LINE__, "Could not connect to client socket. Ensure ARM Streamline is running."); + handleException(); + } +#endif +} + +void OlySocket::createSingleServerConnection(int port) { + createServerSocket(port); + + mSocketID = acceptConnection(); + closeServerSocket(); +} + +void OlySocket::createServerSocket(int port) { + // Create socket + mFDServer = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP); + if (mFDServer < 0) { + logg->logError(__FILE__, __LINE__, "Error creating server socket"); + handleException(); + } + + // Enable address reuse, another solution would be to create the server socket once and only close it when the object exits + int on = 1; + if (setsockopt(mFDServer, SOL_SOCKET, SO_REUSEADDR, (const char*)&on, sizeof(on)) != 0) { + logg->logError(__FILE__, __LINE__, "Setting server socket options failed"); + handleException(); + } + + // Create sockaddr_in structure, ensuring non-populated fields are zero + struct sockaddr_in sockaddr; + memset((void*)&sockaddr, 0, sizeof(struct sockaddr_in)); + sockaddr.sin_family = AF_INET; + sockaddr.sin_port = htons(port); + sockaddr.sin_addr.s_addr = INADDR_ANY; + + // Bind the socket to an address + if (bind(mFDServer, (const struct sockaddr*)&sockaddr, sizeof(sockaddr)) < 0) { + logg->logError(__FILE__, __LINE__, "Binding of server socket failed.\nIs an instance already running?"); + handleException(); + } + + // Listen for connections on this socket + if (listen(mFDServer, 1) < 0) { + logg->logError(__FILE__, __LINE__, "Listening of server socket failed"); + handleException(); + } +} + +// mSocketID is always set to the most recently accepted connection +// The user of this class should maintain the different socket connections, e.g. by forking the process +int OlySocket::acceptConnection() { + if (mFDServer <= 0) { + logg->logError(__FILE__, __LINE__, "Attempting multiple connections on a single connection server socket or attempting to accept on a client socket"); + handleException(); + } + + // Accept a connection, note that this call blocks until a client connects + mSocketID = accept(mFDServer, NULL, NULL); + if (mSocketID < 0) { + logg->logError(__FILE__, __LINE__, "Socket acceptance failed"); + handleException(); + } + return mSocketID; +} + +void OlySocket::send(char* buffer, int size) { + if (size <= 0 || buffer == NULL) { + return; + } + + while (size > 0) { + int n = ::send(mSocketID, buffer, size, 0); + if (n < 0) { + logg->logError(__FILE__, __LINE__, "Socket send error"); + handleException(); + } + size -= n; + buffer += n; + } +} + +// Returns the number of bytes received +int OlySocket::receive(char* buffer, int size) { + if (size <= 0 || buffer == NULL) { + return 0; + } + + int bytes = recv(mSocketID, buffer, size, 0); + if (bytes < 0) { + logg->logError(__FILE__, __LINE__, "Socket receive error"); + handleException(); + } else if (bytes == 0) { + logg->logMessage("Socket disconnected"); + return -1; + } + return bytes; +} + +// Receive exactly size bytes of data. Note, this function will block until all bytes are received +int OlySocket::receiveNBytes(char* buffer, int size) { + int bytes = 0; + while (size > 0 && buffer != NULL) { + bytes = recv(mSocketID, buffer, size, 0); + if (bytes < 0) { + logg->logError(__FILE__, __LINE__, "Socket receive error"); + handleException(); + } else if (bytes == 0) { + logg->logMessage("Socket disconnected"); + return -1; + } + buffer += bytes; + size -= bytes; + } + return bytes; +} + +// Receive data until a carriage return, line feed, or null is encountered, or the buffer fills +int OlySocket::receiveString(char* buffer, int size) { + int bytes_received = 0; + bool found = false; + + if (buffer == 0) { + return 0; + } + + while (!found && bytes_received < size) { + // Receive a single character + int bytes = recv(mSocketID, &buffer[bytes_received], 1, 0); + if (bytes < 0) { + logg->logError(__FILE__, __LINE__, "Socket receive error"); + handleException(); + } else if (bytes == 0) { + logg->logMessage("Socket disconnected"); + return -1; + } + + // Replace carriage returns and line feeds with zero + if (buffer[bytes_received] == '\n' || buffer[bytes_received] == '\r' || buffer[bytes_received] == '\0') { + buffer[bytes_received] = '\0'; + found = true; + } + + bytes_received++; + } + + return bytes_received; +} diff --git a/tools/gator/daemon/OlySocket.h b/tools/gator/daemon/OlySocket.h new file mode 100644 index 00000000000..5bab7d1f4cc --- /dev/null +++ b/tools/gator/daemon/OlySocket.h @@ -0,0 +1,36 @@ +/** + * Copyright (C) ARM Limited 2010-2013. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#ifndef __OLY_SOCKET_H__ +#define __OLY_SOCKET_H__ + +#include + +class OlySocket { +public: + OlySocket(int port, bool multipleConnections = false); + OlySocket(int port, char* hostname); + ~OlySocket(); + int acceptConnection(); + void closeSocket(); + void closeServerSocket(); + void shutdownConnection(); + void send(char* buffer, int size); + void sendString(const char* string) {send((char*)string, strlen(string));} + int receive(char* buffer, int size); + int receiveNBytes(char* buffer, int size); + int receiveString(char* buffer, int size); + int getSocketID() {return mSocketID;} +private: + int mSocketID, mFDServer; + void createClientSocket(char* hostname, int port); + void createSingleServerConnection(int port); + void createServerSocket(int port); +}; + +#endif //__OLY_SOCKET_H__ diff --git a/tools/gator/daemon/OlyUtility.cpp b/tools/gator/daemon/OlyUtility.cpp new file mode 100644 index 00000000000..b29a1e91cc8 --- /dev/null +++ b/tools/gator/daemon/OlyUtility.cpp @@ -0,0 +1,228 @@ +/** + * Copyright (C) ARM Limited 2010-2013. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#include "OlyUtility.h" + +#include +#include +#include +#include + +#if defined(WIN32) +#include +#elif defined(__linux__) +#include +#elif defined(DARWIN) +#include +#endif + +OlyUtility* util = NULL; + +bool OlyUtility::stringToBool(const char* string, bool defValue) { + char value[32]; + + if (string == NULL) { + return defValue; + } + + strncpy(value, string, sizeof(value)); + if (value[0] == 0) { + return defValue; + } + value[sizeof(value) - 1] = 0; // strncpy does not guarantee a null-terminated string + + // Convert to lowercase + int i = 0; + while (value[i]) { + value[i] = tolower(value[i]); + i++; + } + + if (strcmp(value, "true") == 0 || strcmp(value, "yes") == 0 || strcmp(value, "1") == 0 || strcmp(value, "on") == 0) { + return true; + } else if (strcmp(value, "false") == 0 || strcmp(value, "no") == 0 || strcmp(value, "0") == 0 || strcmp(value, "off") == 0) { + return false; + } else { + return defValue; + } +} + +void OlyUtility::stringToLower(char* string) { + if (string == NULL) { + return; + } + + while (*string) { + *string = tolower(*string); + string++; + } +} + +// Modifies fullpath with the path part including the trailing path separator +int OlyUtility::getApplicationFullPath(char* fullpath, int sizeOfPath) { + memset(fullpath, 0, sizeOfPath); +#if defined(WIN32) + int length = GetModuleFileName(NULL, fullpath, sizeOfPath); +#elif defined(__linux__) + int length = readlink("/proc/self/exe", fullpath, sizeOfPath); +#elif defined(DARWIN) + uint32_t length_u = (uint32_t)sizeOfPath; + int length = sizeOfPath; + if (_NSGetExecutablePath(fullpath, &length_u) == 0) { + length = strlen(fullpath); + } +#endif + + if (length == sizeOfPath) { + return -1; + } + + fullpath[length] = 0; + fullpath = getPathPart(fullpath); + + return 0; +} + +char* OlyUtility::readFromDisk(const char* file, unsigned int *size, bool appendNull) { + // Open the file + FILE* pFile = fopen(file, "rb"); + if (pFile==NULL) { + return NULL; + } + + // Obtain file size + fseek(pFile , 0 , SEEK_END); + unsigned int lSize = ftell(pFile); + rewind(pFile); + + // Allocate memory to contain the whole file + char* buffer = (char*)malloc(lSize + (int)appendNull); + if (buffer == NULL) { + fclose(pFile); + return NULL; + } + + // Copy the file into the buffer + if (fread(buffer, 1, lSize, pFile) != lSize) { + free(buffer); + fclose(pFile); + return NULL; + } + + // Terminate + fclose(pFile); + + if (appendNull) { + buffer[lSize] = 0; + } + + if (size) { + *size = lSize; + } + + return buffer; +} + +int OlyUtility::writeToDisk(const char* path, const char* data) { + // Open the file + FILE* pFile = fopen(path, "wb"); + if (pFile == NULL) { + return -1; + } + + // Write the data to disk + if (fwrite(data, 1, strlen(data), pFile) != strlen(data)) { + fclose(pFile); + return -1; + } + + // Terminate + fclose(pFile); + return 0; +} + +int OlyUtility::appendToDisk(const char* path, const char* data) { + // Open the file + FILE* pFile = fopen(path, "a"); + if (pFile == NULL) { + return -1; + } + + // Write the data to disk + if (fwrite(data, 1, strlen(data), pFile) != strlen(data)) { + fclose(pFile); + return -1; + } + + // Terminate + fclose(pFile); + return 0; +} + +/** + * Copies the srcFile into dstFile in 1kB chunks. + * The dstFile will be overwritten if it exists. + * 0 is returned on an error; otherwise 1. + */ +#define TRANSFER_SIZE 1024 +int OlyUtility::copyFile(const char* srcFile, const char* dstFile) { + char* buffer = (char*)malloc(TRANSFER_SIZE); + FILE * f_src = fopen(srcFile,"rb"); + if (!f_src) { + return 0; + } + FILE * f_dst = fopen(dstFile,"wb"); + if (!f_dst) { + fclose(f_src); + return 0; + } + while (!feof(f_src)) { + int num_bytes_read = fread(buffer, 1, TRANSFER_SIZE, f_src); + if (num_bytes_read < TRANSFER_SIZE && !feof(f_src)) { + fclose(f_src); + fclose(f_dst); + return 0; + } + int num_bytes_written = fwrite(buffer, 1, num_bytes_read, f_dst); + if (num_bytes_written != num_bytes_read) { + fclose(f_src); + fclose(f_dst); + return 0; + } + } + fclose(f_src); + fclose(f_dst); + free(buffer); + return 1; +} + +const char* OlyUtility::getFilePart(const char* path) { + const char* last_sep = strrchr(path, PATH_SEPARATOR); + + // in case path is not a full path + if (last_sep == NULL) { + return path; + } + + return last_sep++; +} + +// getPathPart may modify the contents of path +// returns the path including the trailing path separator +char* OlyUtility::getPathPart(char* path) { + char* last_sep = strrchr(path, PATH_SEPARATOR); + + // in case path is not a full path + if (last_sep == NULL) { + return 0; + } + last_sep++; + *last_sep = 0; + + return (path); +} diff --git a/tools/gator/daemon/OlyUtility.h b/tools/gator/daemon/OlyUtility.h new file mode 100644 index 00000000000..abab0a510a7 --- /dev/null +++ b/tools/gator/daemon/OlyUtility.h @@ -0,0 +1,42 @@ +/** + * Copyright (C) ARM Limited 2010-2013. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#ifndef OLY_UTILITY_H +#define OLY_UTILITY_H + +#include + +#ifdef WIN32 +#define PATH_SEPARATOR '\\' +#define CAIMAN_PATH_MAX MAX_PATH +#define snprintf _snprintf +#else +#include +#define PATH_SEPARATOR '/' +#define CAIMAN_PATH_MAX PATH_MAX +#endif + +class OlyUtility { +public: + OlyUtility() {}; + ~OlyUtility() {}; + bool stringToBool(const char* string, bool defValue); + void stringToLower(char* string); + int getApplicationFullPath(char* path, int sizeOfPath); + char* readFromDisk(const char* file, unsigned int *size = NULL, bool appendNull = true); + int writeToDisk(const char* path, const char* file); + int appendToDisk(const char* path, const char* file); + int copyFile(const char* srcFile, const char* dstFile); + const char* getFilePart(const char* path); + char* getPathPart(char* path); +private: +}; + +extern OlyUtility* util; + +#endif // OLY_UTILITY_H diff --git a/tools/gator/daemon/Sender.cpp b/tools/gator/daemon/Sender.cpp new file mode 100644 index 00000000000..159503f845f --- /dev/null +++ b/tools/gator/daemon/Sender.cpp @@ -0,0 +1,130 @@ +/** + * Copyright (C) ARM Limited 2010-2013. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#include +#include +#include +#include +#include +#include +#include +#include "Sender.h" +#include "Logging.h" +#include "OlySocket.h" +#include "SessionData.h" + +Sender::Sender(OlySocket* socket) { + mDataFile = NULL; + mDataSocket = NULL; + + // Set up the socket connection + if (socket) { + char streamline[64] = {0}; + mDataSocket = socket; + + // Receive magic sequence - can wait forever + // Streamline will send data prior to the magic sequence for legacy support, which should be ignored for v4+ + while (strcmp("STREAMLINE", streamline) != 0) { + if (mDataSocket->receiveString(streamline, sizeof(streamline)) == -1) { + logg->logError(__FILE__, __LINE__, "Socket disconnected"); + handleException(); + } + } + + // Send magic sequence - must be done first, after which error messages can be sent + char magic[32]; + snprintf(magic, 32, "GATOR %i\n", PROTOCOL_VERSION); + mDataSocket->send(magic, strlen(magic)); + + gSessionData->mWaitingOnCommand = true; + logg->logMessage("Completed magic sequence"); + } + + pthread_mutex_init(&mSendMutex, NULL); +} + +Sender::~Sender() { + delete mDataSocket; + mDataSocket = NULL; + if (mDataFile) { + fclose(mDataFile); + } +} + +void Sender::createDataFile(char* apcDir) { + if (apcDir == NULL) { + return; + } + + mDataFileName = (char*)malloc(strlen(apcDir) + 12); + sprintf(mDataFileName, "%s/0000000000", apcDir); + mDataFile = fopen(mDataFileName, "wb"); + if (!mDataFile) { + logg->logError(__FILE__, __LINE__, "Failed to open binary file: %s", mDataFileName); + handleException(); + } +} + +template +inline T min(const T a, const T b) { + return (a < b ? a : b); +} + +void Sender::writeData(const char* data, int length, int type) { + if (length < 0 || (data == NULL && length > 0)) { + return; + } + + // Multiple threads call writeData() + pthread_mutex_lock(&mSendMutex); + + // Send data over the socket connection + if (mDataSocket) { + // Start alarm + const int alarmDuration = 8; + alarm(alarmDuration); + + // Send data over the socket, sending the type and size first + logg->logMessage("Sending data with length %d", length); + if (type != RESPONSE_APC_DATA) { + // type and length already added by the Collector for apc data + mDataSocket->send((char*)&type, 1); + mDataSocket->send((char*)&length, sizeof(length)); + } + + // 100Kbits/sec * alarmDuration sec / 8 bits/byte + const int chunkSize = 100*1000 * alarmDuration / 8; + int pos = 0; + while (true) { + mDataSocket->send((char*)data + pos, min(length - pos, chunkSize)); + pos += chunkSize; + if (pos >= length) { + break; + } + + // Reset the alarm + alarm(alarmDuration); + logg->logMessage("Resetting the alarm"); + } + + // Stop alarm + alarm(0); + } + + // Write data to disk as long as it is not meta data + if (mDataFile && type == RESPONSE_APC_DATA) { + logg->logMessage("Writing data with length %d", length); + // Send data to the data file + if (fwrite(data, 1, length, mDataFile) != (unsigned int)length) { + logg->logError(__FILE__, __LINE__, "Failed writing binary file %s", mDataFileName); + handleException(); + } + } + + pthread_mutex_unlock(&mSendMutex); +} diff --git a/tools/gator/daemon/Sender.h b/tools/gator/daemon/Sender.h new file mode 100644 index 00000000000..8f23361a5de --- /dev/null +++ b/tools/gator/daemon/Sender.h @@ -0,0 +1,38 @@ +/** + * Copyright (C) ARM Limited 2010-2013. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#ifndef __SENDER_H__ +#define __SENDER_H__ + +#include +#include + +class OlySocket; + +enum { + RESPONSE_XML = 1, + RESPONSE_APC_DATA = 3, + RESPONSE_ACK = 4, + RESPONSE_NAK = 5, + RESPONSE_ERROR = 0xFF +}; + +class Sender { +public: + Sender(OlySocket* socket); + ~Sender(); + void writeData(const char* data, int length, int type); + void createDataFile(char* apcDir); +private: + OlySocket* mDataSocket; + FILE* mDataFile; + char* mDataFileName; + pthread_mutex_t mSendMutex; +}; + +#endif //__SENDER_H__ diff --git a/tools/gator/daemon/SessionData.cpp b/tools/gator/daemon/SessionData.cpp new file mode 100644 index 00000000000..4068d4e957f --- /dev/null +++ b/tools/gator/daemon/SessionData.cpp @@ -0,0 +1,148 @@ +/** + * Copyright (C) ARM Limited 2010-2013. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#include +#include "SessionData.h" +#include "SessionXML.h" +#include "Logging.h" + +SessionData* gSessionData = NULL; + +SessionData::SessionData() { + initialize(); +} + +SessionData::~SessionData() { +} + +void SessionData::initialize() { + mWaitingOnCommand = false; + mSessionIsActive = false; + mLocalCapture = false; + mOneShot = false; + readCpuInfo(); + mConfigurationXMLPath = NULL; + mSessionXMLPath = NULL; + mEventsXMLPath = NULL; + mTargetPath = NULL; + mAPCDir = NULL; + mSampleRate = 0; + mLiveRate = 0; + mDuration = 0; + mBacktraceDepth = 0; + mTotalBufferSize = 0; + // sysconf(_SC_NPROCESSORS_CONF) is unreliable on 2.6 Android, get the value from the kernel module + mCores = 1; +} + +void SessionData::parseSessionXML(char* xmlString) { + SessionXML session(xmlString); + session.parse(); + + // Set session data values + if (strcmp(session.parameters.sample_rate, "high") == 0) { + mSampleRate = 10000; + } else if (strcmp(session.parameters.sample_rate, "normal") == 0) { + mSampleRate = 1000; + } else if (strcmp(session.parameters.sample_rate, "low") == 0) { + mSampleRate = 100; + } else if (strcmp(session.parameters.sample_rate, "none") == 0) { + mSampleRate = 0; + } else { + logg->logError(__FILE__, __LINE__, "Invalid sample rate (%s) in session xml.", session.parameters.sample_rate); + handleException(); + } + mBacktraceDepth = session.parameters.call_stack_unwinding == true ? 128 : 0; + mDuration = session.parameters.duration; + + // Determine buffer size (in MB) based on buffer mode + mOneShot = true; + if (strcmp(session.parameters.buffer_mode, "streaming") == 0) { + mOneShot = false; + mTotalBufferSize = 1; + } else if (strcmp(session.parameters.buffer_mode, "small") == 0) { + mTotalBufferSize = 1; + } else if (strcmp(session.parameters.buffer_mode, "normal") == 0) { + mTotalBufferSize = 4; + } else if (strcmp(session.parameters.buffer_mode, "large") == 0) { + mTotalBufferSize = 16; + } else { + logg->logError(__FILE__, __LINE__, "Invalid value for buffer mode in session xml."); + handleException(); + } + + mImages = session.parameters.images; + // Convert milli- to nanoseconds + mLiveRate = session.parameters.live_rate * (int64_t)1000000; + if (mLiveRate > 0 && mLocalCapture) { + logg->logMessage("Local capture is not compatable with live, disabling live"); + mLiveRate = 0; + } +} + +void SessionData::readCpuInfo() { + char temp[256]; // arbitrarily large amount + strcpy(mCoreName, "unknown"); + mCpuId = -1; + + FILE* f = fopen("/proc/cpuinfo", "r"); + if (f == NULL) { + logg->logMessage("Error opening /proc/cpuinfo\n" + "The core name in the captured xml file will be 'unknown'."); + return; + } + + bool foundCoreName = false; + bool foundCpuId = false; + while (fgets(temp, sizeof(temp), f) && (!foundCoreName || !foundCpuId)) { + if (strlen(temp) > 0) { + temp[strlen(temp) - 1] = 0; // Replace the line feed with a null + } + + const bool foundHardware = strstr(temp, "Hardware") != 0; + const bool foundCPUPart = strstr(temp, "CPU part") != 0; + if (foundHardware || foundCPUPart) { + char* position = strchr(temp, ':'); + if (position == NULL || (unsigned int)(position - temp) + 2 >= strlen(temp)) { + logg->logMessage("Unknown format of /proc/cpuinfo\n" + "The core name in the captured xml file will be 'unknown'."); + return; + } + position += 2; + + if (foundHardware) { + strncpy(mCoreName, position, sizeof(mCoreName)); + mCoreName[sizeof(mCoreName) - 1] = 0; // strncpy does not guarantee a null-terminated string + foundCoreName = true; + } + + if (foundCPUPart) { + int cpuId = strtol(position, NULL, 16); + if (cpuId > mCpuId) { + mCpuId = cpuId; + } + foundCpuId = true; + } + } + } + + if (!foundCoreName) { + logg->logMessage("Could not determine core name from /proc/cpuinfo\n" + "The core name in the captured xml file will be 'unknown'."); + } + fclose(f); + } + +int getEventKey() { + // Start one after the gator.ko's value of 1 + static int key = 2; + + const int ret = key; + key += 2; + return ret; +} diff --git a/tools/gator/daemon/SessionData.h b/tools/gator/daemon/SessionData.h new file mode 100644 index 00000000000..22a8af05121 --- /dev/null +++ b/tools/gator/daemon/SessionData.h @@ -0,0 +1,71 @@ +/** + * Copyright (C) ARM Limited 2010-2013. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#ifndef SESSION_DATA_H +#define SESSION_DATA_H + +#include + +#include "Counter.h" +#include "Hwmon.h" + +#define MAX_PERFORMANCE_COUNTERS 50 + +#define PROTOCOL_VERSION 14 +#define PROTOCOL_DEV 1000 // Differentiates development versions (timestamp) from release versions + +struct ImageLinkList { + char* path; + struct ImageLinkList *next; +}; + +class SessionData { +public: + static const size_t MAX_STRING_LEN = 80; + + SessionData(); + ~SessionData(); + void initialize(); + void parseSessionXML(char* xmlString); + + Hwmon hwmon; + + char mCoreName[MAX_STRING_LEN]; + struct ImageLinkList *mImages; + char* mConfigurationXMLPath; + char* mSessionXMLPath; + char* mEventsXMLPath; + char* mTargetPath; + char* mAPCDir; + + bool mWaitingOnCommand; + bool mSessionIsActive; + bool mLocalCapture; + bool mOneShot; // halt processing of the driver data until profiling is complete or the buffer is filled + + int mBacktraceDepth; + int mTotalBufferSize; // number of MB to use for the entire collection buffer + int mSampleRate; + int64_t mLiveRate; + int mDuration; + int mCores; + int mCpuId; + + // PMU Counters + int mCounterOverflow; + Counter mCounters[MAX_PERFORMANCE_COUNTERS]; + +private: + void readCpuInfo(); +}; + +extern SessionData* gSessionData; + +int getEventKey(); + +#endif // SESSION_DATA_H diff --git a/tools/gator/daemon/SessionXML.cpp b/tools/gator/daemon/SessionXML.cpp new file mode 100644 index 00000000000..0a0a0277917 --- /dev/null +++ b/tools/gator/daemon/SessionXML.cpp @@ -0,0 +1,109 @@ +/** + * Copyright (C) ARM Limited 2010-2013. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#include +#include +#include +#include "SessionXML.h" +#include "Logging.h" +#include "OlyUtility.h" +#include "SessionData.h" + +static const char* TAG_SESSION = "session"; +static const char* TAG_IMAGE = "image"; + +static const char* ATTR_VERSION = "version"; +static const char* ATTR_CALL_STACK_UNWINDING = "call_stack_unwinding"; +static const char* ATTR_BUFFER_MODE = "buffer_mode"; +static const char* ATTR_SAMPLE_RATE = "sample_rate"; +static const char* ATTR_DURATION = "duration"; +static const char* ATTR_PATH = "path"; +static const char* ATTR_LIVE_RATE = "live_rate"; + +SessionXML::SessionXML(const char* str) { + parameters.buffer_mode[0] = 0; + parameters.sample_rate[0] = 0; + parameters.duration = 0; + parameters.call_stack_unwinding = false; + parameters.live_rate = 0; + parameters.images = NULL; + mPath = 0; + mSessionXML = (char*)str; + logg->logMessage(mSessionXML); +} + +SessionXML::~SessionXML() { + if (mPath != 0) { + free(mSessionXML); + } +} + +void SessionXML::parse() { + mxml_node_t *tree; + mxml_node_t *node; + + tree = mxmlLoadString(NULL, mSessionXML, MXML_NO_CALLBACK); + node = mxmlFindElement(tree, tree, TAG_SESSION, NULL, NULL, MXML_DESCEND); + + if (node) { + sessionTag(tree, node); + mxmlDelete(tree); + return; + } + + logg->logError(__FILE__, __LINE__, "No session tag found in the session.xml file"); + handleException(); +} + +void SessionXML::sessionTag(mxml_node_t *tree, mxml_node_t *node) { + int version = 0; + if (mxmlElementGetAttr(node, ATTR_VERSION)) version = strtol(mxmlElementGetAttr(node, ATTR_VERSION), NULL, 10); + if (version != 1) { + logg->logError(__FILE__, __LINE__, "Invalid session.xml version: %d", version); + handleException(); + } + + // copy to pre-allocated strings + if (mxmlElementGetAttr(node, ATTR_BUFFER_MODE)) { + strncpy(parameters.buffer_mode, mxmlElementGetAttr(node, ATTR_BUFFER_MODE), sizeof(parameters.buffer_mode)); + parameters.buffer_mode[sizeof(parameters.buffer_mode) - 1] = 0; // strncpy does not guarantee a null-terminated string + } + if (mxmlElementGetAttr(node, ATTR_SAMPLE_RATE)) { + strncpy(parameters.sample_rate, mxmlElementGetAttr(node, ATTR_SAMPLE_RATE), sizeof(parameters.sample_rate)); + parameters.sample_rate[sizeof(parameters.sample_rate) - 1] = 0; // strncpy does not guarantee a null-terminated string + } + + // integers/bools + parameters.call_stack_unwinding = util->stringToBool(mxmlElementGetAttr(node, ATTR_CALL_STACK_UNWINDING), false); + if (mxmlElementGetAttr(node, ATTR_DURATION)) parameters.duration = strtol(mxmlElementGetAttr(node, ATTR_DURATION), NULL, 10); + if (mxmlElementGetAttr(node, ATTR_LIVE_RATE)) parameters.live_rate = strtol(mxmlElementGetAttr(node, ATTR_LIVE_RATE), NULL, 10); + + // parse subtags + node = mxmlGetFirstChild(node); + while (node) { + if (mxmlGetType(node) != MXML_ELEMENT) { + node = mxmlWalkNext(node, tree, MXML_NO_DESCEND); + continue; + } + if (strcmp(TAG_IMAGE, mxmlGetElement(node)) == 0) { + sessionImage(node); + } + node = mxmlWalkNext(node, tree, MXML_NO_DESCEND); + } +} + +void SessionXML::sessionImage(mxml_node_t *node) { + int length = strlen(mxmlElementGetAttr(node, ATTR_PATH)); + struct ImageLinkList *image; + + image = (struct ImageLinkList *)malloc(sizeof(struct ImageLinkList)); + image->path = (char*)malloc(length + 1); + image->path = strdup(mxmlElementGetAttr(node, ATTR_PATH)); + image->next = parameters.images; + parameters.images = image; +} diff --git a/tools/gator/daemon/SessionXML.h b/tools/gator/daemon/SessionXML.h new file mode 100644 index 00000000000..c7e3798d695 --- /dev/null +++ b/tools/gator/daemon/SessionXML.h @@ -0,0 +1,38 @@ +/** + * Copyright (C) ARM Limited 2010-2013. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#ifndef SESSION_XML_H +#define SESSION_XML_H + +#include "mxml/mxml.h" + +struct ImageLinkList; + +struct ConfigParameters { + char buffer_mode[64]; // buffer mode, "streaming", "low", "normal", "high" defines oneshot and buffer size + char sample_rate[64]; // capture mode, "high", "normal", or "low" + int duration; // length of profile in seconds + bool call_stack_unwinding; // whether stack unwinding is performed + int live_rate; + struct ImageLinkList *images; // linked list of image strings +}; + +class SessionXML { +public: + SessionXML(const char* str); + ~SessionXML(); + void parse(); + ConfigParameters parameters; +private: + char* mSessionXML; + char* mPath; + void sessionTag(mxml_node_t *tree, mxml_node_t *node); + void sessionImage(mxml_node_t *node); +}; + +#endif // SESSION_XML_H diff --git a/tools/gator/daemon/StreamlineSetup.cpp b/tools/gator/daemon/StreamlineSetup.cpp new file mode 100644 index 00000000000..e196a7dba2c --- /dev/null +++ b/tools/gator/daemon/StreamlineSetup.cpp @@ -0,0 +1,277 @@ +/** + * Copyright (C) ARM Limited 2011-2013. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#include +#include +#include +#include +#include +#include +#include "Sender.h" +#include "Logging.h" +#include "OlyUtility.h" +#include "SessionData.h" +#include "CapturedXML.h" +#include "StreamlineSetup.h" +#include "ConfigurationXML.h" +#include "Driver.h" +#include "EventsXML.h" + +static const char* TAG_SESSION = "session"; +static const char* TAG_REQUEST = "request"; +static const char* TAG_CONFIGURATIONS = "configurations"; + +static const char* ATTR_TYPE = "type"; +static const char* VALUE_EVENTS = "events"; +static const char* VALUE_CONFIGURATION = "configuration"; +static const char* VALUE_COUNTERS = "counters"; +static const char* VALUE_CAPTURED = "captured"; +static const char* VALUE_DEFAULTS = "defaults"; + +StreamlineSetup::StreamlineSetup(OlySocket* s) { + bool ready = false; + char* data = NULL; + int type; + + mSocket = s; + + // Receive commands from Streamline (master) + while (!ready) { + // receive command over socket + gSessionData->mWaitingOnCommand = true; + data = readCommand(&type); + + // parse and handle data + switch (type) { + case COMMAND_REQUEST_XML: + handleRequest(data); + break; + case COMMAND_DELIVER_XML: + handleDeliver(data); + break; + case COMMAND_APC_START: + logg->logMessage("Received apc start request"); + ready = true; + break; + case COMMAND_APC_STOP: + logg->logMessage("Received apc stop request before apc start request"); + exit(0); + break; + case COMMAND_DISCONNECT: + logg->logMessage("Received disconnect command"); + exit(0); + break; + case COMMAND_PING: + logg->logMessage("Received ping command"); + sendData(NULL, 0, RESPONSE_ACK); + break; + default: + logg->logError(__FILE__, __LINE__, "Target error: Unknown command type, %d", type); + handleException(); + } + + free(data); + } + + if (gSessionData->mCounterOverflow > 0) { + logg->logError(__FILE__, __LINE__, "Only %i performance counters are permitted, %i are selected", MAX_PERFORMANCE_COUNTERS, gSessionData->mCounterOverflow); + handleException(); + } +} + +StreamlineSetup::~StreamlineSetup() { +} + +char* StreamlineSetup::readCommand(int* command) { + unsigned char header[5]; + char* data; + int response; + + // receive type and length + response = mSocket->receiveNBytes((char*)&header, sizeof(header)); + + // After receiving a single byte, we are no longer waiting on a command + gSessionData->mWaitingOnCommand = false; + + if (response < 0) { + logg->logError(__FILE__, __LINE__, "Target error: Unexpected socket disconnect"); + handleException(); + } + + const char type = header[0]; + const int length = (header[1] << 0) | (header[2] << 8) | (header[3] << 16) | (header[4] << 24); + + // add artificial limit + if ((length < 0) || length > 1024 * 1024) { + logg->logError(__FILE__, __LINE__, "Target error: Invalid length received, %d", length); + handleException(); + } + + // allocate memory to contain the xml file, size of zero returns a zero size object + data = (char*)calloc(length + 1, 1); + if (data == NULL) { + logg->logError(__FILE__, __LINE__, "Unable to allocate memory for xml"); + handleException(); + } + + // receive data + response = mSocket->receiveNBytes(data, length); + if (response < 0) { + logg->logError(__FILE__, __LINE__, "Target error: Unexpected socket disconnect"); + handleException(); + } + + // null terminate the data for string parsing + if (length > 0) { + data[length] = 0; + } + + *command = type; + return data; +} + +void StreamlineSetup::handleRequest(char* xml) { + mxml_node_t *tree, *node; + const char * attr = NULL; + + tree = mxmlLoadString(NULL, xml, MXML_NO_CALLBACK); + node = mxmlFindElement(tree, tree, TAG_REQUEST, ATTR_TYPE, NULL, MXML_DESCEND_FIRST); + if (node) { + attr = mxmlElementGetAttr(node, ATTR_TYPE); + } + if (attr && strcmp(attr, VALUE_EVENTS) == 0) { + sendEvents(); + logg->logMessage("Sent events xml response"); + } else if (attr && strcmp(attr, VALUE_CONFIGURATION) == 0) { + sendConfiguration(); + logg->logMessage("Sent configuration xml response"); + } else if (attr && strcmp(attr, VALUE_COUNTERS) == 0) { + sendCounters(); + logg->logMessage("Sent counters xml response"); + } else if (attr && strcmp(attr, VALUE_CAPTURED) == 0) { + CapturedXML capturedXML; + char* capturedText = capturedXML.getXML(false); + sendData(capturedText, strlen(capturedText), RESPONSE_XML); + free(capturedText); + logg->logMessage("Sent captured xml response"); + } else if (attr && strcmp(attr, VALUE_DEFAULTS) == 0) { + sendDefaults(); + logg->logMessage("Sent default configuration xml response"); + } else { + char error[] = "Unknown request"; + sendData(error, strlen(error), RESPONSE_NAK); + logg->logMessage("Received unknown request:\n%s", xml); + } + + mxmlDelete(tree); +} + +void StreamlineSetup::handleDeliver(char* xml) { + mxml_node_t *tree; + + // Determine xml type + tree = mxmlLoadString(NULL, xml, MXML_NO_CALLBACK); + if (mxmlFindElement(tree, tree, TAG_SESSION, NULL, NULL, MXML_DESCEND_FIRST)) { + // Session XML + gSessionData->parseSessionXML(xml); + sendData(NULL, 0, RESPONSE_ACK); + logg->logMessage("Received session xml"); + } else if (mxmlFindElement(tree, tree, TAG_CONFIGURATIONS, NULL, NULL, MXML_DESCEND_FIRST)) { + // Configuration XML + writeConfiguration(xml); + sendData(NULL, 0, RESPONSE_ACK); + logg->logMessage("Received configuration xml"); + } else { + // Unknown XML + logg->logMessage("Received unknown XML delivery type"); + sendData(NULL, 0, RESPONSE_NAK); + } + + mxmlDelete(tree); +} + +void StreamlineSetup::sendData(const char* data, uint32_t length, char type) { + unsigned char header[5]; + header[0] = type; + header[1] = (length >> 0) & 0xff; + header[2] = (length >> 8) & 0xff; + header[3] = (length >> 16) & 0xff; + header[4] = (length >> 24) & 0xff; + mSocket->send((char*)&header, sizeof(header)); + mSocket->send((char*)data, length); +} + +void StreamlineSetup::sendEvents() { + EventsXML eventsXML; + char* string = eventsXML.getXML(); + sendString(string, RESPONSE_XML); + free(string); +} + +void StreamlineSetup::sendConfiguration() { + ConfigurationXML xml; + + const char* string = xml.getConfigurationXML(); + sendData(string, strlen(string), RESPONSE_XML); +} + +void StreamlineSetup::sendDefaults() { + // Send the config built into the binary + const char* xml; + unsigned int size; + ConfigurationXML::getDefaultConfigurationXml(xml, size); + + // Artificial size restriction + if (size > 1024*1024) { + logg->logError(__FILE__, __LINE__, "Corrupt default configuration file"); + handleException(); + } + + sendData(xml, size, RESPONSE_XML); +} + +void StreamlineSetup::sendCounters() { + mxml_node_t *xml; + mxml_node_t *counters; + + xml = mxmlNewXML("1.0"); + counters = mxmlNewElement(xml, "counters"); + for (Driver *driver = Driver::getHead(); driver != NULL; driver = driver->getNext()) { + driver->writeCounters(counters); + } + + char* string = mxmlSaveAllocString(xml, mxmlWhitespaceCB); + sendString(string, RESPONSE_XML); + + free(string); + mxmlDelete(xml); +} + +void StreamlineSetup::writeConfiguration(char* xml) { + char path[PATH_MAX]; + + if (gSessionData->mConfigurationXMLPath) { + strncpy(path, gSessionData->mConfigurationXMLPath, PATH_MAX); + } else { + util->getApplicationFullPath(path, PATH_MAX); + strncat(path, "configuration.xml", PATH_MAX - strlen(path) - 1); + } + + if (util->writeToDisk(path, xml) < 0) { + logg->logError(__FILE__, __LINE__, "Error writing %s\nPlease verify write permissions to this path.", path); + handleException(); + } + + // Re-populate gSessionData with the configuration, as it has now changed + { ConfigurationXML configuration; } + + if (gSessionData->mCounterOverflow > 0) { + logg->logError(__FILE__, __LINE__, "Only %i performance counters counters are permitted, %i are selected", MAX_PERFORMANCE_COUNTERS, gSessionData->mCounterOverflow); + handleException(); + } +} diff --git a/tools/gator/daemon/StreamlineSetup.h b/tools/gator/daemon/StreamlineSetup.h new file mode 100644 index 00000000000..a27a7ac97a1 --- /dev/null +++ b/tools/gator/daemon/StreamlineSetup.h @@ -0,0 +1,44 @@ +/** + * Copyright (C) ARM Limited 2010-2013. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#ifndef __STREAMLINE_SETUP_H__ +#define __STREAMLINE_SETUP_H__ + +#include "OlySocket.h" + +// Commands from Streamline +enum { + COMMAND_REQUEST_XML = 0, + COMMAND_DELIVER_XML = 1, + COMMAND_APC_START = 2, + COMMAND_APC_STOP = 3, + COMMAND_DISCONNECT = 4, + COMMAND_PING = 5 +}; + +class StreamlineSetup { +public: + StreamlineSetup(OlySocket *socket); + ~StreamlineSetup(); +private: + int mNumConnections; + OlySocket* mSocket; + + char* readCommand(int*); + void handleRequest(char* xml); + void handleDeliver(char* xml); + void sendData(const char* data, uint32_t length, char type); + void sendString(const char* string, int type) {sendData(string, strlen(string), type);} + void sendEvents(); + void sendConfiguration(); + void sendDefaults(); + void sendCounters(); + void writeConfiguration(char* xml); +}; + +#endif //__STREAMLINE_SETUP_H__ diff --git a/tools/gator/daemon/common.mk b/tools/gator/daemon/common.mk new file mode 100644 index 00000000000..ee2415b8825 --- /dev/null +++ b/tools/gator/daemon/common.mk @@ -0,0 +1,50 @@ +# -g produces debugging information +# -O3 maximum optimization +# -O0 no optimization, used for debugging +# -Wall enables most warnings +# -Werror treats warnings as errors +# -std=c++0x is the planned new c++ standard +# -std=c++98 is the 1998 c++ standard +CFLAGS += -O3 -Wall -fno-exceptions -pthread -MMD -DETCDIR=\"/etc\" -Ilibsensors +CXXFLAGS += -fno-rtti +ifeq ($(WERROR),1) + CFLAGS += -Werror +endif +# -s strips the binary of debug info +LDFLAGS += -s +TARGET = gatord +C_SRC = $(wildcard mxml/*.c) $(wildcard libsensors/*.c) +CPP_SRC = $(wildcard *.cpp) + +all: $(TARGET) + +events.xml: events_header.xml $(wildcard events-*.xml) events_footer.xml + cat $^ > $@ + +include $(wildcard *.d) +include $(wildcard mxml/*.d) + +EventsXML.cpp: events_xml.h +ConfigurationXML.cpp: configuration_xml.h + +# Don't regenerate conf-lex.c or conf-parse.c +libsensors/conf-lex.c: ; +libsensors/conf-parse.c: ; + +%_xml.h: %.xml escape + ./escape $< > $@ + +%.o: %.c + $(GCC) -c $(CFLAGS) -o $@ $< + +%.o: %.cpp + $(CPP) -c $(CFLAGS) $(CXXFLAGS) -o $@ $< + +$(TARGET): $(CPP_SRC:%.cpp=%.o) $(C_SRC:%.c=%.o) + $(CPP) $(LDFLAGS) -o $@ $^ -lrt -pthread + +escape: escape.c + gcc $^ -o $@ + +clean: + rm -f *.d *.o mxml/*.d mxml/*.o libsensors/*.d libsensors/*.o $(TARGET) escape events.xml events_xml.h configuration_xml.h diff --git a/tools/gator/daemon/configuration.xml b/tools/gator/daemon/configuration.xml new file mode 100644 index 00000000000..b44c00a79e8 --- /dev/null +++ b/tools/gator/daemon/configuration.xml @@ -0,0 +1,57 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/tools/gator/daemon/escape.c b/tools/gator/daemon/escape.c new file mode 100644 index 00000000000..3eec1f8d38d --- /dev/null +++ b/tools/gator/daemon/escape.c @@ -0,0 +1,75 @@ +/** + * Copyright (C) ARM Limited 2010-2013. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +/* + * The Makefile in the daemon folder builds and executes 'escape' + * 'escape' creates configuration_xml.h from configuration.xml and events_xml.h from events-*.xml + * these genereated xml files are then #included and built as part of the gatord binary + */ + +#include +#include +#include +#include +#include + +static void print_escaped_path(char *path) { + if (isdigit(*path)) { + printf("__"); + } + for (; *path != '\0'; ++path) { + printf("%c", isalnum(*path) ? *path : '_'); + } +} + +int main(int argc, char *argv[]) { + int i; + char *path; + FILE *in = NULL; + int ch; + unsigned int len = 0; + + for (i = 1; i < argc && argv[i][0] == '-'; ++i) ; + if (i == argc) { + fprintf(stderr, "Usage: %s \n", argv[0]); + return EXIT_FAILURE; + } + path = argv[i]; + + errno = 0; + if ((in = fopen(path, "r")) == NULL) { + fprintf(stderr, "Unable to open '%s': %s\n", path, strerror(errno)); + return EXIT_FAILURE; + } + + printf("static const unsigned char "); + print_escaped_path(path); + printf("[] = {"); + for (;;) { + ch = fgetc(in); + if (len != 0) { + printf(","); + } + if (len % 12 == 0) { + printf("\n "); + } + // Write out a null character after the contents of the file but do not increment len + printf(" 0x%.2x", (ch == EOF ? 0 : ch)); + if (ch == EOF) { + break; + } + ++len; + } + printf("\n};\nstatic const unsigned int "); + print_escaped_path(path); + printf("_len = %i;\n", len); + + fclose(in); + + return EXIT_SUCCESS; +} diff --git a/tools/gator/daemon/events-ARM11.xml b/tools/gator/daemon/events-ARM11.xml new file mode 100644 index 00000000000..57e32354631 --- /dev/null +++ b/tools/gator/daemon/events-ARM11.xml @@ -0,0 +1,39 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/tools/gator/daemon/events-ARM11MPCore.xml b/tools/gator/daemon/events-ARM11MPCore.xml new file mode 100644 index 00000000000..2d5c5e199e6 --- /dev/null +++ b/tools/gator/daemon/events-ARM11MPCore.xml @@ -0,0 +1,26 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/tools/gator/daemon/events-CCI-400.xml b/tools/gator/daemon/events-CCI-400.xml new file mode 100644 index 00000000000..86db2087e1f --- /dev/null +++ b/tools/gator/daemon/events-CCI-400.xml @@ -0,0 +1,47 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/tools/gator/daemon/events-Cortex-A15.xml b/tools/gator/daemon/events-Cortex-A15.xml new file mode 100644 index 00000000000..faa8b1cbcfb --- /dev/null +++ b/tools/gator/daemon/events-Cortex-A15.xml @@ -0,0 +1,68 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/tools/gator/daemon/events-Cortex-A5.xml b/tools/gator/daemon/events-Cortex-A5.xml new file mode 100644 index 00000000000..a5b15466be5 --- /dev/null +++ b/tools/gator/daemon/events-Cortex-A5.xml @@ -0,0 +1,36 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/tools/gator/daemon/events-Cortex-A53.xml b/tools/gator/daemon/events-Cortex-A53.xml new file mode 100644 index 00000000000..577dcd94185 --- /dev/null +++ b/tools/gator/daemon/events-Cortex-A53.xml @@ -0,0 +1,171 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/tools/gator/daemon/events-Cortex-A57.xml b/tools/gator/daemon/events-Cortex-A57.xml new file mode 100644 index 00000000000..b7178c0c742 --- /dev/null +++ b/tools/gator/daemon/events-Cortex-A57.xml @@ -0,0 +1,171 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/tools/gator/daemon/events-Cortex-A7.xml b/tools/gator/daemon/events-Cortex-A7.xml new file mode 100644 index 00000000000..54d7264bc08 --- /dev/null +++ b/tools/gator/daemon/events-Cortex-A7.xml @@ -0,0 +1,43 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/tools/gator/daemon/events-Cortex-A8.xml b/tools/gator/daemon/events-Cortex-A8.xml new file mode 100644 index 00000000000..f2518237983 --- /dev/null +++ b/tools/gator/daemon/events-Cortex-A8.xml @@ -0,0 +1,52 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/tools/gator/daemon/events-Cortex-A9.xml b/tools/gator/daemon/events-Cortex-A9.xml new file mode 100644 index 00000000000..75f09c80425 --- /dev/null +++ b/tools/gator/daemon/events-Cortex-A9.xml @@ -0,0 +1,65 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/tools/gator/daemon/events-Krait-architected.xml b/tools/gator/daemon/events-Krait-architected.xml new file mode 100644 index 00000000000..b8d3bcb48de --- /dev/null +++ b/tools/gator/daemon/events-Krait-architected.xml @@ -0,0 +1,22 @@ + + + + + + + + + + + + + + + + + + + + + + diff --git a/tools/gator/daemon/events-L2C-310.xml b/tools/gator/daemon/events-L2C-310.xml new file mode 100644 index 00000000000..4da4d1d6343 --- /dev/null +++ b/tools/gator/daemon/events-L2C-310.xml @@ -0,0 +1,18 @@ + + + + + + + + + + + + + + + + + + diff --git a/tools/gator/daemon/events-Linux.xml b/tools/gator/daemon/events-Linux.xml new file mode 100644 index 00000000000..05dc613a88a --- /dev/null +++ b/tools/gator/daemon/events-Linux.xml @@ -0,0 +1,17 @@ + + + + + + + + + + + + + + + + + diff --git a/tools/gator/daemon/events-Mali-400.xml b/tools/gator/daemon/events-Mali-400.xml new file mode 100644 index 00000000000..dceccfb4a67 --- /dev/null +++ b/tools/gator/daemon/events-Mali-400.xml @@ -0,0 +1,180 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/tools/gator/daemon/events-Mali-T6xx.xml b/tools/gator/daemon/events-Mali-T6xx.xml new file mode 100644 index 00000000000..647e3d5b0fc --- /dev/null +++ b/tools/gator/daemon/events-Mali-T6xx.xml @@ -0,0 +1,38 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/tools/gator/daemon/events-Mali-T6xx_hw.xml b/tools/gator/daemon/events-Mali-T6xx_hw.xml new file mode 100644 index 00000000000..1fd9c4e59d8 --- /dev/null +++ b/tools/gator/daemon/events-Mali-T6xx_hw.xml @@ -0,0 +1,113 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/tools/gator/daemon/events-Scorpion.xml b/tools/gator/daemon/events-Scorpion.xml new file mode 100644 index 00000000000..fa716fdc484 --- /dev/null +++ b/tools/gator/daemon/events-Scorpion.xml @@ -0,0 +1,107 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/tools/gator/daemon/events-ScorpionMP.xml b/tools/gator/daemon/events-ScorpionMP.xml new file mode 100644 index 00000000000..c648ccefb28 --- /dev/null +++ b/tools/gator/daemon/events-ScorpionMP.xml @@ -0,0 +1,90 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/tools/gator/daemon/events_footer.xml b/tools/gator/daemon/events_footer.xml new file mode 100644 index 00000000000..cd2b44665ba --- /dev/null +++ b/tools/gator/daemon/events_footer.xml @@ -0,0 +1 @@ + diff --git a/tools/gator/daemon/events_header.xml b/tools/gator/daemon/events_header.xml new file mode 100644 index 00000000000..38ec4c03246 --- /dev/null +++ b/tools/gator/daemon/events_header.xml @@ -0,0 +1,2 @@ + + diff --git a/tools/gator/daemon/libsensors/COPYING.LGPL b/tools/gator/daemon/libsensors/COPYING.LGPL new file mode 100644 index 00000000000..4362b49151d --- /dev/null +++ b/tools/gator/daemon/libsensors/COPYING.LGPL @@ -0,0 +1,502 @@ + GNU LESSER GENERAL PUBLIC LICENSE + Version 2.1, February 1999 + + Copyright (C) 1991, 1999 Free Software Foundation, Inc. + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + +[This is the first released version of the Lesser GPL. It also counts + as the successor of the GNU Library Public License, version 2, hence + the version number 2.1.] + + Preamble + + The licenses for most software are designed to take away your +freedom to share and change it. By contrast, the GNU General Public +Licenses are intended to guarantee your freedom to share and change +free software--to make sure the software is free for all its users. + + This license, the Lesser General Public License, applies to some +specially designated software packages--typically libraries--of the +Free Software Foundation and other authors who decide to use it. You +can use it too, but we suggest you first think carefully about whether +this license or the ordinary General Public License is the better +strategy to use in any particular case, based on the explanations below. + + When we speak of free software, we are referring to freedom of use, +not price. Our General Public Licenses are designed to make sure that +you have the freedom to distribute copies of free software (and charge +for this service if you wish); that you receive source code or can get +it if you want it; that you can change the software and use pieces of +it in new free programs; and that you are informed that you can do +these things. + + To protect your rights, we need to make restrictions that forbid +distributors to deny you these rights or to ask you to surrender these +rights. These restrictions translate to certain responsibilities for +you if you distribute copies of the library or if you modify it. + + For example, if you distribute copies of the library, whether gratis +or for a fee, you must give the recipients all the rights that we gave +you. You must make sure that they, too, receive or can get the source +code. If you link other code with the library, you must provide +complete object files to the recipients, so that they can relink them +with the library after making changes to the library and recompiling +it. And you must show them these terms so they know their rights. + + We protect your rights with a two-step method: (1) we copyright the +library, and (2) we offer you this license, which gives you legal +permission to copy, distribute and/or modify the library. + + To protect each distributor, we want to make it very clear that +there is no warranty for the free library. Also, if the library is +modified by someone else and passed on, the recipients should know +that what they have is not the original version, so that the original +author's reputation will not be affected by problems that might be +introduced by others. + + Finally, software patents pose a constant threat to the existence of +any free program. We wish to make sure that a company cannot +effectively restrict the users of a free program by obtaining a +restrictive license from a patent holder. Therefore, we insist that +any patent license obtained for a version of the library must be +consistent with the full freedom of use specified in this license. + + Most GNU software, including some libraries, is covered by the +ordinary GNU General Public License. This license, the GNU Lesser +General Public License, applies to certain designated libraries, and +is quite different from the ordinary General Public License. We use +this license for certain libraries in order to permit linking those +libraries into non-free programs. + + When a program is linked with a library, whether statically or using +a shared library, the combination of the two is legally speaking a +combined work, a derivative of the original library. The ordinary +General Public License therefore permits such linking only if the +entire combination fits its criteria of freedom. The Lesser General +Public License permits more lax criteria for linking other code with +the library. + + We call this license the "Lesser" General Public License because it +does Less to protect the user's freedom than the ordinary General +Public License. It also provides other free software developers Less +of an advantage over competing non-free programs. These disadvantages +are the reason we use the ordinary General Public License for many +libraries. However, the Lesser license provides advantages in certain +special circumstances. + + For example, on rare occasions, there may be a special need to +encourage the widest possible use of a certain library, so that it becomes +a de-facto standard. To achieve this, non-free programs must be +allowed to use the library. A more frequent case is that a free +library does the same job as widely used non-free libraries. In this +case, there is little to gain by limiting the free library to free +software only, so we use the Lesser General Public License. + + In other cases, permission to use a particular library in non-free +programs enables a greater number of people to use a large body of +free software. For example, permission to use the GNU C Library in +non-free programs enables many more people to use the whole GNU +operating system, as well as its variant, the GNU/Linux operating +system. + + Although the Lesser General Public License is Less protective of the +users' freedom, it does ensure that the user of a program that is +linked with the Library has the freedom and the wherewithal to run +that program using a modified version of the Library. + + The precise terms and conditions for copying, distribution and +modification follow. Pay close attention to the difference between a +"work based on the library" and a "work that uses the library". The +former contains code derived from the library, whereas the latter must +be combined with the library in order to run. + + GNU LESSER GENERAL PUBLIC LICENSE + TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + + 0. This License Agreement applies to any software library or other +program which contains a notice placed by the copyright holder or +other authorized party saying it may be distributed under the terms of +this Lesser General Public License (also called "this License"). +Each licensee is addressed as "you". + + A "library" means a collection of software functions and/or data +prepared so as to be conveniently linked with application programs +(which use some of those functions and data) to form executables. + + The "Library", below, refers to any such software library or work +which has been distributed under these terms. A "work based on the +Library" means either the Library or any derivative work under +copyright law: that is to say, a work containing the Library or a +portion of it, either verbatim or with modifications and/or translated +straightforwardly into another language. (Hereinafter, translation is +included without limitation in the term "modification".) + + "Source code" for a work means the preferred form of the work for +making modifications to it. For a library, complete source code means +all the source code for all modules it contains, plus any associated +interface definition files, plus the scripts used to control compilation +and installation of the library. + + Activities other than copying, distribution and modification are not +covered by this License; they are outside its scope. The act of +running a program using the Library is not restricted, and output from +such a program is covered only if its contents constitute a work based +on the Library (independent of the use of the Library in a tool for +writing it). Whether that is true depends on what the Library does +and what the program that uses the Library does. + + 1. You may copy and distribute verbatim copies of the Library's +complete source code as you receive it, in any medium, provided that +you conspicuously and appropriately publish on each copy an +appropriate copyright notice and disclaimer of warranty; keep intact +all the notices that refer to this License and to the absence of any +warranty; and distribute a copy of this License along with the +Library. + + You may charge a fee for the physical act of transferring a copy, +and you may at your option offer warranty protection in exchange for a +fee. + + 2. You may modify your copy or copies of the Library or any portion +of it, thus forming a work based on the Library, and copy and +distribute such modifications or work under the terms of Section 1 +above, provided that you also meet all of these conditions: + + a) The modified work must itself be a software library. + + b) You must cause the files modified to carry prominent notices + stating that you changed the files and the date of any change. + + c) You must cause the whole of the work to be licensed at no + charge to all third parties under the terms of this License. + + d) If a facility in the modified Library refers to a function or a + table of data to be supplied by an application program that uses + the facility, other than as an argument passed when the facility + is invoked, then you must make a good faith effort to ensure that, + in the event an application does not supply such function or + table, the facility still operates, and performs whatever part of + its purpose remains meaningful. + + (For example, a function in a library to compute square roots has + a purpose that is entirely well-defined independent of the + application. Therefore, Subsection 2d requires that any + application-supplied function or table used by this function must + be optional: if the application does not supply it, the square + root function must still compute square roots.) + +These requirements apply to the modified work as a whole. If +identifiable sections of that work are not derived from the Library, +and can be reasonably considered independent and separate works in +themselves, then this License, and its terms, do not apply to those +sections when you distribute them as separate works. But when you +distribute the same sections as part of a whole which is a work based +on the Library, the distribution of the whole must be on the terms of +this License, whose permissions for other licensees extend to the +entire whole, and thus to each and every part regardless of who wrote +it. + +Thus, it is not the intent of this section to claim rights or contest +your rights to work written entirely by you; rather, the intent is to +exercise the right to control the distribution of derivative or +collective works based on the Library. + +In addition, mere aggregation of another work not based on the Library +with the Library (or with a work based on the Library) on a volume of +a storage or distribution medium does not bring the other work under +the scope of this License. + + 3. You may opt to apply the terms of the ordinary GNU General Public +License instead of this License to a given copy of the Library. To do +this, you must alter all the notices that refer to this License, so +that they refer to the ordinary GNU General Public License, version 2, +instead of to this License. (If a newer version than version 2 of the +ordinary GNU General Public License has appeared, then you can specify +that version instead if you wish.) Do not make any other change in +these notices. + + Once this change is made in a given copy, it is irreversible for +that copy, so the ordinary GNU General Public License applies to all +subsequent copies and derivative works made from that copy. + + This option is useful when you wish to copy part of the code of +the Library into a program that is not a library. + + 4. You may copy and distribute the Library (or a portion or +derivative of it, under Section 2) in object code or executable form +under the terms of Sections 1 and 2 above provided that you accompany +it with the complete corresponding machine-readable source code, which +must be distributed under the terms of Sections 1 and 2 above on a +medium customarily used for software interchange. + + If distribution of object code is made by offering access to copy +from a designated place, then offering equivalent access to copy the +source code from the same place satisfies the requirement to +distribute the source code, even though third parties are not +compelled to copy the source along with the object code. + + 5. A program that contains no derivative of any portion of the +Library, but is designed to work with the Library by being compiled or +linked with it, is called a "work that uses the Library". Such a +work, in isolation, is not a derivative work of the Library, and +therefore falls outside the scope of this License. + + However, linking a "work that uses the Library" with the Library +creates an executable that is a derivative of the Library (because it +contains portions of the Library), rather than a "work that uses the +library". The executable is therefore covered by this License. +Section 6 states terms for distribution of such executables. + + When a "work that uses the Library" uses material from a header file +that is part of the Library, the object code for the work may be a +derivative work of the Library even though the source code is not. +Whether this is true is especially significant if the work can be +linked without the Library, or if the work is itself a library. The +threshold for this to be true is not precisely defined by law. + + If such an object file uses only numerical parameters, data +structure layouts and accessors, and small macros and small inline +functions (ten lines or less in length), then the use of the object +file is unrestricted, regardless of whether it is legally a derivative +work. (Executables containing this object code plus portions of the +Library will still fall under Section 6.) + + Otherwise, if the work is a derivative of the Library, you may +distribute the object code for the work under the terms of Section 6. +Any executables containing that work also fall under Section 6, +whether or not they are linked directly with the Library itself. + + 6. As an exception to the Sections above, you may also combine or +link a "work that uses the Library" with the Library to produce a +work containing portions of the Library, and distribute that work +under terms of your choice, provided that the terms permit +modification of the work for the customer's own use and reverse +engineering for debugging such modifications. + + You must give prominent notice with each copy of the work that the +Library is used in it and that the Library and its use are covered by +this License. You must supply a copy of this License. If the work +during execution displays copyright notices, you must include the +copyright notice for the Library among them, as well as a reference +directing the user to the copy of this License. Also, you must do one +of these things: + + a) Accompany the work with the complete corresponding + machine-readable source code for the Library including whatever + changes were used in the work (which must be distributed under + Sections 1 and 2 above); and, if the work is an executable linked + with the Library, with the complete machine-readable "work that + uses the Library", as object code and/or source code, so that the + user can modify the Library and then relink to produce a modified + executable containing the modified Library. (It is understood + that the user who changes the contents of definitions files in the + Library will not necessarily be able to recompile the application + to use the modified definitions.) + + b) Use a suitable shared library mechanism for linking with the + Library. A suitable mechanism is one that (1) uses at run time a + copy of the library already present on the user's computer system, + rather than copying library functions into the executable, and (2) + will operate properly with a modified version of the library, if + the user installs one, as long as the modified version is + interface-compatible with the version that the work was made with. + + c) Accompany the work with a written offer, valid for at + least three years, to give the same user the materials + specified in Subsection 6a, above, for a charge no more + than the cost of performing this distribution. + + d) If distribution of the work is made by offering access to copy + from a designated place, offer equivalent access to copy the above + specified materials from the same place. + + e) Verify that the user has already received a copy of these + materials or that you have already sent this user a copy. + + For an executable, the required form of the "work that uses the +Library" must include any data and utility programs needed for +reproducing the executable from it. However, as a special exception, +the materials to be distributed need not include anything that is +normally distributed (in either source or binary form) with the major +components (compiler, kernel, and so on) of the operating system on +which the executable runs, unless that component itself accompanies +the executable. + + It may happen that this requirement contradicts the license +restrictions of other proprietary libraries that do not normally +accompany the operating system. Such a contradiction means you cannot +use both them and the Library together in an executable that you +distribute. + + 7. You may place library facilities that are a work based on the +Library side-by-side in a single library together with other library +facilities not covered by this License, and distribute such a combined +library, provided that the separate distribution of the work based on +the Library and of the other library facilities is otherwise +permitted, and provided that you do these two things: + + a) Accompany the combined library with a copy of the same work + based on the Library, uncombined with any other library + facilities. This must be distributed under the terms of the + Sections above. + + b) Give prominent notice with the combined library of the fact + that part of it is a work based on the Library, and explaining + where to find the accompanying uncombined form of the same work. + + 8. You may not copy, modify, sublicense, link with, or distribute +the Library except as expressly provided under this License. Any +attempt otherwise to copy, modify, sublicense, link with, or +distribute the Library is void, and will automatically terminate your +rights under this License. However, parties who have received copies, +or rights, from you under this License will not have their licenses +terminated so long as such parties remain in full compliance. + + 9. You are not required to accept this License, since you have not +signed it. However, nothing else grants you permission to modify or +distribute the Library or its derivative works. These actions are +prohibited by law if you do not accept this License. Therefore, by +modifying or distributing the Library (or any work based on the +Library), you indicate your acceptance of this License to do so, and +all its terms and conditions for copying, distributing or modifying +the Library or works based on it. + + 10. Each time you redistribute the Library (or any work based on the +Library), the recipient automatically receives a license from the +original licensor to copy, distribute, link with or modify the Library +subject to these terms and conditions. You may not impose any further +restrictions on the recipients' exercise of the rights granted herein. +You are not responsible for enforcing compliance by third parties with +this License. + + 11. If, as a consequence of a court judgment or allegation of patent +infringement or for any other reason (not limited to patent issues), +conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot +distribute so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you +may not distribute the Library at all. For example, if a patent +license would not permit royalty-free redistribution of the Library by +all those who receive copies directly or indirectly through you, then +the only way you could satisfy both it and this License would be to +refrain entirely from distribution of the Library. + +If any portion of this section is held invalid or unenforceable under any +particular circumstance, the balance of the section is intended to apply, +and the section as a whole is intended to apply in other circumstances. + +It is not the purpose of this section to induce you to infringe any +patents or other property right claims or to contest validity of any +such claims; this section has the sole purpose of protecting the +integrity of the free software distribution system which is +implemented by public license practices. Many people have made +generous contributions to the wide range of software distributed +through that system in reliance on consistent application of that +system; it is up to the author/donor to decide if he or she is willing +to distribute software through any other system and a licensee cannot +impose that choice. + +This section is intended to make thoroughly clear what is believed to +be a consequence of the rest of this License. + + 12. If the distribution and/or use of the Library is restricted in +certain countries either by patents or by copyrighted interfaces, the +original copyright holder who places the Library under this License may add +an explicit geographical distribution limitation excluding those countries, +so that distribution is permitted only in or among countries not thus +excluded. In such case, this License incorporates the limitation as if +written in the body of this License. + + 13. The Free Software Foundation may publish revised and/or new +versions of the Lesser General Public License from time to time. +Such new versions will be similar in spirit to the present version, +but may differ in detail to address new problems or concerns. + +Each version is given a distinguishing version number. If the Library +specifies a version number of this License which applies to it and +"any later version", you have the option of following the terms and +conditions either of that version or of any later version published by +the Free Software Foundation. If the Library does not specify a +license version number, you may choose any version ever published by +the Free Software Foundation. + + 14. If you wish to incorporate parts of the Library into other free +programs whose distribution conditions are incompatible with these, +write to the author to ask for permission. For software which is +copyrighted by the Free Software Foundation, write to the Free +Software Foundation; we sometimes make exceptions for this. Our +decision will be guided by the two goals of preserving the free status +of all derivatives of our free software and of promoting the sharing +and reuse of software generally. + + NO WARRANTY + + 15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO +WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW. +EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR +OTHER PARTIES PROVIDE THE LIBRARY "AS IS" WITHOUT WARRANTY OF ANY +KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE +LIBRARY IS WITH YOU. SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME +THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. + + 16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN +WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY +AND/OR REDISTRIBUTE THE LIBRARY AS PERMITTED ABOVE, BE LIABLE TO YOU +FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR +CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE +LIBRARY (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING +RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A +FAILURE OF THE LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF +SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH +DAMAGES. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Libraries + + If you develop a new library, and you want it to be of the greatest +possible use to the public, we recommend making it free software that +everyone can redistribute and change. You can do so by permitting +redistribution under these terms (or, alternatively, under the terms of the +ordinary General Public License). + + To apply these terms, attach the following notices to the library. It is +safest to attach them to the start of each source file to most effectively +convey the exclusion of warranty; and each file should have at least the +"copyright" line and a pointer to where the full notice is found. + + + Copyright (C) + + 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 + +Also add information on how to contact you by electronic and paper mail. + +You should also get your employer (if you work as a programmer) or your +school, if any, to sign a "copyright disclaimer" for the library, if +necessary. Here is a sample; alter the names: + + Yoyodyne, Inc., hereby disclaims all copyright interest in the + library `Frob' (a library for tweaking knobs) written by James Random Hacker. + + , 1 April 1990 + Ty Coon, President of Vice + +That's all there is to it! diff --git a/tools/gator/daemon/libsensors/access.c b/tools/gator/daemon/libsensors/access.c new file mode 100644 index 00000000000..8e227e2550d --- /dev/null +++ b/tools/gator/daemon/libsensors/access.c @@ -0,0 +1,561 @@ +/* + access.c - Part of libsensors, a Linux library for reading sensor data. + Copyright (c) 1998, 1999 Frodo Looijaard + Copyright (C) 2007-2009 Jean Delvare + + 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 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 +#include +#include +#include "access.h" +#include "sensors.h" +#include "data.h" +#include "error.h" +#include "sysfs.h" + +/* We watch the recursion depth for variables only, as an easy way to + detect cycles. */ +#define DEPTH_MAX 8 + +static int sensors_eval_expr(const sensors_chip_features *chip_features, + const sensors_expr *expr, + double val, int depth, double *result); + +/* Compare two chips name descriptions, to see whether they could match. + Return 0 if it does not match, return 1 if it does match. */ +static int sensors_match_chip(const sensors_chip_name *chip1, + const sensors_chip_name *chip2) +{ + if ((chip1->prefix != SENSORS_CHIP_NAME_PREFIX_ANY) && + (chip2->prefix != SENSORS_CHIP_NAME_PREFIX_ANY) && + strcmp(chip1->prefix, chip2->prefix)) + return 0; + + if ((chip1->bus.type != SENSORS_BUS_TYPE_ANY) && + (chip2->bus.type != SENSORS_BUS_TYPE_ANY) && + (chip1->bus.type != chip2->bus.type)) + return 0; + + if ((chip1->bus.nr != SENSORS_BUS_NR_ANY) && + (chip2->bus.nr != SENSORS_BUS_NR_ANY) && + (chip1->bus.nr != chip2->bus.nr)) + return 0; + + if ((chip1->addr != chip2->addr) && + (chip1->addr != SENSORS_CHIP_NAME_ADDR_ANY) && + (chip2->addr != SENSORS_CHIP_NAME_ADDR_ANY)) + return 0; + + return 1; +} + +/* Returns, one by one, a pointer to all sensor_chip structs of the + config file which match with the given chip name. Last should be + the value returned by the last call, or NULL if this is the first + call. Returns NULL if no more matches are found. Do not modify + the struct the return value points to! + Note that this visits the list of chips from last to first. Usually, + you want the match that was latest in the config file. */ +static sensors_chip * +sensors_for_all_config_chips(const sensors_chip_name *name, + const sensors_chip *last) +{ + int nr, i; + sensors_chip_name_list chips; + + for (nr = last ? last - sensors_config_chips - 1 : + sensors_config_chips_count - 1; nr >= 0; nr--) { + + chips = sensors_config_chips[nr].chips; + for (i = 0; i < chips.fits_count; i++) { + if (sensors_match_chip(&chips.fits[i], name)) + return sensors_config_chips + nr; + } + } + return NULL; +} + +/* Look up a chip in the intern chip list, and return a pointer to it. + Do not modify the struct the return value points to! Returns NULL if + not found.*/ +static const sensors_chip_features * +sensors_lookup_chip(const sensors_chip_name *name) +{ + int i; + + for (i = 0; i < sensors_proc_chips_count; i++) + if (sensors_match_chip(&sensors_proc_chips[i].chip, name)) + return &sensors_proc_chips[i]; + + return NULL; +} + +/* Look up a subfeature of the given chip, and return a pointer to it. + Do not modify the struct the return value points to! Returns NULL if + not found.*/ +static const sensors_subfeature * +sensors_lookup_subfeature_nr(const sensors_chip_features *chip, + int subfeat_nr) +{ + if (subfeat_nr < 0 || + subfeat_nr >= chip->subfeature_count) + return NULL; + return chip->subfeature + subfeat_nr; +} + +/* Look up a feature of the given chip, and return a pointer to it. + Do not modify the struct the return value points to! Returns NULL if + not found.*/ +static const sensors_feature * +sensors_lookup_feature_nr(const sensors_chip_features *chip, int feat_nr) +{ + if (feat_nr < 0 || + feat_nr >= chip->feature_count) + return NULL; + return chip->feature + feat_nr; +} + +/* Look up a subfeature by name, and return a pointer to it. + Do not modify the struct the return value points to! Returns NULL if + not found.*/ +static const sensors_subfeature * +sensors_lookup_subfeature_name(const sensors_chip_features *chip, + const char *name) +{ + int j; + + for (j = 0; j < chip->subfeature_count; j++) + if (!strcmp(chip->subfeature[j].name, name)) + return chip->subfeature + j; + return NULL; +} + +/* Check whether the chip name is an 'absolute' name, which can only match + one chip, or whether it has wildcards. Returns 0 if it is absolute, 1 + if there are wildcards. */ +int sensors_chip_name_has_wildcards(const sensors_chip_name *chip) +{ + if ((chip->prefix == SENSORS_CHIP_NAME_PREFIX_ANY) || + (chip->bus.type == SENSORS_BUS_TYPE_ANY) || + (chip->bus.nr == SENSORS_BUS_NR_ANY) || + (chip->addr == SENSORS_CHIP_NAME_ADDR_ANY)) + return 1; + else + return 0; +} + +/* Look up the label for a given feature. Note that chip should not + contain wildcard values! The returned string is newly allocated (free it + yourself). On failure, NULL is returned. + If no label exists for this feature, its name is returned itself. */ +char *sensors_get_label(const sensors_chip_name *name, + const sensors_feature *feature) +{ + char *label; + const sensors_chip *chip; + char buf[PATH_MAX]; + FILE *f; + int i; + + if (sensors_chip_name_has_wildcards(name)) + return NULL; + + for (chip = NULL; (chip = sensors_for_all_config_chips(name, chip));) + for (i = 0; i < chip->labels_count; i++) + if (!strcmp(feature->name, chip->labels[i].name)) { + label = chip->labels[i].value; + goto sensors_get_label_exit; + } + + /* No user specified label, check for a _label sysfs file */ + snprintf(buf, PATH_MAX, "%s/%s_label", name->path, feature->name); + + if ((f = fopen(buf, "r"))) { + i = fread(buf, 1, sizeof(buf), f); + fclose(f); + if (i > 0) { + /* i - 1 to strip the '\n' at the end */ + buf[i - 1] = 0; + label = buf; + goto sensors_get_label_exit; + } + } + + /* No label, return the feature name instead */ + label = feature->name; + +sensors_get_label_exit: + label = strdup(label); + if (!label) + sensors_fatal_error(__func__, "Allocating label text"); + return label; +} + +/* Looks up whether a feature should be ignored. Returns + 1 if it should be ignored, 0 if not. */ +static int sensors_get_ignored(const sensors_chip_name *name, + const sensors_feature *feature) +{ + const sensors_chip *chip; + int i; + + for (chip = NULL; (chip = sensors_for_all_config_chips(name, chip));) + for (i = 0; i < chip->ignores_count; i++) + if (!strcmp(feature->name, chip->ignores[i].name)) + return 1; + return 0; +} + +/* Read the value of a subfeature of a certain chip. Note that chip should not + contain wildcard values! This function will return 0 on success, and <0 + on failure. */ +static int __sensors_get_value(const sensors_chip_name *name, int subfeat_nr, + int depth, double *result) +{ + const sensors_chip_features *chip_features; + const sensors_subfeature *subfeature; + const sensors_expr *expr = NULL; + double val; + int res, i; + + if (depth >= DEPTH_MAX) + return -SENSORS_ERR_RECURSION; + if (sensors_chip_name_has_wildcards(name)) + return -SENSORS_ERR_WILDCARDS; + if (!(chip_features = sensors_lookup_chip(name))) + return -SENSORS_ERR_NO_ENTRY; + if (!(subfeature = sensors_lookup_subfeature_nr(chip_features, + subfeat_nr))) + return -SENSORS_ERR_NO_ENTRY; + if (!(subfeature->flags & SENSORS_MODE_R)) + return -SENSORS_ERR_ACCESS_R; + + /* Apply compute statement if it exists */ + if (subfeature->flags & SENSORS_COMPUTE_MAPPING) { + const sensors_feature *feature; + const sensors_chip *chip; + + feature = sensors_lookup_feature_nr(chip_features, + subfeature->mapping); + + chip = NULL; + while (!expr && + (chip = sensors_for_all_config_chips(name, chip))) + for (i = 0; i < chip->computes_count; i++) { + if (!strcmp(feature->name, + chip->computes[i].name)) { + expr = chip->computes[i].from_proc; + break; + } + } + } + + res = sensors_read_sysfs_attr(name, subfeature, &val); + if (res) + return res; + if (!expr) + *result = val; + else if ((res = sensors_eval_expr(chip_features, expr, val, depth, + result))) + return res; + return 0; +} + +int sensors_get_value(const sensors_chip_name *name, int subfeat_nr, + double *result) +{ + return __sensors_get_value(name, subfeat_nr, 0, result); +} + +/* Set the value of a subfeature of a certain chip. Note that chip should not + contain wildcard values! This function will return 0 on success, and <0 + on failure. */ +int sensors_set_value(const sensors_chip_name *name, int subfeat_nr, + double value) +{ + const sensors_chip_features *chip_features; + const sensors_subfeature *subfeature; + const sensors_expr *expr = NULL; + int i, res; + double to_write; + + if (sensors_chip_name_has_wildcards(name)) + return -SENSORS_ERR_WILDCARDS; + if (!(chip_features = sensors_lookup_chip(name))) + return -SENSORS_ERR_NO_ENTRY; + if (!(subfeature = sensors_lookup_subfeature_nr(chip_features, + subfeat_nr))) + return -SENSORS_ERR_NO_ENTRY; + if (!(subfeature->flags & SENSORS_MODE_W)) + return -SENSORS_ERR_ACCESS_W; + + /* Apply compute statement if it exists */ + if (subfeature->flags & SENSORS_COMPUTE_MAPPING) { + const sensors_feature *feature; + const sensors_chip *chip; + + feature = sensors_lookup_feature_nr(chip_features, + subfeature->mapping); + + chip = NULL; + while (!expr && + (chip = sensors_for_all_config_chips(name, chip))) + for (i = 0; i < chip->computes_count; i++) { + if (!strcmp(feature->name, + chip->computes[i].name)) { + expr = chip->computes[i].to_proc; + break; + } + } + } + + to_write = value; + if (expr) + if ((res = sensors_eval_expr(chip_features, expr, + value, 0, &to_write))) + return res; + return sensors_write_sysfs_attr(name, subfeature, to_write); +} + +const sensors_chip_name *sensors_get_detected_chips(const sensors_chip_name + *match, int *nr) +{ + const sensors_chip_name *res; + + while (*nr < sensors_proc_chips_count) { + res = &sensors_proc_chips[(*nr)++].chip; + if (!match || sensors_match_chip(res, match)) + return res; + } + return NULL; +} + +const char *sensors_get_adapter_name(const sensors_bus_id *bus) +{ + int i; + + /* bus types with a single instance */ + switch (bus->type) { + case SENSORS_BUS_TYPE_ISA: + return "ISA adapter"; + case SENSORS_BUS_TYPE_PCI: + return "PCI adapter"; + /* SPI should not be here, but for now SPI adapters have no name + so we don't have any custom string to return. */ + case SENSORS_BUS_TYPE_SPI: + return "SPI adapter"; + case SENSORS_BUS_TYPE_VIRTUAL: + return "Virtual device"; + case SENSORS_BUS_TYPE_ACPI: + return "ACPI interface"; + /* HID should probably not be there either, but I don't know if + HID buses have a name nor where to find it. */ + case SENSORS_BUS_TYPE_HID: + return "HID adapter"; + } + + /* bus types with several instances */ + for (i = 0; i < sensors_proc_bus_count; i++) + if (sensors_proc_bus[i].bus.type == bus->type && + sensors_proc_bus[i].bus.nr == bus->nr) + return sensors_proc_bus[i].adapter; + return NULL; +} + +const sensors_feature * +sensors_get_features(const sensors_chip_name *name, int *nr) +{ + const sensors_chip_features *chip; + + if (!(chip = sensors_lookup_chip(name))) + return NULL; /* No such chip */ + + while (*nr < chip->feature_count + && sensors_get_ignored(name, &chip->feature[*nr])) + (*nr)++; + if (*nr >= chip->feature_count) + return NULL; + return &chip->feature[(*nr)++]; +} + +const sensors_subfeature * +sensors_get_all_subfeatures(const sensors_chip_name *name, + const sensors_feature *feature, int *nr) +{ + const sensors_chip_features *chip; + const sensors_subfeature *subfeature; + + if (!(chip = sensors_lookup_chip(name))) + return NULL; /* No such chip */ + + /* Seek directly to the first subfeature */ + if (*nr < feature->first_subfeature) + *nr = feature->first_subfeature; + + if (*nr >= chip->subfeature_count) + return NULL; /* end of list */ + subfeature = &chip->subfeature[(*nr)++]; + if (subfeature->mapping == feature->number) + return subfeature; + return NULL; /* end of subfeature list */ +} + +const sensors_subfeature * +sensors_get_subfeature(const sensors_chip_name *name, + const sensors_feature *feature, + sensors_subfeature_type type) +{ + const sensors_chip_features *chip; + int i; + + if (!(chip = sensors_lookup_chip(name))) + return NULL; /* No such chip */ + + for (i = feature->first_subfeature; i < chip->subfeature_count && + chip->subfeature[i].mapping == feature->number; i++) { + if (chip->subfeature[i].type == type) + return &chip->subfeature[i]; + } + return NULL; /* No such subfeature */ +} + +/* Evaluate an expression */ +int sensors_eval_expr(const sensors_chip_features *chip_features, + const sensors_expr *expr, + double val, int depth, double *result) +{ + double res1, res2; + int res; + const sensors_subfeature *subfeature; + + if (expr->kind == sensors_kind_val) { + *result = expr->data.val; + return 0; + } + if (expr->kind == sensors_kind_source) { + *result = val; + return 0; + } + if (expr->kind == sensors_kind_var) { + if (!(subfeature = sensors_lookup_subfeature_name(chip_features, + expr->data.var))) + return -SENSORS_ERR_NO_ENTRY; + return __sensors_get_value(&chip_features->chip, + subfeature->number, depth + 1, + result); + } + if ((res = sensors_eval_expr(chip_features, expr->data.subexpr.sub1, + val, depth, &res1))) + return res; + if (expr->data.subexpr.sub2 && + (res = sensors_eval_expr(chip_features, expr->data.subexpr.sub2, + val, depth, &res2))) + return res; + switch (expr->data.subexpr.op) { + case sensors_add: + *result = res1 + res2; + return 0; + case sensors_sub: + *result = res1 - res2; + return 0; + case sensors_multiply: + *result = res1 * res2; + return 0; + case sensors_divide: + if (res2 == 0.0) + return -SENSORS_ERR_DIV_ZERO; + *result = res1 / res2; + return 0; + case sensors_negate: + *result = -res1; + return 0; + case sensors_exp: + *result = exp(res1); + return 0; + case sensors_log: + if (res1 < 0.0) + return -SENSORS_ERR_DIV_ZERO; + *result = log(res1); + return 0; + } + return 0; +} + +/* Execute all set statements for this particular chip. The chip may not + contain wildcards! This function will return 0 on success, and <0 on + failure. */ +static int sensors_do_this_chip_sets(const sensors_chip_name *name) +{ + const sensors_chip_features *chip_features; + sensors_chip *chip; + double value; + int i; + int err = 0, res; + const sensors_subfeature *subfeature; + + chip_features = sensors_lookup_chip(name); /* Can't fail */ + + for (chip = NULL; (chip = sensors_for_all_config_chips(name, chip));) + for (i = 0; i < chip->sets_count; i++) { + subfeature = sensors_lookup_subfeature_name(chip_features, + chip->sets[i].name); + if (!subfeature) { + sensors_parse_error_wfn("Unknown feature name", + chip->sets[i].line.filename, + chip->sets[i].line.lineno); + err = -SENSORS_ERR_NO_ENTRY; + continue; + } + + res = sensors_eval_expr(chip_features, + chip->sets[i].value, 0, + 0, &value); + if (res) { + sensors_parse_error_wfn("Error parsing expression", + chip->sets[i].line.filename, + chip->sets[i].line.lineno); + err = res; + continue; + } + if ((res = sensors_set_value(name, subfeature->number, + value))) { + sensors_parse_error_wfn("Failed to set value", + chip->sets[i].line.filename, + chip->sets[i].line.lineno); + err = res; + continue; + } + } + return err; +} + +/* Execute all set statements for this particular chip. The chip may contain + wildcards! This function will return 0 on success, and <0 on failure. */ +int sensors_do_chip_sets(const sensors_chip_name *name) +{ + int nr, this_res; + const sensors_chip_name *found_name; + int res = 0; + + for (nr = 0; (found_name = sensors_get_detected_chips(name, &nr));) { + this_res = sensors_do_this_chip_sets(found_name); + if (this_res) + res = this_res; + } + return res; +} diff --git a/tools/gator/daemon/libsensors/access.h b/tools/gator/daemon/libsensors/access.h new file mode 100644 index 00000000000..1d378434075 --- /dev/null +++ b/tools/gator/daemon/libsensors/access.h @@ -0,0 +1,33 @@ +/* + access.h - Part of libsensors, a Linux library for reading sensor data. + Copyright (c) 1998, 1999 Frodo Looijaard + Copyright (C) 2007 Jean Delvare + + 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 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 LIB_SENSORS_ACCESS_H +#define LIB_SENSORS_ACCESS_H + +#include "sensors.h" +#include "data.h" + +/* Check whether the chip name is an 'absolute' name, which can only match + one chip, or whether it has wildcards. Returns 0 if it is absolute, 1 + if there are wildcards. */ +int sensors_chip_name_has_wildcards(const sensors_chip_name *chip); + +#endif /* def LIB_SENSORS_ACCESS_H */ diff --git a/tools/gator/daemon/libsensors/conf-lex.c b/tools/gator/daemon/libsensors/conf-lex.c new file mode 100644 index 00000000000..a54664b3d77 --- /dev/null +++ b/tools/gator/daemon/libsensors/conf-lex.c @@ -0,0 +1,2881 @@ + +#line 3 "" + +#define YY_INT_ALIGNED short int + +/* A lexical scanner generated by flex */ + +#define yy_create_buffer sensors_yy_create_buffer +#define yy_delete_buffer sensors_yy_delete_buffer +#define yy_flex_debug sensors_yy_flex_debug +#define yy_init_buffer sensors_yy_init_buffer +#define yy_flush_buffer sensors_yy_flush_buffer +#define yy_load_buffer_state sensors_yy_load_buffer_state +#define yy_switch_to_buffer sensors_yy_switch_to_buffer +#define yyin sensors_yyin +#define yyleng sensors_yyleng +#define yylex sensors_yylex +#define yylineno sensors_yylineno +#define yyout sensors_yyout +#define yyrestart sensors_yyrestart +#define yytext sensors_yytext +#define yywrap sensors_yywrap +#define yyalloc sensors_yyalloc +#define yyrealloc sensors_yyrealloc +#define yyfree sensors_yyfree + +#define FLEX_SCANNER +#define YY_FLEX_MAJOR_VERSION 2 +#define YY_FLEX_MINOR_VERSION 5 +#define YY_FLEX_SUBMINOR_VERSION 35 +#if YY_FLEX_SUBMINOR_VERSION > 0 +#define FLEX_BETA +#endif + +/* First, we deal with platform-specific or compiler-specific issues. */ + +/* begin standard C headers. */ +#include +#include +#include +#include + +/* end standard C headers. */ + +/* flex integer type definitions */ + +#ifndef FLEXINT_H +#define FLEXINT_H + +/* C99 systems have . Non-C99 systems may or may not. */ + +#if defined (__STDC_VERSION__) && __STDC_VERSION__ >= 199901L + +/* C99 says to define __STDC_LIMIT_MACROS before including stdint.h, + * if you want the limit (max/min) macros for int types. + */ +#ifndef __STDC_LIMIT_MACROS +#define __STDC_LIMIT_MACROS 1 +#endif + +#include +typedef int8_t flex_int8_t; +typedef uint8_t flex_uint8_t; +typedef int16_t flex_int16_t; +typedef uint16_t flex_uint16_t; +typedef int32_t flex_int32_t; +typedef uint32_t flex_uint32_t; +#else +typedef signed char flex_int8_t; +typedef short int flex_int16_t; +typedef int flex_int32_t; +typedef unsigned char flex_uint8_t; +typedef unsigned short int flex_uint16_t; +typedef unsigned int flex_uint32_t; + +/* Limits of integral types. */ +#ifndef INT8_MIN +#define INT8_MIN (-128) +#endif +#ifndef INT16_MIN +#define INT16_MIN (-32767-1) +#endif +#ifndef INT32_MIN +#define INT32_MIN (-2147483647-1) +#endif +#ifndef INT8_MAX +#define INT8_MAX (127) +#endif +#ifndef INT16_MAX +#define INT16_MAX (32767) +#endif +#ifndef INT32_MAX +#define INT32_MAX (2147483647) +#endif +#ifndef UINT8_MAX +#define UINT8_MAX (255U) +#endif +#ifndef UINT16_MAX +#define UINT16_MAX (65535U) +#endif +#ifndef UINT32_MAX +#define UINT32_MAX (4294967295U) +#endif + +#endif /* ! C99 */ + +#endif /* ! FLEXINT_H */ + +#ifdef __cplusplus + +/* The "const" storage-class-modifier is valid. */ +#define YY_USE_CONST + +#else /* ! __cplusplus */ + +/* C99 requires __STDC__ to be defined as 1. */ +#if defined (__STDC__) + +#define YY_USE_CONST + +#endif /* defined (__STDC__) */ +#endif /* ! __cplusplus */ + +#ifdef YY_USE_CONST +#define yyconst const +#else +#define yyconst +#endif + +/* Returned upon end-of-file. */ +#define YY_NULL 0 + +/* Promotes a possibly negative, possibly signed char to an unsigned + * integer for use as an array index. If the signed char is negative, + * we want to instead treat it as an 8-bit unsigned char, hence the + * double cast. + */ +#define YY_SC_TO_UI(c) ((unsigned int) (unsigned char) c) + +/* Enter a start condition. This macro really ought to take a parameter, + * but we do it the disgusting crufty way forced on us by the ()-less + * definition of BEGIN. + */ +#define BEGIN (yy_start) = 1 + 2 * + +/* Translate the current start state into a value that can be later handed + * to BEGIN to return to the state. The YYSTATE alias is for lex + * compatibility. + */ +#define YY_START (((yy_start) - 1) / 2) +#define YYSTATE YY_START + +/* Action number for EOF rule of a given start state. */ +#define YY_STATE_EOF(state) (YY_END_OF_BUFFER + state + 1) + +/* Special action meaning "start processing a new file". */ +#define YY_NEW_FILE sensors_yyrestart(sensors_yyin ) + +#define YY_END_OF_BUFFER_CHAR 0 + +/* Size of default input buffer. */ +#ifndef YY_BUF_SIZE +#ifdef __ia64__ +/* On IA-64, the buffer size is 16k, not 8k. + * Moreover, YY_BUF_SIZE is 2*YY_READ_BUF_SIZE in the general case. + * Ditto for the __ia64__ case accordingly. + */ +#define YY_BUF_SIZE 32768 +#else +#define YY_BUF_SIZE 16384 +#endif /* __ia64__ */ +#endif + +/* The state buf must be large enough to hold one state per character in the main buffer. + */ +#define YY_STATE_BUF_SIZE ((YY_BUF_SIZE + 2) * sizeof(yy_state_type)) + +#ifndef YY_TYPEDEF_YY_BUFFER_STATE +#define YY_TYPEDEF_YY_BUFFER_STATE +typedef struct yy_buffer_state *YY_BUFFER_STATE; +#endif + +extern int sensors_yyleng; + +extern FILE *sensors_yyin, *sensors_yyout; + +#define EOB_ACT_CONTINUE_SCAN 0 +#define EOB_ACT_END_OF_FILE 1 +#define EOB_ACT_LAST_MATCH 2 + + #define YY_LESS_LINENO(n) + +/* Return all but the first "n" matched characters back to the input stream. */ +#define yyless(n) \ + do \ + { \ + /* Undo effects of setting up sensors_yytext. */ \ + int yyless_macro_arg = (n); \ + YY_LESS_LINENO(yyless_macro_arg);\ + *yy_cp = (yy_hold_char); \ + YY_RESTORE_YY_MORE_OFFSET \ + (yy_c_buf_p) = yy_cp = yy_bp + yyless_macro_arg - YY_MORE_ADJ; \ + YY_DO_BEFORE_ACTION; /* set up sensors_yytext again */ \ + } \ + while ( 0 ) + +#define unput(c) yyunput( c, (yytext_ptr) ) + +#ifndef YY_TYPEDEF_YY_SIZE_T +#define YY_TYPEDEF_YY_SIZE_T +typedef size_t yy_size_t; +#endif + +#ifndef YY_STRUCT_YY_BUFFER_STATE +#define YY_STRUCT_YY_BUFFER_STATE +struct yy_buffer_state + { + FILE *yy_input_file; + + char *yy_ch_buf; /* input buffer */ + char *yy_buf_pos; /* current position in input buffer */ + + /* Size of input buffer in bytes, not including room for EOB + * characters. + */ + yy_size_t yy_buf_size; + + /* Number of characters read into yy_ch_buf, not including EOB + * characters. + */ + int yy_n_chars; + + /* Whether we "own" the buffer - i.e., we know we created it, + * and can realloc() it to grow it, and should free() it to + * delete it. + */ + int yy_is_our_buffer; + + /* Whether this is an "interactive" input source; if so, and + * if we're using stdio for input, then we want to use getc() + * instead of fread(), to make sure we stop fetching input after + * each newline. + */ + int yy_is_interactive; + + /* Whether we're considered to be at the beginning of a line. + * If so, '^' rules will be active on the next match, otherwise + * not. + */ + int yy_at_bol; + + int yy_bs_lineno; /**< The line count. */ + int yy_bs_column; /**< The column count. */ + + /* Whether to try to fill the input buffer when we reach the + * end of it. + */ + int yy_fill_buffer; + + int yy_buffer_status; + +#define YY_BUFFER_NEW 0 +#define YY_BUFFER_NORMAL 1 + /* When an EOF's been seen but there's still some text to process + * then we mark the buffer as YY_EOF_PENDING, to indicate that we + * shouldn't try reading from the input source any more. We might + * still have a bunch of tokens to match, though, because of + * possible backing-up. + * + * When we actually see the EOF, we change the status to "new" + * (via sensors_yyrestart()), so that the user can continue scanning by + * just pointing sensors_yyin at a new input file. + */ +#define YY_BUFFER_EOF_PENDING 2 + + }; +#endif /* !YY_STRUCT_YY_BUFFER_STATE */ + +/* Stack of input buffers. */ +static size_t yy_buffer_stack_top = 0; /**< index of top of stack. */ +static size_t yy_buffer_stack_max = 0; /**< capacity of stack. */ +static YY_BUFFER_STATE * yy_buffer_stack = 0; /**< Stack as an array. */ + +/* We provide macros for accessing buffer states in case in the + * future we want to put the buffer states in a more general + * "scanner state". + * + * Returns the top of the stack, or NULL. + */ +#define YY_CURRENT_BUFFER ( (yy_buffer_stack) \ + ? (yy_buffer_stack)[(yy_buffer_stack_top)] \ + : NULL) + +/* Same as previous macro, but useful when we know that the buffer stack is not + * NULL or when we need an lvalue. For internal use only. + */ +#define YY_CURRENT_BUFFER_LVALUE (yy_buffer_stack)[(yy_buffer_stack_top)] + +/* yy_hold_char holds the character lost when sensors_yytext is formed. */ +static char yy_hold_char; +static int yy_n_chars; /* number of characters read into yy_ch_buf */ +int sensors_yyleng; + +/* Points to current character in buffer. */ +static char *yy_c_buf_p = (char *) 0; +static int yy_init = 0; /* whether we need to initialize */ +static int yy_start = 0; /* start state number */ + +/* Flag which is used to allow sensors_yywrap()'s to do buffer switches + * instead of setting up a fresh sensors_yyin. A bit of a hack ... + */ +static int yy_did_buffer_switch_on_eof; + +void sensors_yyrestart (FILE *input_file ); +void sensors_yy_switch_to_buffer (YY_BUFFER_STATE new_buffer ); +YY_BUFFER_STATE sensors_yy_create_buffer (FILE *file,int size ); +void sensors_yy_delete_buffer (YY_BUFFER_STATE b ); +void sensors_yy_flush_buffer (YY_BUFFER_STATE b ); +void sensors_yypush_buffer_state (YY_BUFFER_STATE new_buffer ); +void sensors_yypop_buffer_state (void ); + +static void sensors_yyensure_buffer_stack (void ); +static void sensors_yy_load_buffer_state (void ); +static void sensors_yy_init_buffer (YY_BUFFER_STATE b,FILE *file ); + +#define YY_FLUSH_BUFFER sensors_yy_flush_buffer(YY_CURRENT_BUFFER ) + +YY_BUFFER_STATE sensors_yy_scan_buffer (char *base,yy_size_t size ); +YY_BUFFER_STATE sensors_yy_scan_string (yyconst char *yy_str ); +YY_BUFFER_STATE sensors_yy_scan_bytes (yyconst char *bytes,int len ); + +void *sensors_yyalloc (yy_size_t ); +void *sensors_yyrealloc (void *,yy_size_t ); +void sensors_yyfree (void * ); + +#define yy_new_buffer sensors_yy_create_buffer + +#define yy_set_interactive(is_interactive) \ + { \ + if ( ! YY_CURRENT_BUFFER ){ \ + sensors_yyensure_buffer_stack (); \ + YY_CURRENT_BUFFER_LVALUE = \ + sensors_yy_create_buffer(sensors_yyin,YY_BUF_SIZE ); \ + } \ + YY_CURRENT_BUFFER_LVALUE->yy_is_interactive = is_interactive; \ + } + +#define yy_set_bol(at_bol) \ + { \ + if ( ! YY_CURRENT_BUFFER ){\ + sensors_yyensure_buffer_stack (); \ + YY_CURRENT_BUFFER_LVALUE = \ + sensors_yy_create_buffer(sensors_yyin,YY_BUF_SIZE ); \ + } \ + YY_CURRENT_BUFFER_LVALUE->yy_at_bol = at_bol; \ + } + +#define YY_AT_BOL() (YY_CURRENT_BUFFER_LVALUE->yy_at_bol) + +/* Begin user sect3 */ + +#define sensors_yywrap(n) 1 +#define YY_SKIP_YYWRAP + +typedef unsigned char YY_CHAR; + +FILE *sensors_yyin = (FILE *) 0, *sensors_yyout = (FILE *) 0; + +typedef int yy_state_type; + +extern int sensors_yylineno; + +int sensors_yylineno = 1; + +extern char *sensors_yytext; +#define yytext_ptr sensors_yytext +static yyconst flex_int16_t yy_nxt[][39] = + { + { + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0 + }, + + { + 9, 10, 11, 12, 10, 13, 10, 10, 10, 10, + 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, + 14, 15, 16, 14, 14, 14, 14, 14, 17, 18, + 14, 14, 14, 14, 14, 19, 14, 14, 14 + }, + + { + 9, 10, 11, 12, 10, 13, 10, 10, 10, 10, + 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, + + 14, 15, 16, 14, 14, 14, 14, 14, 17, 18, + 14, 14, 14, 14, 14, 19, 14, 14, 14 + }, + + { + 9, 20, 21, 22, 23, 24, 25, 26, 27, 28, + 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, + 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, + 35, 35, 35, 35, 35, 35, 35, 35, 35 + }, + + { + 9, 20, 21, 22, 23, 24, 25, 26, 27, 28, + 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, + 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, + 35, 35, 35, 35, 35, 35, 35, 35, 35 + + }, + + { + 9, 39, 39, 40, 41, 39, 39, 39, 39, 39, + 39, 39, 39, 39, 39, 39, 39, 42, 39, 39, + 39, 39, 39, 39, 39, 39, 39, 39, 39, 39, + 39, 39, 39, 39, 39, 39, 39, 39, 39 + }, + + { + 9, 39, 39, 40, 41, 39, 39, 39, 39, 39, + 39, 39, 39, 39, 39, 39, 39, 42, 39, 39, + 39, 39, 39, 39, 39, 39, 39, 39, 39, 39, + 39, 39, 39, 39, 39, 39, 39, 39, 39 + }, + + { + 9, 43, 43, 44, 43, 43, 43, 43, 43, 43, + 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, + + 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, + 43, 43, 43, 43, 43, 43, 43, 43, 43 + }, + + { + 9, 43, 43, 44, 43, 43, 43, 43, 43, 43, + 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, + 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, + 43, 43, 43, 43, 43, 43, 43, 43, 43 + }, + + { + -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, + -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, + -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, + -9, -9, -9, -9, -9, -9, -9, -9, -9 + + }, + + { + 9, -10, -10, -10, -10, -10, -10, -10, -10, -10, + -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, + -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, + -10, -10, -10, -10, -10, -10, -10, -10, -10 + }, + + { + 9, -11, 45, 46, -11, -11, -11, -11, -11, -11, + -11, -11, -11, -11, -11, -11, -11, -11, -11, -11, + -11, -11, -11, -11, -11, -11, -11, -11, -11, -11, + -11, -11, -11, -11, -11, -11, -11, -11, -11 + }, + + { + 9, -12, -12, -12, -12, -12, -12, -12, -12, -12, + -12, -12, -12, -12, -12, -12, -12, -12, -12, -12, + + -12, -12, -12, -12, -12, -12, -12, -12, -12, -12, + -12, -12, -12, -12, -12, -12, -12, -12, -12 + }, + + { + 9, 47, 47, 48, 47, 47, 47, 47, 47, 47, + 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, + 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, + 47, 47, 47, 47, 47, 47, 47, 47, 47 + }, + + { + 9, -14, -14, -14, -14, -14, -14, -14, -14, -14, + -14, -14, -14, -14, -14, -14, -14, -14, -14, -14, + 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, + 49, 49, 49, 49, 49, 49, 49, 49, 49 + + }, + + { + 9, -15, -15, -15, -15, -15, -15, -15, -15, -15, + -15, -15, -15, -15, -15, -15, -15, -15, -15, -15, + 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, + 49, 49, 49, 49, 49, 49, 49, 50, 49 + }, + + { + 9, -16, -16, -16, -16, -16, -16, -16, -16, -16, + -16, -16, -16, -16, -16, -16, -16, -16, -16, -16, + 49, 49, 49, 49, 49, 49, 49, 51, 49, 49, + 49, 49, 52, 49, 49, 49, 49, 49, 49 + }, + + { + 9, -17, -17, -17, -17, -17, -17, -17, -17, -17, + -17, -17, -17, -17, -17, -17, -17, -17, -17, -17, + + 49, 49, 49, 49, 49, 49, 53, 49, 49, 49, + 49, 49, 49, 49, 49, 49, 49, 49, 49 + }, + + { + 9, -18, -18, -18, -18, -18, -18, -18, -18, -18, + -18, -18, -18, -18, -18, -18, -18, -18, -18, -18, + 54, 49, 49, 49, 49, 49, 49, 49, 49, 49, + 49, 49, 49, 49, 49, 49, 49, 49, 49 + }, + + { + 9, -19, -19, -19, -19, -19, -19, -19, -19, -19, + -19, -19, -19, -19, -19, -19, -19, -19, -19, -19, + 49, 49, 49, 49, 55, 49, 49, 49, 49, 49, + 49, 49, 49, 49, 49, 49, 49, 49, 49 + + }, + + { + 9, -20, -20, -20, -20, -20, -20, -20, -20, -20, + -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, + -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, + -20, -20, -20, -20, -20, -20, -20, -20, -20 + }, + + { + 9, -21, 56, -21, -21, -21, -21, -21, -21, -21, + -21, -21, -21, -21, -21, -21, -21, -21, -21, -21, + -21, -21, -21, -21, -21, -21, -21, -21, -21, -21, + -21, -21, -21, -21, -21, -21, -21, -21, -21 + }, + + { + 9, -22, -22, -22, -22, -22, -22, -22, -22, -22, + -22, -22, -22, -22, -22, -22, -22, -22, -22, -22, + + -22, -22, -22, -22, -22, -22, -22, -22, -22, -22, + -22, -22, -22, -22, -22, -22, -22, -22, -22 + }, + + { + 9, -23, -23, -23, -23, -23, -23, -23, -23, -23, + -23, -23, -23, -23, -23, -23, -23, -23, -23, -23, + -23, -23, -23, -23, -23, -23, -23, -23, -23, -23, + -23, -23, -23, -23, -23, -23, -23, -23, -23 + }, + + { + 9, 57, 57, 58, 57, 57, 57, 57, 57, 57, + 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, + 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, + 57, 57, 57, 57, 57, 57, 57, 57, 57 + + }, + + { + 9, -25, -25, -25, -25, -25, -25, -25, -25, -25, + -25, -25, -25, -25, -25, -25, -25, -25, -25, -25, + -25, -25, -25, -25, -25, -25, -25, -25, -25, -25, + -25, -25, -25, -25, -25, -25, -25, -25, -25 + }, + + { + 9, -26, -26, -26, -26, -26, -26, -26, -26, -26, + -26, -26, -26, -26, -26, -26, -26, -26, -26, -26, + -26, -26, -26, -26, -26, -26, -26, -26, -26, -26, + -26, -26, -26, -26, -26, -26, -26, -26, -26 + }, + + { + 9, -27, -27, -27, -27, -27, -27, -27, -27, -27, + -27, -27, -27, -27, -27, -27, -27, -27, -27, -27, + + -27, -27, -27, -27, -27, -27, -27, -27, -27, -27, + -27, -27, -27, -27, -27, -27, -27, -27, -27 + }, + + { + 9, -28, -28, -28, -28, -28, -28, -28, -28, -28, + -28, -28, -28, -28, -28, -28, -28, -28, -28, -28, + -28, -28, -28, -28, -28, -28, -28, -28, -28, -28, + -28, -28, -28, -28, -28, -28, -28, -28, -28 + }, + + { + 9, -29, -29, -29, -29, -29, -29, -29, -29, -29, + -29, -29, -29, -29, -29, -29, -29, -29, -29, -29, + -29, -29, -29, -29, -29, -29, -29, -29, -29, -29, + -29, -29, -29, -29, -29, -29, -29, -29, -29 + + }, + + { + 9, -30, -30, -30, -30, -30, -30, -30, -30, -30, + -30, -30, -30, -30, -30, -30, -30, -30, -30, -30, + -30, -30, -30, -30, -30, -30, -30, -30, -30, -30, + -30, -30, -30, -30, -30, -30, -30, -30, -30 + }, + + { + 9, -31, -31, -31, -31, -31, -31, -31, -31, -31, + -31, -31, -31, -31, 59, -31, -31, -31, -31, -31, + -31, -31, -31, -31, -31, -31, -31, -31, -31, -31, + -31, -31, -31, -31, -31, -31, -31, -31, -31 + }, + + { + 9, -32, -32, -32, -32, -32, -32, -32, -32, -32, + -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, + + -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, + -32, -32, -32, -32, -32, -32, -32, -32, -32 + }, + + { + 9, -33, -33, -33, -33, -33, -33, -33, -33, -33, + -33, -33, 60, -33, 61, -33, 62, -33, -33, -33, + 62, 62, 62, 62, 62, 62, 62, 62, 62, 62, + 62, 62, 62, 62, 62, 62, 62, 62, 62 + }, + + { + 9, -34, -34, -34, -34, -34, -34, -34, -34, -34, + -34, -34, -34, -34, -34, -34, -34, -34, -34, -34, + -34, -34, -34, -34, -34, -34, -34, -34, -34, -34, + -34, -34, -34, -34, -34, -34, -34, -34, -34 + + }, + + { + 9, -35, -35, -35, -35, -35, -35, -35, -35, -35, + -35, -35, -35, -35, 62, -35, 62, -35, -35, -35, + 62, 62, 62, 62, 62, 62, 62, 62, 62, 62, + 62, 62, 62, 62, 62, 62, 62, 62, 62 + }, + + { + 9, -36, 63, 64, -36, -36, -36, -36, -36, -36, + -36, -36, -36, -36, -36, -36, -36, -36, -36, -36, + -36, -36, -36, -36, -36, -36, -36, -36, -36, -36, + -36, -36, -36, -36, -36, -36, -36, -36, -36 + }, + + { + 9, -37, -37, -37, -37, -37, -37, -37, -37, -37, + -37, -37, -37, -37, -37, -37, -37, -37, -37, -37, + + -37, -37, -37, -37, -37, -37, -37, -37, -37, -37, + -37, -37, -37, -37, -37, -37, -37, -37, -37 + }, + + { + 9, -38, -38, -38, -38, -38, -38, -38, -38, -38, + -38, -38, -38, -38, -38, -38, -38, -38, -38, -38, + -38, -38, -38, -38, -38, -38, -38, -38, -38, -38, + -38, -38, -38, -38, -38, -38, -38, -38, -38 + }, + + { + 9, 65, 65, -39, -39, 65, 65, 65, 65, 65, + 65, 65, 65, 65, 65, 65, 65, -39, 65, 65, + 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, + 65, 65, 65, 65, 65, 65, 65, 65, 65 + + }, + + { + 9, -40, -40, -40, -40, -40, -40, -40, -40, -40, + -40, -40, -40, -40, -40, -40, -40, -40, -40, -40, + -40, -40, -40, -40, -40, -40, -40, -40, -40, -40, + -40, -40, -40, -40, -40, -40, -40, -40, -40 + }, + + { + 9, -41, -41, -41, 66, -41, -41, -41, -41, -41, + -41, -41, -41, -41, -41, -41, -41, -41, -41, -41, + -41, -41, -41, -41, -41, -41, -41, -41, -41, -41, + -41, -41, -41, -41, -41, -41, -41, -41, -41 + }, + + { + 9, 67, 67, 68, 67, 67, 67, 67, 67, 67, + 67, 67, 67, 67, 67, 67, 67, 67, 67, 67, + + 69, 70, 67, 67, 67, 71, 67, 67, 67, 67, + 67, 72, 67, 67, 73, 67, 74, 67, 75 + }, + + { + 9, 76, 76, -43, 76, 76, 76, 76, 76, 76, + 76, 76, 76, 76, 76, 76, 76, 76, 76, 76, + 76, 76, 76, 76, 76, 76, 76, 76, 76, 76, + 76, 76, 76, 76, 76, 76, 76, 76, 76 + }, + + { + 9, -44, -44, -44, -44, -44, -44, -44, -44, -44, + -44, -44, -44, -44, -44, -44, -44, -44, -44, -44, + -44, -44, -44, -44, -44, -44, -44, -44, -44, -44, + -44, -44, -44, -44, -44, -44, -44, -44, -44 + + }, + + { + 9, -45, 45, 46, -45, -45, -45, -45, -45, -45, + -45, -45, -45, -45, -45, -45, -45, -45, -45, -45, + -45, -45, -45, -45, -45, -45, -45, -45, -45, -45, + -45, -45, -45, -45, -45, -45, -45, -45, -45 + }, + + { + 9, -46, -46, -46, -46, -46, -46, -46, -46, -46, + -46, -46, -46, -46, -46, -46, -46, -46, -46, -46, + -46, -46, -46, -46, -46, -46, -46, -46, -46, -46, + -46, -46, -46, -46, -46, -46, -46, -46, -46 + }, + + { + 9, 47, 47, 48, 47, 47, 47, 47, 47, 47, + 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, + + 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, + 47, 47, 47, 47, 47, 47, 47, 47, 47 + }, + + { + 9, -48, -48, -48, -48, -48, -48, -48, -48, -48, + -48, -48, -48, -48, -48, -48, -48, -48, -48, -48, + -48, -48, -48, -48, -48, -48, -48, -48, -48, -48, + -48, -48, -48, -48, -48, -48, -48, -48, -48 + }, + + { + 9, -49, -49, -49, -49, -49, -49, -49, -49, -49, + -49, -49, -49, -49, -49, -49, -49, -49, -49, -49, + 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, + 49, 49, 49, 49, 49, 49, 49, 49, 49 + + }, + + { + 9, -50, -50, -50, -50, -50, -50, -50, -50, -50, + -50, -50, -50, -50, -50, -50, -50, -50, -50, -50, + 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, + 49, 49, 49, 49, 49, 77, 49, 49, 49 + }, + + { + 9, -51, -51, -51, -51, -51, -51, -51, -51, -51, + -51, -51, -51, -51, -51, -51, -51, -51, -51, -51, + 49, 49, 49, 49, 49, 49, 49, 49, 78, 49, + 49, 49, 49, 49, 49, 49, 49, 49, 49 + }, + + { + 9, -52, -52, -52, -52, -52, -52, -52, -52, -52, + -52, -52, -52, -52, -52, -52, -52, -52, -52, -52, + + 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, + 79, 49, 49, 49, 49, 49, 49, 49, 49 + }, + + { + 9, -53, -53, -53, -53, -53, -53, -53, -53, -53, + -53, -53, -53, -53, -53, -53, -53, -53, -53, -53, + 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, + 49, 80, 49, 49, 49, 49, 49, 49, 49 + }, + + { + 9, -54, -54, -54, -54, -54, -54, -54, -54, -54, + -54, -54, -54, -54, -54, -54, -54, -54, -54, -54, + 49, 81, 49, 49, 49, 49, 49, 49, 49, 49, + 49, 49, 49, 49, 49, 49, 49, 49, 49 + + }, + + { + 9, -55, -55, -55, -55, -55, -55, -55, -55, -55, + -55, -55, -55, -55, -55, -55, -55, -55, -55, -55, + 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, + 49, 49, 49, 49, 49, 49, 82, 49, 49 + }, + + { + 9, -56, 56, -56, -56, -56, -56, -56, -56, -56, + -56, -56, -56, -56, -56, -56, -56, -56, -56, -56, + -56, -56, -56, -56, -56, -56, -56, -56, -56, -56, + -56, -56, -56, -56, -56, -56, -56, -56, -56 + }, + + { + 9, 57, 57, 58, 57, 57, 57, 57, 57, 57, + 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, + + 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, + 57, 57, 57, 57, 57, 57, 57, 57, 57 + }, + + { + 9, -58, -58, -58, -58, -58, -58, -58, -58, -58, + -58, -58, -58, -58, -58, -58, -58, -58, -58, -58, + -58, -58, -58, -58, -58, -58, -58, -58, -58, -58, + -58, -58, -58, -58, -58, -58, -58, -58, -58 + }, + + { + 9, -59, -59, -59, -59, -59, -59, -59, -59, -59, + -59, -59, -59, -59, 59, -59, -59, -59, -59, -59, + -59, -59, -59, -59, -59, -59, -59, -59, -59, -59, + -59, -59, -59, -59, -59, -59, -59, -59, -59 + + }, + + { + 9, -60, -60, -60, -60, -60, -60, -60, -60, -60, + -60, -60, -60, -60, 59, -60, -60, -60, -60, -60, + -60, -60, -60, -60, -60, -60, -60, -60, -60, -60, + -60, -60, -60, -60, -60, -60, -60, -60, -60 + }, + + { + 9, -61, -61, -61, -61, -61, -61, -61, -61, -61, + -61, -61, 60, -61, 61, -61, 62, -61, -61, -61, + 62, 62, 62, 62, 62, 62, 62, 62, 62, 62, + 62, 62, 62, 62, 62, 62, 62, 62, 62 + }, + + { + 9, -62, -62, -62, -62, -62, -62, -62, -62, -62, + -62, -62, -62, -62, 62, -62, 62, -62, -62, -62, + + 62, 62, 62, 62, 62, 62, 62, 62, 62, 62, + 62, 62, 62, 62, 62, 62, 62, 62, 62 + }, + + { + 9, -63, 63, 64, -63, -63, -63, -63, -63, -63, + -63, -63, -63, -63, -63, -63, -63, -63, -63, -63, + -63, -63, -63, -63, -63, -63, -63, -63, -63, -63, + -63, -63, -63, -63, -63, -63, -63, -63, -63 + }, + + { + 9, -64, -64, -64, -64, -64, -64, -64, -64, -64, + -64, -64, -64, -64, -64, -64, -64, -64, -64, -64, + -64, -64, -64, -64, -64, -64, -64, -64, -64, -64, + -64, -64, -64, -64, -64, -64, -64, -64, -64 + + }, + + { + 9, 65, 65, -65, -65, 65, 65, 65, 65, 65, + 65, 65, 65, 65, 65, 65, 65, -65, 65, 65, + 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, + 65, 65, 65, 65, 65, 65, 65, 65, 65 + }, + + { + 9, -66, -66, -66, -66, -66, -66, -66, -66, -66, + -66, -66, -66, -66, -66, -66, -66, -66, -66, -66, + -66, -66, -66, -66, -66, -66, -66, -66, -66, -66, + -66, -66, -66, -66, -66, -66, -66, -66, -66 + }, + + { + 9, -67, -67, -67, -67, -67, -67, -67, -67, -67, + -67, -67, -67, -67, -67, -67, -67, -67, -67, -67, + + -67, -67, -67, -67, -67, -67, -67, -67, -67, -67, + -67, -67, -67, -67, -67, -67, -67, -67, -67 + }, + + { + 9, -68, -68, -68, -68, -68, -68, -68, -68, -68, + -68, -68, -68, -68, -68, -68, -68, -68, -68, -68, + -68, -68, -68, -68, -68, -68, -68, -68, -68, -68, + -68, -68, -68, -68, -68, -68, -68, -68, -68 + }, + + { + 9, -69, -69, -69, -69, -69, -69, -69, -69, -69, + -69, -69, -69, -69, -69, -69, -69, -69, -69, -69, + -69, -69, -69, -69, -69, -69, -69, -69, -69, -69, + -69, -69, -69, -69, -69, -69, -69, -69, -69 + + }, + + { + 9, -70, -70, -70, -70, -70, -70, -70, -70, -70, + -70, -70, -70, -70, -70, -70, -70, -70, -70, -70, + -70, -70, -70, -70, -70, -70, -70, -70, -70, -70, + -70, -70, -70, -70, -70, -70, -70, -70, -70 + }, + + { + 9, -71, -71, -71, -71, -71, -71, -71, -71, -71, + -71, -71, -71, -71, -71, -71, -71, -71, -71, -71, + -71, -71, -71, -71, -71, -71, -71, -71, -71, -71, + -71, -71, -71, -71, -71, -71, -71, -71, -71 + }, + + { + 9, -72, -72, -72, -72, -72, -72, -72, -72, -72, + -72, -72, -72, -72, -72, -72, -72, -72, -72, -72, + + -72, -72, -72, -72, -72, -72, -72, -72, -72, -72, + -72, -72, -72, -72, -72, -72, -72, -72, -72 + }, + + { + 9, -73, -73, -73, -73, -73, -73, -73, -73, -73, + -73, -73, -73, -73, -73, -73, -73, -73, -73, -73, + -73, -73, -73, -73, -73, -73, -73, -73, -73, -73, + -73, -73, -73, -73, -73, -73, -73, -73, -73 + }, + + { + 9, -74, -74, -74, -74, -74, -74, -74, -74, -74, + -74, -74, -74, -74, -74, -74, -74, -74, -74, -74, + -74, -74, -74, -74, -74, -74, -74, -74, -74, -74, + -74, -74, -74, -74, -74, -74, -74, -74, -74 + + }, + + { + 9, -75, -75, -75, -75, -75, -75, -75, -75, -75, + -75, -75, -75, -75, -75, -75, -75, -75, -75, -75, + -75, -75, -75, -75, -75, -75, -75, -75, -75, -75, + -75, -75, -75, -75, -75, -75, -75, -75, -75 + }, + + { + 9, 76, 76, -76, 76, 76, 76, 76, 76, 76, + 76, 76, 76, 76, 76, 76, 76, 76, 76, 76, + 76, 76, 76, 76, 76, 76, 76, 76, 76, 76, + 76, 76, 76, 76, 76, 76, 76, 76, 76 + }, + + { + 9, -77, 83, -77, -77, -77, -77, -77, -77, -77, + -77, -77, -77, -77, -77, -77, -77, -77, -77, -77, + + 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, + 49, 49, 49, 49, 49, 49, 49, 49, 49 + }, + + { + 9, -78, -78, -78, -78, -78, -78, -78, -78, -78, + -78, -78, -78, -78, -78, -78, -78, -78, -78, -78, + 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, + 49, 49, 49, 84, 49, 49, 49, 49, 49 + }, + + { + 9, -79, -79, -79, -79, -79, -79, -79, -79, -79, + -79, -79, -79, -79, -79, -79, -79, -79, -79, -79, + 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, + 49, 49, 49, 85, 49, 49, 49, 49, 49 + + }, + + { + 9, -80, -80, -80, -80, -80, -80, -80, -80, -80, + -80, -80, -80, -80, -80, -80, -80, -80, -80, -80, + 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, + 49, 49, 86, 49, 49, 49, 49, 49, 49 + }, + + { + 9, -81, -81, -81, -81, -81, -81, -81, -81, -81, + -81, -81, -81, -81, -81, -81, -81, -81, -81, -81, + 49, 49, 49, 49, 87, 49, 49, 49, 49, 49, + 49, 49, 49, 49, 49, 49, 49, 49, 49 + }, + + { + 9, -82, 88, -82, -82, -82, -82, -82, -82, -82, + -82, -82, -82, -82, -82, -82, -82, -82, -82, -82, + + 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, + 49, 49, 49, 49, 49, 49, 49, 49, 49 + }, + + { + 9, -83, 83, -83, -83, -83, -83, -83, -83, -83, + -83, -83, -83, -83, -83, -83, -83, -83, -83, -83, + -83, -83, -83, -83, -83, -83, -83, -83, -83, -83, + -83, -83, -83, -83, -83, -83, -83, -83, -83 + }, + + { + 9, -84, 89, -84, -84, -84, -84, -84, -84, -84, + -84, -84, -84, -84, -84, -84, -84, -84, -84, -84, + 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, + 49, 49, 49, 49, 49, 49, 49, 49, 49 + + }, + + { + 9, -85, -85, -85, -85, -85, -85, -85, -85, -85, + -85, -85, -85, -85, -85, -85, -85, -85, -85, -85, + 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, + 49, 49, 49, 49, 49, 49, 49, 90, 49 + }, + + { + 9, -86, -86, -86, -86, -86, -86, -86, -86, -86, + -86, -86, -86, -86, -86, -86, -86, -86, -86, -86, + 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, + 49, 49, 49, 49, 91, 49, 49, 49, 49 + }, + + { + 9, -87, -87, -87, -87, -87, -87, -87, -87, -87, + -87, -87, -87, -87, -87, -87, -87, -87, -87, -87, + + 49, 49, 49, 49, 49, 49, 49, 49, 49, 92, + 49, 49, 49, 49, 49, 49, 49, 49, 49 + }, + + { + 9, -88, 88, -88, -88, -88, -88, -88, -88, -88, + -88, -88, -88, -88, -88, -88, -88, -88, -88, -88, + -88, -88, -88, -88, -88, -88, -88, -88, -88, -88, + -88, -88, -88, -88, -88, -88, -88, -88, -88 + }, + + { + 9, -89, 89, -89, -89, -89, -89, -89, -89, -89, + -89, -89, -89, -89, -89, -89, -89, -89, -89, -89, + -89, -89, -89, -89, -89, -89, -89, -89, -89, -89, + -89, -89, -89, -89, -89, -89, -89, -89, -89 + + }, + + { + 9, -90, -90, -90, -90, -90, -90, -90, -90, -90, + -90, -90, -90, -90, -90, -90, -90, -90, -90, -90, + 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, + 49, 49, 49, 49, 49, 49, 93, 49, 49 + }, + + { + 9, -91, -91, -91, -91, -91, -91, -91, -91, -91, + -91, -91, -91, -91, -91, -91, -91, -91, -91, -91, + 49, 49, 49, 49, 94, 49, 49, 49, 49, 49, + 49, 49, 49, 49, 49, 49, 49, 49, 49 + }, + + { + 9, -92, 95, -92, -92, -92, -92, -92, -92, -92, + -92, -92, -92, -92, -92, -92, -92, -92, -92, -92, + + 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, + 49, 49, 49, 49, 49, 49, 49, 49, 49 + }, + + { + 9, -93, -93, -93, -93, -93, -93, -93, -93, -93, + -93, -93, -93, -93, -93, -93, -93, -93, -93, -93, + 49, 49, 49, 49, 96, 49, 49, 49, 49, 49, + 49, 49, 49, 49, 49, 49, 49, 49, 49 + }, + + { + 9, -94, 97, -94, -94, -94, -94, -94, -94, -94, + -94, -94, -94, -94, -94, -94, -94, -94, -94, -94, + 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, + 49, 49, 49, 49, 49, 49, 49, 49, 49 + + }, + + { + 9, -95, 95, -95, -95, -95, -95, -95, -95, -95, + -95, -95, -95, -95, -95, -95, -95, -95, -95, -95, + -95, -95, -95, -95, -95, -95, -95, -95, -95, -95, + -95, -95, -95, -95, -95, -95, -95, -95, -95 + }, + + { + 9, -96, 98, -96, -96, -96, -96, -96, -96, -96, + -96, -96, -96, -96, -96, -96, -96, -96, -96, -96, + 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, + 49, 49, 49, 49, 49, 49, 49, 49, 49 + }, + + { + 9, -97, 97, -97, -97, -97, -97, -97, -97, -97, + -97, -97, -97, -97, -97, -97, -97, -97, -97, -97, + + -97, -97, -97, -97, -97, -97, -97, -97, -97, -97, + -97, -97, -97, -97, -97, -97, -97, -97, -97 + }, + + { + 9, -98, 98, -98, -98, -98, -98, -98, -98, -98, + -98, -98, -98, -98, -98, -98, -98, -98, -98, -98, + -98, -98, -98, -98, -98, -98, -98, -98, -98, -98, + -98, -98, -98, -98, -98, -98, -98, -98, -98 + }, + + } ; + +static yy_state_type yy_get_previous_state (void ); +static yy_state_type yy_try_NUL_trans (yy_state_type current_state ); +static int yy_get_next_buffer (void ); +static void yy_fatal_error (yyconst char msg[] ); + +/* Done after the current pattern has been matched and before the + * corresponding action - sets up sensors_yytext. + */ +#define YY_DO_BEFORE_ACTION \ + (yytext_ptr) = yy_bp; \ + sensors_yyleng = (size_t) (yy_cp - yy_bp); \ + (yy_hold_char) = *yy_cp; \ + *yy_cp = '\0'; \ + (yy_c_buf_p) = yy_cp; + +#define YY_NUM_RULES 50 +#define YY_END_OF_BUFFER 51 +/* This struct is not used in this scanner, + but its presence is necessary. */ +struct yy_trans_info + { + flex_int32_t yy_verify; + flex_int32_t yy_nxt; + }; +static yyconst flex_int16_t yy_accept[99] = + { 0, + 0, 0, 0, 0, 0, 0, 13, 13, 51, 12, + 1, 2, 3, 11, 11, 11, 11, 11, 11, 33, + 15, 16, 31, 18, 25, 26, 23, 21, 27, 22, + 33, 24, 20, 28, 32, 33, 29, 30, 49, 36, + 39, 48, 13, 14, 1, 2, 3, 4, 11, 11, + 11, 11, 11, 11, 11, 15, 18, 19, 20, 34, + 20, 32, 35, 17, 49, 38, 47, 37, 40, 41, + 42, 43, 44, 45, 46, 13, 8, 11, 11, 11, + 11, 6, 8, 9, 11, 11, 11, 6, 9, 11, + 11, 5, 11, 10, 5, 7, 10, 7 + + } ; + +static yyconst flex_int32_t yy_ec[256] = + { 0, + 1, 1, 1, 1, 1, 1, 1, 1, 2, 3, + 2, 2, 2, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 2, 1, 4, 5, 1, 1, 1, 1, 6, + 7, 8, 9, 10, 11, 12, 13, 14, 14, 14, + 14, 14, 14, 14, 14, 14, 14, 1, 1, 1, + 1, 1, 1, 15, 16, 16, 16, 16, 16, 16, + 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, + 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, + 1, 17, 1, 18, 16, 19, 20, 21, 22, 23, + + 24, 25, 26, 27, 28, 23, 23, 29, 30, 31, + 32, 33, 23, 34, 35, 36, 37, 38, 23, 23, + 23, 23, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1 + } ; + +extern int sensors_yy_flex_debug; +int sensors_yy_flex_debug = 0; + +/* The intent behind this definition is that it'll catch + * any uses of REJECT which flex missed. + */ +#define REJECT reject_used_but_not_detected +#define yymore() yymore_used_but_not_detected +#define YY_MORE_ADJ 0 +#define YY_RESTORE_YY_MORE_OFFSET +char *sensors_yytext; +#line 1 "lib/conf-lex.l" +#line 2 "lib/conf-lex.l" +/* + conf-lex.l - Part of libsensors, a Linux library for reading sensor data. + Copyright (c) 1998, 1999 Frodo Looijaard + + 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 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 +#include + +#include "general.h" +#include "data.h" +#include "conf-parse.h" +#include "error.h" +#include "scanner.h" + +static int buffer_count; +static int buffer_max; +static char *buffer; + +char sensors_lex_error[100]; + +const char *sensors_yyfilename; +int sensors_yylineno; + +#define buffer_malloc() sensors_malloc_array(&buffer,&buffer_count,\ + &buffer_max,1) +#define buffer_free() sensors_free_array(&buffer,&buffer_count,\ + &buffer_max) +#define buffer_add_char(c) sensors_add_array_el(c,&buffer,\ + &buffer_count,\ + &buffer_max,1) +#define buffer_add_string(s) sensors_add_array_els(s,strlen(s),\ + &buffer, \ + &buffer_count,&buffer_max,1) + +/* Scanner for configuration files */ +/* All states are exclusive */ + + + +/* Any whitespace-like character */ +/* Note: `10', `10.4' and `.4' are valid, `10.' is not */ +/* Only positive whole numbers are recognized here */ +#line 1255 "" + +#define INITIAL 0 +#define MIDDLE 1 +#define STRING 2 +#define ERR 3 + +#ifndef YY_NO_UNISTD_H +/* Special case for "unistd.h", since it is non-ANSI. We include it way + * down here because we want the user's section 1 to have been scanned first. + * The user has a chance to override it with an option. + */ +#include +#endif + +#ifndef YY_EXTRA_TYPE +#define YY_EXTRA_TYPE void * +#endif + +static int yy_init_globals (void ); + +/* Accessor methods to globals. + These are made visible to non-reentrant scanners for convenience. */ + +int sensors_yylex_destroy (void ); + +int sensors_yyget_debug (void ); + +void sensors_yyset_debug (int debug_flag ); + +YY_EXTRA_TYPE sensors_yyget_extra (void ); + +void sensors_yyset_extra (YY_EXTRA_TYPE user_defined ); + +FILE *sensors_yyget_in (void ); + +void sensors_yyset_in (FILE * in_str ); + +FILE *sensors_yyget_out (void ); + +void sensors_yyset_out (FILE * out_str ); + +int sensors_yyget_leng (void ); + +char *sensors_yyget_text (void ); + +int sensors_yyget_lineno (void ); + +void sensors_yyset_lineno (int line_number ); + +/* Macros after this point can all be overridden by user definitions in + * section 1. + */ + +#ifndef YY_SKIP_YYWRAP +#ifdef __cplusplus +extern "C" int sensors_yywrap (void ); +#else +extern int sensors_yywrap (void ); +#endif +#endif + +#ifndef yytext_ptr +static void yy_flex_strncpy (char *,yyconst char *,int ); +#endif + +#ifdef YY_NEED_STRLEN +static int yy_flex_strlen (yyconst char * ); +#endif + +#ifndef YY_NO_INPUT + +#ifdef __cplusplus +static int yyinput (void ); +#else +static int input (void ); +#endif + +#endif + +/* Amount of stuff to slurp up with each read. */ +#ifndef YY_READ_BUF_SIZE +#ifdef __ia64__ +/* On IA-64, the buffer size is 16k, not 8k */ +#define YY_READ_BUF_SIZE 16384 +#else +#define YY_READ_BUF_SIZE 8192 +#endif /* __ia64__ */ +#endif + +/* Copy whatever the last rule matched to the standard output. */ +#ifndef ECHO +/* This used to be an fputs(), but since the string might contain NUL's, + * we now use fwrite(). + */ +#define ECHO do { if (fwrite( sensors_yytext, sensors_yyleng, 1, sensors_yyout )) {} } while (0) +#endif + +/* Gets input and stuffs it into "buf". number of characters read, or YY_NULL, + * is returned in "result". + */ +#ifndef YY_INPUT +#define YY_INPUT(buf,result,max_size) \ + if ( YY_CURRENT_BUFFER_LVALUE->yy_is_interactive ) \ + { \ + int c = '*'; \ + size_t n; \ + for ( n = 0; n < max_size && \ + (c = getc( sensors_yyin )) != EOF && c != '\n'; ++n ) \ + buf[n] = (char) c; \ + if ( c == '\n' ) \ + buf[n++] = (char) c; \ + if ( c == EOF && ferror( sensors_yyin ) ) \ + YY_FATAL_ERROR( "input in flex scanner failed" ); \ + result = n; \ + } \ + else \ + { \ + errno=0; \ + while ( (result = fread(buf, 1, max_size, sensors_yyin))==0 && ferror(sensors_yyin)) \ + { \ + if( errno != EINTR) \ + { \ + YY_FATAL_ERROR( "input in flex scanner failed" ); \ + break; \ + } \ + errno=0; \ + clearerr(sensors_yyin); \ + } \ + }\ +\ + +#endif + +/* No semi-colon after return; correct usage is to write "yyterminate();" - + * we don't want an extra ';' after the "return" because that will cause + * some compilers to complain about unreachable statements. + */ +#ifndef yyterminate +#define yyterminate() return YY_NULL +#endif + +/* Number of entries by which start-condition stack grows. */ +#ifndef YY_START_STACK_INCR +#define YY_START_STACK_INCR 25 +#endif + +/* Report a fatal error. */ +#ifndef YY_FATAL_ERROR +#define YY_FATAL_ERROR(msg) yy_fatal_error( msg ) +#endif + +/* end tables serialization structures and prototypes */ + +/* Default declaration of generated scanner - a define so the user can + * easily add parameters. + */ +#ifndef YY_DECL +#define YY_DECL_IS_OURS 1 + +extern int sensors_yylex (void); + +#define YY_DECL int sensors_yylex (void) +#endif /* !YY_DECL */ + +/* Code executed at the beginning of each rule, after sensors_yytext and sensors_yyleng + * have been set up. + */ +#ifndef YY_USER_ACTION +#define YY_USER_ACTION +#endif + +/* Code executed at the end of each rule. */ +#ifndef YY_BREAK +#define YY_BREAK break; +#endif + +#define YY_RULE_SETUP \ + YY_USER_ACTION + +/** The main scanner function which does all the work. + */ +YY_DECL +{ + register yy_state_type yy_current_state; + register char *yy_cp, *yy_bp; + register int yy_act; + +#line 80 "lib/conf-lex.l" + + + /* + * STATE: INITIAL + */ + +#line 1450 "" + + if ( !(yy_init) ) + { + (yy_init) = 1; + +#ifdef YY_USER_INIT + YY_USER_INIT; +#endif + + if ( ! (yy_start) ) + (yy_start) = 1; /* first start state */ + + if ( ! sensors_yyin ) + sensors_yyin = stdin; + + if ( ! sensors_yyout ) + sensors_yyout = stdout; + + if ( ! YY_CURRENT_BUFFER ) { + sensors_yyensure_buffer_stack (); + YY_CURRENT_BUFFER_LVALUE = + sensors_yy_create_buffer(sensors_yyin,YY_BUF_SIZE ); + } + + sensors_yy_load_buffer_state( ); + } + + while ( 1 ) /* loops until end-of-file is reached */ + { + yy_cp = (yy_c_buf_p); + + /* Support of sensors_yytext. */ + *yy_cp = (yy_hold_char); + + /* yy_bp points to the position in yy_ch_buf of the start of + * the current run. + */ + yy_bp = yy_cp; + + yy_current_state = (yy_start); +yy_match: + while ( (yy_current_state = yy_nxt[yy_current_state][ yy_ec[YY_SC_TO_UI(*yy_cp)] ]) > 0 ) + ++yy_cp; + + yy_current_state = -yy_current_state; + +yy_find_action: + yy_act = yy_accept[yy_current_state]; + + YY_DO_BEFORE_ACTION; + +do_action: /* This label is used only to access EOF actions. */ + + switch ( yy_act ) + { /* beginning of action switch */ + +case YY_STATE_EOF(INITIAL): +#line 88 "lib/conf-lex.l" +{ /* EOF from this state terminates */ + return 0; + } + YY_BREAK +case 1: +YY_RULE_SETUP +#line 92 "lib/conf-lex.l" +; /* eat as many blanks as possible at once */ + YY_BREAK +case 2: +/* rule 2 can match eol */ +YY_RULE_SETUP +#line 94 "lib/conf-lex.l" +{ /* eat a bare newline (possibly preceded by blanks) */ + sensors_yylineno++; + } + YY_BREAK +/* comments */ +case 3: +YY_RULE_SETUP +#line 100 "lib/conf-lex.l" +; /* eat the rest of the line after comment char */ + YY_BREAK +case 4: +/* rule 4 can match eol */ +YY_RULE_SETUP +#line 102 "lib/conf-lex.l" +{ /* eat the rest of the line after comment char */ + sensors_yylineno++; + } + YY_BREAK +/* + * Keywords must be followed by whitespace - eat that too. + * If there isn't trailing whitespace, we still need to + * accept it as lexically correct (even though the parser + * will reject it anyway.) + */ +case 5: +YY_RULE_SETUP +#line 113 "lib/conf-lex.l" +{ + sensors_yylval.line.filename = sensors_yyfilename; + sensors_yylval.line.lineno = sensors_yylineno; + BEGIN(MIDDLE); + return LABEL; + } + YY_BREAK +case 6: +YY_RULE_SETUP +#line 120 "lib/conf-lex.l" +{ + sensors_yylval.line.filename = sensors_yyfilename; + sensors_yylval.line.lineno = sensors_yylineno; + BEGIN(MIDDLE); + return SET; + } + YY_BREAK +case 7: +YY_RULE_SETUP +#line 127 "lib/conf-lex.l" +{ + sensors_yylval.line.filename = sensors_yyfilename; + sensors_yylval.line.lineno = sensors_yylineno; + BEGIN(MIDDLE); + return COMPUTE; + } + YY_BREAK +case 8: +YY_RULE_SETUP +#line 134 "lib/conf-lex.l" +{ + sensors_yylval.line.filename = sensors_yyfilename; + sensors_yylval.line.lineno = sensors_yylineno; + BEGIN(MIDDLE); + return BUS; + } + YY_BREAK +case 9: +YY_RULE_SETUP +#line 141 "lib/conf-lex.l" +{ + sensors_yylval.line.filename = sensors_yyfilename; + sensors_yylval.line.lineno = sensors_yylineno; + BEGIN(MIDDLE); + return CHIP; + } + YY_BREAK +case 10: +YY_RULE_SETUP +#line 148 "lib/conf-lex.l" +{ + sensors_yylval.line.filename = sensors_yyfilename; + sensors_yylval.line.lineno = sensors_yylineno; + BEGIN(MIDDLE); + return IGNORE; + } + YY_BREAK +/* Anything else at the beginning of a line is an error */ +case 11: +#line 158 "lib/conf-lex.l" +case 12: +YY_RULE_SETUP +#line 158 "lib/conf-lex.l" +{ + BEGIN(ERR); + strcpy(sensors_lex_error,"Invalid keyword"); + return ERROR; + } + YY_BREAK + +/* + * STATE: ERROR + */ + +case 13: +YY_RULE_SETUP +#line 171 "lib/conf-lex.l" +; /* eat whatever is left on this line */ + YY_BREAK +case 14: +/* rule 14 can match eol */ +YY_RULE_SETUP +#line 173 "lib/conf-lex.l" +{ + BEGIN(INITIAL); + sensors_yylineno++; + return EOL; + } + YY_BREAK + +/* + * STATE: MIDDLE + */ + +case 15: +YY_RULE_SETUP +#line 186 "lib/conf-lex.l" +; /* eat as many blanks as possible at once */ + YY_BREAK +case 16: +/* rule 16 can match eol */ +YY_RULE_SETUP +#line 188 "lib/conf-lex.l" +{ /* newline here sends EOL token to parser */ + BEGIN(INITIAL); + sensors_yylineno++; + return EOL; + } + YY_BREAK +case YY_STATE_EOF(MIDDLE): +#line 194 "lib/conf-lex.l" +{ /* EOF here sends EOL token to parser also */ + BEGIN(INITIAL); + return EOL; + } + YY_BREAK +case 17: +/* rule 17 can match eol */ +YY_RULE_SETUP +#line 199 "lib/conf-lex.l" +{ /* eat an escaped newline with no state change */ + sensors_yylineno++; + } + YY_BREAK +/* comments */ +case 18: +YY_RULE_SETUP +#line 205 "lib/conf-lex.l" +; /* eat the rest of the line after comment char */ + YY_BREAK +case 19: +/* rule 19 can match eol */ +YY_RULE_SETUP +#line 207 "lib/conf-lex.l" +{ /* eat the rest of the line after comment char */ + BEGIN(INITIAL); + sensors_yylineno++; + return EOL; + } + YY_BREAK +/* A number */ +case 20: +YY_RULE_SETUP +#line 215 "lib/conf-lex.l" +{ + sensors_yylval.value = atof(sensors_yytext); + return FLOAT; + } + YY_BREAK +/* Some operators */ +case 21: +YY_RULE_SETUP +#line 222 "lib/conf-lex.l" +return '+'; + YY_BREAK +case 22: +YY_RULE_SETUP +#line 223 "lib/conf-lex.l" +return '-'; + YY_BREAK +case 23: +YY_RULE_SETUP +#line 224 "lib/conf-lex.l" +return '*'; + YY_BREAK +case 24: +YY_RULE_SETUP +#line 225 "lib/conf-lex.l" +return '/'; + YY_BREAK +case 25: +YY_RULE_SETUP +#line 226 "lib/conf-lex.l" +return '('; + YY_BREAK +case 26: +YY_RULE_SETUP +#line 227 "lib/conf-lex.l" +return ')'; + YY_BREAK +case 27: +YY_RULE_SETUP +#line 228 "lib/conf-lex.l" +return ','; + YY_BREAK +case 28: +YY_RULE_SETUP +#line 229 "lib/conf-lex.l" +return '@'; + YY_BREAK +case 29: +YY_RULE_SETUP +#line 230 "lib/conf-lex.l" +return '^'; + YY_BREAK +case 30: +YY_RULE_SETUP +#line 231 "lib/conf-lex.l" +return '`'; + YY_BREAK +/* Quoted string */ +case 31: +YY_RULE_SETUP +#line 235 "lib/conf-lex.l" +{ + buffer_malloc(); + BEGIN(STRING); + } + YY_BREAK +/* A normal, unquoted identifier */ +case 32: +YY_RULE_SETUP +#line 242 "lib/conf-lex.l" +{ + sensors_yylval.name = strdup(sensors_yytext); + if (! sensors_yylval.name) + sensors_fatal_error("conf-lex.l", + "Allocating a new string"); + + return NAME; + } + YY_BREAK +/* anything else is bogus */ +case 33: +#line 254 "lib/conf-lex.l" +case 34: +#line 255 "lib/conf-lex.l" +case 35: +YY_RULE_SETUP +#line 255 "lib/conf-lex.l" +{ + BEGIN(ERR); + return ERROR; + } + YY_BREAK + +/* + * STATE: STRING + */ + +/* Oops, newline or EOF while in a string is not good */ +case 36: +/* rule 36 can match eol */ +#line 270 "lib/conf-lex.l" +case 37: +/* rule 37 can match eol */ +YY_RULE_SETUP +#line 270 "lib/conf-lex.l" +{ + buffer_add_char("\0"); + strcpy(sensors_lex_error, + "No matching double quote."); + buffer_free(); + yyless(0); + BEGIN(ERR); + return ERROR; + } + YY_BREAK +case YY_STATE_EOF(STRING): +#line 280 "lib/conf-lex.l" +{ + strcpy(sensors_lex_error, + "Reached end-of-file without a matching double quote."); + buffer_free(); + BEGIN(MIDDLE); + return ERROR; + } + YY_BREAK +/* At the end */ +case 38: +YY_RULE_SETUP +#line 290 "lib/conf-lex.l" +{ + buffer_add_char("\0"); + strcpy(sensors_lex_error, + "Quoted strings must be separated by whitespace."); + buffer_free(); + BEGIN(ERR); + return ERROR; + } + YY_BREAK +case 39: +YY_RULE_SETUP +#line 299 "lib/conf-lex.l" +{ + buffer_add_char("\0"); + sensors_yylval.name = strdup(buffer); + if (! sensors_yylval.name) + sensors_fatal_error("conf-lex.l", + "Allocating a new string"); + buffer_free(); + BEGIN(MIDDLE); + return NAME; + } + YY_BREAK +case 40: +YY_RULE_SETUP +#line 310 "lib/conf-lex.l" +buffer_add_char("\a"); + YY_BREAK +case 41: +YY_RULE_SETUP +#line 311 "lib/conf-lex.l" +buffer_add_char("\b"); + YY_BREAK +case 42: +YY_RULE_SETUP +#line 312 "lib/conf-lex.l" +buffer_add_char("\f"); + YY_BREAK +case 43: +YY_RULE_SETUP +#line 313 "lib/conf-lex.l" +buffer_add_char("\n"); + YY_BREAK +case 44: +YY_RULE_SETUP +#line 314 "lib/conf-lex.l" +buffer_add_char("\r"); + YY_BREAK +case 45: +YY_RULE_SETUP +#line 315 "lib/conf-lex.l" +buffer_add_char("\t"); + YY_BREAK +case 46: +YY_RULE_SETUP +#line 316 "lib/conf-lex.l" +buffer_add_char("\v"); + YY_BREAK +/* Other escapes: just copy the character behind the slash */ +case 47: +YY_RULE_SETUP +#line 320 "lib/conf-lex.l" +{ + buffer_add_char(&sensors_yytext[1]); + } + YY_BREAK +/* Anything else (including a bare '\' which may be followed by EOF) */ +case 48: +#line 327 "lib/conf-lex.l" +case 49: +YY_RULE_SETUP +#line 327 "lib/conf-lex.l" +{ + buffer_add_string(sensors_yytext); + } + YY_BREAK + +case 50: +YY_RULE_SETUP +#line 332 "lib/conf-lex.l" +YY_FATAL_ERROR( "flex scanner jammed" ); + YY_BREAK +#line 1903 "" + case YY_STATE_EOF(ERR): + yyterminate(); + + case YY_END_OF_BUFFER: + { + /* Amount of text matched not including the EOB char. */ + int yy_amount_of_matched_text = (int) (yy_cp - (yytext_ptr)) - 1; + + /* Undo the effects of YY_DO_BEFORE_ACTION. */ + *yy_cp = (yy_hold_char); + YY_RESTORE_YY_MORE_OFFSET + + if ( YY_CURRENT_BUFFER_LVALUE->yy_buffer_status == YY_BUFFER_NEW ) + { + /* We're scanning a new file or input source. It's + * possible that this happened because the user + * just pointed sensors_yyin at a new source and called + * sensors_yylex(). If so, then we have to assure + * consistency between YY_CURRENT_BUFFER and our + * globals. Here is the right place to do so, because + * this is the first action (other than possibly a + * back-up) that will match for the new input source. + */ + (yy_n_chars) = YY_CURRENT_BUFFER_LVALUE->yy_n_chars; + YY_CURRENT_BUFFER_LVALUE->yy_input_file = sensors_yyin; + YY_CURRENT_BUFFER_LVALUE->yy_buffer_status = YY_BUFFER_NORMAL; + } + + /* Note that here we test for yy_c_buf_p "<=" to the position + * of the first EOB in the buffer, since yy_c_buf_p will + * already have been incremented past the NUL character + * (since all states make transitions on EOB to the + * end-of-buffer state). Contrast this with the test + * in input(). + */ + if ( (yy_c_buf_p) <= &YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[(yy_n_chars)] ) + { /* This was really a NUL. */ + yy_state_type yy_next_state; + + (yy_c_buf_p) = (yytext_ptr) + yy_amount_of_matched_text; + + yy_current_state = yy_get_previous_state( ); + + /* Okay, we're now positioned to make the NUL + * transition. We couldn't have + * yy_get_previous_state() go ahead and do it + * for us because it doesn't know how to deal + * with the possibility of jamming (and we don't + * want to build jamming into it because then it + * will run more slowly). + */ + + yy_next_state = yy_try_NUL_trans( yy_current_state ); + + yy_bp = (yytext_ptr) + YY_MORE_ADJ; + + if ( yy_next_state ) + { + /* Consume the NUL. */ + yy_cp = ++(yy_c_buf_p); + yy_current_state = yy_next_state; + goto yy_match; + } + + else + { + yy_cp = (yy_c_buf_p); + goto yy_find_action; + } + } + + else switch ( yy_get_next_buffer( ) ) + { + case EOB_ACT_END_OF_FILE: + { + (yy_did_buffer_switch_on_eof) = 0; + + if ( sensors_yywrap( ) ) + { + /* Note: because we've taken care in + * yy_get_next_buffer() to have set up + * sensors_yytext, we can now set up + * yy_c_buf_p so that if some total + * hoser (like flex itself) wants to + * call the scanner after we return the + * YY_NULL, it'll still work - another + * YY_NULL will get returned. + */ + (yy_c_buf_p) = (yytext_ptr) + YY_MORE_ADJ; + + yy_act = YY_STATE_EOF(YY_START); + goto do_action; + } + + else + { + if ( ! (yy_did_buffer_switch_on_eof) ) + YY_NEW_FILE; + } + break; + } + + case EOB_ACT_CONTINUE_SCAN: + (yy_c_buf_p) = + (yytext_ptr) + yy_amount_of_matched_text; + + yy_current_state = yy_get_previous_state( ); + + yy_cp = (yy_c_buf_p); + yy_bp = (yytext_ptr) + YY_MORE_ADJ; + goto yy_match; + + case EOB_ACT_LAST_MATCH: + (yy_c_buf_p) = + &YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[(yy_n_chars)]; + + yy_current_state = yy_get_previous_state( ); + + yy_cp = (yy_c_buf_p); + yy_bp = (yytext_ptr) + YY_MORE_ADJ; + goto yy_find_action; + } + break; + } + + default: + YY_FATAL_ERROR( + "fatal flex scanner internal error--no action found" ); + } /* end of action switch */ + } /* end of scanning one token */ +} /* end of sensors_yylex */ + +/* yy_get_next_buffer - try to read in a new buffer + * + * Returns a code representing an action: + * EOB_ACT_LAST_MATCH - + * EOB_ACT_CONTINUE_SCAN - continue scanning from current position + * EOB_ACT_END_OF_FILE - end of file + */ +static int yy_get_next_buffer (void) +{ + register char *dest = YY_CURRENT_BUFFER_LVALUE->yy_ch_buf; + register char *source = (yytext_ptr); + register int number_to_move, i; + int ret_val; + + if ( (yy_c_buf_p) > &YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[(yy_n_chars) + 1] ) + YY_FATAL_ERROR( + "fatal flex scanner internal error--end of buffer missed" ); + + if ( YY_CURRENT_BUFFER_LVALUE->yy_fill_buffer == 0 ) + { /* Don't try to fill the buffer, so this is an EOF. */ + if ( (yy_c_buf_p) - (yytext_ptr) - YY_MORE_ADJ == 1 ) + { + /* We matched a single character, the EOB, so + * treat this as a final EOF. + */ + return EOB_ACT_END_OF_FILE; + } + + else + { + /* We matched some text prior to the EOB, first + * process it. + */ + return EOB_ACT_LAST_MATCH; + } + } + + /* Try to read more data. */ + + /* First move last chars to start of buffer. */ + number_to_move = (int) ((yy_c_buf_p) - (yytext_ptr)) - 1; + + for ( i = 0; i < number_to_move; ++i ) + *(dest++) = *(source++); + + if ( YY_CURRENT_BUFFER_LVALUE->yy_buffer_status == YY_BUFFER_EOF_PENDING ) + /* don't do the read, it's not guaranteed to return an EOF, + * just force an EOF + */ + YY_CURRENT_BUFFER_LVALUE->yy_n_chars = (yy_n_chars) = 0; + + else + { + int num_to_read = + YY_CURRENT_BUFFER_LVALUE->yy_buf_size - number_to_move - 1; + + while ( num_to_read <= 0 ) + { /* Not enough room in the buffer - grow it. */ + + /* just a shorter name for the current buffer */ + YY_BUFFER_STATE b = YY_CURRENT_BUFFER; + + int yy_c_buf_p_offset = + (int) ((yy_c_buf_p) - b->yy_ch_buf); + + if ( b->yy_is_our_buffer ) + { + int new_size = b->yy_buf_size * 2; + + if ( new_size <= 0 ) + b->yy_buf_size += b->yy_buf_size / 8; + else + b->yy_buf_size *= 2; + + b->yy_ch_buf = (char *) + /* Include room in for 2 EOB chars. */ + sensors_yyrealloc((void *) b->yy_ch_buf,b->yy_buf_size + 2 ); + } + else + /* Can't grow it, we don't own it. */ + b->yy_ch_buf = 0; + + if ( ! b->yy_ch_buf ) + YY_FATAL_ERROR( + "fatal error - scanner input buffer overflow" ); + + (yy_c_buf_p) = &b->yy_ch_buf[yy_c_buf_p_offset]; + + num_to_read = YY_CURRENT_BUFFER_LVALUE->yy_buf_size - + number_to_move - 1; + + } + + if ( num_to_read > YY_READ_BUF_SIZE ) + num_to_read = YY_READ_BUF_SIZE; + + /* Read in more data. */ + YY_INPUT( (&YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[number_to_move]), + (yy_n_chars), (size_t) num_to_read ); + + YY_CURRENT_BUFFER_LVALUE->yy_n_chars = (yy_n_chars); + } + + if ( (yy_n_chars) == 0 ) + { + if ( number_to_move == YY_MORE_ADJ ) + { + ret_val = EOB_ACT_END_OF_FILE; + sensors_yyrestart(sensors_yyin ); + } + + else + { + ret_val = EOB_ACT_LAST_MATCH; + YY_CURRENT_BUFFER_LVALUE->yy_buffer_status = + YY_BUFFER_EOF_PENDING; + } + } + + else + ret_val = EOB_ACT_CONTINUE_SCAN; + + if ((yy_size_t) ((yy_n_chars) + number_to_move) > YY_CURRENT_BUFFER_LVALUE->yy_buf_size) { + /* Extend the array by 50%, plus the number we really need. */ + yy_size_t new_size = (yy_n_chars) + number_to_move + ((yy_n_chars) >> 1); + YY_CURRENT_BUFFER_LVALUE->yy_ch_buf = (char *) sensors_yyrealloc((void *) YY_CURRENT_BUFFER_LVALUE->yy_ch_buf,new_size ); + if ( ! YY_CURRENT_BUFFER_LVALUE->yy_ch_buf ) + YY_FATAL_ERROR( "out of dynamic memory in yy_get_next_buffer()" ); + } + + (yy_n_chars) += number_to_move; + YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[(yy_n_chars)] = YY_END_OF_BUFFER_CHAR; + YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[(yy_n_chars) + 1] = YY_END_OF_BUFFER_CHAR; + + (yytext_ptr) = &YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[0]; + + return ret_val; +} + +/* yy_get_previous_state - get the state just before the EOB char was reached */ + + static yy_state_type yy_get_previous_state (void) +{ + register yy_state_type yy_current_state; + register char *yy_cp; + + yy_current_state = (yy_start); + + for ( yy_cp = (yytext_ptr) + YY_MORE_ADJ; yy_cp < (yy_c_buf_p); ++yy_cp ) + { + yy_current_state = yy_nxt[yy_current_state][(*yy_cp ? yy_ec[YY_SC_TO_UI(*yy_cp)] : 1)]; + } + + return yy_current_state; +} + +/* yy_try_NUL_trans - try to make a transition on the NUL character + * + * synopsis + * next_state = yy_try_NUL_trans( current_state ); + */ + static yy_state_type yy_try_NUL_trans (yy_state_type yy_current_state ) +{ + register int yy_is_jam; + + yy_current_state = yy_nxt[yy_current_state][1]; + yy_is_jam = (yy_current_state <= 0); + + return yy_is_jam ? 0 : yy_current_state; +} + +#ifndef YY_NO_INPUT +#ifdef __cplusplus + static int yyinput (void) +#else + static int input (void) +#endif + +{ + int c; + + *(yy_c_buf_p) = (yy_hold_char); + + if ( *(yy_c_buf_p) == YY_END_OF_BUFFER_CHAR ) + { + /* yy_c_buf_p now points to the character we want to return. + * If this occurs *before* the EOB characters, then it's a + * valid NUL; if not, then we've hit the end of the buffer. + */ + if ( (yy_c_buf_p) < &YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[(yy_n_chars)] ) + /* This was really a NUL. */ + *(yy_c_buf_p) = '\0'; + + else + { /* need more input */ + int offset = (yy_c_buf_p) - (yytext_ptr); + ++(yy_c_buf_p); + + switch ( yy_get_next_buffer( ) ) + { + case EOB_ACT_LAST_MATCH: + /* This happens because yy_g_n_b() + * sees that we've accumulated a + * token and flags that we need to + * try matching the token before + * proceeding. But for input(), + * there's no matching to consider. + * So convert the EOB_ACT_LAST_MATCH + * to EOB_ACT_END_OF_FILE. + */ + + /* Reset buffer status. */ + sensors_yyrestart(sensors_yyin ); + + /*FALLTHROUGH*/ + + case EOB_ACT_END_OF_FILE: + { + if ( sensors_yywrap( ) ) + return EOF; + + if ( ! (yy_did_buffer_switch_on_eof) ) + YY_NEW_FILE; +#ifdef __cplusplus + return yyinput(); +#else + return input(); +#endif + } + + case EOB_ACT_CONTINUE_SCAN: + (yy_c_buf_p) = (yytext_ptr) + offset; + break; + } + } + } + + c = *(unsigned char *) (yy_c_buf_p); /* cast for 8-bit char's */ + *(yy_c_buf_p) = '\0'; /* preserve sensors_yytext */ + (yy_hold_char) = *++(yy_c_buf_p); + + return c; +} +#endif /* ifndef YY_NO_INPUT */ + +/** Immediately switch to a different input stream. + * @param input_file A readable stream. + * + * @note This function does not reset the start condition to @c INITIAL . + */ + void sensors_yyrestart (FILE * input_file ) +{ + + if ( ! YY_CURRENT_BUFFER ){ + sensors_yyensure_buffer_stack (); + YY_CURRENT_BUFFER_LVALUE = + sensors_yy_create_buffer(sensors_yyin,YY_BUF_SIZE ); + } + + sensors_yy_init_buffer(YY_CURRENT_BUFFER,input_file ); + sensors_yy_load_buffer_state( ); +} + +/** Switch to a different input buffer. + * @param new_buffer The new input buffer. + * + */ + void sensors_yy_switch_to_buffer (YY_BUFFER_STATE new_buffer ) +{ + + /* TODO. We should be able to replace this entire function body + * with + * sensors_yypop_buffer_state(); + * sensors_yypush_buffer_state(new_buffer); + */ + sensors_yyensure_buffer_stack (); + if ( YY_CURRENT_BUFFER == new_buffer ) + return; + + if ( YY_CURRENT_BUFFER ) + { + /* Flush out information for old buffer. */ + *(yy_c_buf_p) = (yy_hold_char); + YY_CURRENT_BUFFER_LVALUE->yy_buf_pos = (yy_c_buf_p); + YY_CURRENT_BUFFER_LVALUE->yy_n_chars = (yy_n_chars); + } + + YY_CURRENT_BUFFER_LVALUE = new_buffer; + sensors_yy_load_buffer_state( ); + + /* We don't actually know whether we did this switch during + * EOF (sensors_yywrap()) processing, but the only time this flag + * is looked at is after sensors_yywrap() is called, so it's safe + * to go ahead and always set it. + */ + (yy_did_buffer_switch_on_eof) = 1; +} + +static void sensors_yy_load_buffer_state (void) +{ + (yy_n_chars) = YY_CURRENT_BUFFER_LVALUE->yy_n_chars; + (yytext_ptr) = (yy_c_buf_p) = YY_CURRENT_BUFFER_LVALUE->yy_buf_pos; + sensors_yyin = YY_CURRENT_BUFFER_LVALUE->yy_input_file; + (yy_hold_char) = *(yy_c_buf_p); +} + +/** Allocate and initialize an input buffer state. + * @param file A readable stream. + * @param size The character buffer size in bytes. When in doubt, use @c YY_BUF_SIZE. + * + * @return the allocated buffer state. + */ + YY_BUFFER_STATE sensors_yy_create_buffer (FILE * file, int size ) +{ + YY_BUFFER_STATE b; + + b = (YY_BUFFER_STATE) sensors_yyalloc(sizeof( struct yy_buffer_state ) ); + if ( ! b ) + YY_FATAL_ERROR( "out of dynamic memory in sensors_yy_create_buffer()" ); + + b->yy_buf_size = size; + + /* yy_ch_buf has to be 2 characters longer than the size given because + * we need to put in 2 end-of-buffer characters. + */ + b->yy_ch_buf = (char *) sensors_yyalloc(b->yy_buf_size + 2 ); + if ( ! b->yy_ch_buf ) + YY_FATAL_ERROR( "out of dynamic memory in sensors_yy_create_buffer()" ); + + b->yy_is_our_buffer = 1; + + sensors_yy_init_buffer(b,file ); + + return b; +} + +/** Destroy the buffer. + * @param b a buffer created with sensors_yy_create_buffer() + * + */ + void sensors_yy_delete_buffer (YY_BUFFER_STATE b ) +{ + + if ( ! b ) + return; + + if ( b == YY_CURRENT_BUFFER ) /* Not sure if we should pop here. */ + YY_CURRENT_BUFFER_LVALUE = (YY_BUFFER_STATE) 0; + + if ( b->yy_is_our_buffer ) + sensors_yyfree((void *) b->yy_ch_buf ); + + sensors_yyfree((void *) b ); +} + +#ifndef __cplusplus +extern int isatty (int ); +#endif /* __cplusplus */ + +/* Initializes or reinitializes a buffer. + * This function is sometimes called more than once on the same buffer, + * such as during a sensors_yyrestart() or at EOF. + */ + static void sensors_yy_init_buffer (YY_BUFFER_STATE b, FILE * file ) + +{ + int oerrno = errno; + + sensors_yy_flush_buffer(b ); + + b->yy_input_file = file; + b->yy_fill_buffer = 1; + + /* If b is the current buffer, then sensors_yy_init_buffer was _probably_ + * called from sensors_yyrestart() or through yy_get_next_buffer. + * In that case, we don't want to reset the lineno or column. + */ + if (b != YY_CURRENT_BUFFER){ + b->yy_bs_lineno = 1; + b->yy_bs_column = 0; + } + + b->yy_is_interactive = file ? (isatty( fileno(file) ) > 0) : 0; + + errno = oerrno; +} + +/** Discard all buffered characters. On the next scan, YY_INPUT will be called. + * @param b the buffer state to be flushed, usually @c YY_CURRENT_BUFFER. + * + */ + void sensors_yy_flush_buffer (YY_BUFFER_STATE b ) +{ + if ( ! b ) + return; + + b->yy_n_chars = 0; + + /* We always need two end-of-buffer characters. The first causes + * a transition to the end-of-buffer state. The second causes + * a jam in that state. + */ + b->yy_ch_buf[0] = YY_END_OF_BUFFER_CHAR; + b->yy_ch_buf[1] = YY_END_OF_BUFFER_CHAR; + + b->yy_buf_pos = &b->yy_ch_buf[0]; + + b->yy_at_bol = 1; + b->yy_buffer_status = YY_BUFFER_NEW; + + if ( b == YY_CURRENT_BUFFER ) + sensors_yy_load_buffer_state( ); +} + +/** Pushes the new state onto the stack. The new state becomes + * the current state. This function will allocate the stack + * if necessary. + * @param new_buffer The new state. + * + */ +void sensors_yypush_buffer_state (YY_BUFFER_STATE new_buffer ) +{ + if (new_buffer == NULL) + return; + + sensors_yyensure_buffer_stack(); + + /* This block is copied from sensors_yy_switch_to_buffer. */ + if ( YY_CURRENT_BUFFER ) + { + /* Flush out information for old buffer. */ + *(yy_c_buf_p) = (yy_hold_char); + YY_CURRENT_BUFFER_LVALUE->yy_buf_pos = (yy_c_buf_p); + YY_CURRENT_BUFFER_LVALUE->yy_n_chars = (yy_n_chars); + } + + /* Only push if top exists. Otherwise, replace top. */ + if (YY_CURRENT_BUFFER) + (yy_buffer_stack_top)++; + YY_CURRENT_BUFFER_LVALUE = new_buffer; + + /* copied from sensors_yy_switch_to_buffer. */ + sensors_yy_load_buffer_state( ); + (yy_did_buffer_switch_on_eof) = 1; +} + +/** Removes and deletes the top of the stack, if present. + * The next element becomes the new top. + * + */ +void sensors_yypop_buffer_state (void) +{ + if (!YY_CURRENT_BUFFER) + return; + + sensors_yy_delete_buffer(YY_CURRENT_BUFFER ); + YY_CURRENT_BUFFER_LVALUE = NULL; + if ((yy_buffer_stack_top) > 0) + --(yy_buffer_stack_top); + + if (YY_CURRENT_BUFFER) { + sensors_yy_load_buffer_state( ); + (yy_did_buffer_switch_on_eof) = 1; + } +} + +/* Allocates the stack if it does not exist. + * Guarantees space for at least one push. + */ +static void sensors_yyensure_buffer_stack (void) +{ + int num_to_alloc; + + if (!(yy_buffer_stack)) { + + /* First allocation is just for 2 elements, since we don't know if this + * scanner will even need a stack. We use 2 instead of 1 to avoid an + * immediate realloc on the next call. + */ + num_to_alloc = 1; + (yy_buffer_stack) = (struct yy_buffer_state**)sensors_yyalloc + (num_to_alloc * sizeof(struct yy_buffer_state*) + ); + if ( ! (yy_buffer_stack) ) + YY_FATAL_ERROR( "out of dynamic memory in sensors_yyensure_buffer_stack()" ); + + memset((yy_buffer_stack), 0, num_to_alloc * sizeof(struct yy_buffer_state*)); + + (yy_buffer_stack_max) = num_to_alloc; + (yy_buffer_stack_top) = 0; + return; + } + + if ((yy_buffer_stack_top) >= ((yy_buffer_stack_max)) - 1){ + + /* Increase the buffer to prepare for a possible push. */ + int grow_size = 8 /* arbitrary grow size */; + + num_to_alloc = (yy_buffer_stack_max) + grow_size; + (yy_buffer_stack) = (struct yy_buffer_state**)sensors_yyrealloc + ((yy_buffer_stack), + num_to_alloc * sizeof(struct yy_buffer_state*) + ); + if ( ! (yy_buffer_stack) ) + YY_FATAL_ERROR( "out of dynamic memory in sensors_yyensure_buffer_stack()" ); + + /* zero only the new slots.*/ + memset((yy_buffer_stack) + (yy_buffer_stack_max), 0, grow_size * sizeof(struct yy_buffer_state*)); + (yy_buffer_stack_max) = num_to_alloc; + } +} + +/** Setup the input buffer state to scan directly from a user-specified character buffer. + * @param base the character buffer + * @param size the size in bytes of the character buffer + * + * @return the newly allocated buffer state object. + */ +YY_BUFFER_STATE sensors_yy_scan_buffer (char * base, yy_size_t size ) +{ + YY_BUFFER_STATE b; + + if ( size < 2 || + base[size-2] != YY_END_OF_BUFFER_CHAR || + base[size-1] != YY_END_OF_BUFFER_CHAR ) + /* They forgot to leave room for the EOB's. */ + return 0; + + b = (YY_BUFFER_STATE) sensors_yyalloc(sizeof( struct yy_buffer_state ) ); + if ( ! b ) + YY_FATAL_ERROR( "out of dynamic memory in sensors_yy_scan_buffer()" ); + + b->yy_buf_size = size - 2; /* "- 2" to take care of EOB's */ + b->yy_buf_pos = b->yy_ch_buf = base; + b->yy_is_our_buffer = 0; + b->yy_input_file = 0; + b->yy_n_chars = b->yy_buf_size; + b->yy_is_interactive = 0; + b->yy_at_bol = 1; + b->yy_fill_buffer = 0; + b->yy_buffer_status = YY_BUFFER_NEW; + + sensors_yy_switch_to_buffer(b ); + + return b; +} + +/** Setup the input buffer state to scan a string. The next call to sensors_yylex() will + * scan from a @e copy of @a str. + * @param yystr a NUL-terminated string to scan + * + * @return the newly allocated buffer state object. + * @note If you want to scan bytes that may contain NUL values, then use + * sensors_yy_scan_bytes() instead. + */ +YY_BUFFER_STATE sensors_yy_scan_string (yyconst char * yystr ) +{ + + return sensors_yy_scan_bytes(yystr,strlen(yystr) ); +} + +/** Setup the input buffer state to scan the given bytes. The next call to sensors_yylex() will + * scan from a @e copy of @a bytes. + * @param yybytes the byte buffer to scan + * @param _yybytes_len the number of bytes in the buffer pointed to by @a bytes. + * + * @return the newly allocated buffer state object. + */ +YY_BUFFER_STATE sensors_yy_scan_bytes (yyconst char * yybytes, int _yybytes_len ) +{ + YY_BUFFER_STATE b; + char *buf; + yy_size_t n; + int i; + + /* Get memory for full buffer, including space for trailing EOB's. */ + n = _yybytes_len + 2; + buf = (char *) sensors_yyalloc(n ); + if ( ! buf ) + YY_FATAL_ERROR( "out of dynamic memory in sensors_yy_scan_bytes()" ); + + for ( i = 0; i < _yybytes_len; ++i ) + buf[i] = yybytes[i]; + + buf[_yybytes_len] = buf[_yybytes_len+1] = YY_END_OF_BUFFER_CHAR; + + b = sensors_yy_scan_buffer(buf,n ); + if ( ! b ) + YY_FATAL_ERROR( "bad buffer in sensors_yy_scan_bytes()" ); + + /* It's okay to grow etc. this buffer, and we should throw it + * away when we're done. + */ + b->yy_is_our_buffer = 1; + + return b; +} + +#ifndef YY_EXIT_FAILURE +#define YY_EXIT_FAILURE 2 +#endif + +static void yy_fatal_error (yyconst char* msg ) +{ + (void) fprintf( stderr, "%s\n", msg ); + exit( YY_EXIT_FAILURE ); +} + +/* Redefine yyless() so it works in section 3 code. */ + +#undef yyless +#define yyless(n) \ + do \ + { \ + /* Undo effects of setting up sensors_yytext. */ \ + int yyless_macro_arg = (n); \ + YY_LESS_LINENO(yyless_macro_arg);\ + sensors_yytext[sensors_yyleng] = (yy_hold_char); \ + (yy_c_buf_p) = sensors_yytext + yyless_macro_arg; \ + (yy_hold_char) = *(yy_c_buf_p); \ + *(yy_c_buf_p) = '\0'; \ + sensors_yyleng = yyless_macro_arg; \ + } \ + while ( 0 ) + +/* Accessor methods (get/set functions) to struct members. */ + +/** Get the current line number. + * + */ +int sensors_yyget_lineno (void) +{ + + return sensors_yylineno; +} + +/** Get the input stream. + * + */ +FILE *sensors_yyget_in (void) +{ + return sensors_yyin; +} + +/** Get the output stream. + * + */ +FILE *sensors_yyget_out (void) +{ + return sensors_yyout; +} + +/** Get the length of the current token. + * + */ +int sensors_yyget_leng (void) +{ + return sensors_yyleng; +} + +/** Get the current token. + * + */ + +char *sensors_yyget_text (void) +{ + return sensors_yytext; +} + +/** Set the current line number. + * @param line_number + * + */ +void sensors_yyset_lineno (int line_number ) +{ + + sensors_yylineno = line_number; +} + +/** Set the input stream. This does not discard the current + * input buffer. + * @param in_str A readable stream. + * + * @see sensors_yy_switch_to_buffer + */ +void sensors_yyset_in (FILE * in_str ) +{ + sensors_yyin = in_str ; +} + +void sensors_yyset_out (FILE * out_str ) +{ + sensors_yyout = out_str ; +} + +int sensors_yyget_debug (void) +{ + return sensors_yy_flex_debug; +} + +void sensors_yyset_debug (int bdebug ) +{ + sensors_yy_flex_debug = bdebug ; +} + +static int yy_init_globals (void) +{ + /* Initialization is the same as for the non-reentrant scanner. + * This function is called from sensors_yylex_destroy(), so don't allocate here. + */ + + (yy_buffer_stack) = 0; + (yy_buffer_stack_top) = 0; + (yy_buffer_stack_max) = 0; + (yy_c_buf_p) = (char *) 0; + (yy_init) = 0; + (yy_start) = 0; + +/* Defined in main.c */ +#ifdef YY_STDINIT + sensors_yyin = stdin; + sensors_yyout = stdout; +#else + sensors_yyin = (FILE *) 0; + sensors_yyout = (FILE *) 0; +#endif + + /* For future reference: Set errno on error, since we are called by + * sensors_yylex_init() + */ + return 0; +} + +/* sensors_yylex_destroy is for both reentrant and non-reentrant scanners. */ +int sensors_yylex_destroy (void) +{ + + /* Pop the buffer stack, destroying each element. */ + while(YY_CURRENT_BUFFER){ + sensors_yy_delete_buffer(YY_CURRENT_BUFFER ); + YY_CURRENT_BUFFER_LVALUE = NULL; + sensors_yypop_buffer_state(); + } + + /* Destroy the stack itself. */ + sensors_yyfree((yy_buffer_stack) ); + (yy_buffer_stack) = NULL; + + /* Reset the globals. This is important in a non-reentrant scanner so the next time + * sensors_yylex() is called, initialization will occur. */ + yy_init_globals( ); + + return 0; +} + +/* + * Internal utility routines. + */ + +#ifndef yytext_ptr +static void yy_flex_strncpy (char* s1, yyconst char * s2, int n ) +{ + register int i; + for ( i = 0; i < n; ++i ) + s1[i] = s2[i]; +} +#endif + +#ifdef YY_NEED_STRLEN +static int yy_flex_strlen (yyconst char * s ) +{ + register int n; + for ( n = 0; s[n]; ++n ) + ; + + return n; +} +#endif + +void *sensors_yyalloc (yy_size_t size ) +{ + return (void *) malloc( size ); +} + +void *sensors_yyrealloc (void * ptr, yy_size_t size ) +{ + /* The cast to (char *) in the following accommodates both + * implementations that use char* generic pointers, and those + * that use void* generic pointers. It works with the latter + * because both ANSI C and C++ allow castless assignment from + * any pointer type to void*, and deal with argument conversions + * as though doing an assignment. + */ + return (void *) realloc( (char *) ptr, size ); +} + +void sensors_yyfree (void * ptr ) +{ + free( (char *) ptr ); /* see sensors_yyrealloc() for (char *) cast */ +} + +#define YYTABLES_NAME "yytables" + +#line 332 "lib/conf-lex.l" + + + +/* + Do the buffer handling manually. This allows us to scan as many + config files as we need to, while cleaning up properly after each + one. The "BEGIN(0)" line ensures that we start in the default state, + even if e.g. the previous config file was syntactically broken. + + Returns 0 if successful, !0 otherwise. +*/ + +static YY_BUFFER_STATE scan_buf = (YY_BUFFER_STATE)0; + +int sensors_scanner_init(FILE *input, const char *filename) +{ + BEGIN(0); + if (!(scan_buf = sensors_yy_create_buffer(input, YY_BUF_SIZE))) + return -1; + + sensors_yy_switch_to_buffer(scan_buf); + sensors_yyfilename = filename; + sensors_yylineno = 1; + return 0; +} + +void sensors_scanner_exit(void) +{ + sensors_yy_delete_buffer(scan_buf); + scan_buf = (YY_BUFFER_STATE)0; + +/* As of flex 2.5.9, sensors_yylex_destroy() must be called when done with the + scaller, otherwise we'll leak memory. */ +#if defined(YY_FLEX_MAJOR_VERSION) && defined(YY_FLEX_MINOR_VERSION) && defined(YY_FLEX_SUBMINOR_VERSION) +#if YY_FLEX_MAJOR_VERSION > 2 || \ + (YY_FLEX_MAJOR_VERSION == 2 && (YY_FLEX_MINOR_VERSION > 5 || \ + (YY_FLEX_MINOR_VERSION == 5 && YY_FLEX_SUBMINOR_VERSION >= 9))) + sensors_yylex_destroy(); +#endif +#endif +} + + diff --git a/tools/gator/daemon/libsensors/conf-lex.l b/tools/gator/daemon/libsensors/conf-lex.l new file mode 100644 index 00000000000..43ddbd8f5bd --- /dev/null +++ b/tools/gator/daemon/libsensors/conf-lex.l @@ -0,0 +1,372 @@ +%{ +/* + conf-lex.l - Part of libsensors, a Linux library for reading sensor data. + Copyright (c) 1998, 1999 Frodo Looijaard + + 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 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 +#include + +#include "general.h" +#include "data.h" +#include "conf-parse.h" +#include "error.h" +#include "scanner.h" + +static int buffer_count; +static int buffer_max; +static char *buffer; + +char sensors_lex_error[100]; + +const char *sensors_yyfilename; +int sensors_yylineno; + +#define buffer_malloc() sensors_malloc_array(&buffer,&buffer_count,\ + &buffer_max,1) +#define buffer_free() sensors_free_array(&buffer,&buffer_count,\ + &buffer_max) +#define buffer_add_char(c) sensors_add_array_el(c,&buffer,\ + &buffer_count,\ + &buffer_max,1) +#define buffer_add_string(s) sensors_add_array_els(s,strlen(s),\ + &buffer, \ + &buffer_count,&buffer_max,1) + +%} + + /* Scanner for configuration files */ + +%option nodefault +%option noyywrap +%option nounput + + /* All states are exclusive */ + +%x MIDDLE +%x STRING +%x ERR + + /* Any whitespace-like character */ + +BLANK [ \f\r\t\v] + +IDCHAR [[:alnum:]_] + + /* Note: `10', `10.4' and `.4' are valid, `10.' is not */ + +FLOAT [[:digit:]]*\.?[[:digit:]]+ + + /* Only positive whole numbers are recognized here */ + +NUM 0|([1-9][[:digit:]]*) + + +%% + + /* + * STATE: INITIAL + */ + +{ + +<> { /* EOF from this state terminates */ + return 0; + } + +{BLANK}+ ; /* eat as many blanks as possible at once */ + +{BLANK}*\n { /* eat a bare newline (possibly preceded by blanks) */ + sensors_yylineno++; + } + + /* comments */ + +#.* ; /* eat the rest of the line after comment char */ + +#.*\n { /* eat the rest of the line after comment char */ + sensors_yylineno++; + } + + /* + * Keywords must be followed by whitespace - eat that too. + * If there isn't trailing whitespace, we still need to + * accept it as lexically correct (even though the parser + * will reject it anyway.) + */ + +label{BLANK}* { + sensors_yylval.line.filename = sensors_yyfilename; + sensors_yylval.line.lineno = sensors_yylineno; + BEGIN(MIDDLE); + return LABEL; + } + +set{BLANK}* { + sensors_yylval.line.filename = sensors_yyfilename; + sensors_yylval.line.lineno = sensors_yylineno; + BEGIN(MIDDLE); + return SET; + } + +compute{BLANK}* { + sensors_yylval.line.filename = sensors_yyfilename; + sensors_yylval.line.lineno = sensors_yylineno; + BEGIN(MIDDLE); + return COMPUTE; + } + +bus{BLANK}* { + sensors_yylval.line.filename = sensors_yyfilename; + sensors_yylval.line.lineno = sensors_yylineno; + BEGIN(MIDDLE); + return BUS; + } + +chip{BLANK}* { + sensors_yylval.line.filename = sensors_yyfilename; + sensors_yylval.line.lineno = sensors_yylineno; + BEGIN(MIDDLE); + return CHIP; + } + +ignore{BLANK}* { + sensors_yylval.line.filename = sensors_yyfilename; + sensors_yylval.line.lineno = sensors_yylineno; + BEGIN(MIDDLE); + return IGNORE; + } + + /* Anything else at the beginning of a line is an error */ + +[a-z]+ | +. { + BEGIN(ERR); + strcpy(sensors_lex_error,"Invalid keyword"); + return ERROR; + } +} + + /* + * STATE: ERROR + */ + +{ + +.* ; /* eat whatever is left on this line */ + +\n { + BEGIN(INITIAL); + sensors_yylineno++; + return EOL; + } +} + + /* + * STATE: MIDDLE + */ + +{ + +{BLANK}+ ; /* eat as many blanks as possible at once */ + +\n { /* newline here sends EOL token to parser */ + BEGIN(INITIAL); + sensors_yylineno++; + return EOL; + } + +<> { /* EOF here sends EOL token to parser also */ + BEGIN(INITIAL); + return EOL; + } + +\\{BLANK}*\n { /* eat an escaped newline with no state change */ + sensors_yylineno++; + } + + /* comments */ + +#.* ; /* eat the rest of the line after comment char */ + +#.*\n { /* eat the rest of the line after comment char */ + BEGIN(INITIAL); + sensors_yylineno++; + return EOL; + } + + /* A number */ + +{FLOAT} { + sensors_yylval.value = atof(sensors_yytext); + return FLOAT; + } + + /* Some operators */ + +"+" return '+'; +"-" return '-'; +"*" return '*'; +"/" return '/'; +"(" return '('; +")" return ')'; +"," return ','; +"@" return '@'; +"^" return '^'; +"`" return '`'; + + /* Quoted string */ + +\" { + buffer_malloc(); + BEGIN(STRING); + } + + /* A normal, unquoted identifier */ + +{IDCHAR}+ { + sensors_yylval.name = strdup(sensors_yytext); + if (! sensors_yylval.name) + sensors_fatal_error("conf-lex.l", + "Allocating a new string"); + + return NAME; + } + + /* anything else is bogus */ + +. | +[[:digit:]]*\. | +\\{BLANK}* { + BEGIN(ERR); + return ERROR; + } +} + + /* + * STATE: STRING + */ + +{ + + /* Oops, newline or EOF while in a string is not good */ + +\n | +\\\n { + buffer_add_char("\0"); + strcpy(sensors_lex_error, + "No matching double quote."); + buffer_free(); + yyless(0); + BEGIN(ERR); + return ERROR; + } + +<> { + strcpy(sensors_lex_error, + "Reached end-of-file without a matching double quote."); + buffer_free(); + BEGIN(MIDDLE); + return ERROR; + } + + /* At the end */ + +\"\" { + buffer_add_char("\0"); + strcpy(sensors_lex_error, + "Quoted strings must be separated by whitespace."); + buffer_free(); + BEGIN(ERR); + return ERROR; + } + +\" { + buffer_add_char("\0"); + sensors_yylval.name = strdup(buffer); + if (! sensors_yylval.name) + sensors_fatal_error("conf-lex.l", + "Allocating a new string"); + buffer_free(); + BEGIN(MIDDLE); + return NAME; + } + +\\a buffer_add_char("\a"); +\\b buffer_add_char("\b"); +\\f buffer_add_char("\f"); +\\n buffer_add_char("\n"); +\\r buffer_add_char("\r"); +\\t buffer_add_char("\t"); +\\v buffer_add_char("\v"); + + /* Other escapes: just copy the character behind the slash */ + +\\. { + buffer_add_char(&sensors_yytext[1]); + } + + /* Anything else (including a bare '\' which may be followed by EOF) */ + +\\ | +[^\\\n\"]+ { + buffer_add_string(sensors_yytext); + } +} + +%% + +/* + Do the buffer handling manually. This allows us to scan as many + config files as we need to, while cleaning up properly after each + one. The "BEGIN(0)" line ensures that we start in the default state, + even if e.g. the previous config file was syntactically broken. + + Returns 0 if successful, !0 otherwise. +*/ + +static YY_BUFFER_STATE scan_buf = (YY_BUFFER_STATE)0; + +int sensors_scanner_init(FILE *input, const char *filename) +{ + BEGIN(0); + if (!(scan_buf = sensors_yy_create_buffer(input, YY_BUF_SIZE))) + return -1; + + sensors_yy_switch_to_buffer(scan_buf); + sensors_yyfilename = filename; + sensors_yylineno = 1; + return 0; +} + +void sensors_scanner_exit(void) +{ + sensors_yy_delete_buffer(scan_buf); + scan_buf = (YY_BUFFER_STATE)0; + +/* As of flex 2.5.9, yylex_destroy() must be called when done with the + scaller, otherwise we'll leak memory. */ +#if defined(YY_FLEX_MAJOR_VERSION) && defined(YY_FLEX_MINOR_VERSION) && defined(YY_FLEX_SUBMINOR_VERSION) +#if YY_FLEX_MAJOR_VERSION > 2 || \ + (YY_FLEX_MAJOR_VERSION == 2 && (YY_FLEX_MINOR_VERSION > 5 || \ + (YY_FLEX_MINOR_VERSION == 5 && YY_FLEX_SUBMINOR_VERSION >= 9))) + sensors_yylex_destroy(); +#endif +#endif +} + diff --git a/tools/gator/daemon/libsensors/conf-parse.c b/tools/gator/daemon/libsensors/conf-parse.c new file mode 100644 index 00000000000..fb775460a1c --- /dev/null +++ b/tools/gator/daemon/libsensors/conf-parse.c @@ -0,0 +1,2042 @@ +/* A Bison parser, made by GNU Bison 2.5. */ + +/* Bison implementation for Yacc-like parsers in C + + Copyright (C) 1984, 1989-1990, 2000-2011 Free Software Foundation, Inc. + + 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 3 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, see . */ + +/* As a special exception, you may create a larger work that contains + part or all of the Bison parser skeleton and distribute that work + under terms of your choice, so long as that work isn't itself a + parser generator using the skeleton or a modified version thereof + as a parser skeleton. Alternatively, if you modify or redistribute + the parser skeleton itself, you may (at your option) remove this + special exception, which will cause the skeleton and the resulting + Bison output files to be licensed under the GNU General Public + License without this special exception. + + This special exception was added by the Free Software Foundation in + version 2.2 of Bison. */ + +/* C LALR(1) parser skeleton written by Richard Stallman, by + simplifying the original so-called "semantic" parser. */ + +/* All symbols defined below should begin with yy or YY, to avoid + infringing on user name space. This should be done even for local + variables, as they might otherwise be expanded by user macros. + There are some unavoidable exceptions within include files to + define necessary library symbols; they are noted "INFRINGES ON + USER NAME SPACE" below. */ + +/* Identify Bison output. */ +#define YYBISON 1 + +/* Bison version. */ +#define YYBISON_VERSION "2.5" + +/* Skeleton name. */ +#define YYSKELETON_NAME "yacc.c" + +/* Pure parsers. */ +#define YYPURE 0 + +/* Push parsers. */ +#define YYPUSH 0 + +/* Pull parsers. */ +#define YYPULL 1 + +/* Using locations. */ +#define YYLSP_NEEDED 0 + +/* Substitute the variable and function names. */ +#define yyparse sensors_yyparse +#define yylex sensors_yylex +#define yyerror sensors_yyerror +#define yylval sensors_yylval +#define yychar sensors_yychar +#define yydebug sensors_yydebug +#define yynerrs sensors_yynerrs + + +/* Copy the first part of user declarations. */ + +/* Line 268 of yacc.c */ +#line 1 "lib/conf-parse.y" + +/* + conf-parse.y - Part of libsensors, a Linux library for reading sensor data. + Copyright (c) 1998, 1999 Frodo Looijaard + + 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 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. +*/ + +#define YYERROR_VERBOSE + +#include +#include +#include + +#include "data.h" +#include "general.h" +#include "error.h" +#include "conf.h" +#include "access.h" +#include "init.h" + +static void sensors_yyerror(const char *err); +static sensors_expr *malloc_expr(void); + +static sensors_chip *current_chip = NULL; + +#define bus_add_el(el) sensors_add_array_el(el,\ + &sensors_config_busses,\ + &sensors_config_busses_count,\ + &sensors_config_busses_max,\ + sizeof(sensors_bus)) +#define label_add_el(el) sensors_add_array_el(el,\ + ¤t_chip->labels,\ + ¤t_chip->labels_count,\ + ¤t_chip->labels_max,\ + sizeof(sensors_label)); +#define set_add_el(el) sensors_add_array_el(el,\ + ¤t_chip->sets,\ + ¤t_chip->sets_count,\ + ¤t_chip->sets_max,\ + sizeof(sensors_set)); +#define compute_add_el(el) sensors_add_array_el(el,\ + ¤t_chip->computes,\ + ¤t_chip->computes_count,\ + ¤t_chip->computes_max,\ + sizeof(sensors_compute)); +#define ignore_add_el(el) sensors_add_array_el(el,\ + ¤t_chip->ignores,\ + ¤t_chip->ignores_count,\ + ¤t_chip->ignores_max,\ + sizeof(sensors_ignore)); +#define chip_add_el(el) sensors_add_array_el(el,\ + &sensors_config_chips,\ + &sensors_config_chips_count,\ + &sensors_config_chips_max,\ + sizeof(sensors_chip)); + +#define fits_add_el(el,list) sensors_add_array_el(el,\ + &(list).fits,\ + &(list).fits_count,\ + &(list).fits_max, \ + sizeof(sensors_chip_name)); + + + +/* Line 268 of yacc.c */ +#line 158 "lib/conf-parse.c" + +/* Enabling traces. */ +#ifndef YYDEBUG +# define YYDEBUG 0 +#endif + +/* Enabling verbose error messages. */ +#ifdef YYERROR_VERBOSE +# undef YYERROR_VERBOSE +# define YYERROR_VERBOSE 1 +#else +# define YYERROR_VERBOSE 0 +#endif + +/* Enabling the token table. */ +#ifndef YYTOKEN_TABLE +# define YYTOKEN_TABLE 0 +#endif + + +/* Tokens. */ +#ifndef YYTOKENTYPE +# define YYTOKENTYPE + /* Put the tokens into the symbol table, so that GDB and other debuggers + know about them. */ + enum yytokentype { + NEG = 258, + EOL = 259, + BUS = 260, + LABEL = 261, + SET = 262, + CHIP = 263, + COMPUTE = 264, + IGNORE = 265, + FLOAT = 266, + NAME = 267, + ERROR = 268 + }; +#endif + + + +#if ! defined YYSTYPE && ! defined YYSTYPE_IS_DECLARED +typedef union YYSTYPE +{ + +/* Line 293 of yacc.c */ +#line 79 "lib/conf-parse.y" + + double value; + char *name; + void *nothing; + sensors_chip_name_list chips; + sensors_expr *expr; + sensors_bus_id bus; + sensors_chip_name chip; + sensors_config_line line; + + + +/* Line 293 of yacc.c */ +#line 220 "lib/conf-parse.c" +} YYSTYPE; +# define YYSTYPE_IS_TRIVIAL 1 +# define yystype YYSTYPE /* obsolescent; will be withdrawn */ +# define YYSTYPE_IS_DECLARED 1 +#endif + + +/* Copy the second part of user declarations. */ + + +/* Line 343 of yacc.c */ +#line 232 "lib/conf-parse.c" + +#ifdef short +# undef short +#endif + +#ifdef YYTYPE_UINT8 +typedef YYTYPE_UINT8 yytype_uint8; +#else +typedef unsigned char yytype_uint8; +#endif + +#ifdef YYTYPE_INT8 +typedef YYTYPE_INT8 yytype_int8; +#elif (defined __STDC__ || defined __C99__FUNC__ \ + || defined __cplusplus || defined _MSC_VER) +typedef signed char yytype_int8; +#else +typedef short int yytype_int8; +#endif + +#ifdef YYTYPE_UINT16 +typedef YYTYPE_UINT16 yytype_uint16; +#else +typedef unsigned short int yytype_uint16; +#endif + +#ifdef YYTYPE_INT16 +typedef YYTYPE_INT16 yytype_int16; +#else +typedef short int yytype_int16; +#endif + +#ifndef YYSIZE_T +# ifdef __SIZE_TYPE__ +# define YYSIZE_T __SIZE_TYPE__ +# elif defined size_t +# define YYSIZE_T size_t +# elif ! defined YYSIZE_T && (defined __STDC__ || defined __C99__FUNC__ \ + || defined __cplusplus || defined _MSC_VER) +# include /* INFRINGES ON USER NAME SPACE */ +# define YYSIZE_T size_t +# else +# define YYSIZE_T unsigned int +# endif +#endif + +#define YYSIZE_MAXIMUM ((YYSIZE_T) -1) + +#ifndef YY_ +# if defined YYENABLE_NLS && YYENABLE_NLS +# if ENABLE_NLS +# include /* INFRINGES ON USER NAME SPACE */ +# define YY_(msgid) dgettext ("bison-runtime", msgid) +# endif +# endif +# ifndef YY_ +# define YY_(msgid) msgid +# endif +#endif + +/* Suppress unused-variable warnings by "using" E. */ +#if ! defined lint || defined __GNUC__ +# define YYUSE(e) ((void) (e)) +#else +# define YYUSE(e) /* empty */ +#endif + +/* Identity function, used to suppress warnings about constant conditions. */ +#ifndef lint +# define YYID(n) (n) +#else +#if (defined __STDC__ || defined __C99__FUNC__ \ + || defined __cplusplus || defined _MSC_VER) +static int +YYID (int yyi) +#else +static int +YYID (yyi) + int yyi; +#endif +{ + return yyi; +} +#endif + +#if ! defined yyoverflow || YYERROR_VERBOSE + +/* The parser invokes alloca or malloc; define the necessary symbols. */ + +# ifdef YYSTACK_USE_ALLOCA +# if YYSTACK_USE_ALLOCA +# ifdef __GNUC__ +# define YYSTACK_ALLOC __builtin_alloca +# elif defined __BUILTIN_VA_ARG_INCR +# include /* INFRINGES ON USER NAME SPACE */ +# elif defined _AIX +# define YYSTACK_ALLOC __alloca +# elif defined _MSC_VER +# include /* INFRINGES ON USER NAME SPACE */ +# define alloca _alloca +# else +# define YYSTACK_ALLOC alloca +# if ! defined _ALLOCA_H && ! defined EXIT_SUCCESS && (defined __STDC__ || defined __C99__FUNC__ \ + || defined __cplusplus || defined _MSC_VER) +# include /* INFRINGES ON USER NAME SPACE */ +# ifndef EXIT_SUCCESS +# define EXIT_SUCCESS 0 +# endif +# endif +# endif +# endif +# endif + +# ifdef YYSTACK_ALLOC + /* Pacify GCC's `empty if-body' warning. */ +# define YYSTACK_FREE(Ptr) do { /* empty */; } while (YYID (0)) +# ifndef YYSTACK_ALLOC_MAXIMUM + /* The OS might guarantee only one guard page at the bottom of the stack, + and a page size can be as small as 4096 bytes. So we cannot safely + invoke alloca (N) if N exceeds 4096. Use a slightly smaller number + to allow for a few compiler-allocated temporary stack slots. */ +# define YYSTACK_ALLOC_MAXIMUM 4032 /* reasonable circa 2006 */ +# endif +# else +# define YYSTACK_ALLOC YYMALLOC +# define YYSTACK_FREE YYFREE +# ifndef YYSTACK_ALLOC_MAXIMUM +# define YYSTACK_ALLOC_MAXIMUM YYSIZE_MAXIMUM +# endif +# if (defined __cplusplus && ! defined EXIT_SUCCESS \ + && ! ((defined YYMALLOC || defined malloc) \ + && (defined YYFREE || defined free))) +# include /* INFRINGES ON USER NAME SPACE */ +# ifndef EXIT_SUCCESS +# define EXIT_SUCCESS 0 +# endif +# endif +# ifndef YYMALLOC +# define YYMALLOC malloc +# if ! defined malloc && ! defined EXIT_SUCCESS && (defined __STDC__ || defined __C99__FUNC__ \ + || defined __cplusplus || defined _MSC_VER) +void *malloc (YYSIZE_T); /* INFRINGES ON USER NAME SPACE */ +# endif +# endif +# ifndef YYFREE +# define YYFREE free +# if ! defined free && ! defined EXIT_SUCCESS && (defined __STDC__ || defined __C99__FUNC__ \ + || defined __cplusplus || defined _MSC_VER) +void free (void *); /* INFRINGES ON USER NAME SPACE */ +# endif +# endif +# endif +#endif /* ! defined yyoverflow || YYERROR_VERBOSE */ + + +#if (! defined yyoverflow \ + && (! defined __cplusplus \ + || (defined YYSTYPE_IS_TRIVIAL && YYSTYPE_IS_TRIVIAL))) + +/* A type that is properly aligned for any stack member. */ +union yyalloc +{ + yytype_int16 yyss_alloc; + YYSTYPE yyvs_alloc; +}; + +/* The size of the maximum gap between one aligned stack and the next. */ +# define YYSTACK_GAP_MAXIMUM (sizeof (union yyalloc) - 1) + +/* The size of an array large to enough to hold all stacks, each with + N elements. */ +# define YYSTACK_BYTES(N) \ + ((N) * (sizeof (yytype_int16) + sizeof (YYSTYPE)) \ + + YYSTACK_GAP_MAXIMUM) + +# define YYCOPY_NEEDED 1 + +/* Relocate STACK from its old location to the new one. The + local variables YYSIZE and YYSTACKSIZE give the old and new number of + elements in the stack, and YYPTR gives the new location of the + stack. Advance YYPTR to a properly aligned location for the next + stack. */ +# define YYSTACK_RELOCATE(Stack_alloc, Stack) \ + do \ + { \ + YYSIZE_T yynewbytes; \ + YYCOPY (&yyptr->Stack_alloc, Stack, yysize); \ + Stack = &yyptr->Stack_alloc; \ + yynewbytes = yystacksize * sizeof (*Stack) + YYSTACK_GAP_MAXIMUM; \ + yyptr += yynewbytes / sizeof (*yyptr); \ + } \ + while (YYID (0)) + +#endif + +#if defined YYCOPY_NEEDED && YYCOPY_NEEDED +/* Copy COUNT objects from FROM to TO. The source and destination do + not overlap. */ +# ifndef YYCOPY +# if defined __GNUC__ && 1 < __GNUC__ +# define YYCOPY(To, From, Count) \ + __builtin_memcpy (To, From, (Count) * sizeof (*(From))) +# else +# define YYCOPY(To, From, Count) \ + do \ + { \ + YYSIZE_T yyi; \ + for (yyi = 0; yyi < (Count); yyi++) \ + (To)[yyi] = (From)[yyi]; \ + } \ + while (YYID (0)) +# endif +# endif +#endif /* !YYCOPY_NEEDED */ + +/* YYFINAL -- State number of the termination state. */ +#define YYFINAL 2 +/* YYLAST -- Last index in YYTABLE. */ +#define YYLAST 58 + +/* YYNTOKENS -- Number of terminals. */ +#define YYNTOKENS 24 +/* YYNNTS -- Number of nonterminals. */ +#define YYNNTS 16 +/* YYNRULES -- Number of rules. */ +#define YYNRULES 34 +/* YYNRULES -- Number of states. */ +#define YYNSTATES 63 + +/* YYTRANSLATE(YYLEX) -- Bison symbol number corresponding to YYLEX. */ +#define YYUNDEFTOK 2 +#define YYMAXUTOK 268 + +#define YYTRANSLATE(YYX) \ + ((unsigned int) (YYX) <= YYMAXUTOK ? yytranslate[YYX] : YYUNDEFTOK) + +/* YYTRANSLATE[YYLEX] -- Bison symbol number corresponding to YYLEX. */ +static const yytype_uint8 yytranslate[] = +{ + 0, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 22, 23, 5, 4, 10, 3, 2, 6, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 21, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 8, 2, 9, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 1, 2, 7, 11, + 12, 13, 14, 15, 16, 17, 18, 19, 20 +}; + +#if YYDEBUG +/* YYPRHS[YYN] -- Index of the first RHS symbol of rule number YYN in + YYRHS. */ +static const yytype_uint8 yyprhs[] = +{ + 0, 0, 3, 4, 7, 10, 13, 16, 19, 22, + 25, 28, 32, 36, 40, 46, 49, 52, 54, 57, + 59, 61, 63, 67, 71, 75, 79, 82, 86, 89, + 92, 94, 96, 98, 100 +}; + +/* YYRHS -- A `-1'-separated list of the rules' RHS. */ +static const yytype_int8 yyrhs[] = +{ + 25, 0, -1, -1, 25, 26, -1, 27, 11, -1, + 28, 11, -1, 29, 11, -1, 32, 11, -1, 30, + 11, -1, 31, 11, -1, 1, 11, -1, 12, 35, + 36, -1, 13, 37, 38, -1, 14, 37, 34, -1, + 16, 37, 34, 10, 34, -1, 17, 37, -1, 15, + 33, -1, 39, -1, 33, 39, -1, 18, -1, 19, + -1, 21, -1, 34, 4, 34, -1, 34, 3, 34, + -1, 34, 5, 34, -1, 34, 6, 34, -1, 3, + 34, -1, 22, 34, 23, -1, 8, 34, -1, 9, + 34, -1, 19, -1, 19, -1, 19, -1, 19, -1, + 19, -1 +}; + +/* YYRLINE[YYN] -- source line where rule number YYN was defined. */ +static const yytype_uint16 yyrline[] = +{ + 0, 119, 119, 120, 123, 124, 125, 126, 127, 128, + 129, 132, 141, 156, 171, 188, 201, 219, 225, 231, + 236, 241, 245, 252, 259, 266, 273, 280, 282, 289, + 298, 308, 312, 316, 320 +}; +#endif + +#if YYDEBUG || YYERROR_VERBOSE || YYTOKEN_TABLE +/* YYTNAME[SYMBOL-NUM] -- String name of the symbol SYMBOL-NUM. + First, the terminals, then, starting at YYNTOKENS, nonterminals. */ +static const char *const yytname[] = +{ + "$end", "error", "$undefined", "'-'", "'+'", "'*'", "'/'", "NEG", "'^'", + "'`'", "','", "EOL", "BUS", "LABEL", "SET", "CHIP", "COMPUTE", "IGNORE", + "FLOAT", "NAME", "ERROR", "'@'", "'('", "')'", "$accept", "input", + "line", "bus_statement", "label_statement", "set_statement", + "compute_statement", "ignore_statement", "chip_statement", + "chip_name_list", "expression", "bus_id", "adapter_name", + "function_name", "string", "chip_name", 0 +}; +#endif + +# ifdef YYPRINT +/* YYTOKNUM[YYLEX-NUM] -- Internal token number corresponding to + token YYLEX-NUM. */ +static const yytype_uint16 yytoknum[] = +{ + 0, 256, 257, 45, 43, 42, 47, 258, 94, 96, + 44, 259, 260, 261, 262, 263, 264, 265, 266, 267, + 268, 64, 40, 41 +}; +# endif + +/* YYR1[YYN] -- Symbol number of symbol that rule YYN derives. */ +static const yytype_uint8 yyr1[] = +{ + 0, 24, 25, 25, 26, 26, 26, 26, 26, 26, + 26, 27, 28, 29, 30, 31, 32, 33, 33, 34, + 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, + 35, 36, 37, 38, 39 +}; + +/* YYR2[YYN] -- Number of symbols composing right hand side of rule YYN. */ +static const yytype_uint8 yyr2[] = +{ + 0, 2, 0, 2, 2, 2, 2, 2, 2, 2, + 2, 3, 3, 3, 5, 2, 2, 1, 2, 1, + 1, 1, 3, 3, 3, 3, 2, 3, 2, 2, + 1, 1, 1, 1, 1 +}; + +/* YYDEFACT[STATE-NAME] -- Default reduction number in state STATE-NUM. + Performed when YYTABLE doesn't specify something else to do. Zero + means the default is an error. */ +static const yytype_uint8 yydefact[] = +{ + 2, 0, 1, 0, 0, 0, 0, 0, 0, 0, + 3, 0, 0, 0, 0, 0, 0, 10, 30, 0, + 32, 0, 0, 34, 16, 17, 0, 15, 4, 5, + 6, 8, 9, 7, 31, 11, 33, 12, 0, 0, + 0, 19, 20, 21, 0, 13, 18, 0, 26, 28, + 29, 0, 0, 0, 0, 0, 0, 27, 23, 22, + 24, 25, 14 +}; + +/* YYDEFGOTO[NTERM-NUM]. */ +static const yytype_int8 yydefgoto[] = +{ + -1, 1, 10, 11, 12, 13, 14, 15, 16, 24, + 45, 19, 35, 21, 37, 25 +}; + +/* YYPACT[STATE-NUM] -- Index in YYTABLE of the portion describing + STATE-NUM. */ +#define YYPACT_NINF -27 +static const yytype_int8 yypact[] = +{ + -27, 37, -27, -4, -3, 1, 1, 6, 1, 1, + -27, 8, 13, 20, 23, 32, 34, -27, -27, 29, + -27, 39, 14, -27, 6, -27, 14, -27, -27, -27, + -27, -27, -27, -27, -27, -27, -27, -27, 14, 14, + 14, -27, -27, -27, 14, 36, -27, 5, -27, -27, + -27, -2, 14, 14, 14, 14, 14, -27, 0, 0, + -27, -27, 36 +}; + +/* YYPGOTO[NTERM-NUM]. */ +static const yytype_int8 yypgoto[] = +{ + -27, -27, -27, -27, -27, -27, -27, -27, -27, -27, + -26, -27, -27, 38, -27, 31 +}; + +/* YYTABLE[YYPACT[STATE-NUM]]. What to do in state STATE-NUM. If + positive, shift that token. If negative, reduce the rule which + number is the opposite. If YYTABLE_NINF, syntax error. */ +#define YYTABLE_NINF -1 +static const yytype_uint8 yytable[] = +{ + 47, 52, 53, 54, 55, 54, 55, 17, 52, 53, + 54, 55, 48, 49, 50, 56, 18, 38, 51, 28, + 20, 57, 39, 40, 29, 23, 58, 59, 60, 61, + 62, 30, 41, 42, 31, 43, 44, 2, 3, 52, + 53, 54, 55, 32, 22, 33, 26, 27, 34, 4, + 5, 6, 7, 8, 9, 46, 0, 0, 36 +}; + +#define yypact_value_is_default(yystate) \ + ((yystate) == (-27)) + +#define yytable_value_is_error(yytable_value) \ + YYID (0) + +static const yytype_int8 yycheck[] = +{ + 26, 3, 4, 5, 6, 5, 6, 11, 3, 4, + 5, 6, 38, 39, 40, 10, 19, 3, 44, 11, + 19, 23, 8, 9, 11, 19, 52, 53, 54, 55, + 56, 11, 18, 19, 11, 21, 22, 0, 1, 3, + 4, 5, 6, 11, 6, 11, 8, 9, 19, 12, + 13, 14, 15, 16, 17, 24, -1, -1, 19 +}; + +/* YYSTOS[STATE-NUM] -- The (internal number of the) accessing + symbol of state STATE-NUM. */ +static const yytype_uint8 yystos[] = +{ + 0, 25, 0, 1, 12, 13, 14, 15, 16, 17, + 26, 27, 28, 29, 30, 31, 32, 11, 19, 35, + 19, 37, 37, 19, 33, 39, 37, 37, 11, 11, + 11, 11, 11, 11, 19, 36, 19, 38, 3, 8, + 9, 18, 19, 21, 22, 34, 39, 34, 34, 34, + 34, 34, 3, 4, 5, 6, 10, 23, 34, 34, + 34, 34, 34 +}; + +#define yyerrok (yyerrstatus = 0) +#define yyclearin (yychar = YYEMPTY) +#define YYEMPTY (-2) +#define YYEOF 0 + +#define YYACCEPT goto yyacceptlab +#define YYABORT goto yyabortlab +#define YYERROR goto yyerrorlab + + +/* Like YYERROR except do call yyerror. This remains here temporarily + to ease the transition to the new meaning of YYERROR, for GCC. + Once GCC version 2 has supplanted version 1, this can go. However, + YYFAIL appears to be in use. Nevertheless, it is formally deprecated + in Bison 2.4.2's NEWS entry, where a plan to phase it out is + discussed. */ + +#define YYFAIL goto yyerrlab +#if defined YYFAIL + /* This is here to suppress warnings from the GCC cpp's + -Wunused-macros. Normally we don't worry about that warning, but + some users do, and we want to make it easy for users to remove + YYFAIL uses, which will produce warnings from Bison 2.5. */ +#endif + +#define YYRECOVERING() (!!yyerrstatus) + +#define YYBACKUP(Token, Value) \ +do \ + if (yychar == YYEMPTY && yylen == 1) \ + { \ + yychar = (Token); \ + yylval = (Value); \ + YYPOPSTACK (1); \ + goto yybackup; \ + } \ + else \ + { \ + yyerror (YY_("syntax error: cannot back up")); \ + YYERROR; \ + } \ +while (YYID (0)) + + +#define YYTERROR 1 +#define YYERRCODE 256 + + +/* YYLLOC_DEFAULT -- Set CURRENT to span from RHS[1] to RHS[N]. + If N is 0, then set CURRENT to the empty location which ends + the previous symbol: RHS[0] (always defined). */ + +#define YYRHSLOC(Rhs, K) ((Rhs)[K]) +#ifndef YYLLOC_DEFAULT +# define YYLLOC_DEFAULT(Current, Rhs, N) \ + do \ + if (YYID (N)) \ + { \ + (Current).first_line = YYRHSLOC (Rhs, 1).first_line; \ + (Current).first_column = YYRHSLOC (Rhs, 1).first_column; \ + (Current).last_line = YYRHSLOC (Rhs, N).last_line; \ + (Current).last_column = YYRHSLOC (Rhs, N).last_column; \ + } \ + else \ + { \ + (Current).first_line = (Current).last_line = \ + YYRHSLOC (Rhs, 0).last_line; \ + (Current).first_column = (Current).last_column = \ + YYRHSLOC (Rhs, 0).last_column; \ + } \ + while (YYID (0)) +#endif + + +/* This macro is provided for backward compatibility. */ + +#ifndef YY_LOCATION_PRINT +# define YY_LOCATION_PRINT(File, Loc) ((void) 0) +#endif + + +/* YYLEX -- calling `yylex' with the right arguments. */ + +#ifdef YYLEX_PARAM +# define YYLEX yylex (YYLEX_PARAM) +#else +# define YYLEX yylex () +#endif + +/* Enable debugging if requested. */ +#if YYDEBUG + +# ifndef YYFPRINTF +# include /* INFRINGES ON USER NAME SPACE */ +# define YYFPRINTF fprintf +# endif + +# define YYDPRINTF(Args) \ +do { \ + if (yydebug) \ + YYFPRINTF Args; \ +} while (YYID (0)) + +# define YY_SYMBOL_PRINT(Title, Type, Value, Location) \ +do { \ + if (yydebug) \ + { \ + YYFPRINTF (stderr, "%s ", Title); \ + yy_symbol_print (stderr, \ + Type, Value); \ + YYFPRINTF (stderr, "\n"); \ + } \ +} while (YYID (0)) + + +/*--------------------------------. +| Print this symbol on YYOUTPUT. | +`--------------------------------*/ + +/*ARGSUSED*/ +#if (defined __STDC__ || defined __C99__FUNC__ \ + || defined __cplusplus || defined _MSC_VER) +static void +yy_symbol_value_print (FILE *yyoutput, int yytype, YYSTYPE const * const yyvaluep) +#else +static void +yy_symbol_value_print (yyoutput, yytype, yyvaluep) + FILE *yyoutput; + int yytype; + YYSTYPE const * const yyvaluep; +#endif +{ + if (!yyvaluep) + return; +# ifdef YYPRINT + if (yytype < YYNTOKENS) + YYPRINT (yyoutput, yytoknum[yytype], *yyvaluep); +# else + YYUSE (yyoutput); +# endif + switch (yytype) + { + default: + break; + } +} + + +/*--------------------------------. +| Print this symbol on YYOUTPUT. | +`--------------------------------*/ + +#if (defined __STDC__ || defined __C99__FUNC__ \ + || defined __cplusplus || defined _MSC_VER) +static void +yy_symbol_print (FILE *yyoutput, int yytype, YYSTYPE const * const yyvaluep) +#else +static void +yy_symbol_print (yyoutput, yytype, yyvaluep) + FILE *yyoutput; + int yytype; + YYSTYPE const * const yyvaluep; +#endif +{ + if (yytype < YYNTOKENS) + YYFPRINTF (yyoutput, "token %s (", yytname[yytype]); + else + YYFPRINTF (yyoutput, "nterm %s (", yytname[yytype]); + + yy_symbol_value_print (yyoutput, yytype, yyvaluep); + YYFPRINTF (yyoutput, ")"); +} + +/*------------------------------------------------------------------. +| yy_stack_print -- Print the state stack from its BOTTOM up to its | +| TOP (included). | +`------------------------------------------------------------------*/ + +#if (defined __STDC__ || defined __C99__FUNC__ \ + || defined __cplusplus || defined _MSC_VER) +static void +yy_stack_print (yytype_int16 *yybottom, yytype_int16 *yytop) +#else +static void +yy_stack_print (yybottom, yytop) + yytype_int16 *yybottom; + yytype_int16 *yytop; +#endif +{ + YYFPRINTF (stderr, "Stack now"); + for (; yybottom <= yytop; yybottom++) + { + int yybot = *yybottom; + YYFPRINTF (stderr, " %d", yybot); + } + YYFPRINTF (stderr, "\n"); +} + +# define YY_STACK_PRINT(Bottom, Top) \ +do { \ + if (yydebug) \ + yy_stack_print ((Bottom), (Top)); \ +} while (YYID (0)) + + +/*------------------------------------------------. +| Report that the YYRULE is going to be reduced. | +`------------------------------------------------*/ + +#if (defined __STDC__ || defined __C99__FUNC__ \ + || defined __cplusplus || defined _MSC_VER) +static void +yy_reduce_print (YYSTYPE *yyvsp, int yyrule) +#else +static void +yy_reduce_print (yyvsp, yyrule) + YYSTYPE *yyvsp; + int yyrule; +#endif +{ + int yynrhs = yyr2[yyrule]; + int yyi; + unsigned long int yylno = yyrline[yyrule]; + YYFPRINTF (stderr, "Reducing stack by rule %d (line %lu):\n", + yyrule - 1, yylno); + /* The symbols being reduced. */ + for (yyi = 0; yyi < yynrhs; yyi++) + { + YYFPRINTF (stderr, " $%d = ", yyi + 1); + yy_symbol_print (stderr, yyrhs[yyprhs[yyrule] + yyi], + &(yyvsp[(yyi + 1) - (yynrhs)]) + ); + YYFPRINTF (stderr, "\n"); + } +} + +# define YY_REDUCE_PRINT(Rule) \ +do { \ + if (yydebug) \ + yy_reduce_print (yyvsp, Rule); \ +} while (YYID (0)) + +/* Nonzero means print parse trace. It is left uninitialized so that + multiple parsers can coexist. */ +int yydebug; +#else /* !YYDEBUG */ +# define YYDPRINTF(Args) +# define YY_SYMBOL_PRINT(Title, Type, Value, Location) +# define YY_STACK_PRINT(Bottom, Top) +# define YY_REDUCE_PRINT(Rule) +#endif /* !YYDEBUG */ + + +/* YYINITDEPTH -- initial size of the parser's stacks. */ +#ifndef YYINITDEPTH +# define YYINITDEPTH 200 +#endif + +/* YYMAXDEPTH -- maximum size the stacks can grow to (effective only + if the built-in stack extension method is used). + + Do not make this value too large; the results are undefined if + YYSTACK_ALLOC_MAXIMUM < YYSTACK_BYTES (YYMAXDEPTH) + evaluated with infinite-precision integer arithmetic. */ + +#ifndef YYMAXDEPTH +# define YYMAXDEPTH 10000 +#endif + + +#if YYERROR_VERBOSE + +# ifndef yystrlen +# if defined __GLIBC__ && defined _STRING_H +# define yystrlen strlen +# else +/* Return the length of YYSTR. */ +#if (defined __STDC__ || defined __C99__FUNC__ \ + || defined __cplusplus || defined _MSC_VER) +static YYSIZE_T +yystrlen (const char *yystr) +#else +static YYSIZE_T +yystrlen (yystr) + const char *yystr; +#endif +{ + YYSIZE_T yylen; + for (yylen = 0; yystr[yylen]; yylen++) + continue; + return yylen; +} +# endif +# endif + +# ifndef yystpcpy +# if defined __GLIBC__ && defined _STRING_H && defined _GNU_SOURCE +# define yystpcpy stpcpy +# else +/* Copy YYSRC to YYDEST, returning the address of the terminating '\0' in + YYDEST. */ +#if (defined __STDC__ || defined __C99__FUNC__ \ + || defined __cplusplus || defined _MSC_VER) +static char * +yystpcpy (char *yydest, const char *yysrc) +#else +static char * +yystpcpy (yydest, yysrc) + char *yydest; + const char *yysrc; +#endif +{ + char *yyd = yydest; + const char *yys = yysrc; + + while ((*yyd++ = *yys++) != '\0') + continue; + + return yyd - 1; +} +# endif +# endif + +# ifndef yytnamerr +/* Copy to YYRES the contents of YYSTR after stripping away unnecessary + quotes and backslashes, so that it's suitable for yyerror. The + heuristic is that double-quoting is unnecessary unless the string + contains an apostrophe, a comma, or backslash (other than + backslash-backslash). YYSTR is taken from yytname. If YYRES is + null, do not copy; instead, return the length of what the result + would have been. */ +static YYSIZE_T +yytnamerr (char *yyres, const char *yystr) +{ + if (*yystr == '"') + { + YYSIZE_T yyn = 0; + char const *yyp = yystr; + + for (;;) + switch (*++yyp) + { + case '\'': + case ',': + goto do_not_strip_quotes; + + case '\\': + if (*++yyp != '\\') + goto do_not_strip_quotes; + /* Fall through. */ + default: + if (yyres) + yyres[yyn] = *yyp; + yyn++; + break; + + case '"': + if (yyres) + yyres[yyn] = '\0'; + return yyn; + } + do_not_strip_quotes: ; + } + + if (! yyres) + return yystrlen (yystr); + + return yystpcpy (yyres, yystr) - yyres; +} +# endif + +/* Copy into *YYMSG, which is of size *YYMSG_ALLOC, an error message + about the unexpected token YYTOKEN for the state stack whose top is + YYSSP. + + Return 0 if *YYMSG was successfully written. Return 1 if *YYMSG is + not large enough to hold the message. In that case, also set + *YYMSG_ALLOC to the required number of bytes. Return 2 if the + required number of bytes is too large to store. */ +static int +yysyntax_error (YYSIZE_T *yymsg_alloc, char **yymsg, + yytype_int16 *yyssp, int yytoken) +{ + YYSIZE_T yysize0 = yytnamerr (0, yytname[yytoken]); + YYSIZE_T yysize = yysize0; + YYSIZE_T yysize1; + enum { YYERROR_VERBOSE_ARGS_MAXIMUM = 5 }; + /* Internationalized format string. */ + const char *yyformat = 0; + /* Arguments of yyformat. */ + char const *yyarg[YYERROR_VERBOSE_ARGS_MAXIMUM]; + /* Number of reported tokens (one for the "unexpected", one per + "expected"). */ + int yycount = 0; + + /* There are many possibilities here to consider: + - Assume YYFAIL is not used. It's too flawed to consider. See + + for details. YYERROR is fine as it does not invoke this + function. + - If this state is a consistent state with a default action, then + the only way this function was invoked is if the default action + is an error action. In that case, don't check for expected + tokens because there are none. + - The only way there can be no lookahead present (in yychar) is if + this state is a consistent state with a default action. Thus, + detecting the absence of a lookahead is sufficient to determine + that there is no unexpected or expected token to report. In that + case, just report a simple "syntax error". + - Don't assume there isn't a lookahead just because this state is a + consistent state with a default action. There might have been a + previous inconsistent state, consistent state with a non-default + action, or user semantic action that manipulated yychar. + - Of course, the expected token list depends on states to have + correct lookahead information, and it depends on the parser not + to perform extra reductions after fetching a lookahead from the + scanner and before detecting a syntax error. Thus, state merging + (from LALR or IELR) and default reductions corrupt the expected + token list. However, the list is correct for canonical LR with + one exception: it will still contain any token that will not be + accepted due to an error action in a later state. + */ + if (yytoken != YYEMPTY) + { + int yyn = yypact[*yyssp]; + yyarg[yycount++] = yytname[yytoken]; + if (!yypact_value_is_default (yyn)) + { + /* Start YYX at -YYN if negative to avoid negative indexes in + YYCHECK. In other words, skip the first -YYN actions for + this state because they are default actions. */ + int yyxbegin = yyn < 0 ? -yyn : 0; + /* Stay within bounds of both yycheck and yytname. */ + int yychecklim = YYLAST - yyn + 1; + int yyxend = yychecklim < YYNTOKENS ? yychecklim : YYNTOKENS; + int yyx; + + for (yyx = yyxbegin; yyx < yyxend; ++yyx) + if (yycheck[yyx + yyn] == yyx && yyx != YYTERROR + && !yytable_value_is_error (yytable[yyx + yyn])) + { + if (yycount == YYERROR_VERBOSE_ARGS_MAXIMUM) + { + yycount = 1; + yysize = yysize0; + break; + } + yyarg[yycount++] = yytname[yyx]; + yysize1 = yysize + yytnamerr (0, yytname[yyx]); + if (! (yysize <= yysize1 + && yysize1 <= YYSTACK_ALLOC_MAXIMUM)) + return 2; + yysize = yysize1; + } + } + } + + switch (yycount) + { +# define YYCASE_(N, S) \ + case N: \ + yyformat = S; \ + break + YYCASE_(0, YY_("syntax error")); + YYCASE_(1, YY_("syntax error, unexpected %s")); + YYCASE_(2, YY_("syntax error, unexpected %s, expecting %s")); + YYCASE_(3, YY_("syntax error, unexpected %s, expecting %s or %s")); + YYCASE_(4, YY_("syntax error, unexpected %s, expecting %s or %s or %s")); + YYCASE_(5, YY_("syntax error, unexpected %s, expecting %s or %s or %s or %s")); +# undef YYCASE_ + } + + yysize1 = yysize + yystrlen (yyformat); + if (! (yysize <= yysize1 && yysize1 <= YYSTACK_ALLOC_MAXIMUM)) + return 2; + yysize = yysize1; + + if (*yymsg_alloc < yysize) + { + *yymsg_alloc = 2 * yysize; + if (! (yysize <= *yymsg_alloc + && *yymsg_alloc <= YYSTACK_ALLOC_MAXIMUM)) + *yymsg_alloc = YYSTACK_ALLOC_MAXIMUM; + return 1; + } + + /* Avoid sprintf, as that infringes on the user's name space. + Don't have undefined behavior even if the translation + produced a string with the wrong number of "%s"s. */ + { + char *yyp = *yymsg; + int yyi = 0; + while ((*yyp = *yyformat) != '\0') + if (*yyp == '%' && yyformat[1] == 's' && yyi < yycount) + { + yyp += yytnamerr (yyp, yyarg[yyi++]); + yyformat += 2; + } + else + { + yyp++; + yyformat++; + } + } + return 0; +} +#endif /* YYERROR_VERBOSE */ + +/*-----------------------------------------------. +| Release the memory associated to this symbol. | +`-----------------------------------------------*/ + +/*ARGSUSED*/ +#if (defined __STDC__ || defined __C99__FUNC__ \ + || defined __cplusplus || defined _MSC_VER) +static void +yydestruct (const char *yymsg, int yytype, YYSTYPE *yyvaluep) +#else +static void +yydestruct (yymsg, yytype, yyvaluep) + const char *yymsg; + int yytype; + YYSTYPE *yyvaluep; +#endif +{ + YYUSE (yyvaluep); + + if (!yymsg) + yymsg = "Deleting"; + YY_SYMBOL_PRINT (yymsg, yytype, yyvaluep, yylocationp); + + switch (yytype) + { + + default: + break; + } +} + + +/* Prevent warnings from -Wmissing-prototypes. */ +#ifdef YYPARSE_PARAM +#if defined __STDC__ || defined __cplusplus +int yyparse (void *YYPARSE_PARAM); +#else +int yyparse (); +#endif +#else /* ! YYPARSE_PARAM */ +#if defined __STDC__ || defined __cplusplus +int yyparse (void); +#else +int yyparse (); +#endif +#endif /* ! YYPARSE_PARAM */ + + +/* The lookahead symbol. */ +int yychar; + +/* The semantic value of the lookahead symbol. */ +YYSTYPE yylval; + +/* Number of syntax errors so far. */ +int yynerrs; + + +/*----------. +| yyparse. | +`----------*/ + +#ifdef YYPARSE_PARAM +#if (defined __STDC__ || defined __C99__FUNC__ \ + || defined __cplusplus || defined _MSC_VER) +int +yyparse (void *YYPARSE_PARAM) +#else +int +yyparse (YYPARSE_PARAM) + void *YYPARSE_PARAM; +#endif +#else /* ! YYPARSE_PARAM */ +#if (defined __STDC__ || defined __C99__FUNC__ \ + || defined __cplusplus || defined _MSC_VER) +int +yyparse (void) +#else +int +yyparse () + +#endif +#endif +{ + int yystate; + /* Number of tokens to shift before error messages enabled. */ + int yyerrstatus; + + /* The stacks and their tools: + `yyss': related to states. + `yyvs': related to semantic values. + + Refer to the stacks thru separate pointers, to allow yyoverflow + to reallocate them elsewhere. */ + + /* The state stack. */ + yytype_int16 yyssa[YYINITDEPTH]; + yytype_int16 *yyss; + yytype_int16 *yyssp; + + /* The semantic value stack. */ + YYSTYPE yyvsa[YYINITDEPTH]; + YYSTYPE *yyvs; + YYSTYPE *yyvsp; + + YYSIZE_T yystacksize; + + int yyn; + int yyresult; + /* Lookahead token as an internal (translated) token number. */ + int yytoken; + /* The variables used to return semantic value and location from the + action routines. */ + YYSTYPE yyval; + +#if YYERROR_VERBOSE + /* Buffer for error messages, and its allocated size. */ + char yymsgbuf[128]; + char *yymsg = yymsgbuf; + YYSIZE_T yymsg_alloc = sizeof yymsgbuf; +#endif + +#define YYPOPSTACK(N) (yyvsp -= (N), yyssp -= (N)) + + /* The number of symbols on the RHS of the reduced rule. + Keep to zero when no symbol should be popped. */ + int yylen = 0; + + yytoken = 0; + yyss = yyssa; + yyvs = yyvsa; + yystacksize = YYINITDEPTH; + + YYDPRINTF ((stderr, "Starting parse\n")); + + yystate = 0; + yyerrstatus = 0; + yynerrs = 0; + yychar = YYEMPTY; /* Cause a token to be read. */ + + /* Initialize stack pointers. + Waste one element of value and location stack + so that they stay on the same level as the state stack. + The wasted elements are never initialized. */ + yyssp = yyss; + yyvsp = yyvs; + + goto yysetstate; + +/*------------------------------------------------------------. +| yynewstate -- Push a new state, which is found in yystate. | +`------------------------------------------------------------*/ + yynewstate: + /* In all cases, when you get here, the value and location stacks + have just been pushed. So pushing a state here evens the stacks. */ + yyssp++; + + yysetstate: + *yyssp = yystate; + + if (yyss + yystacksize - 1 <= yyssp) + { + /* Get the current used size of the three stacks, in elements. */ + YYSIZE_T yysize = yyssp - yyss + 1; + +#ifdef yyoverflow + { + /* Give user a chance to reallocate the stack. Use copies of + these so that the &'s don't force the real ones into + memory. */ + YYSTYPE *yyvs1 = yyvs; + yytype_int16 *yyss1 = yyss; + + /* Each stack pointer address is followed by the size of the + data in use in that stack, in bytes. This used to be a + conditional around just the two extra args, but that might + be undefined if yyoverflow is a macro. */ + yyoverflow (YY_("memory exhausted"), + &yyss1, yysize * sizeof (*yyssp), + &yyvs1, yysize * sizeof (*yyvsp), + &yystacksize); + + yyss = yyss1; + yyvs = yyvs1; + } +#else /* no yyoverflow */ +# ifndef YYSTACK_RELOCATE + goto yyexhaustedlab; +# else + /* Extend the stack our own way. */ + if (YYMAXDEPTH <= yystacksize) + goto yyexhaustedlab; + yystacksize *= 2; + if (YYMAXDEPTH < yystacksize) + yystacksize = YYMAXDEPTH; + + { + yytype_int16 *yyss1 = yyss; + union yyalloc *yyptr = + (union yyalloc *) YYSTACK_ALLOC (YYSTACK_BYTES (yystacksize)); + if (! yyptr) + goto yyexhaustedlab; + YYSTACK_RELOCATE (yyss_alloc, yyss); + YYSTACK_RELOCATE (yyvs_alloc, yyvs); +# undef YYSTACK_RELOCATE + if (yyss1 != yyssa) + YYSTACK_FREE (yyss1); + } +# endif +#endif /* no yyoverflow */ + + yyssp = yyss + yysize - 1; + yyvsp = yyvs + yysize - 1; + + YYDPRINTF ((stderr, "Stack size increased to %lu\n", + (unsigned long int) yystacksize)); + + if (yyss + yystacksize - 1 <= yyssp) + YYABORT; + } + + YYDPRINTF ((stderr, "Entering state %d\n", yystate)); + + if (yystate == YYFINAL) + YYACCEPT; + + goto yybackup; + +/*-----------. +| yybackup. | +`-----------*/ +yybackup: + + /* Do appropriate processing given the current state. Read a + lookahead token if we need one and don't already have one. */ + + /* First try to decide what to do without reference to lookahead token. */ + yyn = yypact[yystate]; + if (yypact_value_is_default (yyn)) + goto yydefault; + + /* Not known => get a lookahead token if don't already have one. */ + + /* YYCHAR is either YYEMPTY or YYEOF or a valid lookahead symbol. */ + if (yychar == YYEMPTY) + { + YYDPRINTF ((stderr, "Reading a token: ")); + yychar = YYLEX; + } + + if (yychar <= YYEOF) + { + yychar = yytoken = YYEOF; + YYDPRINTF ((stderr, "Now at end of input.\n")); + } + else + { + yytoken = YYTRANSLATE (yychar); + YY_SYMBOL_PRINT ("Next token is", yytoken, &yylval, &yylloc); + } + + /* If the proper action on seeing token YYTOKEN is to reduce or to + detect an error, take that action. */ + yyn += yytoken; + if (yyn < 0 || YYLAST < yyn || yycheck[yyn] != yytoken) + goto yydefault; + yyn = yytable[yyn]; + if (yyn <= 0) + { + if (yytable_value_is_error (yyn)) + goto yyerrlab; + yyn = -yyn; + goto yyreduce; + } + + /* Count tokens shifted since error; after three, turn off error + status. */ + if (yyerrstatus) + yyerrstatus--; + + /* Shift the lookahead token. */ + YY_SYMBOL_PRINT ("Shifting", yytoken, &yylval, &yylloc); + + /* Discard the shifted token. */ + yychar = YYEMPTY; + + yystate = yyn; + *++yyvsp = yylval; + + goto yynewstate; + + +/*-----------------------------------------------------------. +| yydefault -- do the default action for the current state. | +`-----------------------------------------------------------*/ +yydefault: + yyn = yydefact[yystate]; + if (yyn == 0) + goto yyerrlab; + goto yyreduce; + + +/*-----------------------------. +| yyreduce -- Do a reduction. | +`-----------------------------*/ +yyreduce: + /* yyn is the number of a rule to reduce with. */ + yylen = yyr2[yyn]; + + /* If YYLEN is nonzero, implement the default value of the action: + `$$ = $1'. + + Otherwise, the following line sets YYVAL to garbage. + This behavior is undocumented and Bison + users should not rely upon it. Assigning to YYVAL + unconditionally makes the parser a bit smaller, and it avoids a + GCC warning that YYVAL may be used uninitialized. */ + yyval = yyvsp[1-yylen]; + + + YY_REDUCE_PRINT (yyn); + switch (yyn) + { + case 11: + +/* Line 1806 of yacc.c */ +#line 133 "lib/conf-parse.y" + { sensors_bus new_el; + new_el.line = (yyvsp[(1) - (3)].line); + new_el.bus = (yyvsp[(2) - (3)].bus); + new_el.adapter = (yyvsp[(3) - (3)].name); + bus_add_el(&new_el); + } + break; + + case 12: + +/* Line 1806 of yacc.c */ +#line 142 "lib/conf-parse.y" + { sensors_label new_el; + if (!current_chip) { + sensors_yyerror("Label statement before first chip statement"); + free((yyvsp[(2) - (3)].name)); + free((yyvsp[(3) - (3)].name)); + YYERROR; + } + new_el.line = (yyvsp[(1) - (3)].line); + new_el.name = (yyvsp[(2) - (3)].name); + new_el.value = (yyvsp[(3) - (3)].name); + label_add_el(&new_el); + } + break; + + case 13: + +/* Line 1806 of yacc.c */ +#line 157 "lib/conf-parse.y" + { sensors_set new_el; + if (!current_chip) { + sensors_yyerror("Set statement before first chip statement"); + free((yyvsp[(2) - (3)].name)); + sensors_free_expr((yyvsp[(3) - (3)].expr)); + YYERROR; + } + new_el.line = (yyvsp[(1) - (3)].line); + new_el.name = (yyvsp[(2) - (3)].name); + new_el.value = (yyvsp[(3) - (3)].expr); + set_add_el(&new_el); + } + break; + + case 14: + +/* Line 1806 of yacc.c */ +#line 172 "lib/conf-parse.y" + { sensors_compute new_el; + if (!current_chip) { + sensors_yyerror("Compute statement before first chip statement"); + free((yyvsp[(2) - (5)].name)); + sensors_free_expr((yyvsp[(3) - (5)].expr)); + sensors_free_expr((yyvsp[(5) - (5)].expr)); + YYERROR; + } + new_el.line = (yyvsp[(1) - (5)].line); + new_el.name = (yyvsp[(2) - (5)].name); + new_el.from_proc = (yyvsp[(3) - (5)].expr); + new_el.to_proc = (yyvsp[(5) - (5)].expr); + compute_add_el(&new_el); + } + break; + + case 15: + +/* Line 1806 of yacc.c */ +#line 189 "lib/conf-parse.y" + { sensors_ignore new_el; + if (!current_chip) { + sensors_yyerror("Ignore statement before first chip statement"); + free((yyvsp[(2) - (2)].name)); + YYERROR; + } + new_el.line = (yyvsp[(1) - (2)].line); + new_el.name = (yyvsp[(2) - (2)].name); + ignore_add_el(&new_el); + } + break; + + case 16: + +/* Line 1806 of yacc.c */ +#line 202 "lib/conf-parse.y" + { sensors_chip new_el; + new_el.line = (yyvsp[(1) - (2)].line); + new_el.labels = NULL; + new_el.sets = NULL; + new_el.computes = NULL; + new_el.ignores = NULL; + new_el.labels_count = new_el.labels_max = 0; + new_el.sets_count = new_el.sets_max = 0; + new_el.computes_count = new_el.computes_max = 0; + new_el.ignores_count = new_el.ignores_max = 0; + new_el.chips = (yyvsp[(2) - (2)].chips); + chip_add_el(&new_el); + current_chip = sensors_config_chips + + sensors_config_chips_count - 1; + } + break; + + case 17: + +/* Line 1806 of yacc.c */ +#line 220 "lib/conf-parse.y" + { + (yyval.chips).fits = NULL; + (yyval.chips).fits_count = (yyval.chips).fits_max = 0; + fits_add_el(&(yyvsp[(1) - (1)].chip),(yyval.chips)); + } + break; + + case 18: + +/* Line 1806 of yacc.c */ +#line 226 "lib/conf-parse.y" + { (yyval.chips) = (yyvsp[(1) - (2)].chips); + fits_add_el(&(yyvsp[(2) - (2)].chip),(yyval.chips)); + } + break; + + case 19: + +/* Line 1806 of yacc.c */ +#line 232 "lib/conf-parse.y" + { (yyval.expr) = malloc_expr(); + (yyval.expr)->data.val = (yyvsp[(1) - (1)].value); + (yyval.expr)->kind = sensors_kind_val; + } + break; + + case 20: + +/* Line 1806 of yacc.c */ +#line 237 "lib/conf-parse.y" + { (yyval.expr) = malloc_expr(); + (yyval.expr)->data.var = (yyvsp[(1) - (1)].name); + (yyval.expr)->kind = sensors_kind_var; + } + break; + + case 21: + +/* Line 1806 of yacc.c */ +#line 242 "lib/conf-parse.y" + { (yyval.expr) = malloc_expr(); + (yyval.expr)->kind = sensors_kind_source; + } + break; + + case 22: + +/* Line 1806 of yacc.c */ +#line 246 "lib/conf-parse.y" + { (yyval.expr) = malloc_expr(); + (yyval.expr)->kind = sensors_kind_sub; + (yyval.expr)->data.subexpr.op = sensors_add; + (yyval.expr)->data.subexpr.sub1 = (yyvsp[(1) - (3)].expr); + (yyval.expr)->data.subexpr.sub2 = (yyvsp[(3) - (3)].expr); + } + break; + + case 23: + +/* Line 1806 of yacc.c */ +#line 253 "lib/conf-parse.y" + { (yyval.expr) = malloc_expr(); + (yyval.expr)->kind = sensors_kind_sub; + (yyval.expr)->data.subexpr.op = sensors_sub; + (yyval.expr)->data.subexpr.sub1 = (yyvsp[(1) - (3)].expr); + (yyval.expr)->data.subexpr.sub2 = (yyvsp[(3) - (3)].expr); + } + break; + + case 24: + +/* Line 1806 of yacc.c */ +#line 260 "lib/conf-parse.y" + { (yyval.expr) = malloc_expr(); + (yyval.expr)->kind = sensors_kind_sub; + (yyval.expr)->data.subexpr.op = sensors_multiply; + (yyval.expr)->data.subexpr.sub1 = (yyvsp[(1) - (3)].expr); + (yyval.expr)->data.subexpr.sub2 = (yyvsp[(3) - (3)].expr); + } + break; + + case 25: + +/* Line 1806 of yacc.c */ +#line 267 "lib/conf-parse.y" + { (yyval.expr) = malloc_expr(); + (yyval.expr)->kind = sensors_kind_sub; + (yyval.expr)->data.subexpr.op = sensors_divide; + (yyval.expr)->data.subexpr.sub1 = (yyvsp[(1) - (3)].expr); + (yyval.expr)->data.subexpr.sub2 = (yyvsp[(3) - (3)].expr); + } + break; + + case 26: + +/* Line 1806 of yacc.c */ +#line 274 "lib/conf-parse.y" + { (yyval.expr) = malloc_expr(); + (yyval.expr)->kind = sensors_kind_sub; + (yyval.expr)->data.subexpr.op = sensors_negate; + (yyval.expr)->data.subexpr.sub1 = (yyvsp[(2) - (2)].expr); + (yyval.expr)->data.subexpr.sub2 = NULL; + } + break; + + case 27: + +/* Line 1806 of yacc.c */ +#line 281 "lib/conf-parse.y" + { (yyval.expr) = (yyvsp[(2) - (3)].expr); } + break; + + case 28: + +/* Line 1806 of yacc.c */ +#line 283 "lib/conf-parse.y" + { (yyval.expr) = malloc_expr(); + (yyval.expr)->kind = sensors_kind_sub; + (yyval.expr)->data.subexpr.op = sensors_exp; + (yyval.expr)->data.subexpr.sub1 = (yyvsp[(2) - (2)].expr); + (yyval.expr)->data.subexpr.sub2 = NULL; + } + break; + + case 29: + +/* Line 1806 of yacc.c */ +#line 290 "lib/conf-parse.y" + { (yyval.expr) = malloc_expr(); + (yyval.expr)->kind = sensors_kind_sub; + (yyval.expr)->data.subexpr.op = sensors_log; + (yyval.expr)->data.subexpr.sub1 = (yyvsp[(2) - (2)].expr); + (yyval.expr)->data.subexpr.sub2 = NULL; + } + break; + + case 30: + +/* Line 1806 of yacc.c */ +#line 299 "lib/conf-parse.y" + { int res = sensors_parse_bus_id((yyvsp[(1) - (1)].name),&(yyval.bus)); + free((yyvsp[(1) - (1)].name)); + if (res) { + sensors_yyerror("Parse error in bus id"); + YYERROR; + } + } + break; + + case 31: + +/* Line 1806 of yacc.c */ +#line 309 "lib/conf-parse.y" + { (yyval.name) = (yyvsp[(1) - (1)].name); } + break; + + case 32: + +/* Line 1806 of yacc.c */ +#line 313 "lib/conf-parse.y" + { (yyval.name) = (yyvsp[(1) - (1)].name); } + break; + + case 33: + +/* Line 1806 of yacc.c */ +#line 317 "lib/conf-parse.y" + { (yyval.name) = (yyvsp[(1) - (1)].name); } + break; + + case 34: + +/* Line 1806 of yacc.c */ +#line 321 "lib/conf-parse.y" + { int res = sensors_parse_chip_name((yyvsp[(1) - (1)].name),&(yyval.chip)); + free((yyvsp[(1) - (1)].name)); + if (res) { + sensors_yyerror("Parse error in chip name"); + YYERROR; + } + } + break; + + + +/* Line 1806 of yacc.c */ +#line 1793 "lib/conf-parse.c" + default: break; + } + /* User semantic actions sometimes alter yychar, and that requires + that yytoken be updated with the new translation. We take the + approach of translating immediately before every use of yytoken. + One alternative is translating here after every semantic action, + but that translation would be missed if the semantic action invokes + YYABORT, YYACCEPT, or YYERROR immediately after altering yychar or + if it invokes YYBACKUP. In the case of YYABORT or YYACCEPT, an + incorrect destructor might then be invoked immediately. In the + case of YYERROR or YYBACKUP, subsequent parser actions might lead + to an incorrect destructor call or verbose syntax error message + before the lookahead is translated. */ + YY_SYMBOL_PRINT ("-> $$ =", yyr1[yyn], &yyval, &yyloc); + + YYPOPSTACK (yylen); + yylen = 0; + YY_STACK_PRINT (yyss, yyssp); + + *++yyvsp = yyval; + + /* Now `shift' the result of the reduction. Determine what state + that goes to, based on the state we popped back to and the rule + number reduced by. */ + + yyn = yyr1[yyn]; + + yystate = yypgoto[yyn - YYNTOKENS] + *yyssp; + if (0 <= yystate && yystate <= YYLAST && yycheck[yystate] == *yyssp) + yystate = yytable[yystate]; + else + yystate = yydefgoto[yyn - YYNTOKENS]; + + goto yynewstate; + + +/*------------------------------------. +| yyerrlab -- here on detecting error | +`------------------------------------*/ +yyerrlab: + /* Make sure we have latest lookahead translation. See comments at + user semantic actions for why this is necessary. */ + yytoken = yychar == YYEMPTY ? YYEMPTY : YYTRANSLATE (yychar); + + /* If not already recovering from an error, report this error. */ + if (!yyerrstatus) + { + ++yynerrs; +#if ! YYERROR_VERBOSE + yyerror (YY_("syntax error")); +#else +# define YYSYNTAX_ERROR yysyntax_error (&yymsg_alloc, &yymsg, \ + yyssp, yytoken) + { + char const *yymsgp = YY_("syntax error"); + int yysyntax_error_status; + yysyntax_error_status = YYSYNTAX_ERROR; + if (yysyntax_error_status == 0) + yymsgp = yymsg; + else if (yysyntax_error_status == 1) + { + if (yymsg != yymsgbuf) + YYSTACK_FREE (yymsg); + yymsg = (char *) YYSTACK_ALLOC (yymsg_alloc); + if (!yymsg) + { + yymsg = yymsgbuf; + yymsg_alloc = sizeof yymsgbuf; + yysyntax_error_status = 2; + } + else + { + yysyntax_error_status = YYSYNTAX_ERROR; + yymsgp = yymsg; + } + } + yyerror (yymsgp); + if (yysyntax_error_status == 2) + goto yyexhaustedlab; + } +# undef YYSYNTAX_ERROR +#endif + } + + + + if (yyerrstatus == 3) + { + /* If just tried and failed to reuse lookahead token after an + error, discard it. */ + + if (yychar <= YYEOF) + { + /* Return failure if at end of input. */ + if (yychar == YYEOF) + YYABORT; + } + else + { + yydestruct ("Error: discarding", + yytoken, &yylval); + yychar = YYEMPTY; + } + } + + /* Else will try to reuse lookahead token after shifting the error + token. */ + goto yyerrlab1; + + +/*---------------------------------------------------. +| yyerrorlab -- error raised explicitly by YYERROR. | +`---------------------------------------------------*/ +yyerrorlab: + + /* Pacify compilers like GCC when the user code never invokes + YYERROR and the label yyerrorlab therefore never appears in user + code. */ + if (/*CONSTCOND*/ 0) + goto yyerrorlab; + + /* Do not reclaim the symbols of the rule which action triggered + this YYERROR. */ + YYPOPSTACK (yylen); + yylen = 0; + YY_STACK_PRINT (yyss, yyssp); + yystate = *yyssp; + goto yyerrlab1; + + +/*-------------------------------------------------------------. +| yyerrlab1 -- common code for both syntax error and YYERROR. | +`-------------------------------------------------------------*/ +yyerrlab1: + yyerrstatus = 3; /* Each real token shifted decrements this. */ + + for (;;) + { + yyn = yypact[yystate]; + if (!yypact_value_is_default (yyn)) + { + yyn += YYTERROR; + if (0 <= yyn && yyn <= YYLAST && yycheck[yyn] == YYTERROR) + { + yyn = yytable[yyn]; + if (0 < yyn) + break; + } + } + + /* Pop the current state because it cannot handle the error token. */ + if (yyssp == yyss) + YYABORT; + + + yydestruct ("Error: popping", + yystos[yystate], yyvsp); + YYPOPSTACK (1); + yystate = *yyssp; + YY_STACK_PRINT (yyss, yyssp); + } + + *++yyvsp = yylval; + + + /* Shift the error token. */ + YY_SYMBOL_PRINT ("Shifting", yystos[yyn], yyvsp, yylsp); + + yystate = yyn; + goto yynewstate; + + +/*-------------------------------------. +| yyacceptlab -- YYACCEPT comes here. | +`-------------------------------------*/ +yyacceptlab: + yyresult = 0; + goto yyreturn; + +/*-----------------------------------. +| yyabortlab -- YYABORT comes here. | +`-----------------------------------*/ +yyabortlab: + yyresult = 1; + goto yyreturn; + +#if !defined(yyoverflow) || YYERROR_VERBOSE +/*-------------------------------------------------. +| yyexhaustedlab -- memory exhaustion comes here. | +`-------------------------------------------------*/ +yyexhaustedlab: + yyerror (YY_("memory exhausted")); + yyresult = 2; + /* Fall through. */ +#endif + +yyreturn: + if (yychar != YYEMPTY) + { + /* Make sure we have latest lookahead translation. See comments at + user semantic actions for why this is necessary. */ + yytoken = YYTRANSLATE (yychar); + yydestruct ("Cleanup: discarding lookahead", + yytoken, &yylval); + } + /* Do not reclaim the symbols of the rule which action triggered + this YYABORT or YYACCEPT. */ + YYPOPSTACK (yylen); + YY_STACK_PRINT (yyss, yyssp); + while (yyssp != yyss) + { + yydestruct ("Cleanup: popping", + yystos[*yyssp], yyvsp); + YYPOPSTACK (1); + } +#ifndef yyoverflow + if (yyss != yyssa) + YYSTACK_FREE (yyss); +#endif +#if YYERROR_VERBOSE + if (yymsg != yymsgbuf) + YYSTACK_FREE (yymsg); +#endif + /* Make sure YYID is used. */ + return YYID (yyresult); +} + + + +/* Line 2067 of yacc.c */ +#line 330 "lib/conf-parse.y" + + +void sensors_yyerror(const char *err) +{ + if (sensors_lex_error[0]) { + sensors_parse_error_wfn(sensors_lex_error, sensors_yyfilename, sensors_yylineno); + sensors_lex_error[0] = '\0'; + } else + sensors_parse_error_wfn(err, sensors_yyfilename, sensors_yylineno); +} + +sensors_expr *malloc_expr(void) +{ + sensors_expr *res = malloc(sizeof(sensors_expr)); + if (! res) + sensors_fatal_error(__func__, "Allocating a new expression"); + return res; +} + diff --git a/tools/gator/daemon/libsensors/conf-parse.h b/tools/gator/daemon/libsensors/conf-parse.h new file mode 100644 index 00000000000..89c9c1a0d59 --- /dev/null +++ b/tools/gator/daemon/libsensors/conf-parse.h @@ -0,0 +1,84 @@ +/* A Bison parser, made by GNU Bison 2.5. */ + +/* Bison interface for Yacc-like parsers in C + + Copyright (C) 1984, 1989-1990, 2000-2011 Free Software Foundation, Inc. + + 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 3 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, see . */ + +/* As a special exception, you may create a larger work that contains + part or all of the Bison parser skeleton and distribute that work + under terms of your choice, so long as that work isn't itself a + parser generator using the skeleton or a modified version thereof + as a parser skeleton. Alternatively, if you modify or redistribute + the parser skeleton itself, you may (at your option) remove this + special exception, which will cause the skeleton and the resulting + Bison output files to be licensed under the GNU General Public + License without this special exception. + + This special exception was added by the Free Software Foundation in + version 2.2 of Bison. */ + + +/* Tokens. */ +#ifndef YYTOKENTYPE +# define YYTOKENTYPE + /* Put the tokens into the symbol table, so that GDB and other debuggers + know about them. */ + enum yytokentype { + NEG = 258, + EOL = 259, + BUS = 260, + LABEL = 261, + SET = 262, + CHIP = 263, + COMPUTE = 264, + IGNORE = 265, + FLOAT = 266, + NAME = 267, + ERROR = 268 + }; +#endif + + + +#if ! defined YYSTYPE && ! defined YYSTYPE_IS_DECLARED +typedef union YYSTYPE +{ + +/* Line 2068 of yacc.c */ +#line 79 "lib/conf-parse.y" + + double value; + char *name; + void *nothing; + sensors_chip_name_list chips; + sensors_expr *expr; + sensors_bus_id bus; + sensors_chip_name chip; + sensors_config_line line; + + + +/* Line 2068 of yacc.c */ +#line 76 "lib/conf-parse.h" +} YYSTYPE; +# define YYSTYPE_IS_TRIVIAL 1 +# define yystype YYSTYPE /* obsolescent; will be withdrawn */ +# define YYSTYPE_IS_DECLARED 1 +#endif + +extern YYSTYPE sensors_yylval; + + diff --git a/tools/gator/daemon/libsensors/conf-parse.y b/tools/gator/daemon/libsensors/conf-parse.y new file mode 100644 index 00000000000..1937f544dc6 --- /dev/null +++ b/tools/gator/daemon/libsensors/conf-parse.y @@ -0,0 +1,347 @@ +%{ +/* + conf-parse.y - Part of libsensors, a Linux library for reading sensor data. + Copyright (c) 1998, 1999 Frodo Looijaard + + 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 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. +*/ + +#define YYERROR_VERBOSE + +#include +#include +#include + +#include "data.h" +#include "general.h" +#include "error.h" +#include "conf.h" +#include "access.h" +#include "init.h" + +static void sensors_yyerror(const char *err); +static sensors_expr *malloc_expr(void); + +static sensors_chip *current_chip = NULL; + +#define bus_add_el(el) sensors_add_array_el(el,\ + &sensors_config_busses,\ + &sensors_config_busses_count,\ + &sensors_config_busses_max,\ + sizeof(sensors_bus)) +#define label_add_el(el) sensors_add_array_el(el,\ + ¤t_chip->labels,\ + ¤t_chip->labels_count,\ + ¤t_chip->labels_max,\ + sizeof(sensors_label)); +#define set_add_el(el) sensors_add_array_el(el,\ + ¤t_chip->sets,\ + ¤t_chip->sets_count,\ + ¤t_chip->sets_max,\ + sizeof(sensors_set)); +#define compute_add_el(el) sensors_add_array_el(el,\ + ¤t_chip->computes,\ + ¤t_chip->computes_count,\ + ¤t_chip->computes_max,\ + sizeof(sensors_compute)); +#define ignore_add_el(el) sensors_add_array_el(el,\ + ¤t_chip->ignores,\ + ¤t_chip->ignores_count,\ + ¤t_chip->ignores_max,\ + sizeof(sensors_ignore)); +#define chip_add_el(el) sensors_add_array_el(el,\ + &sensors_config_chips,\ + &sensors_config_chips_count,\ + &sensors_config_chips_max,\ + sizeof(sensors_chip)); + +#define fits_add_el(el,list) sensors_add_array_el(el,\ + &(list).fits,\ + &(list).fits_count,\ + &(list).fits_max, \ + sizeof(sensors_chip_name)); + +%} + +%union { + double value; + char *name; + void *nothing; + sensors_chip_name_list chips; + sensors_expr *expr; + sensors_bus_id bus; + sensors_chip_name chip; + sensors_config_line line; +} + +%left '-' '+' +%left '*' '/' +%left NEG +%right '^' '`' + +%token ',' +%token EOL +%token BUS +%token LABEL +%token SET +%token CHIP +%token COMPUTE +%token IGNORE +%token FLOAT +%token NAME +%token ERROR + +%type chip_name_list +%type expression +%type bus_id +%type adapter_name +%type function_name +%type string +%type chip_name + +%start input + +%% + +input: /* empty */ + | input line +; + +line: bus_statement EOL + | label_statement EOL + | set_statement EOL + | chip_statement EOL + | compute_statement EOL + | ignore_statement EOL + | error EOL +; + +bus_statement: BUS bus_id adapter_name + { sensors_bus new_el; + new_el.line = $1; + new_el.bus = $2; + new_el.adapter = $3; + bus_add_el(&new_el); + } +; + +label_statement: LABEL function_name string + { sensors_label new_el; + if (!current_chip) { + sensors_yyerror("Label statement before first chip statement"); + free($2); + free($3); + YYERROR; + } + new_el.line = $1; + new_el.name = $2; + new_el.value = $3; + label_add_el(&new_el); + } +; + +set_statement: SET function_name expression + { sensors_set new_el; + if (!current_chip) { + sensors_yyerror("Set statement before first chip statement"); + free($2); + sensors_free_expr($3); + YYERROR; + } + new_el.line = $1; + new_el.name = $2; + new_el.value = $3; + set_add_el(&new_el); + } +; + +compute_statement: COMPUTE function_name expression ',' expression + { sensors_compute new_el; + if (!current_chip) { + sensors_yyerror("Compute statement before first chip statement"); + free($2); + sensors_free_expr($3); + sensors_free_expr($5); + YYERROR; + } + new_el.line = $1; + new_el.name = $2; + new_el.from_proc = $3; + new_el.to_proc = $5; + compute_add_el(&new_el); + } +; + +ignore_statement: IGNORE function_name + { sensors_ignore new_el; + if (!current_chip) { + sensors_yyerror("Ignore statement before first chip statement"); + free($2); + YYERROR; + } + new_el.line = $1; + new_el.name = $2; + ignore_add_el(&new_el); + } +; + +chip_statement: CHIP chip_name_list + { sensors_chip new_el; + new_el.line = $1; + new_el.labels = NULL; + new_el.sets = NULL; + new_el.computes = NULL; + new_el.ignores = NULL; + new_el.labels_count = new_el.labels_max = 0; + new_el.sets_count = new_el.sets_max = 0; + new_el.computes_count = new_el.computes_max = 0; + new_el.ignores_count = new_el.ignores_max = 0; + new_el.chips = $2; + chip_add_el(&new_el); + current_chip = sensors_config_chips + + sensors_config_chips_count - 1; + } +; + +chip_name_list: chip_name + { + $$.fits = NULL; + $$.fits_count = $$.fits_max = 0; + fits_add_el(&$1,$$); + } + | chip_name_list chip_name + { $$ = $1; + fits_add_el(&$2,$$); + } +; + +expression: FLOAT + { $$ = malloc_expr(); + $$->data.val = $1; + $$->kind = sensors_kind_val; + } + | NAME + { $$ = malloc_expr(); + $$->data.var = $1; + $$->kind = sensors_kind_var; + } + | '@' + { $$ = malloc_expr(); + $$->kind = sensors_kind_source; + } + | expression '+' expression + { $$ = malloc_expr(); + $$->kind = sensors_kind_sub; + $$->data.subexpr.op = sensors_add; + $$->data.subexpr.sub1 = $1; + $$->data.subexpr.sub2 = $3; + } + | expression '-' expression + { $$ = malloc_expr(); + $$->kind = sensors_kind_sub; + $$->data.subexpr.op = sensors_sub; + $$->data.subexpr.sub1 = $1; + $$->data.subexpr.sub2 = $3; + } + | expression '*' expression + { $$ = malloc_expr(); + $$->kind = sensors_kind_sub; + $$->data.subexpr.op = sensors_multiply; + $$->data.subexpr.sub1 = $1; + $$->data.subexpr.sub2 = $3; + } + | expression '/' expression + { $$ = malloc_expr(); + $$->kind = sensors_kind_sub; + $$->data.subexpr.op = sensors_divide; + $$->data.subexpr.sub1 = $1; + $$->data.subexpr.sub2 = $3; + } + | '-' expression %prec NEG + { $$ = malloc_expr(); + $$->kind = sensors_kind_sub; + $$->data.subexpr.op = sensors_negate; + $$->data.subexpr.sub1 = $2; + $$->data.subexpr.sub2 = NULL; + } + | '(' expression ')' + { $$ = $2; } + | '^' expression + { $$ = malloc_expr(); + $$->kind = sensors_kind_sub; + $$->data.subexpr.op = sensors_exp; + $$->data.subexpr.sub1 = $2; + $$->data.subexpr.sub2 = NULL; + } + | '`' expression + { $$ = malloc_expr(); + $$->kind = sensors_kind_sub; + $$->data.subexpr.op = sensors_log; + $$->data.subexpr.sub1 = $2; + $$->data.subexpr.sub2 = NULL; + } +; + +bus_id: NAME + { int res = sensors_parse_bus_id($1,&$$); + free($1); + if (res) { + sensors_yyerror("Parse error in bus id"); + YYERROR; + } + } +; + +adapter_name: NAME + { $$ = $1; } +; + +function_name: NAME + { $$ = $1; } +; + +string: NAME + { $$ = $1; } +; + +chip_name: NAME + { int res = sensors_parse_chip_name($1,&$$); + free($1); + if (res) { + sensors_yyerror("Parse error in chip name"); + YYERROR; + } + } +; + +%% + +void sensors_yyerror(const char *err) +{ + if (sensors_lex_error[0]) { + sensors_parse_error_wfn(sensors_lex_error, sensors_yyfilename, sensors_yylineno); + sensors_lex_error[0] = '\0'; + } else + sensors_parse_error_wfn(err, sensors_yyfilename, sensors_yylineno); +} + +sensors_expr *malloc_expr(void) +{ + sensors_expr *res = malloc(sizeof(sensors_expr)); + if (! res) + sensors_fatal_error(__func__, "Allocating a new expression"); + return res; +} diff --git a/tools/gator/daemon/libsensors/conf.h b/tools/gator/daemon/libsensors/conf.h new file mode 100644 index 00000000000..b7ce4f7ca83 --- /dev/null +++ b/tools/gator/daemon/libsensors/conf.h @@ -0,0 +1,34 @@ +/* + conf.h - Part of libsensors, a Linux library for reading sensor data. + Copyright (c) 1998, 1999 Frodo Looijaard + + 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 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 LIB_SENSORS_CONF_H +#define LIB_SENSORS_CONF_H + +/* This is defined in conf-lex.l */ +int sensors_yylex(void); +extern char sensors_lex_error[]; +extern const char *sensors_yyfilename; +extern int sensors_yylineno; +extern FILE *sensors_yyin; + +/* This is defined in conf-parse.y */ +int sensors_yyparse(void); + +#endif /* LIB_SENSORS_CONF_H */ diff --git a/tools/gator/daemon/libsensors/data.c b/tools/gator/daemon/libsensors/data.c new file mode 100644 index 00000000000..cac9c8dc6eb --- /dev/null +++ b/tools/gator/daemon/libsensors/data.c @@ -0,0 +1,278 @@ +/* + data.c - Part of libsensors, a Linux library for reading sensor data. + Copyright (c) 1998, 1999 Frodo Looijaard + Copyright (C) 2007, 2009 Jean Delvare + + 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 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. +*/ + +/*** This file modified by ARM on Jan 23, 2013 to move version.h to the current directory. ***/ + +/* this define needed for strndup() */ +#define _GNU_SOURCE + +#include +#include + +#include "access.h" +#include "error.h" +#include "data.h" +#include "sensors.h" +#include "version.h" + +const char *libsensors_version = LM_VERSION; + +char **sensors_config_files = NULL; +int sensors_config_files_count = 0; +int sensors_config_files_max = 0; + +sensors_chip *sensors_config_chips = NULL; +int sensors_config_chips_count = 0; +int sensors_config_chips_subst = 0; +int sensors_config_chips_max = 0; + +sensors_bus *sensors_config_busses = NULL; +int sensors_config_busses_count = 0; +int sensors_config_busses_max = 0; + +sensors_chip_features *sensors_proc_chips = NULL; +int sensors_proc_chips_count = 0; +int sensors_proc_chips_max = 0; + +sensors_bus *sensors_proc_bus = NULL; +int sensors_proc_bus_count = 0; +int sensors_proc_bus_max = 0; + +void sensors_free_chip_name(sensors_chip_name *chip) +{ + free(chip->prefix); +} + +/* + Parse a chip name to the internal representation. These are valid names: + + lm78-i2c-10-5e *-i2c-10-5e + lm78-i2c-10-* *-i2c-10-* + lm78-i2c-*-5e *-i2c-*-5e + lm78-i2c-*-* *-i2c-*-* + lm78-isa-10dd *-isa-10dd + lm78-isa-* *-isa-* + lm78-* *-* + + Here 'lm78' can be any prefix. 'i2c' and 'isa' are + literal strings, just like all dashes '-' and wildcards '*'. '10' can + be any decimal i2c bus number. '5e' can be any hexadecimal i2c device + address, and '10dd' any hexadecimal isa address. + + The 'prefix' part in the result is freshly allocated. All old contents + of res is overwritten. res itself is not allocated. In case of an error + return (ie. != 0), res is undefined, but all allocations are undone. +*/ + +int sensors_parse_chip_name(const char *name, sensors_chip_name *res) +{ + char *dash; + + /* First, the prefix. It's either "*" or a real chip name. */ + if (!strncmp(name, "*-", 2)) { + res->prefix = SENSORS_CHIP_NAME_PREFIX_ANY; + name += 2; + } else { + if (!(dash = strchr(name, '-'))) + return -SENSORS_ERR_CHIP_NAME; + res->prefix = strndup(name, dash - name); + if (!res->prefix) + sensors_fatal_error(__func__, + "Allocating name prefix"); + name = dash + 1; + } + + /* Then we have either a sole "*" (all chips with this name) or a bus + type and an address. */ + if (!strcmp(name, "*")) { + res->bus.type = SENSORS_BUS_TYPE_ANY; + res->bus.nr = SENSORS_BUS_NR_ANY; + res->addr = SENSORS_CHIP_NAME_ADDR_ANY; + return 0; + } + + if (!(dash = strchr(name, '-'))) + goto ERROR; + if (!strncmp(name, "i2c", dash - name)) + res->bus.type = SENSORS_BUS_TYPE_I2C; + else if (!strncmp(name, "isa", dash - name)) + res->bus.type = SENSORS_BUS_TYPE_ISA; + else if (!strncmp(name, "pci", dash - name)) + res->bus.type = SENSORS_BUS_TYPE_PCI; + else if (!strncmp(name, "spi", dash - name)) + res->bus.type = SENSORS_BUS_TYPE_SPI; + else if (!strncmp(name, "virtual", dash - name)) + res->bus.type = SENSORS_BUS_TYPE_VIRTUAL; + else if (!strncmp(name, "acpi", dash - name)) + res->bus.type = SENSORS_BUS_TYPE_ACPI; + else if (!strncmp(name, "hid", dash - name)) + res->bus.type = SENSORS_BUS_TYPE_HID; + else + goto ERROR; + name = dash + 1; + + /* Some bus types (i2c, spi) have an additional bus number. + For these, the next part is either a "*" (any bus of that type) + or a decimal number. */ + switch (res->bus.type) { + case SENSORS_BUS_TYPE_I2C: + case SENSORS_BUS_TYPE_SPI: + case SENSORS_BUS_TYPE_HID: + if (!strncmp(name, "*-", 2)) { + res->bus.nr = SENSORS_BUS_NR_ANY; + name += 2; + break; + } + + res->bus.nr = strtoul(name, &dash, 10); + if (*name == '\0' || *dash != '-' || res->bus.nr < 0) + goto ERROR; + name = dash + 1; + break; + default: + res->bus.nr = SENSORS_BUS_NR_ANY; + } + + /* Last part is the chip address, or "*" for any address. */ + if (!strcmp(name, "*")) { + res->addr = SENSORS_CHIP_NAME_ADDR_ANY; + } else { + res->addr = strtoul(name, &dash, 16); + if (*name == '\0' || *dash != '\0' || res->addr < 0) + goto ERROR; + } + + return 0; + +ERROR: + free(res->prefix); + return -SENSORS_ERR_CHIP_NAME; +} + +int sensors_snprintf_chip_name(char *str, size_t size, + const sensors_chip_name *chip) +{ + if (sensors_chip_name_has_wildcards(chip)) + return -SENSORS_ERR_WILDCARDS; + + switch (chip->bus.type) { + case SENSORS_BUS_TYPE_ISA: + return snprintf(str, size, "%s-isa-%04x", chip->prefix, + chip->addr); + case SENSORS_BUS_TYPE_PCI: + return snprintf(str, size, "%s-pci-%04x", chip->prefix, + chip->addr); + case SENSORS_BUS_TYPE_I2C: + return snprintf(str, size, "%s-i2c-%hd-%02x", chip->prefix, + chip->bus.nr, chip->addr); + case SENSORS_BUS_TYPE_SPI: + return snprintf(str, size, "%s-spi-%hd-%x", chip->prefix, + chip->bus.nr, chip->addr); + case SENSORS_BUS_TYPE_VIRTUAL: + return snprintf(str, size, "%s-virtual-%x", chip->prefix, + chip->addr); + case SENSORS_BUS_TYPE_ACPI: + return snprintf(str, size, "%s-acpi-%x", chip->prefix, + chip->addr); + case SENSORS_BUS_TYPE_HID: + return snprintf(str, size, "%s-hid-%hd-%x", chip->prefix, + chip->bus.nr, chip->addr); + } + + return -SENSORS_ERR_CHIP_NAME; +} + +int sensors_parse_bus_id(const char *name, sensors_bus_id *bus) +{ + char *endptr; + + if (strncmp(name, "i2c-", 4)) { + return -SENSORS_ERR_BUS_NAME; + } + name += 4; + bus->type = SENSORS_BUS_TYPE_I2C; + bus->nr = strtoul(name, &endptr, 10); + if (*name == '\0' || *endptr != '\0' || bus->nr < 0) + return -SENSORS_ERR_BUS_NAME; + return 0; +} + +static int sensors_substitute_chip(sensors_chip_name *name, + const char *filename, int lineno) +{ + int i, j; + for (i = 0; i < sensors_config_busses_count; i++) + if (sensors_config_busses[i].bus.type == name->bus.type && + sensors_config_busses[i].bus.nr == name->bus.nr) + break; + + if (i == sensors_config_busses_count) { + sensors_parse_error_wfn("Undeclared bus id referenced", + filename, lineno); + name->bus.nr = SENSORS_BUS_NR_IGNORE; + return -SENSORS_ERR_BUS_NAME; + } + + /* Compare the adapter names */ + for (j = 0; j < sensors_proc_bus_count; j++) { + if (!strcmp(sensors_config_busses[i].adapter, + sensors_proc_bus[j].adapter)) { + name->bus.nr = sensors_proc_bus[j].bus.nr; + return 0; + } + } + + /* We did not find a matching bus name, simply ignore this chip + config entry. */ + name->bus.nr = SENSORS_BUS_NR_IGNORE; + return 0; +} + +/* Bus substitution is on a per-configuration file basis, so we keep + memory (in sensors_config_chips_subst) of which chip entries have been + already substituted. */ +int sensors_substitute_busses(void) +{ + int err, i, j, lineno; + sensors_chip_name_list *chips; + const char *filename; + int res = 0; + + for (i = sensors_config_chips_subst; + i < sensors_config_chips_count; i++) { + filename = sensors_config_chips[i].line.filename; + lineno = sensors_config_chips[i].line.lineno; + chips = &sensors_config_chips[i].chips; + for (j = 0; j < chips->fits_count; j++) { + /* We can only substitute if a specific bus number + is given. */ + if (chips->fits[j].bus.nr == SENSORS_BUS_NR_ANY) + continue; + + err = sensors_substitute_chip(&chips->fits[j], + filename, lineno); + if (err) + res = err; + } + } + sensors_config_chips_subst = sensors_config_chips_count; + return res; +} diff --git a/tools/gator/daemon/libsensors/data.h b/tools/gator/daemon/libsensors/data.h new file mode 100644 index 00000000000..4a23eabe141 --- /dev/null +++ b/tools/gator/daemon/libsensors/data.h @@ -0,0 +1,184 @@ +/* + data.h - Part of libsensors, a Linux library for reading sensor data. + Copyright (c) 1998, 1999 Frodo Looijaard + Copyright (C) 2007, 2009 Jean Delvare + + 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 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 LIB_SENSORS_DATA_H +#define LIB_SENSORS_DATA_H + +#include "sensors.h" +#include "general.h" + +/* This header file contains all kinds of data structures which are used + for the representation of the config file data and the sensors + data. */ + +/* Kinds of expression operators recognized */ +typedef enum sensors_operation { + sensors_add, sensors_sub, sensors_multiply, sensors_divide, + sensors_negate, sensors_exp, sensors_log, +} sensors_operation; + +/* An expression can have several forms */ +typedef enum sensors_expr_kind { + sensors_kind_val, sensors_kind_source, sensors_kind_var, + sensors_kind_sub +} sensors_expr_kind; + +/* An expression. It is either a floating point value, a variable name, + an operation on subexpressions, or the special value 'sub' } */ +struct sensors_expr; + +typedef struct sensors_subexpr { + sensors_operation op; + struct sensors_expr *sub1; + struct sensors_expr *sub2; +} sensors_subexpr; + +typedef struct sensors_expr { + sensors_expr_kind kind; + union { + double val; + char *var; + sensors_subexpr subexpr; + } data; +} sensors_expr; + +/* Config file line reference */ +typedef struct sensors_config_line { + const char *filename; + int lineno; +} sensors_config_line; + +/* Config file label declaration: a feature name, combined with the label + value */ +typedef struct sensors_label { + char *name; + char *value; + sensors_config_line line; +} sensors_label; + +/* Config file set declaration: a subfeature name, combined with an + expression */ +typedef struct sensors_set { + char *name; + sensors_expr *value; + sensors_config_line line; +} sensors_set; + +/* Config file compute declaration: a feature name, combined with two + expressions */ +typedef struct sensors_compute { + char *name; + sensors_expr *from_proc; + sensors_expr *to_proc; + sensors_config_line line; +} sensors_compute; + +/* Config file ignore declaration: a feature name */ +typedef struct sensors_ignore { + char *name; + sensors_config_line line; +} sensors_ignore; + +/* A list of chip names, used to represent a config file chips declaration */ +typedef struct sensors_chip_name_list { + sensors_chip_name *fits; + int fits_count; + int fits_max; +} sensors_chip_name_list; + +/* A config file chip block */ +typedef struct sensors_chip { + sensors_chip_name_list chips; + sensors_label *labels; + int labels_count; + int labels_max; + sensors_set *sets; + int sets_count; + int sets_max; + sensors_compute *computes; + int computes_count; + int computes_max; + sensors_ignore *ignores; + int ignores_count; + int ignores_max; + sensors_config_line line; +} sensors_chip; + +/* Config file bus declaration: the bus type and number, combined with adapter + name */ +typedef struct sensors_bus { + char *adapter; + sensors_bus_id bus; + sensors_config_line line; +} sensors_bus; + +/* Internal data about all features and subfeatures of a chip */ +typedef struct sensors_chip_features { + struct sensors_chip_name chip; + struct sensors_feature *feature; + struct sensors_subfeature *subfeature; + int feature_count; + int subfeature_count; +} sensors_chip_features; + +extern char **sensors_config_files; +extern int sensors_config_files_count; +extern int sensors_config_files_max; + +#define sensors_add_config_files(el) sensors_add_array_el( \ + (el), &sensors_config_files, &sensors_config_files_count, \ + &sensors_config_files_max, sizeof(char *)) + +extern sensors_chip *sensors_config_chips; +extern int sensors_config_chips_count; +extern int sensors_config_chips_subst; +extern int sensors_config_chips_max; + +extern sensors_bus *sensors_config_busses; +extern int sensors_config_busses_count; +extern int sensors_config_busses_max; + +extern sensors_chip_features *sensors_proc_chips; +extern int sensors_proc_chips_count; +extern int sensors_proc_chips_max; + +#define sensors_add_proc_chips(el) sensors_add_array_el( \ + (el), &sensors_proc_chips, &sensors_proc_chips_count,\ + &sensors_proc_chips_max, sizeof(struct sensors_chip_features)) + +extern sensors_bus *sensors_proc_bus; +extern int sensors_proc_bus_count; +extern int sensors_proc_bus_max; + +#define sensors_add_proc_bus(el) sensors_add_array_el( \ + (el), &sensors_proc_bus, &sensors_proc_bus_count,\ + &sensors_proc_bus_max, sizeof(struct sensors_bus)) + +/* Substitute configuration bus numbers with real-world bus numbers + in the chips lists */ +int sensors_substitute_busses(void); + + +/* Parse a bus id into its components. Returns 0 on success, a value from + error.h on failure. */ +int sensors_parse_bus_id(const char *name, sensors_bus_id *bus); + +#endif /* def LIB_SENSORS_DATA_H */ diff --git a/tools/gator/daemon/libsensors/error.c b/tools/gator/daemon/libsensors/error.c new file mode 100644 index 00000000000..55bde81295f --- /dev/null +++ b/tools/gator/daemon/libsensors/error.c @@ -0,0 +1,92 @@ +/* + error.c - Part of libsensors, a Linux library for reading sensor data. + Copyright (c) 1998, 1999 Frodo Looijaard + Copyright (C) 2007-2009 Jean Delvare + + 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 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 +#include +#include "error.h" +#include "general.h" + +static void sensors_default_parse_error(const char *err, int lineno); +static void sensors_default_parse_error_wfn(const char *err, + const char *filename, int lineno); +static void sensors_default_fatal_error(const char *proc, const char *err); + +void (*sensors_parse_error) (const char *err, int lineno) = + sensors_default_parse_error; +void (*sensors_parse_error_wfn) (const char *err, const char *filename, + int lineno) = sensors_default_parse_error_wfn; +void (*sensors_fatal_error) (const char *proc, const char *err) = + sensors_default_fatal_error; + +static const char *errorlist[] = { + /* Invalid error code */ "Unknown error", + /* SENSORS_ERR_WILDCARDS */ "Wildcard found in chip name", + /* SENSORS_ERR_NO_ENTRY */ "No such subfeature known", + /* SENSORS_ERR_ACCESS_R */ "Can't read", + /* SENSORS_ERR_KERNEL */ "Kernel interface error", + /* SENSORS_ERR_DIV_ZERO */ "Divide by zero", + /* SENSORS_ERR_CHIP_NAME */ "Can't parse chip name", + /* SENSORS_ERR_BUS_NAME */ "Can't parse bus name", + /* SENSORS_ERR_PARSE */ "General parse error", + /* SENSORS_ERR_ACCESS_W */ "Can't write", + /* SENSORS_ERR_IO */ "I/O error", + /* SENSORS_ERR_RECURSION */ "Evaluation recurses too deep", +}; + +const char *sensors_strerror(int errnum) +{ + if (errnum < 0) + errnum = -errnum; + if (errnum >= ARRAY_SIZE(errorlist)) + errnum = 0; + return errorlist[errnum]; +} + +void sensors_default_parse_error(const char *err, int lineno) +{ + if (lineno) + fprintf(stderr, "Error: Line %d: %s\n", lineno, err); + else + fprintf(stderr, "Error: %s\n", err); +} + +void sensors_default_parse_error_wfn(const char *err, + const char *filename, int lineno) +{ + /* If application provided a custom parse error reporting function + but not the variant with the filename, fall back to the original + variant without the filename, for backwards compatibility. */ + if (sensors_parse_error != sensors_default_parse_error || + !filename) + return sensors_parse_error(err, lineno); + + if (lineno) + fprintf(stderr, "Error: File %s, line %d: %s\n", filename, + lineno, err); + else + fprintf(stderr, "Error: File %s: %s\n", filename, err); +} + +void sensors_default_fatal_error(const char *proc, const char *err) +{ + fprintf(stderr, "Fatal error in `%s': %s\n", proc, err); + exit(1); +} diff --git a/tools/gator/daemon/libsensors/error.h b/tools/gator/daemon/libsensors/error.h new file mode 100644 index 00000000000..37cdc956151 --- /dev/null +++ b/tools/gator/daemon/libsensors/error.h @@ -0,0 +1,74 @@ +/* + error.h - Part of libsensors, a Linux library for reading sensor data. + Copyright (c) 1998, 1999 Frodo Looijaard + Copyright (C) 2007-2009 Jean Delvare + + 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 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 LIB_SENSORS_ERROR_H +#define LIB_SENSORS_ERROR_H + +#define SENSORS_ERR_WILDCARDS 1 /* Wildcard found in chip name */ +#define SENSORS_ERR_NO_ENTRY 2 /* No such subfeature known */ +#define SENSORS_ERR_ACCESS_R 3 /* Can't read */ +#define SENSORS_ERR_KERNEL 4 /* Kernel interface error */ +#define SENSORS_ERR_DIV_ZERO 5 /* Divide by zero */ +#define SENSORS_ERR_CHIP_NAME 6 /* Can't parse chip name */ +#define SENSORS_ERR_BUS_NAME 7 /* Can't parse bus name */ +#define SENSORS_ERR_PARSE 8 /* General parse error */ +#define SENSORS_ERR_ACCESS_W 9 /* Can't write */ +#define SENSORS_ERR_IO 10 /* I/O error */ +#define SENSORS_ERR_RECURSION 11 /* Evaluation recurses too deep */ + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + + +/* This function returns a pointer to a string which describes the error. + errnum may be negative (the corresponding positive error is returned). + You may not modify the result! */ +const char *sensors_strerror(int errnum); + +/* These functions are called when a parse error is detected. Give them new + values, and your own functions are called instead of the default (which + print to stderr). These functions may terminate the program, but they + usually output an error and return. The first function is the original + one, the second one was added later when support for multiple + configuration files was added. + The library code now only calls the second function. However, for + backwards compatibility, if an application provides a custom handling + function for the first function but not the second, then all parse + errors will be reported using the first function (that is, the filename + is never reported.) + Note that filename can be NULL (if filename isn't known) and lineno + can be 0 (if the error occurs before the actual parsing starts.) */ +extern void (*sensors_parse_error) (const char *err, int lineno); +extern void (*sensors_parse_error_wfn) (const char *err, + const char *filename, int lineno); + +/* This function is called when an immediately fatal error (like no + memory left) is detected. Give it a new value, and your own function + is called instead of the default (which prints to stderr and ends + the program). Never let it return! */ +extern void (*sensors_fatal_error) (const char *proc, const char *err); + +#ifdef __cplusplus +} +#endif /* __cplusplus */ + +#endif /* def LIB_SENSORS_ERROR_H */ diff --git a/tools/gator/daemon/libsensors/general.c b/tools/gator/daemon/libsensors/general.c new file mode 100644 index 00000000000..f237e3b59c2 --- /dev/null +++ b/tools/gator/daemon/libsensors/general.c @@ -0,0 +1,85 @@ +/* + general.c - Part of libsensors, a Linux library for reading sensor data. + Copyright (c) 1998, 1999 Frodo Looijaard + + 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 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 "error.h" +#include "general.h" +#include +#include +#include +#include + + +#define A_BUNCH 16 + +void sensors_malloc_array(void *list, int *num_el, int *max_el, int el_size) +{ + void **my_list = (void **)list; + + *my_list = malloc(el_size*A_BUNCH); + if (! *my_list) + sensors_fatal_error(__func__, "Allocating new elements"); + *max_el = A_BUNCH; + *num_el = 0; +} + +void sensors_free_array(void *list, int *num_el, int *max_el) +{ + void **my_list = (void **)list; + + free(*my_list); + *my_list = NULL; + *num_el = 0; + *max_el = 0; +} + +void sensors_add_array_el(const void *el, void *list, int *num_el, + int *max_el, int el_size) +{ + int new_max_el; + void **my_list = (void *)list; + if (*num_el + 1 > *max_el) { + new_max_el = *max_el + A_BUNCH; + *my_list = realloc(*my_list, new_max_el * el_size); + if (! *my_list) + sensors_fatal_error(__func__, + "Allocating new elements"); + *max_el = new_max_el; + } + memcpy(((char *) *my_list) + *num_el * el_size, el, el_size); + (*num_el) ++; +} + +void sensors_add_array_els(const void *els, int nr_els, void *list, + int *num_el, int *max_el, int el_size) +{ + int new_max_el; + void **my_list = (void *)list; + if (*num_el + nr_els > *max_el) { + new_max_el = (*max_el + nr_els + A_BUNCH); + new_max_el -= new_max_el % A_BUNCH; + *my_list = realloc(*my_list, new_max_el * el_size); + if (! *my_list) + sensors_fatal_error(__func__, + "Allocating new elements"); + *max_el = new_max_el; + } + memcpy(((char *)*my_list) + *num_el * el_size, els, el_size * nr_els); + *num_el += nr_els; +} diff --git a/tools/gator/daemon/libsensors/general.h b/tools/gator/daemon/libsensors/general.h new file mode 100644 index 00000000000..a3971e02e1b --- /dev/null +++ b/tools/gator/daemon/libsensors/general.h @@ -0,0 +1,39 @@ +/* + general.h - Part of libsensors, a Linux library for reading sensor data. + Copyright (c) 1998, 1999 Frodo Looijaard + + 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 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 LIB_SENSORS_GENERAL +#define LIB_SENSORS_GENERAL + +/* These are general purpose functions. They allow you to use variable- + length arrays, which are extended automatically. A distinction is + made between the current number of elements and the maximum number. + You can only add elements at the end. Primitive, but very useful + for internal use. */ +void sensors_malloc_array(void *list, int *num_el, int *max_el, + int el_size); +void sensors_free_array(void *list, int *num_el, int *max_el); +void sensors_add_array_el(const void *el, void *list, int *num_el, + int *max_el, int el_size); +void sensors_add_array_els(const void *els, int nr_els, void *list, + int *num_el, int *max_el, int el_size); + +#define ARRAY_SIZE(arr) (int)(sizeof(arr) / sizeof((arr)[0])) + +#endif /* LIB_SENSORS_GENERAL */ diff --git a/tools/gator/daemon/libsensors/init.c b/tools/gator/daemon/libsensors/init.c new file mode 100644 index 00000000000..558046e76c2 --- /dev/null +++ b/tools/gator/daemon/libsensors/init.c @@ -0,0 +1,341 @@ +/* + init.c - Part of libsensors, a Linux library for reading sensor data. + Copyright (c) 1998, 1999 Frodo Looijaard + Copyright (C) 2007, 2009 Jean Delvare + + 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 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. +*/ + +/*** This file modified by ARM on Jan 23, 2013 to cast alphasort to supress a warning as it's prototype is different on android. ***/ + +/* Needed for scandir() and alphasort() */ +#define _BSD_SOURCE + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "sensors.h" +#include "data.h" +#include "error.h" +#include "access.h" +#include "conf.h" +#include "sysfs.h" +#include "scanner.h" +#include "init.h" + +#define DEFAULT_CONFIG_FILE ETCDIR "/sensors3.conf" +#define ALT_CONFIG_FILE ETCDIR "/sensors.conf" +#define DEFAULT_CONFIG_DIR ETCDIR "/sensors.d" + +/* Wrapper around sensors_yyparse(), which clears the locale so that + the decimal numbers are always parsed properly. */ +static int sensors_parse(void) +{ + int res; + char *locale; + + /* Remember the current locale and clear it */ + locale = setlocale(LC_ALL, NULL); + if (locale) { + locale = strdup(locale); + if (!locale) + sensors_fatal_error(__func__, "Out of memory"); + + setlocale(LC_ALL, "C"); + } + + res = sensors_yyparse(); + + /* Restore the old locale */ + if (locale) { + setlocale(LC_ALL, locale); + free(locale); + } + + return res; +} + +static void free_bus(sensors_bus *bus) +{ + free(bus->adapter); +} + +static void free_config_busses(void) +{ + int i; + + for (i = 0; i < sensors_config_busses_count; i++) + free_bus(&sensors_config_busses[i]); + free(sensors_config_busses); + sensors_config_busses = NULL; + sensors_config_busses_count = sensors_config_busses_max = 0; +} + +static int parse_config(FILE *input, const char *name) +{ + int err; + char *name_copy; + + if (name) { + /* Record configuration file name for error reporting */ + name_copy = strdup(name); + if (!name_copy) + sensors_fatal_error(__func__, "Out of memory"); + sensors_add_config_files(&name_copy); + } else + name_copy = NULL; + + if (sensors_scanner_init(input, name_copy)) { + err = -SENSORS_ERR_PARSE; + goto exit_cleanup; + } + err = sensors_parse(); + sensors_scanner_exit(); + if (err) { + err = -SENSORS_ERR_PARSE; + goto exit_cleanup; + } + + err = sensors_substitute_busses(); + +exit_cleanup: + free_config_busses(); + return err; +} + +static int config_file_filter(const struct dirent *entry) +{ + return entry->d_name[0] != '.'; /* Skip hidden files */ +} + +static int add_config_from_dir(const char *dir) +{ + int count, res, i; + struct dirent **namelist; + + count = scandir(dir, &namelist, config_file_filter, (int (*)(const struct dirent **, const struct dirent **))alphasort); + if (count < 0) { + /* Do not return an error if directory does not exist */ + if (errno == ENOENT) + return 0; + + sensors_parse_error_wfn(strerror(errno), NULL, 0); + return -SENSORS_ERR_PARSE; + } + + for (res = 0, i = 0; !res && i < count; i++) { + int len; + char path[PATH_MAX]; + FILE *input; + struct stat st; + + len = snprintf(path, sizeof(path), "%s/%s", dir, + namelist[i]->d_name); + if (len < 0 || len >= (int)sizeof(path)) { + res = -SENSORS_ERR_PARSE; + continue; + } + + /* Only accept regular files */ + if (stat(path, &st) < 0 || !S_ISREG(st.st_mode)) + continue; + + input = fopen(path, "r"); + if (input) { + res = parse_config(input, path); + fclose(input); + } else { + res = -SENSORS_ERR_PARSE; + sensors_parse_error_wfn(strerror(errno), path, 0); + } + } + + /* Free memory allocated by scandir() */ + for (i = 0; i < count; i++) + free(namelist[i]); + free(namelist); + + return res; +} + +int sensors_init(FILE *input) +{ + int res; + + if (!sensors_init_sysfs()) + return -SENSORS_ERR_KERNEL; + if ((res = sensors_read_sysfs_bus()) || + (res = sensors_read_sysfs_chips())) + goto exit_cleanup; + + if (input) { + res = parse_config(input, NULL); + if (res) + goto exit_cleanup; + } else { + const char* name; + + /* No configuration provided, use default */ + input = fopen(name = DEFAULT_CONFIG_FILE, "r"); + if (!input && errno == ENOENT) + input = fopen(name = ALT_CONFIG_FILE, "r"); + if (input) { + res = parse_config(input, name); + fclose(input); + if (res) + goto exit_cleanup; + + } else if (errno != ENOENT) { + sensors_parse_error_wfn(strerror(errno), name, 0); + res = -SENSORS_ERR_PARSE; + goto exit_cleanup; + } + + /* Also check for files in default directory */ + res = add_config_from_dir(DEFAULT_CONFIG_DIR); + if (res) + goto exit_cleanup; + } + + return 0; + +exit_cleanup: + sensors_cleanup(); + return res; +} + +static void free_chip_name(sensors_chip_name *name) +{ + free(name->prefix); + free(name->path); +} + +static void free_chip_features(sensors_chip_features *features) +{ + int i; + + for (i = 0; i < features->subfeature_count; i++) + free(features->subfeature[i].name); + free(features->subfeature); + for (i = 0; i < features->feature_count; i++) + free(features->feature[i].name); + free(features->feature); +} + +static void free_label(sensors_label *label) +{ + free(label->name); + free(label->value); +} + +void sensors_free_expr(sensors_expr *expr) +{ + if (expr->kind == sensors_kind_var) + free(expr->data.var); + else if (expr->kind == sensors_kind_sub) { + if (expr->data.subexpr.sub1) + sensors_free_expr(expr->data.subexpr.sub1); + if (expr->data.subexpr.sub2) + sensors_free_expr(expr->data.subexpr.sub2); + } + free(expr); +} + +static void free_set(sensors_set *set) +{ + free(set->name); + sensors_free_expr(set->value); +} + +static void free_compute(sensors_compute *compute) +{ + free(compute->name); + sensors_free_expr(compute->from_proc); + sensors_free_expr(compute->to_proc); +} + +static void free_ignore(sensors_ignore *ignore) +{ + free(ignore->name); +} + +static void free_chip(sensors_chip *chip) +{ + int i; + + for (i = 0; i < chip->chips.fits_count; i++) + free_chip_name(&chip->chips.fits[i]); + free(chip->chips.fits); + chip->chips.fits_count = chip->chips.fits_max = 0; + + for (i = 0; i < chip->labels_count; i++) + free_label(&chip->labels[i]); + free(chip->labels); + chip->labels_count = chip->labels_max = 0; + + for (i = 0; i < chip->sets_count; i++) + free_set(&chip->sets[i]); + free(chip->sets); + chip->sets_count = chip->sets_max = 0; + + for (i = 0; i < chip->computes_count; i++) + free_compute(&chip->computes[i]); + free(chip->computes); + chip->computes_count = chip->computes_max = 0; + + for (i = 0; i < chip->ignores_count; i++) + free_ignore(&chip->ignores[i]); + free(chip->ignores); + chip->ignores_count = chip->ignores_max = 0; +} + +void sensors_cleanup(void) +{ + int i; + + for (i = 0; i < sensors_proc_chips_count; i++) { + free_chip_name(&sensors_proc_chips[i].chip); + free_chip_features(&sensors_proc_chips[i]); + } + free(sensors_proc_chips); + sensors_proc_chips = NULL; + sensors_proc_chips_count = sensors_proc_chips_max = 0; + + for (i = 0; i < sensors_config_chips_count; i++) + free_chip(&sensors_config_chips[i]); + free(sensors_config_chips); + sensors_config_chips = NULL; + sensors_config_chips_count = sensors_config_chips_max = 0; + sensors_config_chips_subst = 0; + + for (i = 0; i < sensors_proc_bus_count; i++) + free_bus(&sensors_proc_bus[i]); + free(sensors_proc_bus); + sensors_proc_bus = NULL; + sensors_proc_bus_count = sensors_proc_bus_max = 0; + + for (i = 0; i < sensors_config_files_count; i++) + free(sensors_config_files[i]); + free(sensors_config_files); + sensors_config_files = NULL; + sensors_config_files_count = sensors_config_files_max = 0; +} diff --git a/tools/gator/daemon/libsensors/init.h b/tools/gator/daemon/libsensors/init.h new file mode 100644 index 00000000000..47006a62db6 --- /dev/null +++ b/tools/gator/daemon/libsensors/init.h @@ -0,0 +1,28 @@ +/* + init.h - Part of libsensors, a Linux library for reading sensor data. + Copyright (C) 2007 Jean Delvare + + 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 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 LIB_SENSORS_INIT_H +#define LIB_SENSORS_INIT_H + +#include "data.h" + +void sensors_free_expr(sensors_expr *expr); + +#endif /* def LIB_SENSORS_INIT_H */ diff --git a/tools/gator/daemon/libsensors/scanner.h b/tools/gator/daemon/libsensors/scanner.h new file mode 100644 index 00000000000..4c415167de5 --- /dev/null +++ b/tools/gator/daemon/libsensors/scanner.h @@ -0,0 +1,32 @@ +/* + scanner.h - Part of libsensors, a Linux library for reading sensor data. + Copyright (c) 2006 Mark M. Hoffman + + 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 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. +*/ + +/*** This file modified by ARM on Jan 23, 2013 to fix input defined but not used warning from conf-lex.c. ***/ + +#ifndef LIB_SENSORS_SCANNER_H +#define LIB_SENSORS_SCANNER_H + +int sensors_scanner_init(FILE *input, const char *filename); +void sensors_scanner_exit(void); + +#define YY_NO_INPUT + +#endif + diff --git a/tools/gator/daemon/libsensors/sensors.h b/tools/gator/daemon/libsensors/sensors.h new file mode 100644 index 00000000000..7874d028dc8 --- /dev/null +++ b/tools/gator/daemon/libsensors/sensors.h @@ -0,0 +1,311 @@ +/* + sensors.h - Part of libsensors, a Linux library for reading sensor data. + Copyright (c) 1998, 1999 Frodo Looijaard + Copyright (C) 2007, 2010 Jean Delvare + + 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 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. +*/ + +/*** This file modified by ARM on Jan 23, 2013 to read non-scaled values. ***/ + +#ifndef LIB_SENSORS_SENSORS_H +#define LIB_SENSORS_SENSORS_H + +#include +#include + +/* Publicly accessible library functions */ + +/* libsensors API version define, first digit is the major version (changed + when the API + ABI breaks), the third digit is incremented to track small + API additions like new flags / enum values. The second digit is for tracking + larger additions like new methods. */ +#define SENSORS_API_VERSION 0x432 + +#define SENSORS_CHIP_NAME_PREFIX_ANY NULL +#define SENSORS_CHIP_NAME_ADDR_ANY (-1) + +#define SENSORS_BUS_TYPE_ANY (-1) +#define SENSORS_BUS_TYPE_I2C 0 +#define SENSORS_BUS_TYPE_ISA 1 +#define SENSORS_BUS_TYPE_PCI 2 +#define SENSORS_BUS_TYPE_SPI 3 +#define SENSORS_BUS_TYPE_VIRTUAL 4 +#define SENSORS_BUS_TYPE_ACPI 5 +#define SENSORS_BUS_TYPE_HID 6 +#define SENSORS_BUS_NR_ANY (-1) +#define SENSORS_BUS_NR_IGNORE (-2) + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + +extern const char *libsensors_version; + +extern int sensors_sysfs_no_scaling; + +typedef struct sensors_bus_id { + short type; + short nr; +} sensors_bus_id; + +/* A chip name is encoded in this structure */ +typedef struct sensors_chip_name { + char *prefix; + sensors_bus_id bus; + int addr; + char *path; +} sensors_chip_name; + +/* Load the configuration file and the detected chips list. If this + returns a value unequal to zero, you are in trouble; you can not + assume anything will be initialized properly. If you want to + reload the configuration file, call sensors_cleanup() below before + calling sensors_init() again. */ +int sensors_init(FILE *input); + +/* Clean-up function: You can't access anything after + this, until the next sensors_init() call! */ +void sensors_cleanup(void); + +/* Parse a chip name to the internal representation. Return 0 on success, <0 + on error. */ +int sensors_parse_chip_name(const char *orig_name, sensors_chip_name *res); + +/* Free memory allocated for the internal representation of a chip name. */ +void sensors_free_chip_name(sensors_chip_name *chip); + +/* Print a chip name from its internal representation. Note that chip should + not contain wildcard values! Return the number of characters printed on + success (same as snprintf), <0 on error. */ +int sensors_snprintf_chip_name(char *str, size_t size, + const sensors_chip_name *chip); + +/* This function returns the adapter name of a bus, + as used within the sensors_chip_name structure. If it could not be found, + it returns NULL */ +const char *sensors_get_adapter_name(const sensors_bus_id *bus); + +typedef struct sensors_feature sensors_feature; + +/* Look up the label for a given feature. Note that chip should not + contain wildcard values! The returned string is newly allocated (free it + yourself). On failure, NULL is returned. + If no label exists for this feature, its name is returned itself. */ +char *sensors_get_label(const sensors_chip_name *name, + const sensors_feature *feature); + +/* Read the value of a subfeature of a certain chip. Note that chip should not + contain wildcard values! This function will return 0 on success, and <0 + on failure. */ +int sensors_get_value(const sensors_chip_name *name, int subfeat_nr, + double *value); + +/* Set the value of a subfeature of a certain chip. Note that chip should not + contain wildcard values! This function will return 0 on success, and <0 + on failure. */ +int sensors_set_value(const sensors_chip_name *name, int subfeat_nr, + double value); + +/* Execute all set statements for this particular chip. The chip may contain + wildcards! This function will return 0 on success, and <0 on failure. */ +int sensors_do_chip_sets(const sensors_chip_name *name); + +/* This function returns all detected chips that match a given chip name, + one by one. If no chip name is provided, all detected chips are returned. + To start at the beginning of the list, use 0 for nr; NULL is returned if + we are at the end of the list. Do not try to change these chip names, as + they point to internal structures! */ +const sensors_chip_name *sensors_get_detected_chips(const sensors_chip_name + *match, int *nr); + +/* These defines are used in the flags field of sensors_subfeature */ +#define SENSORS_MODE_R 1 +#define SENSORS_MODE_W 2 +#define SENSORS_COMPUTE_MAPPING 4 + +typedef enum sensors_feature_type { + SENSORS_FEATURE_IN = 0x00, + SENSORS_FEATURE_FAN = 0x01, + SENSORS_FEATURE_TEMP = 0x02, + SENSORS_FEATURE_POWER = 0x03, + SENSORS_FEATURE_ENERGY = 0x04, + SENSORS_FEATURE_CURR = 0x05, + SENSORS_FEATURE_HUMIDITY = 0x06, + SENSORS_FEATURE_MAX_MAIN, + SENSORS_FEATURE_VID = 0x10, + SENSORS_FEATURE_INTRUSION = 0x11, + SENSORS_FEATURE_MAX_OTHER, + SENSORS_FEATURE_BEEP_ENABLE = 0x18, + SENSORS_FEATURE_UNKNOWN = INT_MAX, +} sensors_feature_type; + +/* All the sensor types (in, fan, temp, vid) are a multiple of 0x100 apart, + and sensor subfeatures which have no compute mapping have bit 7 set. */ +typedef enum sensors_subfeature_type { + SENSORS_SUBFEATURE_IN_INPUT = SENSORS_FEATURE_IN << 8, + SENSORS_SUBFEATURE_IN_MIN, + SENSORS_SUBFEATURE_IN_MAX, + SENSORS_SUBFEATURE_IN_LCRIT, + SENSORS_SUBFEATURE_IN_CRIT, + SENSORS_SUBFEATURE_IN_AVERAGE, + SENSORS_SUBFEATURE_IN_LOWEST, + SENSORS_SUBFEATURE_IN_HIGHEST, + SENSORS_SUBFEATURE_IN_ALARM = (SENSORS_FEATURE_IN << 8) | 0x80, + SENSORS_SUBFEATURE_IN_MIN_ALARM, + SENSORS_SUBFEATURE_IN_MAX_ALARM, + SENSORS_SUBFEATURE_IN_BEEP, + SENSORS_SUBFEATURE_IN_LCRIT_ALARM, + SENSORS_SUBFEATURE_IN_CRIT_ALARM, + + SENSORS_SUBFEATURE_FAN_INPUT = SENSORS_FEATURE_FAN << 8, + SENSORS_SUBFEATURE_FAN_MIN, + SENSORS_SUBFEATURE_FAN_MAX, + SENSORS_SUBFEATURE_FAN_ALARM = (SENSORS_FEATURE_FAN << 8) | 0x80, + SENSORS_SUBFEATURE_FAN_FAULT, + SENSORS_SUBFEATURE_FAN_DIV, + SENSORS_SUBFEATURE_FAN_BEEP, + SENSORS_SUBFEATURE_FAN_PULSES, + SENSORS_SUBFEATURE_FAN_MIN_ALARM, + SENSORS_SUBFEATURE_FAN_MAX_ALARM, + + SENSORS_SUBFEATURE_TEMP_INPUT = SENSORS_FEATURE_TEMP << 8, + SENSORS_SUBFEATURE_TEMP_MAX, + SENSORS_SUBFEATURE_TEMP_MAX_HYST, + SENSORS_SUBFEATURE_TEMP_MIN, + SENSORS_SUBFEATURE_TEMP_CRIT, + SENSORS_SUBFEATURE_TEMP_CRIT_HYST, + SENSORS_SUBFEATURE_TEMP_LCRIT, + SENSORS_SUBFEATURE_TEMP_EMERGENCY, + SENSORS_SUBFEATURE_TEMP_EMERGENCY_HYST, + SENSORS_SUBFEATURE_TEMP_LOWEST, + SENSORS_SUBFEATURE_TEMP_HIGHEST, + SENSORS_SUBFEATURE_TEMP_ALARM = (SENSORS_FEATURE_TEMP << 8) | 0x80, + SENSORS_SUBFEATURE_TEMP_MAX_ALARM, + SENSORS_SUBFEATURE_TEMP_MIN_ALARM, + SENSORS_SUBFEATURE_TEMP_CRIT_ALARM, + SENSORS_SUBFEATURE_TEMP_FAULT, + SENSORS_SUBFEATURE_TEMP_TYPE, + SENSORS_SUBFEATURE_TEMP_OFFSET, + SENSORS_SUBFEATURE_TEMP_BEEP, + SENSORS_SUBFEATURE_TEMP_EMERGENCY_ALARM, + SENSORS_SUBFEATURE_TEMP_LCRIT_ALARM, + + SENSORS_SUBFEATURE_POWER_AVERAGE = SENSORS_FEATURE_POWER << 8, + SENSORS_SUBFEATURE_POWER_AVERAGE_HIGHEST, + SENSORS_SUBFEATURE_POWER_AVERAGE_LOWEST, + SENSORS_SUBFEATURE_POWER_INPUT, + SENSORS_SUBFEATURE_POWER_INPUT_HIGHEST, + SENSORS_SUBFEATURE_POWER_INPUT_LOWEST, + SENSORS_SUBFEATURE_POWER_CAP, + SENSORS_SUBFEATURE_POWER_CAP_HYST, + SENSORS_SUBFEATURE_POWER_MAX, + SENSORS_SUBFEATURE_POWER_CRIT, + SENSORS_SUBFEATURE_POWER_AVERAGE_INTERVAL = (SENSORS_FEATURE_POWER << 8) | 0x80, + SENSORS_SUBFEATURE_POWER_ALARM, + SENSORS_SUBFEATURE_POWER_CAP_ALARM, + SENSORS_SUBFEATURE_POWER_MAX_ALARM, + SENSORS_SUBFEATURE_POWER_CRIT_ALARM, + + SENSORS_SUBFEATURE_ENERGY_INPUT = SENSORS_FEATURE_ENERGY << 8, + + SENSORS_SUBFEATURE_CURR_INPUT = SENSORS_FEATURE_CURR << 8, + SENSORS_SUBFEATURE_CURR_MIN, + SENSORS_SUBFEATURE_CURR_MAX, + SENSORS_SUBFEATURE_CURR_LCRIT, + SENSORS_SUBFEATURE_CURR_CRIT, + SENSORS_SUBFEATURE_CURR_AVERAGE, + SENSORS_SUBFEATURE_CURR_LOWEST, + SENSORS_SUBFEATURE_CURR_HIGHEST, + SENSORS_SUBFEATURE_CURR_ALARM = (SENSORS_FEATURE_CURR << 8) | 0x80, + SENSORS_SUBFEATURE_CURR_MIN_ALARM, + SENSORS_SUBFEATURE_CURR_MAX_ALARM, + SENSORS_SUBFEATURE_CURR_BEEP, + SENSORS_SUBFEATURE_CURR_LCRIT_ALARM, + SENSORS_SUBFEATURE_CURR_CRIT_ALARM, + + SENSORS_SUBFEATURE_HUMIDITY_INPUT = SENSORS_FEATURE_HUMIDITY << 8, + + SENSORS_SUBFEATURE_VID = SENSORS_FEATURE_VID << 8, + + SENSORS_SUBFEATURE_INTRUSION_ALARM = SENSORS_FEATURE_INTRUSION << 8, + SENSORS_SUBFEATURE_INTRUSION_BEEP, + + SENSORS_SUBFEATURE_BEEP_ENABLE = SENSORS_FEATURE_BEEP_ENABLE << 8, + + SENSORS_SUBFEATURE_UNKNOWN = INT_MAX, +} sensors_subfeature_type; + +/* Data about a single chip feature (or category leader) */ +struct sensors_feature { + char *name; + int number; + sensors_feature_type type; + /* Members below are for libsensors internal use only */ + int first_subfeature; + int padding1; +}; + +/* Data about a single chip subfeature: + name is the string name used to refer to this subfeature (in config files) + number is the internal subfeature number, used in many functions to refer + to this subfeature + type is the subfeature type + mapping is the number of a main feature this subfeature belongs to + (for example subfeatures fan1_input, fan1_min, fan1_div and fan1_alarm + are mapped to main feature fan1) + flags is a bitfield, its value is a combination of SENSORS_MODE_R (readable), + SENSORS_MODE_W (writable) and SENSORS_COMPUTE_MAPPING (affected by the + computation rules of the main feature) */ +typedef struct sensors_subfeature { + char *name; + int number; + sensors_subfeature_type type; + int mapping; + unsigned int flags; +} sensors_subfeature; + +/* This returns all main features of a specific chip. nr is an internally + used variable. Set it to zero to start at the begin of the list. If no + more features are found NULL is returned. + Do not try to change the returned structure; you will corrupt internal + data structures. */ +const sensors_feature * +sensors_get_features(const sensors_chip_name *name, int *nr); + +/* This returns all subfeatures of a given main feature. nr is an internally + used variable. Set it to zero to start at the begin of the list. If no + more features are found NULL is returned. + Do not try to change the returned structure; you will corrupt internal + data structures. */ +const sensors_subfeature * +sensors_get_all_subfeatures(const sensors_chip_name *name, + const sensors_feature *feature, int *nr); + +/* This returns the subfeature of the given type for a given main feature, + if it exists, NULL otherwise. + Do not try to change the returned structure; you will corrupt internal + data structures. */ +const sensors_subfeature * +sensors_get_subfeature(const sensors_chip_name *name, + const sensors_feature *feature, + sensors_subfeature_type type); + +#ifdef __cplusplus +} +#endif /* __cplusplus */ + +#endif /* def LIB_SENSORS_ERROR_H */ diff --git a/tools/gator/daemon/libsensors/sysfs.c b/tools/gator/daemon/libsensors/sysfs.c new file mode 100644 index 00000000000..2b494c9975b --- /dev/null +++ b/tools/gator/daemon/libsensors/sysfs.c @@ -0,0 +1,926 @@ +/* + sysfs.c - Part of libsensors, a library for reading Linux sensor data + Copyright (c) 2005 Mark M. Hoffman + Copyright (C) 2007-2010 Jean Delvare + + 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 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. +*/ + +/*** This file modified by ARM on Jan 23, 2013 to improve performance by substituting calls to fread() with calls to read() and to read non-scaled values. ***/ + +/* this define needed for strndup() */ +#define _GNU_SOURCE + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "data.h" +#include "error.h" +#include "access.h" +#include "general.h" +#include "sysfs.h" + + +/****************************************************************************/ + +#define ATTR_MAX 128 +#define SYSFS_MAGIC 0x62656572 + +int sensors_sysfs_no_scaling; + +/* + * Read an attribute from sysfs + * Returns a pointer to a freshly allocated string; free it yourself. + * If the file doesn't exist or can't be read, NULL is returned. + */ +static char *sysfs_read_attr(const char *device, const char *attr) +{ + char path[NAME_MAX]; + char buf[ATTR_MAX], *p; + FILE *f; + + snprintf(path, NAME_MAX, "%s/%s", device, attr); + + if (!(f = fopen(path, "r"))) + return NULL; + p = fgets(buf, ATTR_MAX, f); + fclose(f); + if (!p) + return NULL; + + /* Last byte is a '\n'; chop that off */ + p = strndup(buf, strlen(buf) - 1); + if (!p) + sensors_fatal_error(__func__, "Out of memory"); + return p; +} + +/* + * Call an arbitrary function for each class device of the given class + * Returns 0 on success (all calls returned 0), a positive errno for + * local errors, or a negative error value if any call fails. + */ +static int sysfs_foreach_classdev(const char *class_name, + int (*func)(const char *, const char *)) +{ + char path[NAME_MAX]; + int path_off, ret; + DIR *dir; + struct dirent *ent; + + path_off = snprintf(path, NAME_MAX, "%s/class/%s", + sensors_sysfs_mount, class_name); + if (!(dir = opendir(path))) + return errno; + + ret = 0; + while (!ret && (ent = readdir(dir))) { + if (ent->d_name[0] == '.') /* skip hidden entries */ + continue; + + snprintf(path + path_off, NAME_MAX - path_off, "/%s", + ent->d_name); + ret = func(path, ent->d_name); + } + + closedir(dir); + return ret; +} + +/* + * Call an arbitrary function for each device of the given bus type + * Returns 0 on success (all calls returned 0), a positive errno for + * local errors, or a negative error value if any call fails. + */ +static int sysfs_foreach_busdev(const char *bus_type, + int (*func)(const char *, const char *)) +{ + char path[NAME_MAX]; + int path_off, ret; + DIR *dir; + struct dirent *ent; + + path_off = snprintf(path, NAME_MAX, "%s/bus/%s/devices", + sensors_sysfs_mount, bus_type); + if (!(dir = opendir(path))) + return errno; + + ret = 0; + while (!ret && (ent = readdir(dir))) { + if (ent->d_name[0] == '.') /* skip hidden entries */ + continue; + + snprintf(path + path_off, NAME_MAX - path_off, "/%s", + ent->d_name); + ret = func(path, ent->d_name); + } + + closedir(dir); + return ret; +} + +/****************************************************************************/ + +char sensors_sysfs_mount[NAME_MAX]; + +#define MAX_MAIN_SENSOR_TYPES (SENSORS_FEATURE_MAX_MAIN - SENSORS_FEATURE_IN) +#define MAX_OTHER_SENSOR_TYPES (SENSORS_FEATURE_MAX_OTHER - SENSORS_FEATURE_VID) +#define MAX_SENSORS_PER_TYPE 24 +/* max_subfeatures is now computed dynamically */ +#define FEATURE_SIZE (max_subfeatures * 2) +#define FEATURE_TYPE_SIZE (MAX_SENSORS_PER_TYPE * FEATURE_SIZE) + +/* + * Room for all 7 main types (in, fan, temp, power, energy, current, humidity) + * and 2 other types (VID, intrusion) with all their subfeatures + misc features + */ +#define SUB_OFFSET_OTHER (MAX_MAIN_SENSOR_TYPES * FEATURE_TYPE_SIZE) +#define SUB_OFFSET_MISC (SUB_OFFSET_OTHER + \ + MAX_OTHER_SENSOR_TYPES * FEATURE_TYPE_SIZE) +#define ALL_POSSIBLE_SUBFEATURES (SUB_OFFSET_MISC + 1) + +static +int get_type_scaling(sensors_subfeature_type type) +{ + /* Multipliers for subfeatures */ + switch (type & 0xFF80) { + case SENSORS_SUBFEATURE_IN_INPUT: + case SENSORS_SUBFEATURE_TEMP_INPUT: + case SENSORS_SUBFEATURE_CURR_INPUT: + case SENSORS_SUBFEATURE_HUMIDITY_INPUT: + return 1000; + case SENSORS_SUBFEATURE_FAN_INPUT: + return 1; + case SENSORS_SUBFEATURE_POWER_AVERAGE: + case SENSORS_SUBFEATURE_ENERGY_INPUT: + return 1000000; + } + + /* Multipliers for second class subfeatures + that need their own multiplier */ + switch (type) { + case SENSORS_SUBFEATURE_POWER_AVERAGE_INTERVAL: + case SENSORS_SUBFEATURE_VID: + case SENSORS_SUBFEATURE_TEMP_OFFSET: + return 1000; + default: + return 1; + } +} + +static +char *get_feature_name(sensors_feature_type ftype, char *sfname) +{ + char *name, *underscore; + + switch (ftype) { + case SENSORS_FEATURE_IN: + case SENSORS_FEATURE_FAN: + case SENSORS_FEATURE_TEMP: + case SENSORS_FEATURE_POWER: + case SENSORS_FEATURE_ENERGY: + case SENSORS_FEATURE_CURR: + case SENSORS_FEATURE_HUMIDITY: + case SENSORS_FEATURE_INTRUSION: + underscore = strchr(sfname, '_'); + name = strndup(sfname, underscore - sfname); + if (!name) + sensors_fatal_error(__func__, "Out of memory"); + + break; + default: + name = strdup(sfname); + if (!name) + sensors_fatal_error(__func__, "Out of memory"); + } + + return name; +} + +/* Static mappings for use by sensors_subfeature_get_type() */ +struct subfeature_type_match +{ + const char *name; + sensors_subfeature_type type; +}; + +struct feature_type_match +{ + const char *name; + const struct subfeature_type_match *submatches; +}; + +static const struct subfeature_type_match temp_matches[] = { + { "input", SENSORS_SUBFEATURE_TEMP_INPUT }, + { "max", SENSORS_SUBFEATURE_TEMP_MAX }, + { "max_hyst", SENSORS_SUBFEATURE_TEMP_MAX_HYST }, + { "min", SENSORS_SUBFEATURE_TEMP_MIN }, + { "crit", SENSORS_SUBFEATURE_TEMP_CRIT }, + { "crit_hyst", SENSORS_SUBFEATURE_TEMP_CRIT_HYST }, + { "lcrit", SENSORS_SUBFEATURE_TEMP_LCRIT }, + { "emergency", SENSORS_SUBFEATURE_TEMP_EMERGENCY }, + { "emergency_hyst", SENSORS_SUBFEATURE_TEMP_EMERGENCY_HYST }, + { "lowest", SENSORS_SUBFEATURE_TEMP_LOWEST }, + { "highest", SENSORS_SUBFEATURE_TEMP_HIGHEST }, + { "alarm", SENSORS_SUBFEATURE_TEMP_ALARM }, + { "min_alarm", SENSORS_SUBFEATURE_TEMP_MIN_ALARM }, + { "max_alarm", SENSORS_SUBFEATURE_TEMP_MAX_ALARM }, + { "crit_alarm", SENSORS_SUBFEATURE_TEMP_CRIT_ALARM }, + { "emergency_alarm", SENSORS_SUBFEATURE_TEMP_EMERGENCY_ALARM }, + { "lcrit_alarm", SENSORS_SUBFEATURE_TEMP_LCRIT_ALARM }, + { "fault", SENSORS_SUBFEATURE_TEMP_FAULT }, + { "type", SENSORS_SUBFEATURE_TEMP_TYPE }, + { "offset", SENSORS_SUBFEATURE_TEMP_OFFSET }, + { "beep", SENSORS_SUBFEATURE_TEMP_BEEP }, + { NULL, 0 } +}; + +static const struct subfeature_type_match in_matches[] = { + { "input", SENSORS_SUBFEATURE_IN_INPUT }, + { "min", SENSORS_SUBFEATURE_IN_MIN }, + { "max", SENSORS_SUBFEATURE_IN_MAX }, + { "lcrit", SENSORS_SUBFEATURE_IN_LCRIT }, + { "crit", SENSORS_SUBFEATURE_IN_CRIT }, + { "average", SENSORS_SUBFEATURE_IN_AVERAGE }, + { "lowest", SENSORS_SUBFEATURE_IN_LOWEST }, + { "highest", SENSORS_SUBFEATURE_IN_HIGHEST }, + { "alarm", SENSORS_SUBFEATURE_IN_ALARM }, + { "min_alarm", SENSORS_SUBFEATURE_IN_MIN_ALARM }, + { "max_alarm", SENSORS_SUBFEATURE_IN_MAX_ALARM }, + { "lcrit_alarm", SENSORS_SUBFEATURE_IN_LCRIT_ALARM }, + { "crit_alarm", SENSORS_SUBFEATURE_IN_CRIT_ALARM }, + { "beep", SENSORS_SUBFEATURE_IN_BEEP }, + { NULL, 0 } +}; + +static const struct subfeature_type_match fan_matches[] = { + { "input", SENSORS_SUBFEATURE_FAN_INPUT }, + { "min", SENSORS_SUBFEATURE_FAN_MIN }, + { "max", SENSORS_SUBFEATURE_FAN_MAX }, + { "div", SENSORS_SUBFEATURE_FAN_DIV }, + { "pulses", SENSORS_SUBFEATURE_FAN_PULSES }, + { "alarm", SENSORS_SUBFEATURE_FAN_ALARM }, + { "min_alarm", SENSORS_SUBFEATURE_FAN_MIN_ALARM }, + { "max_alarm", SENSORS_SUBFEATURE_FAN_MAX_ALARM }, + { "fault", SENSORS_SUBFEATURE_FAN_FAULT }, + { "beep", SENSORS_SUBFEATURE_FAN_BEEP }, + { NULL, 0 } +}; + +static const struct subfeature_type_match power_matches[] = { + { "average", SENSORS_SUBFEATURE_POWER_AVERAGE }, + { "average_highest", SENSORS_SUBFEATURE_POWER_AVERAGE_HIGHEST }, + { "average_lowest", SENSORS_SUBFEATURE_POWER_AVERAGE_LOWEST }, + { "input", SENSORS_SUBFEATURE_POWER_INPUT }, + { "input_highest", SENSORS_SUBFEATURE_POWER_INPUT_HIGHEST }, + { "input_lowest", SENSORS_SUBFEATURE_POWER_INPUT_LOWEST }, + { "cap", SENSORS_SUBFEATURE_POWER_CAP }, + { "cap_hyst", SENSORS_SUBFEATURE_POWER_CAP_HYST }, + { "cap_alarm", SENSORS_SUBFEATURE_POWER_CAP_ALARM }, + { "alarm", SENSORS_SUBFEATURE_POWER_ALARM }, + { "max", SENSORS_SUBFEATURE_POWER_MAX }, + { "max_alarm", SENSORS_SUBFEATURE_POWER_MAX_ALARM }, + { "crit", SENSORS_SUBFEATURE_POWER_CRIT }, + { "crit_alarm", SENSORS_SUBFEATURE_POWER_CRIT_ALARM }, + { "average_interval", SENSORS_SUBFEATURE_POWER_AVERAGE_INTERVAL }, + { NULL, 0 } +}; + +static const struct subfeature_type_match energy_matches[] = { + { "input", SENSORS_SUBFEATURE_ENERGY_INPUT }, + { NULL, 0 } +}; + +static const struct subfeature_type_match curr_matches[] = { + { "input", SENSORS_SUBFEATURE_CURR_INPUT }, + { "min", SENSORS_SUBFEATURE_CURR_MIN }, + { "max", SENSORS_SUBFEATURE_CURR_MAX }, + { "lcrit", SENSORS_SUBFEATURE_CURR_LCRIT }, + { "crit", SENSORS_SUBFEATURE_CURR_CRIT }, + { "average", SENSORS_SUBFEATURE_CURR_AVERAGE }, + { "lowest", SENSORS_SUBFEATURE_CURR_LOWEST }, + { "highest", SENSORS_SUBFEATURE_CURR_HIGHEST }, + { "alarm", SENSORS_SUBFEATURE_CURR_ALARM }, + { "min_alarm", SENSORS_SUBFEATURE_CURR_MIN_ALARM }, + { "max_alarm", SENSORS_SUBFEATURE_CURR_MAX_ALARM }, + { "lcrit_alarm", SENSORS_SUBFEATURE_CURR_LCRIT_ALARM }, + { "crit_alarm", SENSORS_SUBFEATURE_CURR_CRIT_ALARM }, + { "beep", SENSORS_SUBFEATURE_CURR_BEEP }, + { NULL, 0 } +}; + +static const struct subfeature_type_match humidity_matches[] = { + { "input", SENSORS_SUBFEATURE_HUMIDITY_INPUT }, + { NULL, 0 } +}; + +static const struct subfeature_type_match cpu_matches[] = { + { "vid", SENSORS_SUBFEATURE_VID }, + { NULL, 0 } +}; + +static const struct subfeature_type_match intrusion_matches[] = { + { "alarm", SENSORS_SUBFEATURE_INTRUSION_ALARM }, + { "beep", SENSORS_SUBFEATURE_INTRUSION_BEEP }, + { NULL, 0 } +}; +static struct feature_type_match matches[] = { + { "temp%d%c", temp_matches }, + { "in%d%c", in_matches }, + { "fan%d%c", fan_matches }, + { "cpu%d%c", cpu_matches }, + { "power%d%c", power_matches }, + { "curr%d%c", curr_matches }, + { "energy%d%c", energy_matches }, + { "intrusion%d%c", intrusion_matches }, + { "humidity%d%c", humidity_matches }, +}; + +/* Return the subfeature type and channel number based on the subfeature + name */ +static +sensors_subfeature_type sensors_subfeature_get_type(const char *name, int *nr) +{ + char c; + int i, count; + const struct subfeature_type_match *submatches; + + /* Special case */ + if (!strcmp(name, "beep_enable")) { + *nr = 0; + return SENSORS_SUBFEATURE_BEEP_ENABLE; + } + + for (i = 0; i < ARRAY_SIZE(matches); i++) + if ((count = sscanf(name, matches[i].name, nr, &c))) + break; + + if (i == ARRAY_SIZE(matches) || count != 2 || c != '_') + return SENSORS_SUBFEATURE_UNKNOWN; /* no match */ + + submatches = matches[i].submatches; + name = strchr(name + 3, '_') + 1; + for (i = 0; submatches[i].name != NULL; i++) + if (!strcmp(name, submatches[i].name)) + return submatches[i].type; + + return SENSORS_SUBFEATURE_UNKNOWN; +} + +static int sensors_compute_max(void) +{ + int i, j, max, offset; + const struct subfeature_type_match *submatches; + sensors_feature_type ftype; + + max = 0; + for (i = 0; i < ARRAY_SIZE(matches); i++) { + submatches = matches[i].submatches; + for (j = 0; submatches[j].name != NULL; j++) { + ftype = submatches[j].type >> 8; + + if (ftype < SENSORS_FEATURE_VID) { + offset = submatches[j].type & 0x7F; + if (offset >= max) + max = offset + 1; + } else { + offset = submatches[j].type & 0xFF; + if (offset >= max * 2) + max = ((offset + 1) + 1) / 2; + } + } + } + + return max; +} + +static int sensors_get_attr_mode(const char *device, const char *attr) +{ + char path[NAME_MAX]; + struct stat st; + int mode = 0; + + snprintf(path, NAME_MAX, "%s/%s", device, attr); + if (!stat(path, &st)) { + if (st.st_mode & S_IRUSR) + mode |= SENSORS_MODE_R; + if (st.st_mode & S_IWUSR) + mode |= SENSORS_MODE_W; + } + return mode; +} + +static int sensors_read_dynamic_chip(sensors_chip_features *chip, + const char *dev_path) +{ + int i, fnum = 0, sfnum = 0, prev_slot; + static int max_subfeatures; + DIR *dir; + struct dirent *ent; + sensors_subfeature *all_subfeatures; + sensors_subfeature *dyn_subfeatures; + sensors_feature *dyn_features; + sensors_feature_type ftype; + sensors_subfeature_type sftype; + + if (!(dir = opendir(dev_path))) + return -errno; + + /* Dynamically figure out the max number of subfeatures */ + if (!max_subfeatures) + max_subfeatures = sensors_compute_max(); + + /* We use a large sparse table at first to store all found + subfeatures, so that we can store them sorted at type and index + and then later create a dense sorted table. */ + all_subfeatures = calloc(ALL_POSSIBLE_SUBFEATURES, + sizeof(sensors_subfeature)); + if (!all_subfeatures) + sensors_fatal_error(__func__, "Out of memory"); + + while ((ent = readdir(dir))) { + char *name; + int nr; + + /* Skip directories and symlinks */ + if (ent->d_type != DT_REG) + continue; + + name = ent->d_name; + + sftype = sensors_subfeature_get_type(name, &nr); + if (sftype == SENSORS_SUBFEATURE_UNKNOWN) + continue; + ftype = sftype >> 8; + + /* Adjust the channel number */ + switch (ftype) { + case SENSORS_FEATURE_FAN: + case SENSORS_FEATURE_TEMP: + case SENSORS_FEATURE_POWER: + case SENSORS_FEATURE_ENERGY: + case SENSORS_FEATURE_CURR: + case SENSORS_FEATURE_HUMIDITY: + nr--; + break; + default: + break; + } + + if (nr < 0 || nr >= MAX_SENSORS_PER_TYPE) { + /* More sensors of one type than MAX_SENSORS_PER_TYPE, + we have to ignore it */ +#ifdef DEBUG + sensors_fatal_error(__func__, + "Increase MAX_SENSORS_PER_TYPE!"); +#endif + continue; + } + + /* "calculate" a place to store the subfeature in our sparse, + sorted table */ + switch (ftype) { + case SENSORS_FEATURE_VID: + case SENSORS_FEATURE_INTRUSION: + i = SUB_OFFSET_OTHER + + (ftype - SENSORS_FEATURE_VID) * FEATURE_TYPE_SIZE + + nr * FEATURE_SIZE + (sftype & 0xFF); + break; + case SENSORS_FEATURE_BEEP_ENABLE: + i = SUB_OFFSET_MISC + + (ftype - SENSORS_FEATURE_BEEP_ENABLE); + break; + default: + i = ftype * FEATURE_TYPE_SIZE + + nr * FEATURE_SIZE + + ((sftype & 0x80) >> 7) * max_subfeatures + + (sftype & 0x7F); + } + + if (all_subfeatures[i].name) { +#ifdef DEBUG + sensors_fatal_error(__func__, "Duplicate subfeature"); +#endif + continue; + } + + /* fill in the subfeature members */ + all_subfeatures[i].type = sftype; + all_subfeatures[i].name = strdup(name); + if (!all_subfeatures[i].name) + sensors_fatal_error(__func__, "Out of memory"); + + /* Other and misc subfeatures are never scaled */ + if (sftype < SENSORS_SUBFEATURE_VID && !(sftype & 0x80)) + all_subfeatures[i].flags |= SENSORS_COMPUTE_MAPPING; + all_subfeatures[i].flags |= sensors_get_attr_mode(dev_path, name); + + sfnum++; + } + closedir(dir); + + if (!sfnum) { /* No subfeature */ + chip->subfeature = NULL; + goto exit_free; + } + + /* How many main features? */ + prev_slot = -1; + for (i = 0; i < ALL_POSSIBLE_SUBFEATURES; i++) { + if (!all_subfeatures[i].name) + continue; + + if (i >= SUB_OFFSET_MISC || i / FEATURE_SIZE != prev_slot) { + fnum++; + prev_slot = i / FEATURE_SIZE; + } + } + + dyn_subfeatures = calloc(sfnum, sizeof(sensors_subfeature)); + dyn_features = calloc(fnum, sizeof(sensors_feature)); + if (!dyn_subfeatures || !dyn_features) + sensors_fatal_error(__func__, "Out of memory"); + + /* Copy from the sparse array to the compact array */ + sfnum = 0; + fnum = -1; + prev_slot = -1; + for (i = 0; i < ALL_POSSIBLE_SUBFEATURES; i++) { + if (!all_subfeatures[i].name) + continue; + + /* New main feature? */ + if (i >= SUB_OFFSET_MISC || i / FEATURE_SIZE != prev_slot) { + ftype = all_subfeatures[i].type >> 8; + fnum++; + prev_slot = i / FEATURE_SIZE; + + dyn_features[fnum].name = get_feature_name(ftype, + all_subfeatures[i].name); + dyn_features[fnum].number = fnum; + dyn_features[fnum].first_subfeature = sfnum; + dyn_features[fnum].type = ftype; + } + + dyn_subfeatures[sfnum] = all_subfeatures[i]; + dyn_subfeatures[sfnum].number = sfnum; + /* Back to the feature */ + dyn_subfeatures[sfnum].mapping = fnum; + + sfnum++; + } + + chip->subfeature = dyn_subfeatures; + chip->subfeature_count = sfnum; + chip->feature = dyn_features; + chip->feature_count = ++fnum; + +exit_free: + free(all_subfeatures); + return 0; +} + +/* returns !0 if sysfs filesystem was found, 0 otherwise */ +int sensors_init_sysfs(void) +{ + struct statfs statfsbuf; + + snprintf(sensors_sysfs_mount, NAME_MAX, "%s", "/sys"); + if (statfs(sensors_sysfs_mount, &statfsbuf) < 0 + || statfsbuf.f_type != SYSFS_MAGIC) + return 0; + + return 1; +} + +/* returns: number of devices added (0 or 1) if successful, <0 otherwise */ +static int sensors_read_one_sysfs_chip(const char *dev_path, + const char *dev_name, + const char *hwmon_path) +{ + int domain, bus, slot, fn, vendor, product, id; + int err = -SENSORS_ERR_KERNEL; + char *bus_attr; + char bus_path[NAME_MAX]; + char linkpath[NAME_MAX]; + char subsys_path[NAME_MAX], *subsys; + int sub_len; + sensors_chip_features entry; + + /* ignore any device without name attribute */ + if (!(entry.chip.prefix = sysfs_read_attr(hwmon_path, "name"))) + return 0; + + entry.chip.path = strdup(hwmon_path); + if (!entry.chip.path) + sensors_fatal_error(__func__, "Out of memory"); + + if (dev_path == NULL) { + /* Virtual device */ + entry.chip.bus.type = SENSORS_BUS_TYPE_VIRTUAL; + entry.chip.bus.nr = 0; + /* For now we assume that virtual devices are unique */ + entry.chip.addr = 0; + goto done; + } + + /* Find bus type */ + snprintf(linkpath, NAME_MAX, "%s/subsystem", dev_path); + sub_len = readlink(linkpath, subsys_path, NAME_MAX - 1); + if (sub_len < 0 && errno == ENOENT) { + /* Fallback to "bus" link for kernels <= 2.6.17 */ + snprintf(linkpath, NAME_MAX, "%s/bus", dev_path); + sub_len = readlink(linkpath, subsys_path, NAME_MAX - 1); + } + if (sub_len < 0) { + /* Older kernels (<= 2.6.11) have neither the subsystem + symlink nor the bus symlink */ + if (errno == ENOENT) + subsys = NULL; + else + goto exit_free; + } else { + subsys_path[sub_len] = '\0'; + subsys = strrchr(subsys_path, '/') + 1; + } + + if ((!subsys || !strcmp(subsys, "i2c")) && + sscanf(dev_name, "%hd-%x", &entry.chip.bus.nr, + &entry.chip.addr) == 2) { + /* find out if legacy ISA or not */ + if (entry.chip.bus.nr == 9191) { + entry.chip.bus.type = SENSORS_BUS_TYPE_ISA; + entry.chip.bus.nr = 0; + } else { + entry.chip.bus.type = SENSORS_BUS_TYPE_I2C; + snprintf(bus_path, sizeof(bus_path), + "%s/class/i2c-adapter/i2c-%d/device", + sensors_sysfs_mount, entry.chip.bus.nr); + + if ((bus_attr = sysfs_read_attr(bus_path, "name"))) { + if (!strncmp(bus_attr, "ISA ", 4)) { + entry.chip.bus.type = SENSORS_BUS_TYPE_ISA; + entry.chip.bus.nr = 0; + } + + free(bus_attr); + } + } + } else + if ((!subsys || !strcmp(subsys, "spi")) && + sscanf(dev_name, "spi%hd.%d", &entry.chip.bus.nr, + &entry.chip.addr) == 2) { + /* SPI */ + entry.chip.bus.type = SENSORS_BUS_TYPE_SPI; + } else + if ((!subsys || !strcmp(subsys, "pci")) && + sscanf(dev_name, "%x:%x:%x.%x", &domain, &bus, &slot, &fn) == 4) { + /* PCI */ + entry.chip.addr = (domain << 16) + (bus << 8) + (slot << 3) + fn; + entry.chip.bus.type = SENSORS_BUS_TYPE_PCI; + entry.chip.bus.nr = 0; + } else + if ((!subsys || !strcmp(subsys, "platform") || + !strcmp(subsys, "of_platform"))) { + /* must be new ISA (platform driver) */ + if (sscanf(dev_name, "%*[a-z0-9_].%d", &entry.chip.addr) != 1) + entry.chip.addr = 0; + entry.chip.bus.type = SENSORS_BUS_TYPE_ISA; + entry.chip.bus.nr = 0; + } else if (subsys && !strcmp(subsys, "acpi")) { + entry.chip.bus.type = SENSORS_BUS_TYPE_ACPI; + /* For now we assume that acpi devices are unique */ + entry.chip.bus.nr = 0; + entry.chip.addr = 0; + } else + if (subsys && !strcmp(subsys, "hid") && + sscanf(dev_name, "%x:%x:%x.%x", &bus, &vendor, &product, &id) == 4) { + entry.chip.bus.type = SENSORS_BUS_TYPE_HID; + /* As of kernel 2.6.32, the hid device names don't look good */ + entry.chip.bus.nr = bus; + entry.chip.addr = id; + } else { + /* Ignore unknown device */ + err = 0; + goto exit_free; + } + +done: + if (sensors_read_dynamic_chip(&entry, hwmon_path) < 0) + goto exit_free; + if (!entry.subfeature) { /* No subfeature, discard chip */ + err = 0; + goto exit_free; + } + sensors_add_proc_chips(&entry); + + return 1; + +exit_free: + free(entry.chip.prefix); + free(entry.chip.path); + return err; +} + +static int sensors_add_hwmon_device_compat(const char *path, + const char *dev_name) +{ + int err; + + err = sensors_read_one_sysfs_chip(path, dev_name, path); + if (err < 0) + return err; + return 0; +} + +/* returns 0 if successful, !0 otherwise */ +static int sensors_read_sysfs_chips_compat(void) +{ + int ret; + + ret = sysfs_foreach_busdev("i2c", sensors_add_hwmon_device_compat); + if (ret && ret != ENOENT) + return -SENSORS_ERR_KERNEL; + + return 0; +} + +static int sensors_add_hwmon_device(const char *path, const char *classdev) +{ + char linkpath[NAME_MAX]; + char device[NAME_MAX], *device_p; + int dev_len, err; + (void)classdev; /* hide warning */ + + snprintf(linkpath, NAME_MAX, "%s/device", path); + dev_len = readlink(linkpath, device, NAME_MAX - 1); + if (dev_len < 0) { + /* No device link? Treat as virtual */ + err = sensors_read_one_sysfs_chip(NULL, NULL, path); + } else { + device[dev_len] = '\0'; + device_p = strrchr(device, '/') + 1; + + /* The attributes we want might be those of the hwmon class + device, or those of the device itself. */ + err = sensors_read_one_sysfs_chip(linkpath, device_p, path); + if (err == 0) + err = sensors_read_one_sysfs_chip(linkpath, device_p, + linkpath); + } + if (err < 0) + return err; + return 0; +} + +/* returns 0 if successful, !0 otherwise */ +int sensors_read_sysfs_chips(void) +{ + int ret; + + ret = sysfs_foreach_classdev("hwmon", sensors_add_hwmon_device); + if (ret == ENOENT) { + /* compatibility function for kernel 2.6.n where n <= 13 */ + return sensors_read_sysfs_chips_compat(); + } + + if (ret > 0) + ret = -SENSORS_ERR_KERNEL; + return ret; +} + +/* returns 0 if successful, !0 otherwise */ +static int sensors_add_i2c_bus(const char *path, const char *classdev) +{ + sensors_bus entry; + + if (sscanf(classdev, "i2c-%hd", &entry.bus.nr) != 1 || + entry.bus.nr == 9191) /* legacy ISA */ + return 0; + entry.bus.type = SENSORS_BUS_TYPE_I2C; + + /* Get the adapter name from the classdev "name" attribute + * (Linux 2.6.20 and later). If it fails, fall back to + * the device "name" attribute (for older kernels). */ + entry.adapter = sysfs_read_attr(path, "name"); + if (!entry.adapter) + entry.adapter = sysfs_read_attr(path, "device/name"); + if (entry.adapter) + sensors_add_proc_bus(&entry); + + return 0; +} + +/* returns 0 if successful, !0 otherwise */ +int sensors_read_sysfs_bus(void) +{ + int ret; + + ret = sysfs_foreach_classdev("i2c-adapter", sensors_add_i2c_bus); + if (ret == ENOENT) + ret = sysfs_foreach_busdev("i2c", sensors_add_i2c_bus); + if (ret && ret != ENOENT) + return -SENSORS_ERR_KERNEL; + + return 0; +} + +int sensors_read_sysfs_attr(const sensors_chip_name *name, + const sensors_subfeature *subfeature, + double *value) +{ + char n[NAME_MAX]; + int f; + + snprintf(n, NAME_MAX, "%s/%s", name->path, subfeature->name); + if ((f = open(n, O_RDONLY)) != -1) { + int res, err = 0; + char buf[512]; + int count; + + errno = 0; + if ((count = read(f, buf, sizeof(buf) - 1)) == -1) { + if (errno == EIO) + err = -SENSORS_ERR_IO; + else + err = -SENSORS_ERR_ACCESS_R; + } else { + buf[count] = '\0'; + errno = 0; + res = sscanf(buf, "%lf", value); + if (res == EOF && errno == EIO) + err = -SENSORS_ERR_IO; + else if (res != 1) + err = -SENSORS_ERR_ACCESS_R; + } + res = close(f); + if (err) + return err; + + if (res != 0) { + if (errno == EIO) + return -SENSORS_ERR_IO; + else + return -SENSORS_ERR_ACCESS_R; + } + if (!sensors_sysfs_no_scaling) + *value /= get_type_scaling(subfeature->type); + } else + return -SENSORS_ERR_KERNEL; + + return 0; +} + +int sensors_write_sysfs_attr(const sensors_chip_name *name, + const sensors_subfeature *subfeature, + double value) +{ + char n[NAME_MAX]; + FILE *f; + + snprintf(n, NAME_MAX, "%s/%s", name->path, subfeature->name); + if ((f = fopen(n, "w"))) { + int res, err = 0; + + if (!sensors_sysfs_no_scaling) + value *= get_type_scaling(subfeature->type); + res = fprintf(f, "%d", (int) value); + if (res == -EIO) + err = -SENSORS_ERR_IO; + else if (res < 0) + err = -SENSORS_ERR_ACCESS_W; + res = fclose(f); + if (err) + return err; + + if (res == EOF) { + if (errno == EIO) + return -SENSORS_ERR_IO; + else + return -SENSORS_ERR_ACCESS_W; + } + } else + return -SENSORS_ERR_KERNEL; + + return 0; +} diff --git a/tools/gator/daemon/libsensors/sysfs.h b/tools/gator/daemon/libsensors/sysfs.h new file mode 100644 index 00000000000..38584afd028 --- /dev/null +++ b/tools/gator/daemon/libsensors/sysfs.h @@ -0,0 +1,43 @@ +/* + sysfs.h - part of libsensors, a library for reading Linux sensor data + Copyright (C) Mark M. Hoffman + Copyright (C) 2007 Jean Delvare + + 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 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 SENSORS_LIB_SYSFS_H +#define SENSORS_LIB_SYSFS_H + +extern char sensors_sysfs_mount[]; + +int sensors_init_sysfs(void); + +int sensors_read_sysfs_chips(void); + +int sensors_read_sysfs_bus(void); + +/* Read a value out of a sysfs attribute file */ +int sensors_read_sysfs_attr(const sensors_chip_name *name, + const sensors_subfeature *subfeature, + double *value); + +/* Write a value to a sysfs attribute file */ +int sensors_write_sysfs_attr(const sensors_chip_name *name, + const sensors_subfeature *subfeature, + double value); + +#endif /* !SENSORS_LIB_SYSFS_H */ diff --git a/tools/gator/daemon/libsensors/version.h b/tools/gator/daemon/libsensors/version.h new file mode 100644 index 00000000000..76ceb08c29d --- /dev/null +++ b/tools/gator/daemon/libsensors/version.h @@ -0,0 +1 @@ +#define LM_VERSION "3.3.2" diff --git a/tools/gator/daemon/main.cpp b/tools/gator/daemon/main.cpp new file mode 100644 index 00000000000..a6ddfe26865 --- /dev/null +++ b/tools/gator/daemon/main.cpp @@ -0,0 +1,385 @@ +/** + * Copyright (C) ARM Limited 2010-2013. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "Child.h" +#include "SessionData.h" +#include "OlySocket.h" +#include "Logging.h" +#include "OlyUtility.h" +#include "KMod.h" + +#define DEBUG false + +extern Child* child; +static int shutdownFilesystem(); +static pthread_mutex_t numSessions_mutex; +static int numSessions = 0; +static OlySocket* socket = NULL; +static bool driverRunningAtStart = false; +static bool driverMountedAtStart = false; + +struct cmdline_t { + int port; + char* module; +}; + +void cleanUp() { + if (shutdownFilesystem() == -1) { + logg->logMessage("Error shutting down gator filesystem"); + } + delete socket; + delete util; + delete logg; +} + +// CTRL C Signal Handler +static void handler(int signum) { + logg->logMessage("Received signal %d, gator daemon exiting", signum); + + // Case 1: both child and parent receive the signal + if (numSessions > 0) { + // Arbitrary sleep of 1 second to give time for the child to exit; + // if something bad happens, continue the shutdown process regardless + sleep(1); + } + + // Case 2: only the parent received the signal + if (numSessions > 0) { + // Kill child threads - the first signal exits gracefully + logg->logMessage("Killing process group as %d child was running when signal was received", numSessions); + kill(0, SIGINT); + + // Give time for the child to exit + sleep(1); + + if (numSessions > 0) { + // The second signal force kills the child + logg->logMessage("Force kill the child"); + kill(0, SIGINT); + // Again, sleep for 1 second + sleep(1); + + if (numSessions > 0) { + // Something bad has really happened; the child is not exiting and therefore may hold the /dev/gator resource open + printf("Unable to kill the gatord child process, thus gator.ko may still be loaded.\n"); + } + } + } + + cleanUp(); + exit(0); +} + +// Child exit Signal Handler +static void child_exit(int signum) { + int status; + int pid = wait(&status); + if (pid != -1) { + pthread_mutex_lock(&numSessions_mutex); + numSessions--; + pthread_mutex_unlock(&numSessions_mutex); + logg->logMessage("Child process %d exited with status %d", pid, status); + } +} + +// retval: -1 = failure; 0 = was already mounted; 1 = successfully mounted +static int mountGatorFS() { + // If already mounted, + if (access("/dev/gator/buffer", F_OK) == 0) { + return 0; + } + + // else, mount the filesystem + mkdir("/dev/gator", S_IRWXU | S_IRWXG | S_IROTH | S_IXOTH); + if (mount("nodev", "/dev/gator", "gatorfs", 0, NULL) != 0) { + return -1; + } else { + return 1; + } +} + +static bool init_module (const char * const location) { + bool ret(false); + const int fd = open(location, O_RDONLY); + if (fd >= 0) { + struct stat st; + if (fstat(fd, &st) == 0) { + void * const p = mmap(NULL, st.st_size, PROT_READ, MAP_PRIVATE, fd, 0); + if (p != MAP_FAILED) { + if (syscall(__NR_init_module, p, st.st_size, "") == 0) { + ret = true; + } + munmap(p, st.st_size); + } + } + close(fd); + } + + return ret; +} + +static int setupFilesystem(char* module) { + int retval; + + // Verify root permissions + uid_t euid = geteuid(); + if (euid) { + logg->logError(__FILE__, __LINE__, "gatord must be launched with root privileges"); + handleException(); + } + + if (module) { + // unmount and rmmod if the module was specified on the commandline, i.e. ensure that the specified module is indeed running + shutdownFilesystem(); + + // if still mounted + if (access("/dev/gator/buffer", F_OK) == 0) { + logg->logError(__FILE__, __LINE__, "Unable to remove the running gator.ko. Manually remove the module or use the running module by not specifying one on the commandline"); + handleException(); + } + } + + retval = mountGatorFS(); + if (retval == 1) { + logg->logMessage("Driver already running at startup"); + driverRunningAtStart = true; + } else if (retval == 0) { + logg->logMessage("Driver already mounted at startup"); + driverRunningAtStart = driverMountedAtStart = true; + } else { + char command[256]; // arbitrarily large amount + char location[256]; // arbitrarily large amount + + if (module) { + strncpy(location, module, sizeof(location)); + } else { + // Is the driver co-located in the same directory? + if (util->getApplicationFullPath(location, sizeof(location)) != 0) { // allow some buffer space + logg->logMessage("Unable to determine the full path of gatord, the cwd will be used"); + } + strncat(location, "gator.ko", sizeof(location) - strlen(location) - 1); + } + + if (access(location, F_OK) == -1) { + logg->logError(__FILE__, __LINE__, "Unable to locate gator.ko driver:\n >>> gator.ko should be co-located with gatord in the same directory\n >>> OR insmod gator.ko prior to launching gatord\n >>> OR specify the location of gator.ko on the command line"); + handleException(); + } + + // Load driver + bool success = init_module(location); + if (!success) { + logg->logMessage("init_module failed, trying insmod"); + snprintf(command, sizeof(command), "insmod %s >/dev/null 2>&1", location); + if (system(command) != 0) { + logg->logMessage("Unable to load gator.ko driver with command: %s", command); + logg->logError(__FILE__, __LINE__, "Unable to load (insmod) gator.ko driver:\n >>> gator.ko must be built against the current kernel version & configuration\n >>> See dmesg for more details"); + handleException(); + } + } + + if (mountGatorFS() == -1) { + logg->logError(__FILE__, __LINE__, "Unable to mount the gator filesystem needed for profiling."); + handleException(); + } + } + + return 0; +} + +static int shutdownFilesystem() { + if (driverMountedAtStart == false) { + umount("/dev/gator"); + } + if (driverRunningAtStart == false) { + if (syscall(__NR_delete_module, "gator", O_NONBLOCK) != 0) { + logg->logMessage("delete_module failed, trying rmmod"); + if (system("rmmod gator >/dev/null 2>&1") != 0) { + return -1; + } + } + } + + return 0; // success +} + +static struct cmdline_t parseCommandLine(int argc, char** argv) { + struct cmdline_t cmdline; + cmdline.port = 8080; + cmdline.module = NULL; + char version_string[256]; // arbitrary length to hold the version information + int c; + + // build the version string + if (PROTOCOL_VERSION < PROTOCOL_DEV) { + snprintf(version_string, sizeof(version_string), "Streamline gatord version %d (DS-5 v5.%d)", PROTOCOL_VERSION, PROTOCOL_VERSION + 1); + } else { + snprintf(version_string, sizeof(version_string), "Streamline gatord development version %d", PROTOCOL_VERSION); + } + + while ((c = getopt(argc, argv, "hvp:s:c:e:m:o:")) != -1) { + switch(c) { + case 'c': + gSessionData->mConfigurationXMLPath = optarg; + break; + case 'e': + gSessionData->mEventsXMLPath = optarg; + break; + case 'm': + cmdline.module = optarg; + break; + case 'p': + cmdline.port = strtol(optarg, NULL, 10); + break; + case 's': + gSessionData->mSessionXMLPath = optarg; + break; + case 'o': + gSessionData->mTargetPath = optarg; + break; + case 'h': + case '?': + logg->logError(__FILE__, __LINE__, + "%s. All parameters are optional:\n" + "-c config_xml path and filename of the configuration.xml to use\n" + "-e events_xml path and filename of the events.xml to use\n" + "-h this help page\n" + "-m module path and filename of gator.ko\n" + "-p port_number port upon which the server listens; default is 8080\n" + "-s session_xml path and filename of a session xml used for local capture\n" + "-o apc_dir path and name of the output for a local capture\n" + "-v version information\n" + , version_string); + handleException(); + break; + case 'v': + logg->logError(__FILE__, __LINE__, version_string); + handleException(); + break; + } + } + + // Error checking + if (cmdline.port != 8080 && gSessionData->mSessionXMLPath != NULL) { + logg->logError(__FILE__, __LINE__, "Only a port or a session xml can be specified, not both"); + handleException(); + } + + if (gSessionData->mTargetPath != NULL && gSessionData->mSessionXMLPath == NULL) { + logg->logError(__FILE__, __LINE__, "Missing -s command line option required for a local capture."); + handleException(); + } + + if (optind < argc) { + logg->logError(__FILE__, __LINE__, "Unknown argument: %s. Use '-h' for help.", argv[optind]); + handleException(); + } + + return cmdline; +} + +// Gator data flow: collector -> collector fifo -> sender +int main(int argc, char** argv, char* envp[]) { + // Ensure proper signal handling by making gatord the process group leader + // e.g. it may not be the group leader when launched as 'sudo gatord' + setsid(); + + logg = new Logging(DEBUG); // Set up global thread-safe logging + gSessionData = new SessionData(); // Global data class + util = new OlyUtility(); // Set up global utility class + + // Initialize drivers + new KMod(); + + prctl(PR_SET_NAME, (unsigned long)&"gatord-main", 0, 0, 0); + pthread_mutex_init(&numSessions_mutex, NULL); + + signal(SIGINT, handler); + signal(SIGTERM, handler); + signal(SIGABRT, handler); + + // Set to high priority + if (setpriority(PRIO_PROCESS, syscall(__NR_gettid), -19) == -1) { + logg->logMessage("setpriority() failed"); + } + + // Parse the command line parameters + struct cmdline_t cmdline = parseCommandLine(argc, argv); + + // Call before setting up the SIGCHLD handler, as system() spawns child processes + setupFilesystem(cmdline.module); + + // Handle child exit codes + signal(SIGCHLD, child_exit); + + // Ignore the SIGPIPE signal so that any send to a broken socket will return an error code instead of asserting a signal + // Handling the error at the send function call is much easier than trying to do anything intelligent in the sig handler + signal(SIGPIPE, SIG_IGN); + + // If the command line argument is a session xml file, no need to open a socket + if (gSessionData->mSessionXMLPath) { + child = new Child(); + child->run(); + delete child; + } else { + socket = new OlySocket(cmdline.port, true); + // Forever loop, can be exited via a signal or exception + while (1) { + logg->logMessage("Waiting on connection..."); + socket->acceptConnection(); + + int pid = fork(); + if (pid < 0) { + // Error + logg->logError(__FILE__, __LINE__, "Fork process failed. Please power cycle the target device if this error persists."); + } else if (pid == 0) { + // Child + socket->closeServerSocket(); + child = new Child(socket, numSessions + 1); + child->run(); + delete child; + exit(0); + } else { + // Parent + socket->closeSocket(); + + pthread_mutex_lock(&numSessions_mutex); + numSessions++; + pthread_mutex_unlock(&numSessions_mutex); + + // Maximum number of connections is 2 + int wait = 0; + while (numSessions > 1) { + // Throttle until one of the children exits before continuing to accept another socket connection + logg->logMessage("%d sessions active!", numSessions); + if (wait++ >= 10) { // Wait no more than 10 seconds + // Kill last created child + kill(pid, SIGALRM); + break; + } + sleep(1); + } + } + } + } + + cleanUp(); + return 0; +} diff --git a/tools/gator/daemon/mxml/COPYING b/tools/gator/daemon/mxml/COPYING new file mode 100644 index 00000000000..4d0aa78af22 --- /dev/null +++ b/tools/gator/daemon/mxml/COPYING @@ -0,0 +1,507 @@ + Mini-XML License + September 18, 2010 + + +The Mini-XML library and included programs are provided under the +terms of the GNU Library General Public License version 2 (LGPL2) +with the following exceptions: + + 1. Static linking of applications to the Mini-XML library +does not constitute a derivative work and does not require +the author to provide source code for the application, use +the shared Mini-XML libraries, or link their applications +against a user-supplied version of Mini-XML. + +If you link the application to a modified version of +Mini-XML, then the changes to Mini-XML must be provided +under the terms of the LGPL2 in sections 1, 2, and 4. + + 2. You do not have to provide a copy of the Mini-XML license +with programs that are linked to the Mini-XML library, nor +do you have to identify the Mini-XML license in your +program or documentation as required by section 6 of the +LGPL2. + + + GNU LIBRARY GENERAL PUBLIC LICENSE + Version 2, June 1991 + + Copyright (C) 1991 Free Software Foundation, Inc. + 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + [This is the first released version of the library GPL. It is + numbered 2 because it goes with version 2 of the ordinary GPL.] + + Preamble + + The licenses for most software are designed to take away your +freedom to share and change it. By contrast, the GNU General Public +Licenses are intended to guarantee your freedom to share and change +free software--to make sure the software is free for all its users. + + This license, the Library General Public License, applies to some +specially designated Free Software Foundation software, and to any +other libraries whose authors decide to use it. You can use it for +your libraries, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +this service if you wish), that you receive source code or can get it +if you want it, that you can change the software or use pieces of it +in new free programs; and that you know you can do these things. + + To protect your rights, we need to make restrictions that forbid +anyone to deny you these rights or to ask you to surrender the rights. +These restrictions translate to certain responsibilities for you if +you distribute copies of the library, or if you modify it. + + For example, if you distribute copies of the library, whether gratis +or for a fee, you must give the recipients all the rights that we gave +you. You must make sure that they, too, receive or can get the source +code. If you link a program with the library, you must provide +complete object files to the recipients so that they can relink them +with the library, after making changes to the library and recompiling +it. And you must show them these terms so they know their rights. + + Our method of protecting your rights has two steps: (1) copyright +the library, and (2) offer you this license which gives you legal +permission to copy, distribute and/or modify the library. + + Also, for each distributor's protection, we want to make certain +that everyone understands that there is no warranty for this free +library. If the library is modified by someone else and passed on, we +want its recipients to know that what they have is not the original +version, so that any problems introduced by others will not reflect on +the original authors' reputations. + + Finally, any free program is threatened constantly by software +patents. We wish to avoid the danger that companies distributing free +software will individually obtain patent licenses, thus in effect +transforming the program into proprietary software. To prevent this, +we have made it clear that any patent must be licensed for everyone's +free use or not licensed at all. + + Most GNU software, including some libraries, is covered by the ordinary +GNU General Public License, which was designed for utility programs. This +license, the GNU Library General Public License, applies to certain +designated libraries. This license is quite different from the ordinary +one; be sure to read it in full, and don't assume that anything in it is +the same as in the ordinary license. + + The reason we have a separate public license for some libraries is that +they blur the distinction we usually make between modifying or adding to a +program and simply using it. Linking a program with a library, without +changing the library, is in some sense simply using the library, and is +analogous to running a utility program or application program. However, in +a textual and legal sense, the linked executable is a combined work, a +derivative of the original library, and the ordinary General Public License +treats it as such. + + Because of this blurred distinction, using the ordinary General +Public License for libraries did not effectively promote software +sharing, because most developers did not use the libraries. We +concluded that weaker conditions might promote sharing better. + + However, unrestricted linking of non-free programs would deprive the +users of those programs of all benefit from the free status of the +libraries themselves. This Library General Public License is intended to +permit developers of non-free programs to use free libraries, while +preserving your freedom as a user of such programs to change the free +libraries that are incorporated in them. (We have not seen how to achieve +this as regards changes in header files, but we have achieved it as regards +changes in the actual functions of the Library.) The hope is that this +will lead to faster development of free libraries. + + The precise terms and conditions for copying, distribution and +modification follow. Pay close attention to the difference between a +"work based on the library" and a "work that uses the library". The +former contains code derived from the library, while the latter only +works together with the library. + + Note that it is possible for a library to be covered by the ordinary +General Public License rather than by this special one. + + GNU LIBRARY GENERAL PUBLIC LICENSE + TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + + 0. This License Agreement applies to any software library which +contains a notice placed by the copyright holder or other authorized +party saying it may be distributed under the terms of this Library +General Public License (also called "this License"). Each licensee is +addressed as "you". + + A "library" means a collection of software functions and/or data +prepared so as to be conveniently linked with application programs +(which use some of those functions and data) to form executables. + + The "Library", below, refers to any such software library or work +which has been distributed under these terms. A "work based on the +Library" means either the Library or any derivative work under +copyright law: that is to say, a work containing the Library or a +portion of it, either verbatim or with modifications and/or translated +straightforwardly into another language. (Hereinafter, translation is +included without limitation in the term "modification".) + + "Source code" for a work means the preferred form of the work for +making modifications to it. For a library, complete source code means +all the source code for all modules it contains, plus any associated +interface definition files, plus the scripts used to control compilation +and installation of the library. + + Activities other than copying, distribution and modification are not +covered by this License; they are outside its scope. The act of +running a program using the Library is not restricted, and output from +such a program is covered only if its contents constitute a work based +on the Library (independent of the use of the Library in a tool for +writing it). Whether that is true depends on what the Library does +and what the program that uses the Library does. + + 1. You may copy and distribute verbatim copies of the Library's +complete source code as you receive it, in any medium, provided that +you conspicuously and appropriately publish on each copy an +appropriate copyright notice and disclaimer of warranty; keep intact +all the notices that refer to this License and to the absence of any +warranty; and distribute a copy of this License along with the +Library. + + You may charge a fee for the physical act of transferring a copy, +and you may at your option offer warranty protection in exchange for a +fee. + + 2. You may modify your copy or copies of the Library or any portion +of it, thus forming a work based on the Library, and copy and +distribute such modifications or work under the terms of Section 1 +above, provided that you also meet all of these conditions: + + a) The modified work must itself be a software library. + + b) You must cause the files modified to carry prominent notices + stating that you changed the files and the date of any change. + + c) You must cause the whole of the work to be licensed at no + charge to all third parties under the terms of this License. + + d) If a facility in the modified Library refers to a function or a + table of data to be supplied by an application program that uses + the facility, other than as an argument passed when the facility + is invoked, then you must make a good faith effort to ensure that, + in the event an application does not supply such function or + table, the facility still operates, and performs whatever part of + its purpose remains meaningful. + + (For example, a function in a library to compute square roots has + a purpose that is entirely well-defined independent of the + application. Therefore, Subsection 2d requires that any + application-supplied function or table used by this function must + be optional: if the application does not supply it, the square + root function must still compute square roots.) + +These requirements apply to the modified work as a whole. If +identifiable sections of that work are not derived from the Library, +and can be reasonably considered independent and separate works in +themselves, then this License, and its terms, do not apply to those +sections when you distribute them as separate works. But when you +distribute the same sections as part of a whole which is a work based +on the Library, the distribution of the whole must be on the terms of +this License, whose permissions for other licensees extend to the +entire whole, and thus to each and every part regardless of who wrote +it. + +Thus, it is not the intent of this section to claim rights or contest +your rights to work written entirely by you; rather, the intent is to +exercise the right to control the distribution of derivative or +collective works based on the Library. + +In addition, mere aggregation of another work not based on the Library +with the Library (or with a work based on the Library) on a volume of +a storage or distribution medium does not bring the other work under +the scope of this License. + + 3. You may opt to apply the terms of the ordinary GNU General Public +License instead of this License to a given copy of the Library. To do +this, you must alter all the notices that refer to this License, so +that they refer to the ordinary GNU General Public License, version 2, +instead of to this License. (If a newer version than version 2 of the +ordinary GNU General Public License has appeared, then you can specify +that version instead if you wish.) Do not make any other change in +these notices. + + Once this change is made in a given copy, it is irreversible for +that copy, so the ordinary GNU General Public License applies to all +subsequent copies and derivative works made from that copy. + + This option is useful when you wish to copy part of the code of +the Library into a program that is not a library. + + 4. You may copy and distribute the Library (or a portion or +derivative of it, under Section 2) in object code or executable form +under the terms of Sections 1 and 2 above provided that you accompany +it with the complete corresponding machine-readable source code, which +must be distributed under the terms of Sections 1 and 2 above on a +medium customarily used for software interchange. + + If distribution of object code is made by offering access to copy +from a designated place, then offering equivalent access to copy the +source code from the same place satisfies the requirement to +distribute the source code, even though third parties are not +compelled to copy the source along with the object code. + + 5. A program that contains no derivative of any portion of the +Library, but is designed to work with the Library by being compiled or +linked with it, is called a "work that uses the Library". Such a +work, in isolation, is not a derivative work of the Library, and +therefore falls outside the scope of this License. + + However, linking a "work that uses the Library" with the Library +creates an executable that is a derivative of the Library (because it +contains portions of the Library), rather than a "work that uses the +library". The executable is therefore covered by this License. +Section 6 states terms for distribution of such executables. + + When a "work that uses the Library" uses material from a header file +that is part of the Library, the object code for the work may be a +derivative work of the Library even though the source code is not. +Whether this is true is especially significant if the work can be +linked without the Library, or if the work is itself a library. The +threshold for this to be true is not precisely defined by law. + + If such an object file uses only numerical parameters, data +structure layouts and accessors, and small macros and small inline +functions (ten lines or less in length), then the use of the object +file is unrestricted, regardless of whether it is legally a derivative +work. (Executables containing this object code plus portions of the +Library will still fall under Section 6.) + + Otherwise, if the work is a derivative of the Library, you may +distribute the object code for the work under the terms of Section 6. +Any executables containing that work also fall under Section 6, +whether or not they are linked directly with the Library itself. + + 6. As an exception to the Sections above, you may also compile or +link a "work that uses the Library" with the Library to produce a +work containing portions of the Library, and distribute that work +under terms of your choice, provided that the terms permit +modification of the work for the customer's own use and reverse +engineering for debugging such modifications. + + You must give prominent notice with each copy of the work that the +Library is used in it and that the Library and its use are covered by +this License. You must supply a copy of this License. If the work +during execution displays copyright notices, you must include the +copyright notice for the Library among them, as well as a reference +directing the user to the copy of this License. Also, you must do one +of these things: + + a) Accompany the work with the complete corresponding + machine-readable source code for the Library including whatever + changes were used in the work (which must be distributed under + Sections 1 and 2 above); and, if the work is an executable linked + with the Library, with the complete machine-readable "work that + uses the Library", as object code and/or source code, so that the + user can modify the Library and then relink to produce a modified + executable containing the modified Library. (It is understood + that the user who changes the contents of definitions files in the + Library will not necessarily be able to recompile the application + to use the modified definitions.) + + b) Accompany the work with a written offer, valid for at + least three years, to give the same user the materials + specified in Subsection 6a, above, for a charge no more + than the cost of performing this distribution. + + c) If distribution of the work is made by offering access to copy + from a designated place, offer equivalent access to copy the above + specified materials from the same place. + + d) Verify that the user has already received a copy of these + materials or that you have already sent this user a copy. + + For an executable, the required form of the "work that uses the +Library" must include any data and utility programs needed for +reproducing the executable from it. However, as a special exception, +the source code distributed need not include anything that is normally +distributed (in either source or binary form) with the major +components (compiler, kernel, and so on) of the operating system on +which the executable runs, unless that component itself accompanies +the executable. + + It may happen that this requirement contradicts the license +restrictions of other proprietary libraries that do not normally +accompany the operating system. Such a contradiction means you cannot +use both them and the Library together in an executable that you +distribute. + + 7. You may place library facilities that are a work based on the +Library side-by-side in a single library together with other library +facilities not covered by this License, and distribute such a combined +library, provided that the separate distribution of the work based on +the Library and of the other library facilities is otherwise +permitted, and provided that you do these two things: + + a) Accompany the combined library with a copy of the same work + based on the Library, uncombined with any other library + facilities. This must be distributed under the terms of the + Sections above. + + b) Give prominent notice with the combined library of the fact + that part of it is a work based on the Library, and explaining + where to find the accompanying uncombined form of the same work. + + 8. You may not copy, modify, sublicense, link with, or distribute +the Library except as expressly provided under this License. Any +attempt otherwise to copy, modify, sublicense, link with, or +distribute the Library is void, and will automatically terminate your +rights under this License. However, parties who have received copies, +or rights, from you under this License will not have their licenses +terminated so long as such parties remain in full compliance. + + 9. You are not required to accept this License, since you have not +signed it. However, nothing else grants you permission to modify or +distribute the Library or its derivative works. These actions are +prohibited by law if you do not accept this License. Therefore, by +modifying or distributing the Library (or any work based on the +Library), you indicate your acceptance of this License to do so, and +all its terms and conditions for copying, distributing or modifying +the Library or works based on it. + + 10. Each time you redistribute the Library (or any work based on the +Library), the recipient automatically receives a license from the +original licensor to copy, distribute, link with or modify the Library +subject to these terms and conditions. You may not impose any further +restrictions on the recipients' exercise of the rights granted herein. +You are not responsible for enforcing compliance by third parties to +this License. + + 11. If, as a consequence of a court judgment or allegation of patent +infringement or for any other reason (not limited to patent issues), +conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot +distribute so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you +may not distribute the Library at all. For example, if a patent +license would not permit royalty-free redistribution of the Library by +all those who receive copies directly or indirectly through you, then +the only way you could satisfy both it and this License would be to +refrain entirely from distribution of the Library. + +If any portion of this section is held invalid or unenforceable under any +particular circumstance, the balance of the section is intended to apply, +and the section as a whole is intended to apply in other circumstances. + +It is not the purpose of this section to induce you to infringe any +patents or other property right claims or to contest validity of any +such claims; this section has the sole purpose of protecting the +integrity of the free software distribution system which is +implemented by public license practices. Many people have made +generous contributions to the wide range of software distributed +through that system in reliance on consistent application of that +system; it is up to the author/donor to decide if he or she is willing +to distribute software through any other system and a licensee cannot +impose that choice. + +This section is intended to make thoroughly clear what is believed to +be a consequence of the rest of this License. + + 12. If the distribution and/or use of the Library is restricted in +certain countries either by patents or by copyrighted interfaces, the +original copyright holder who places the Library under this License may add +an explicit geographical distribution limitation excluding those countries, +so that distribution is permitted only in or among countries not thus +excluded. In such case, this License incorporates the limitation as if +written in the body of this License. + + 13. The Free Software Foundation may publish revised and/or new +versions of the Library General Public License from time to time. +Such new versions will be similar in spirit to the present version, +but may differ in detail to address new problems or concerns. + +Each version is given a distinguishing version number. If the Library +specifies a version number of this License which applies to it and +"any later version", you have the option of following the terms and +conditions either of that version or of any later version published by +the Free Software Foundation. If the Library does not specify a +license version number, you may choose any version ever published by +the Free Software Foundation. + + 14. If you wish to incorporate parts of the Library into other free +programs whose distribution conditions are incompatible with these, +write to the author to ask for permission. For software which is +copyrighted by the Free Software Foundation, write to the Free +Software Foundation; we sometimes make exceptions for this. Our +decision will be guided by the two goals of preserving the free status +of all derivatives of our free software and of promoting the sharing +and reuse of software generally. + + NO WARRANTY + + 15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO +WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW. +EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR +OTHER PARTIES PROVIDE THE LIBRARY "AS IS" WITHOUT WARRANTY OF ANY +KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE +LIBRARY IS WITH YOU. SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME +THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. + + 16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN +WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY +AND/OR REDISTRIBUTE THE LIBRARY AS PERMITTED ABOVE, BE LIABLE TO YOU +FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR +CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE +LIBRARY (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING +RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A +FAILURE OF THE LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF +SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH +DAMAGES. + + END OF TERMS AND CONDITIONS + + Appendix: How to Apply These Terms to Your New Libraries + + If you develop a new library, and you want it to be of the greatest +possible use to the public, we recommend making it free software that +everyone can redistribute and change. You can do so by permitting +redistribution under these terms (or, alternatively, under the terms of the +ordinary General Public License). + + To apply these terms, attach the following notices to the library. It is +safest to attach them to the start of each source file to most effectively +convey the exclusion of warranty; and each file should have at least the +"copyright" line and a pointer to where the full notice is found. + + + Copyright (C) + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 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 + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public + License along with this library; if not, write to the Free + Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + +Also add information on how to contact you by electronic and paper mail. + +You should also get your employer (if you work as a programmer) or your +school, if any, to sign a "copyright disclaimer" for the library, if +necessary. Here is a sample; alter the names: + + Yoyodyne, Inc., hereby disclaims all copyright interest in the + library `Frob' (a library for tweaking knobs) written by James Random Hacker. + + , 1 April 1990 + Ty Coon, President of Vice + +That's all there is to it! diff --git a/tools/gator/daemon/mxml/config.h b/tools/gator/daemon/mxml/config.h new file mode 100644 index 00000000000..1f59ba34a47 --- /dev/null +++ b/tools/gator/daemon/mxml/config.h @@ -0,0 +1,96 @@ +/* config.h. Generated from config.h.in by configure. */ +/* + * "$Id: config.h.in 408 2010-09-19 05:26:46Z mike $" + * + * Configuration file for Mini-XML, a small XML-like file parsing library. + * + * Copyright 2003-2010 by Michael R Sweet. + * + * These coded instructions, statements, and computer programs are the + * property of Michael R Sweet and are protected by Federal copyright + * law. Distribution and use rights are outlined in the file "COPYING" + * which should have been included with this file. If this file is + * missing or damaged, see the license at: + * + * http://www.minixml.org/ + */ + +/* + * Include necessary headers... + */ + +#include +#include +#include +#include +#include + + +/* + * Version number... + */ + +#define MXML_VERSION "Mini-XML v2.7" + + +/* + * Inline function support... + */ + +#define inline + + +/* + * Long long support... + */ + +#define HAVE_LONG_LONG 1 + + +/* + * Do we have the snprintf() and vsnprintf() functions? + */ + +#define HAVE_SNPRINTF 1 +#define HAVE_VSNPRINTF 1 + + +/* + * Do we have the strXXX() functions? + */ + +#define HAVE_STRDUP 1 + + +/* + * Do we have threading support? + */ + +#define HAVE_PTHREAD_H 1 + + +/* + * Define prototypes for string functions as needed... + */ + +# ifndef HAVE_STRDUP +extern char *_mxml_strdup(const char *); +# define strdup _mxml_strdup +# endif /* !HAVE_STRDUP */ + +extern char *_mxml_strdupf(const char *, ...); +extern char *_mxml_vstrdupf(const char *, va_list); + +# ifndef HAVE_SNPRINTF +extern int _mxml_snprintf(char *, size_t, const char *, ...); +# define snprintf _mxml_snprintf +# endif /* !HAVE_SNPRINTF */ + +# ifndef HAVE_VSNPRINTF +extern int _mxml_vsnprintf(char *, size_t, const char *, va_list); +# define vsnprintf _mxml_vsnprintf +# endif /* !HAVE_VSNPRINTF */ + +/* + * End of "$Id: config.h.in 408 2010-09-19 05:26:46Z mike $". + */ diff --git a/tools/gator/daemon/mxml/mxml-attr.c b/tools/gator/daemon/mxml/mxml-attr.c new file mode 100644 index 00000000000..c9950f5fb73 --- /dev/null +++ b/tools/gator/daemon/mxml/mxml-attr.c @@ -0,0 +1,319 @@ +/* + * "$Id: mxml-attr.c 408 2010-09-19 05:26:46Z mike $" + * + * Attribute support code for Mini-XML, a small XML-like file parsing library. + * + * Copyright 2003-2010 by Michael R Sweet. + * + * These coded instructions, statements, and computer programs are the + * property of Michael R Sweet and are protected by Federal copyright + * law. Distribution and use rights are outlined in the file "COPYING" + * which should have been included with this file. If this file is + * missing or damaged, see the license at: + * + * http://www.minixml.org/ + * + * Contents: + * + * mxmlElementDeleteAttr() - Delete an attribute. + * mxmlElementGetAttr() - Get an attribute. + * mxmlElementSetAttr() - Set an attribute. + * mxmlElementSetAttrf() - Set an attribute with a formatted value. + * mxml_set_attr() - Set or add an attribute name/value pair. + */ + +/* + * Include necessary headers... + */ + +#include "config.h" +#include "mxml.h" + + +/* + * Local functions... + */ + +static int mxml_set_attr(mxml_node_t *node, const char *name, + char *value); + + +/* + * 'mxmlElementDeleteAttr()' - Delete an attribute. + * + * @since Mini-XML 2.4@ + */ + +void +mxmlElementDeleteAttr(mxml_node_t *node,/* I - Element */ + const char *name)/* I - Attribute name */ +{ + int i; /* Looping var */ + mxml_attr_t *attr; /* Cirrent attribute */ + + +#ifdef DEBUG + fprintf(stderr, "mxmlElementDeleteAttr(node=%p, name=\"%s\")\n", + node, name ? name : "(null)"); +#endif /* DEBUG */ + + /* + * Range check input... + */ + + if (!node || node->type != MXML_ELEMENT || !name) + return; + + /* + * Look for the attribute... + */ + + for (i = node->value.element.num_attrs, attr = node->value.element.attrs; + i > 0; + i --, attr ++) + { +#ifdef DEBUG + printf(" %s=\"%s\"\n", attr->name, attr->value); +#endif /* DEBUG */ + + if (!strcmp(attr->name, name)) + { + /* + * Delete this attribute... + */ + + free(attr->name); + free(attr->value); + + i --; + if (i > 0) + memmove(attr, attr + 1, i * sizeof(mxml_attr_t)); + + node->value.element.num_attrs --; + return; + } + } +} + + +/* + * 'mxmlElementGetAttr()' - Get an attribute. + * + * This function returns NULL if the node is not an element or the + * named attribute does not exist. + */ + +const char * /* O - Attribute value or NULL */ +mxmlElementGetAttr(mxml_node_t *node, /* I - Element node */ + const char *name) /* I - Name of attribute */ +{ + int i; /* Looping var */ + mxml_attr_t *attr; /* Cirrent attribute */ + + +#ifdef DEBUG + fprintf(stderr, "mxmlElementGetAttr(node=%p, name=\"%s\")\n", + node, name ? name : "(null)"); +#endif /* DEBUG */ + + /* + * Range check input... + */ + + if (!node || node->type != MXML_ELEMENT || !name) + return (NULL); + + /* + * Look for the attribute... + */ + + for (i = node->value.element.num_attrs, attr = node->value.element.attrs; + i > 0; + i --, attr ++) + { +#ifdef DEBUG + printf(" %s=\"%s\"\n", attr->name, attr->value); +#endif /* DEBUG */ + + if (!strcmp(attr->name, name)) + { +#ifdef DEBUG + printf(" Returning \"%s\"!\n", attr->value); +#endif /* DEBUG */ + return (attr->value); + } + } + + /* + * Didn't find attribute, so return NULL... + */ + +#ifdef DEBUG + puts(" Returning NULL!\n"); +#endif /* DEBUG */ + + return (NULL); +} + + +/* + * 'mxmlElementSetAttr()' - Set an attribute. + * + * If the named attribute already exists, the value of the attribute + * is replaced by the new string value. The string value is copied + * into the element node. This function does nothing if the node is + * not an element. + */ + +void +mxmlElementSetAttr(mxml_node_t *node, /* I - Element node */ + const char *name, /* I - Name of attribute */ + const char *value) /* I - Attribute value */ +{ + char *valuec; /* Copy of value */ + + +#ifdef DEBUG + fprintf(stderr, "mxmlElementSetAttr(node=%p, name=\"%s\", value=\"%s\")\n", + node, name ? name : "(null)", value ? value : "(null)"); +#endif /* DEBUG */ + + /* + * Range check input... + */ + + if (!node || node->type != MXML_ELEMENT || !name) + return; + + if (value) + valuec = strdup(value); + else + valuec = NULL; + + if (mxml_set_attr(node, name, valuec)) + free(valuec); +} + + +/* + * 'mxmlElementSetAttrf()' - Set an attribute with a formatted value. + * + * If the named attribute already exists, the value of the attribute + * is replaced by the new formatted string. The formatted string value is + * copied into the element node. This function does nothing if the node + * is not an element. + * + * @since Mini-XML 2.3@ + */ + +void +mxmlElementSetAttrf(mxml_node_t *node, /* I - Element node */ + const char *name, /* I - Name of attribute */ + const char *format,/* I - Printf-style attribute value */ + ...) /* I - Additional arguments as needed */ +{ + va_list ap; /* Argument pointer */ + char *value; /* Value */ + + +#ifdef DEBUG + fprintf(stderr, + "mxmlElementSetAttrf(node=%p, name=\"%s\", format=\"%s\", ...)\n", + node, name ? name : "(null)", format ? format : "(null)"); +#endif /* DEBUG */ + + /* + * Range check input... + */ + + if (!node || node->type != MXML_ELEMENT || !name || !format) + return; + + /* + * Format the value... + */ + + va_start(ap, format); + value = _mxml_vstrdupf(format, ap); + va_end(ap); + + if (!value) + mxml_error("Unable to allocate memory for attribute '%s' in element %s!", + name, node->value.element.name); + else if (mxml_set_attr(node, name, value)) + free(value); +} + + +/* + * 'mxml_set_attr()' - Set or add an attribute name/value pair. + */ + +static int /* O - 0 on success, -1 on failure */ +mxml_set_attr(mxml_node_t *node, /* I - Element node */ + const char *name, /* I - Attribute name */ + char *value) /* I - Attribute value */ +{ + int i; /* Looping var */ + mxml_attr_t *attr; /* New attribute */ + + + /* + * Look for the attribute... + */ + + for (i = node->value.element.num_attrs, attr = node->value.element.attrs; + i > 0; + i --, attr ++) + if (!strcmp(attr->name, name)) + { + /* + * Free the old value as needed... + */ + + if (attr->value) + free(attr->value); + + attr->value = value; + + return (0); + } + + /* + * Add a new attribute... + */ + + if (node->value.element.num_attrs == 0) + attr = malloc(sizeof(mxml_attr_t)); + else + attr = realloc(node->value.element.attrs, + (node->value.element.num_attrs + 1) * sizeof(mxml_attr_t)); + + if (!attr) + { + mxml_error("Unable to allocate memory for attribute '%s' in element %s!", + name, node->value.element.name); + return (-1); + } + + node->value.element.attrs = attr; + attr += node->value.element.num_attrs; + + if ((attr->name = strdup(name)) == NULL) + { + mxml_error("Unable to allocate memory for attribute '%s' in element %s!", + name, node->value.element.name); + return (-1); + } + + attr->value = value; + + node->value.element.num_attrs ++; + + return (0); +} + + +/* + * End of "$Id: mxml-attr.c 408 2010-09-19 05:26:46Z mike $". + */ diff --git a/tools/gator/daemon/mxml/mxml-entity.c b/tools/gator/daemon/mxml/mxml-entity.c new file mode 100644 index 00000000000..c5c9f61f73c --- /dev/null +++ b/tools/gator/daemon/mxml/mxml-entity.c @@ -0,0 +1,460 @@ +/* + * "$Id: mxml-entity.c 408 2010-09-19 05:26:46Z mike $" + * + * Character entity support code for Mini-XML, a small XML-like + * file parsing library. + * + * Copyright 2003-2010 by Michael R Sweet. + * + * These coded instructions, statements, and computer programs are the + * property of Michael R Sweet and are protected by Federal copyright + * law. Distribution and use rights are outlined in the file "COPYING" + * which should have been included with this file. If this file is + * missing or damaged, see the license at: + * + * http://www.minixml.org/ + * + * Contents: + * + * mxmlEntityAddCallback() - Add a callback to convert entities to + * Unicode. + * mxmlEntityGetName() - Get the name that corresponds to the + * character value. + * mxmlEntityGetValue() - Get the character corresponding to a named + * entity. + * mxmlEntityRemoveCallback() - Remove a callback. + * _mxml_entity_cb() - Lookup standard (X)HTML entities. + */ + +/* + * Include necessary headers... + */ + +#include "mxml-private.h" + + +/* + * 'mxmlEntityAddCallback()' - Add a callback to convert entities to Unicode. + */ + +int /* O - 0 on success, -1 on failure */ +mxmlEntityAddCallback( + mxml_entity_cb_t cb) /* I - Callback function to add */ +{ + _mxml_global_t *global = _mxml_global(); + /* Global data */ + + + if (global->num_entity_cbs < (int)(sizeof(global->entity_cbs) / sizeof(global->entity_cbs[0]))) + { + global->entity_cbs[global->num_entity_cbs] = cb; + global->num_entity_cbs ++; + + return (0); + } + else + { + mxml_error("Unable to add entity callback!"); + + return (-1); + } +} + + +/* + * 'mxmlEntityGetName()' - Get the name that corresponds to the character value. + * + * If val does not need to be represented by a named entity, NULL is returned. + */ + +const char * /* O - Entity name or NULL */ +mxmlEntityGetName(int val) /* I - Character value */ +{ + switch (val) + { + case '&' : + return ("amp"); + + case '<' : + return ("lt"); + + case '>' : + return ("gt"); + + case '\"' : + return ("quot"); + + default : + return (NULL); + } +} + + +/* + * 'mxmlEntityGetValue()' - Get the character corresponding to a named entity. + * + * The entity name can also be a numeric constant. -1 is returned if the + * name is not known. + */ + +int /* O - Character value or -1 on error */ +mxmlEntityGetValue(const char *name) /* I - Entity name */ +{ + int i; /* Looping var */ + int ch; /* Character value */ + _mxml_global_t *global = _mxml_global(); + /* Global data */ + + + for (i = 0; i < global->num_entity_cbs; i ++) + if ((ch = (global->entity_cbs[i])(name)) >= 0) + return (ch); + + return (-1); +} + + +/* + * 'mxmlEntityRemoveCallback()' - Remove a callback. + */ + +void +mxmlEntityRemoveCallback( + mxml_entity_cb_t cb) /* I - Callback function to remove */ +{ + int i; /* Looping var */ + _mxml_global_t *global = _mxml_global(); + /* Global data */ + + + for (i = 0; i < global->num_entity_cbs; i ++) + if (cb == global->entity_cbs[i]) + { + /* + * Remove the callback... + */ + + global->num_entity_cbs --; + + if (i < global->num_entity_cbs) + memmove(global->entity_cbs + i, global->entity_cbs + i + 1, + (global->num_entity_cbs - i) * sizeof(global->entity_cbs[0])); + + return; + } +} + + +/* + * '_mxml_entity_cb()' - Lookup standard (X)HTML entities. + */ + +int /* O - Unicode value or -1 */ +_mxml_entity_cb(const char *name) /* I - Entity name */ +{ + int diff, /* Difference between names */ + current, /* Current entity in search */ + first, /* First entity in search */ + last; /* Last entity in search */ + static const struct + { + const char *name; /* Entity name */ + int val; /* Character value */ + } entities[] = + { + { "AElig", 198 }, + { "Aacute", 193 }, + { "Acirc", 194 }, + { "Agrave", 192 }, + { "Alpha", 913 }, + { "Aring", 197 }, + { "Atilde", 195 }, + { "Auml", 196 }, + { "Beta", 914 }, + { "Ccedil", 199 }, + { "Chi", 935 }, + { "Dagger", 8225 }, + { "Delta", 916 }, + { "Dstrok", 208 }, + { "ETH", 208 }, + { "Eacute", 201 }, + { "Ecirc", 202 }, + { "Egrave", 200 }, + { "Epsilon", 917 }, + { "Eta", 919 }, + { "Euml", 203 }, + { "Gamma", 915 }, + { "Iacute", 205 }, + { "Icirc", 206 }, + { "Igrave", 204 }, + { "Iota", 921 }, + { "Iuml", 207 }, + { "Kappa", 922 }, + { "Lambda", 923 }, + { "Mu", 924 }, + { "Ntilde", 209 }, + { "Nu", 925 }, + { "OElig", 338 }, + { "Oacute", 211 }, + { "Ocirc", 212 }, + { "Ograve", 210 }, + { "Omega", 937 }, + { "Omicron", 927 }, + { "Oslash", 216 }, + { "Otilde", 213 }, + { "Ouml", 214 }, + { "Phi", 934 }, + { "Pi", 928 }, + { "Prime", 8243 }, + { "Psi", 936 }, + { "Rho", 929 }, + { "Scaron", 352 }, + { "Sigma", 931 }, + { "THORN", 222 }, + { "Tau", 932 }, + { "Theta", 920 }, + { "Uacute", 218 }, + { "Ucirc", 219 }, + { "Ugrave", 217 }, + { "Upsilon", 933 }, + { "Uuml", 220 }, + { "Xi", 926 }, + { "Yacute", 221 }, + { "Yuml", 376 }, + { "Zeta", 918 }, + { "aacute", 225 }, + { "acirc", 226 }, + { "acute", 180 }, + { "aelig", 230 }, + { "agrave", 224 }, + { "alefsym", 8501 }, + { "alpha", 945 }, + { "amp", '&' }, + { "and", 8743 }, + { "ang", 8736 }, + { "apos", '\'' }, + { "aring", 229 }, + { "asymp", 8776 }, + { "atilde", 227 }, + { "auml", 228 }, + { "bdquo", 8222 }, + { "beta", 946 }, + { "brkbar", 166 }, + { "brvbar", 166 }, + { "bull", 8226 }, + { "cap", 8745 }, + { "ccedil", 231 }, + { "cedil", 184 }, + { "cent", 162 }, + { "chi", 967 }, + { "circ", 710 }, + { "clubs", 9827 }, + { "cong", 8773 }, + { "copy", 169 }, + { "crarr", 8629 }, + { "cup", 8746 }, + { "curren", 164 }, + { "dArr", 8659 }, + { "dagger", 8224 }, + { "darr", 8595 }, + { "deg", 176 }, + { "delta", 948 }, + { "diams", 9830 }, + { "die", 168 }, + { "divide", 247 }, + { "eacute", 233 }, + { "ecirc", 234 }, + { "egrave", 232 }, + { "empty", 8709 }, + { "emsp", 8195 }, + { "ensp", 8194 }, + { "epsilon", 949 }, + { "equiv", 8801 }, + { "eta", 951 }, + { "eth", 240 }, + { "euml", 235 }, + { "euro", 8364 }, + { "exist", 8707 }, + { "fnof", 402 }, + { "forall", 8704 }, + { "frac12", 189 }, + { "frac14", 188 }, + { "frac34", 190 }, + { "frasl", 8260 }, + { "gamma", 947 }, + { "ge", 8805 }, + { "gt", '>' }, + { "hArr", 8660 }, + { "harr", 8596 }, + { "hearts", 9829 }, + { "hellip", 8230 }, + { "hibar", 175 }, + { "iacute", 237 }, + { "icirc", 238 }, + { "iexcl", 161 }, + { "igrave", 236 }, + { "image", 8465 }, + { "infin", 8734 }, + { "int", 8747 }, + { "iota", 953 }, + { "iquest", 191 }, + { "isin", 8712 }, + { "iuml", 239 }, + { "kappa", 954 }, + { "lArr", 8656 }, + { "lambda", 955 }, + { "lang", 9001 }, + { "laquo", 171 }, + { "larr", 8592 }, + { "lceil", 8968 }, + { "ldquo", 8220 }, + { "le", 8804 }, + { "lfloor", 8970 }, + { "lowast", 8727 }, + { "loz", 9674 }, + { "lrm", 8206 }, + { "lsaquo", 8249 }, + { "lsquo", 8216 }, + { "lt", '<' }, + { "macr", 175 }, + { "mdash", 8212 }, + { "micro", 181 }, + { "middot", 183 }, + { "minus", 8722 }, + { "mu", 956 }, + { "nabla", 8711 }, + { "nbsp", 160 }, + { "ndash", 8211 }, + { "ne", 8800 }, + { "ni", 8715 }, + { "not", 172 }, + { "notin", 8713 }, + { "nsub", 8836 }, + { "ntilde", 241 }, + { "nu", 957 }, + { "oacute", 243 }, + { "ocirc", 244 }, + { "oelig", 339 }, + { "ograve", 242 }, + { "oline", 8254 }, + { "omega", 969 }, + { "omicron", 959 }, + { "oplus", 8853 }, + { "or", 8744 }, + { "ordf", 170 }, + { "ordm", 186 }, + { "oslash", 248 }, + { "otilde", 245 }, + { "otimes", 8855 }, + { "ouml", 246 }, + { "para", 182 }, + { "part", 8706 }, + { "permil", 8240 }, + { "perp", 8869 }, + { "phi", 966 }, + { "pi", 960 }, + { "piv", 982 }, + { "plusmn", 177 }, + { "pound", 163 }, + { "prime", 8242 }, + { "prod", 8719 }, + { "prop", 8733 }, + { "psi", 968 }, + { "quot", '\"' }, + { "rArr", 8658 }, + { "radic", 8730 }, + { "rang", 9002 }, + { "raquo", 187 }, + { "rarr", 8594 }, + { "rceil", 8969 }, + { "rdquo", 8221 }, + { "real", 8476 }, + { "reg", 174 }, + { "rfloor", 8971 }, + { "rho", 961 }, + { "rlm", 8207 }, + { "rsaquo", 8250 }, + { "rsquo", 8217 }, + { "sbquo", 8218 }, + { "scaron", 353 }, + { "sdot", 8901 }, + { "sect", 167 }, + { "shy", 173 }, + { "sigma", 963 }, + { "sigmaf", 962 }, + { "sim", 8764 }, + { "spades", 9824 }, + { "sub", 8834 }, + { "sube", 8838 }, + { "sum", 8721 }, + { "sup", 8835 }, + { "sup1", 185 }, + { "sup2", 178 }, + { "sup3", 179 }, + { "supe", 8839 }, + { "szlig", 223 }, + { "tau", 964 }, + { "there4", 8756 }, + { "theta", 952 }, + { "thetasym", 977 }, + { "thinsp", 8201 }, + { "thorn", 254 }, + { "tilde", 732 }, + { "times", 215 }, + { "trade", 8482 }, + { "uArr", 8657 }, + { "uacute", 250 }, + { "uarr", 8593 }, + { "ucirc", 251 }, + { "ugrave", 249 }, + { "uml", 168 }, + { "upsih", 978 }, + { "upsilon", 965 }, + { "uuml", 252 }, + { "weierp", 8472 }, + { "xi", 958 }, + { "yacute", 253 }, + { "yen", 165 }, + { "yuml", 255 }, + { "zeta", 950 }, + { "zwj", 8205 }, + { "zwnj", 8204 } + }; + + + /* + * Do a binary search for the named entity... + */ + + first = 0; + last = (int)(sizeof(entities) / sizeof(entities[0]) - 1); + + while ((last - first) > 1) + { + current = (first + last) / 2; + + if ((diff = strcmp(name, entities[current].name)) == 0) + return (entities[current].val); + else if (diff < 0) + last = current; + else + first = current; + } + + /* + * If we get here, there is a small chance that there is still + * a match; check first and last... + */ + + if (!strcmp(name, entities[first].name)) + return (entities[first].val); + else if (!strcmp(name, entities[last].name)) + return (entities[last].val); + else + return (-1); +} + + +/* + * End of "$Id: mxml-entity.c 408 2010-09-19 05:26:46Z mike $". + */ diff --git a/tools/gator/daemon/mxml/mxml-file.c b/tools/gator/daemon/mxml/mxml-file.c new file mode 100644 index 00000000000..7860ee5f837 --- /dev/null +++ b/tools/gator/daemon/mxml/mxml-file.c @@ -0,0 +1,3082 @@ +/* + * "$Id: mxml-file.c 438 2011-03-24 05:47:51Z mike $" + * + * File loading code for Mini-XML, a small XML-like file parsing library. + * + * Copyright 2003-2011 by Michael R Sweet. + * + * These coded instructions, statements, and computer programs are the + * property of Michael R Sweet and are protected by Federal copyright + * law. Distribution and use rights are outlined in the file "COPYING" + * which should have been included with this file. If this file is + * missing or damaged, see the license at: + * + * http://www.minixml.org/ + * + * Contents: + * + * mxmlLoadFd() - Load a file descriptor into an XML node tree. + * mxmlLoadFile() - Load a file into an XML node tree. + * mxmlLoadString() - Load a string into an XML node tree. + * mxmlSaveAllocString() - Save an XML tree to an allocated string. + * mxmlSaveFd() - Save an XML tree to a file descriptor. + * mxmlSaveFile() - Save an XML tree to a file. + * mxmlSaveString() - Save an XML node tree to a string. + * mxmlSAXLoadFd() - Load a file descriptor into an XML node tree + * using a SAX callback. + * mxmlSAXLoadFile() - Load a file into an XML node tree + * using a SAX callback. + * mxmlSAXLoadString() - Load a string into an XML node tree + * using a SAX callback. + * mxmlSetCustomHandlers() - Set the handling functions for custom data. + * mxmlSetErrorCallback() - Set the error message callback. + * mxmlSetWrapMargin() - Set the wrap margin when saving XML data. + * mxml_add_char() - Add a character to a buffer, expanding as needed. + * mxml_fd_getc() - Read a character from a file descriptor. + * mxml_fd_putc() - Write a character to a file descriptor. + * mxml_fd_read() - Read a buffer of data from a file descriptor. + * mxml_fd_write() - Write a buffer of data to a file descriptor. + * mxml_file_getc() - Get a character from a file. + * mxml_file_putc() - Write a character to a file. + * mxml_get_entity() - Get the character corresponding to an entity... + * mxml_load_data() - Load data into an XML node tree. + * mxml_parse_element() - Parse an element for any attributes... + * mxml_string_getc() - Get a character from a string. + * mxml_string_putc() - Write a character to a string. + * mxml_write_name() - Write a name string. + * mxml_write_node() - Save an XML node to a file. + * mxml_write_string() - Write a string, escaping & and < as needed. + * mxml_write_ws() - Do whitespace callback... + */ + +/* + * Include necessary headers... + */ + +#ifndef WIN32 +# include +#endif /* !WIN32 */ +#include "mxml-private.h" + + +/* + * Character encoding... + */ + +#define ENCODE_UTF8 0 /* UTF-8 */ +#define ENCODE_UTF16BE 1 /* UTF-16 Big-Endian */ +#define ENCODE_UTF16LE 2 /* UTF-16 Little-Endian */ + + +/* + * Macro to test for a bad XML character... + */ + +#define mxml_bad_char(ch) ((ch) < ' ' && (ch) != '\n' && (ch) != '\r' && (ch) != '\t') + + +/* + * Types and structures... + */ + +typedef int (*_mxml_getc_cb_t)(void *, int *); +typedef int (*_mxml_putc_cb_t)(int, void *); + +typedef struct _mxml_fdbuf_s /**** File descriptor buffer ****/ +{ + int fd; /* File descriptor */ + unsigned char *current, /* Current position in buffer */ + *end, /* End of buffer */ + buffer[8192]; /* Character buffer */ +} _mxml_fdbuf_t; + + +/* + * Local functions... + */ + +static int mxml_add_char(int ch, char **ptr, char **buffer, + int *bufsize); +static int mxml_fd_getc(void *p, int *encoding); +static int mxml_fd_putc(int ch, void *p); +static int mxml_fd_read(_mxml_fdbuf_t *buf); +static int mxml_fd_write(_mxml_fdbuf_t *buf); +static int mxml_file_getc(void *p, int *encoding); +static int mxml_file_putc(int ch, void *p); +static int mxml_get_entity(mxml_node_t *parent, void *p, + int *encoding, + _mxml_getc_cb_t getc_cb); +static inline int mxml_isspace(int ch) + { + return (ch == ' ' || ch == '\t' || ch == '\r' || + ch == '\n'); + } +static mxml_node_t *mxml_load_data(mxml_node_t *top, void *p, + mxml_load_cb_t cb, + _mxml_getc_cb_t getc_cb, + mxml_sax_cb_t sax_cb, void *sax_data); +static int mxml_parse_element(mxml_node_t *node, void *p, + int *encoding, + _mxml_getc_cb_t getc_cb); +static int mxml_string_getc(void *p, int *encoding); +static int mxml_string_putc(int ch, void *p); +static int mxml_write_name(const char *s, void *p, + _mxml_putc_cb_t putc_cb); +static int mxml_write_node(mxml_node_t *node, void *p, + mxml_save_cb_t cb, int col, + _mxml_putc_cb_t putc_cb, + _mxml_global_t *global); +static int mxml_write_string(const char *s, void *p, + _mxml_putc_cb_t putc_cb); +static int mxml_write_ws(mxml_node_t *node, void *p, + mxml_save_cb_t cb, int ws, + int col, _mxml_putc_cb_t putc_cb); + + +/* + * 'mxmlLoadFd()' - Load a file descriptor into an XML node tree. + * + * The nodes in the specified file are added to the specified top node. + * If no top node is provided, the XML file MUST be well-formed with a + * single parent node like for the entire file. The callback + * function returns the value type that should be used for child nodes. + * If MXML_NO_CALLBACK is specified then all child nodes will be either + * MXML_ELEMENT or MXML_TEXT nodes. + * + * The constants MXML_INTEGER_CALLBACK, MXML_OPAQUE_CALLBACK, + * MXML_REAL_CALLBACK, and MXML_TEXT_CALLBACK are defined for loading + * child nodes of the specified type. + */ + +mxml_node_t * /* O - First node or NULL if the file could not be read. */ +mxmlLoadFd(mxml_node_t *top, /* I - Top node */ + int fd, /* I - File descriptor to read from */ + mxml_load_cb_t cb) /* I - Callback function or MXML_NO_CALLBACK */ +{ + _mxml_fdbuf_t buf; /* File descriptor buffer */ + + + /* + * Initialize the file descriptor buffer... + */ + + buf.fd = fd; + buf.current = buf.buffer; + buf.end = buf.buffer; + + /* + * Read the XML data... + */ + + return (mxml_load_data(top, &buf, cb, mxml_fd_getc, MXML_NO_CALLBACK, NULL)); +} + + +/* + * 'mxmlLoadFile()' - Load a file into an XML node tree. + * + * The nodes in the specified file are added to the specified top node. + * If no top node is provided, the XML file MUST be well-formed with a + * single parent node like for the entire file. The callback + * function returns the value type that should be used for child nodes. + * If MXML_NO_CALLBACK is specified then all child nodes will be either + * MXML_ELEMENT or MXML_TEXT nodes. + * + * The constants MXML_INTEGER_CALLBACK, MXML_OPAQUE_CALLBACK, + * MXML_REAL_CALLBACK, and MXML_TEXT_CALLBACK are defined for loading + * child nodes of the specified type. + */ + +mxml_node_t * /* O - First node or NULL if the file could not be read. */ +mxmlLoadFile(mxml_node_t *top, /* I - Top node */ + FILE *fp, /* I - File to read from */ + mxml_load_cb_t cb) /* I - Callback function or MXML_NO_CALLBACK */ +{ + /* + * Read the XML data... + */ + + return (mxml_load_data(top, fp, cb, mxml_file_getc, MXML_NO_CALLBACK, NULL)); +} + + +/* + * 'mxmlLoadString()' - Load a string into an XML node tree. + * + * The nodes in the specified string are added to the specified top node. + * If no top node is provided, the XML string MUST be well-formed with a + * single parent node like for the entire string. The callback + * function returns the value type that should be used for child nodes. + * If MXML_NO_CALLBACK is specified then all child nodes will be either + * MXML_ELEMENT or MXML_TEXT nodes. + * + * The constants MXML_INTEGER_CALLBACK, MXML_OPAQUE_CALLBACK, + * MXML_REAL_CALLBACK, and MXML_TEXT_CALLBACK are defined for loading + * child nodes of the specified type. + */ + +mxml_node_t * /* O - First node or NULL if the string has errors. */ +mxmlLoadString(mxml_node_t *top, /* I - Top node */ + const char *s, /* I - String to load */ + mxml_load_cb_t cb) /* I - Callback function or MXML_NO_CALLBACK */ +{ + /* + * Read the XML data... + */ + + return (mxml_load_data(top, (void *)&s, cb, mxml_string_getc, MXML_NO_CALLBACK, + NULL)); +} + + +/* + * 'mxmlSaveAllocString()' - Save an XML tree to an allocated string. + * + * This function returns a pointer to a string containing the textual + * representation of the XML node tree. The string should be freed + * using the free() function when you are done with it. NULL is returned + * if the node would produce an empty string or if the string cannot be + * allocated. + * + * The callback argument specifies a function that returns a whitespace + * string or NULL before and after each element. If MXML_NO_CALLBACK + * is specified, whitespace will only be added before MXML_TEXT nodes + * with leading whitespace and before attribute names inside opening + * element tags. + */ + +char * /* O - Allocated string or NULL */ +mxmlSaveAllocString( + mxml_node_t *node, /* I - Node to write */ + mxml_save_cb_t cb) /* I - Whitespace callback or MXML_NO_CALLBACK */ +{ + int bytes; /* Required bytes */ + char buffer[8192]; /* Temporary buffer */ + char *s; /* Allocated string */ + + + /* + * Write the node to the temporary buffer... + */ + + bytes = mxmlSaveString(node, buffer, sizeof(buffer), cb); + + if (bytes <= 0) + return (NULL); + + if (bytes < (int)(sizeof(buffer) - 1)) + { + /* + * Node fit inside the buffer, so just duplicate that string and + * return... + */ + + return (strdup(buffer)); + } + + /* + * Allocate a buffer of the required size and save the node to the + * new buffer... + */ + + if ((s = malloc(bytes + 1)) == NULL) + return (NULL); + + mxmlSaveString(node, s, bytes + 1, cb); + + /* + * Return the allocated string... + */ + + return (s); +} + + +/* + * 'mxmlSaveFd()' - Save an XML tree to a file descriptor. + * + * The callback argument specifies a function that returns a whitespace + * string or NULL before and after each element. If MXML_NO_CALLBACK + * is specified, whitespace will only be added before MXML_TEXT nodes + * with leading whitespace and before attribute names inside opening + * element tags. + */ + +int /* O - 0 on success, -1 on error. */ +mxmlSaveFd(mxml_node_t *node, /* I - Node to write */ + int fd, /* I - File descriptor to write to */ + mxml_save_cb_t cb) /* I - Whitespace callback or MXML_NO_CALLBACK */ +{ + int col; /* Final column */ + _mxml_fdbuf_t buf; /* File descriptor buffer */ + _mxml_global_t *global = _mxml_global(); + /* Global data */ + + + /* + * Initialize the file descriptor buffer... + */ + + buf.fd = fd; + buf.current = buf.buffer; + buf.end = buf.buffer + sizeof(buf.buffer); + + /* + * Write the node... + */ + + if ((col = mxml_write_node(node, &buf, cb, 0, mxml_fd_putc, global)) < 0) + return (-1); + + if (col > 0) + if (mxml_fd_putc('\n', &buf) < 0) + return (-1); + + /* + * Flush and return... + */ + + return (mxml_fd_write(&buf)); +} + + +/* + * 'mxmlSaveFile()' - Save an XML tree to a file. + * + * The callback argument specifies a function that returns a whitespace + * string or NULL before and after each element. If MXML_NO_CALLBACK + * is specified, whitespace will only be added before MXML_TEXT nodes + * with leading whitespace and before attribute names inside opening + * element tags. + */ + +int /* O - 0 on success, -1 on error. */ +mxmlSaveFile(mxml_node_t *node, /* I - Node to write */ + FILE *fp, /* I - File to write to */ + mxml_save_cb_t cb) /* I - Whitespace callback or MXML_NO_CALLBACK */ +{ + int col; /* Final column */ + _mxml_global_t *global = _mxml_global(); + /* Global data */ + + + /* + * Write the node... + */ + + if ((col = mxml_write_node(node, fp, cb, 0, mxml_file_putc, global)) < 0) + return (-1); + + if (col > 0) + if (putc('\n', fp) < 0) + return (-1); + + /* + * Return 0 (success)... + */ + + return (0); +} + + +/* + * 'mxmlSaveString()' - Save an XML node tree to a string. + * + * This function returns the total number of bytes that would be + * required for the string but only copies (bufsize - 1) characters + * into the specified buffer. + * + * The callback argument specifies a function that returns a whitespace + * string or NULL before and after each element. If MXML_NO_CALLBACK + * is specified, whitespace will only be added before MXML_TEXT nodes + * with leading whitespace and before attribute names inside opening + * element tags. + */ + +int /* O - Size of string */ +mxmlSaveString(mxml_node_t *node, /* I - Node to write */ + char *buffer, /* I - String buffer */ + int bufsize, /* I - Size of string buffer */ + mxml_save_cb_t cb) /* I - Whitespace callback or MXML_NO_CALLBACK */ +{ + int col; /* Final column */ + char *ptr[2]; /* Pointers for putc_cb */ + _mxml_global_t *global = _mxml_global(); + /* Global data */ + + + /* + * Write the node... + */ + + ptr[0] = buffer; + ptr[1] = buffer + bufsize; + + if ((col = mxml_write_node(node, ptr, cb, 0, mxml_string_putc, global)) < 0) + return (-1); + + if (col > 0) + mxml_string_putc('\n', ptr); + + /* + * Nul-terminate the buffer... + */ + + if (ptr[0] >= ptr[1]) + buffer[bufsize - 1] = '\0'; + else + ptr[0][0] = '\0'; + + /* + * Return the number of characters... + */ + + return (ptr[0] - buffer); +} + + +/* + * 'mxmlSAXLoadFd()' - Load a file descriptor into an XML node tree + * using a SAX callback. + * + * The nodes in the specified file are added to the specified top node. + * If no top node is provided, the XML file MUST be well-formed with a + * single parent node like for the entire file. The callback + * function returns the value type that should be used for child nodes. + * If MXML_NO_CALLBACK is specified then all child nodes will be either + * MXML_ELEMENT or MXML_TEXT nodes. + * + * The constants MXML_INTEGER_CALLBACK, MXML_OPAQUE_CALLBACK, + * MXML_REAL_CALLBACK, and MXML_TEXT_CALLBACK are defined for loading + * child nodes of the specified type. + * + * The SAX callback must call mxmlRetain() for any nodes that need to + * be kept for later use. Otherwise, nodes are deleted when the parent + * node is closed or after each data, comment, CDATA, or directive node. + * + * @since Mini-XML 2.3@ + */ + +mxml_node_t * /* O - First node or NULL if the file could not be read. */ +mxmlSAXLoadFd(mxml_node_t *top, /* I - Top node */ + int fd, /* I - File descriptor to read from */ + mxml_load_cb_t cb, /* I - Callback function or MXML_NO_CALLBACK */ + mxml_sax_cb_t sax_cb, /* I - SAX callback or MXML_NO_CALLBACK */ + void *sax_data) /* I - SAX user data */ +{ + _mxml_fdbuf_t buf; /* File descriptor buffer */ + + + /* + * Initialize the file descriptor buffer... + */ + + buf.fd = fd; + buf.current = buf.buffer; + buf.end = buf.buffer; + + /* + * Read the XML data... + */ + + return (mxml_load_data(top, &buf, cb, mxml_fd_getc, sax_cb, sax_data)); +} + + +/* + * 'mxmlSAXLoadFile()' - Load a file into an XML node tree + * using a SAX callback. + * + * The nodes in the specified file are added to the specified top node. + * If no top node is provided, the XML file MUST be well-formed with a + * single parent node like for the entire file. The callback + * function returns the value type that should be used for child nodes. + * If MXML_NO_CALLBACK is specified then all child nodes will be either + * MXML_ELEMENT or MXML_TEXT nodes. + * + * The constants MXML_INTEGER_CALLBACK, MXML_OPAQUE_CALLBACK, + * MXML_REAL_CALLBACK, and MXML_TEXT_CALLBACK are defined for loading + * child nodes of the specified type. + * + * The SAX callback must call mxmlRetain() for any nodes that need to + * be kept for later use. Otherwise, nodes are deleted when the parent + * node is closed or after each data, comment, CDATA, or directive node. + * + * @since Mini-XML 2.3@ + */ + +mxml_node_t * /* O - First node or NULL if the file could not be read. */ +mxmlSAXLoadFile( + mxml_node_t *top, /* I - Top node */ + FILE *fp, /* I - File to read from */ + mxml_load_cb_t cb, /* I - Callback function or MXML_NO_CALLBACK */ + mxml_sax_cb_t sax_cb, /* I - SAX callback or MXML_NO_CALLBACK */ + void *sax_data) /* I - SAX user data */ +{ + /* + * Read the XML data... + */ + + return (mxml_load_data(top, fp, cb, mxml_file_getc, sax_cb, sax_data)); +} + + +/* + * 'mxmlSAXLoadString()' - Load a string into an XML node tree + * using a SAX callback. + * + * The nodes in the specified string are added to the specified top node. + * If no top node is provided, the XML string MUST be well-formed with a + * single parent node like for the entire string. The callback + * function returns the value type that should be used for child nodes. + * If MXML_NO_CALLBACK is specified then all child nodes will be either + * MXML_ELEMENT or MXML_TEXT nodes. + * + * The constants MXML_INTEGER_CALLBACK, MXML_OPAQUE_CALLBACK, + * MXML_REAL_CALLBACK, and MXML_TEXT_CALLBACK are defined for loading + * child nodes of the specified type. + * + * The SAX callback must call mxmlRetain() for any nodes that need to + * be kept for later use. Otherwise, nodes are deleted when the parent + * node is closed or after each data, comment, CDATA, or directive node. + * + * @since Mini-XML 2.3@ + */ + +mxml_node_t * /* O - First node or NULL if the string has errors. */ +mxmlSAXLoadString( + mxml_node_t *top, /* I - Top node */ + const char *s, /* I - String to load */ + mxml_load_cb_t cb, /* I - Callback function or MXML_NO_CALLBACK */ + mxml_sax_cb_t sax_cb, /* I - SAX callback or MXML_NO_CALLBACK */ + void *sax_data) /* I - SAX user data */ +{ + /* + * Read the XML data... + */ + + return (mxml_load_data(top, (void *)&s, cb, mxml_string_getc, sax_cb, sax_data)); +} + + +/* + * 'mxmlSetCustomHandlers()' - Set the handling functions for custom data. + * + * The load function accepts a node pointer and a data string and must + * return 0 on success and non-zero on error. + * + * The save function accepts a node pointer and must return a malloc'd + * string on success and NULL on error. + * + */ + +void +mxmlSetCustomHandlers( + mxml_custom_load_cb_t load, /* I - Load function */ + mxml_custom_save_cb_t save) /* I - Save function */ +{ + _mxml_global_t *global = _mxml_global(); + /* Global data */ + + + global->custom_load_cb = load; + global->custom_save_cb = save; +} + + +/* + * 'mxmlSetErrorCallback()' - Set the error message callback. + */ + +void +mxmlSetErrorCallback(mxml_error_cb_t cb)/* I - Error callback function */ +{ + _mxml_global_t *global = _mxml_global(); + /* Global data */ + + + global->error_cb = cb; +} + + +/* + * 'mxmlSetWrapMargin()' - Set the wrap margin when saving XML data. + * + * Wrapping is disabled when "column" is 0. + * + * @since Mini-XML 2.3@ + */ + +void +mxmlSetWrapMargin(int column) /* I - Column for wrapping, 0 to disable wrapping */ +{ + _mxml_global_t *global = _mxml_global(); + /* Global data */ + + + global->wrap = column; +} + + +/* + * 'mxml_add_char()' - Add a character to a buffer, expanding as needed. + */ + +static int /* O - 0 on success, -1 on error */ +mxml_add_char(int ch, /* I - Character to add */ + char **bufptr, /* IO - Current position in buffer */ + char **buffer, /* IO - Current buffer */ + int *bufsize) /* IO - Current buffer size */ +{ + char *newbuffer; /* New buffer value */ + + + if (*bufptr >= (*buffer + *bufsize - 4)) + { + /* + * Increase the size of the buffer... + */ + + if (*bufsize < 1024) + (*bufsize) *= 2; + else + (*bufsize) += 1024; + + if ((newbuffer = realloc(*buffer, *bufsize)) == NULL) + { + free(*buffer); + + mxml_error("Unable to expand string buffer to %d bytes!", *bufsize); + + return (-1); + } + + *bufptr = newbuffer + (*bufptr - *buffer); + *buffer = newbuffer; + } + + if (ch < 0x80) + { + /* + * Single byte ASCII... + */ + + *(*bufptr)++ = ch; + } + else if (ch < 0x800) + { + /* + * Two-byte UTF-8... + */ + + *(*bufptr)++ = 0xc0 | (ch >> 6); + *(*bufptr)++ = 0x80 | (ch & 0x3f); + } + else if (ch < 0x10000) + { + /* + * Three-byte UTF-8... + */ + + *(*bufptr)++ = 0xe0 | (ch >> 12); + *(*bufptr)++ = 0x80 | ((ch >> 6) & 0x3f); + *(*bufptr)++ = 0x80 | (ch & 0x3f); + } + else + { + /* + * Four-byte UTF-8... + */ + + *(*bufptr)++ = 0xf0 | (ch >> 18); + *(*bufptr)++ = 0x80 | ((ch >> 12) & 0x3f); + *(*bufptr)++ = 0x80 | ((ch >> 6) & 0x3f); + *(*bufptr)++ = 0x80 | (ch & 0x3f); + } + + return (0); +} + + +/* + * 'mxml_fd_getc()' - Read a character from a file descriptor. + */ + +static int /* O - Character or EOF */ +mxml_fd_getc(void *p, /* I - File descriptor buffer */ + int *encoding) /* IO - Encoding */ +{ + _mxml_fdbuf_t *buf; /* File descriptor buffer */ + int ch, /* Current character */ + temp; /* Temporary character */ + + + /* + * Grab the next character in the buffer... + */ + + buf = (_mxml_fdbuf_t *)p; + + if (buf->current >= buf->end) + if (mxml_fd_read(buf) < 0) + return (EOF); + + ch = *(buf->current)++; + + switch (*encoding) + { + case ENCODE_UTF8 : + /* + * Got a UTF-8 character; convert UTF-8 to Unicode and return... + */ + + if (!(ch & 0x80)) + { +#if DEBUG > 1 + printf("mxml_fd_getc: %c (0x%04x)\n", ch < ' ' ? '.' : ch, ch); +#endif /* DEBUG > 1 */ + + if (mxml_bad_char(ch)) + { + mxml_error("Bad control character 0x%02x not allowed by XML standard!", + ch); + return (EOF); + } + + return (ch); + } + else if (ch == 0xfe) + { + /* + * UTF-16 big-endian BOM? + */ + + if (buf->current >= buf->end) + if (mxml_fd_read(buf) < 0) + return (EOF); + + ch = *(buf->current)++; + + if (ch != 0xff) + return (EOF); + + *encoding = ENCODE_UTF16BE; + + return (mxml_fd_getc(p, encoding)); + } + else if (ch == 0xff) + { + /* + * UTF-16 little-endian BOM? + */ + + if (buf->current >= buf->end) + if (mxml_fd_read(buf) < 0) + return (EOF); + + ch = *(buf->current)++; + + if (ch != 0xfe) + return (EOF); + + *encoding = ENCODE_UTF16LE; + + return (mxml_fd_getc(p, encoding)); + } + else if ((ch & 0xe0) == 0xc0) + { + /* + * Two-byte value... + */ + + if (buf->current >= buf->end) + if (mxml_fd_read(buf) < 0) + return (EOF); + + temp = *(buf->current)++; + + if ((temp & 0xc0) != 0x80) + return (EOF); + + ch = ((ch & 0x1f) << 6) | (temp & 0x3f); + + if (ch < 0x80) + { + mxml_error("Invalid UTF-8 sequence for character 0x%04x!", ch); + return (EOF); + } + } + else if ((ch & 0xf0) == 0xe0) + { + /* + * Three-byte value... + */ + + if (buf->current >= buf->end) + if (mxml_fd_read(buf) < 0) + return (EOF); + + temp = *(buf->current)++; + + if ((temp & 0xc0) != 0x80) + return (EOF); + + ch = ((ch & 0x0f) << 6) | (temp & 0x3f); + + if (buf->current >= buf->end) + if (mxml_fd_read(buf) < 0) + return (EOF); + + temp = *(buf->current)++; + + if ((temp & 0xc0) != 0x80) + return (EOF); + + ch = (ch << 6) | (temp & 0x3f); + + if (ch < 0x800) + { + mxml_error("Invalid UTF-8 sequence for character 0x%04x!", ch); + return (EOF); + } + + /* + * Ignore (strip) Byte Order Mark (BOM)... + */ + + if (ch == 0xfeff) + return (mxml_fd_getc(p, encoding)); + } + else if ((ch & 0xf8) == 0xf0) + { + /* + * Four-byte value... + */ + + if (buf->current >= buf->end) + if (mxml_fd_read(buf) < 0) + return (EOF); + + temp = *(buf->current)++; + + if ((temp & 0xc0) != 0x80) + return (EOF); + + ch = ((ch & 0x07) << 6) | (temp & 0x3f); + + if (buf->current >= buf->end) + if (mxml_fd_read(buf) < 0) + return (EOF); + + temp = *(buf->current)++; + + if ((temp & 0xc0) != 0x80) + return (EOF); + + ch = (ch << 6) | (temp & 0x3f); + + if (buf->current >= buf->end) + if (mxml_fd_read(buf) < 0) + return (EOF); + + temp = *(buf->current)++; + + if ((temp & 0xc0) != 0x80) + return (EOF); + + ch = (ch << 6) | (temp & 0x3f); + + if (ch < 0x10000) + { + mxml_error("Invalid UTF-8 sequence for character 0x%04x!", ch); + return (EOF); + } + } + else + return (EOF); + break; + + case ENCODE_UTF16BE : + /* + * Read UTF-16 big-endian char... + */ + + if (buf->current >= buf->end) + if (mxml_fd_read(buf) < 0) + return (EOF); + + temp = *(buf->current)++; + + ch = (ch << 8) | temp; + + if (mxml_bad_char(ch)) + { + mxml_error("Bad control character 0x%02x not allowed by XML standard!", + ch); + return (EOF); + } + else if (ch >= 0xd800 && ch <= 0xdbff) + { + /* + * Multi-word UTF-16 char... + */ + + int lch; + + if (buf->current >= buf->end) + if (mxml_fd_read(buf) < 0) + return (EOF); + + lch = *(buf->current)++; + + if (buf->current >= buf->end) + if (mxml_fd_read(buf) < 0) + return (EOF); + + temp = *(buf->current)++; + + lch = (lch << 8) | temp; + + if (lch < 0xdc00 || lch >= 0xdfff) + return (EOF); + + ch = (((ch & 0x3ff) << 10) | (lch & 0x3ff)) + 0x10000; + } + break; + + case ENCODE_UTF16LE : + /* + * Read UTF-16 little-endian char... + */ + + if (buf->current >= buf->end) + if (mxml_fd_read(buf) < 0) + return (EOF); + + temp = *(buf->current)++; + + ch |= (temp << 8); + + if (mxml_bad_char(ch)) + { + mxml_error("Bad control character 0x%02x not allowed by XML standard!", + ch); + return (EOF); + } + else if (ch >= 0xd800 && ch <= 0xdbff) + { + /* + * Multi-word UTF-16 char... + */ + + int lch; + + if (buf->current >= buf->end) + if (mxml_fd_read(buf) < 0) + return (EOF); + + lch = *(buf->current)++; + + if (buf->current >= buf->end) + if (mxml_fd_read(buf) < 0) + return (EOF); + + temp = *(buf->current)++; + + lch |= (temp << 8); + + if (lch < 0xdc00 || lch >= 0xdfff) + return (EOF); + + ch = (((ch & 0x3ff) << 10) | (lch & 0x3ff)) + 0x10000; + } + break; + } + +#if DEBUG > 1 + printf("mxml_fd_getc: %c (0x%04x)\n", ch < ' ' ? '.' : ch, ch); +#endif /* DEBUG > 1 */ + + return (ch); +} + + +/* + * 'mxml_fd_putc()' - Write a character to a file descriptor. + */ + +static int /* O - 0 on success, -1 on error */ +mxml_fd_putc(int ch, /* I - Character */ + void *p) /* I - File descriptor buffer */ +{ + _mxml_fdbuf_t *buf; /* File descriptor buffer */ + + + /* + * Flush the write buffer as needed... + */ + + buf = (_mxml_fdbuf_t *)p; + + if (buf->current >= buf->end) + if (mxml_fd_write(buf) < 0) + return (-1); + + *(buf->current)++ = ch; + + /* + * Return successfully... + */ + + return (0); +} + + +/* + * 'mxml_fd_read()' - Read a buffer of data from a file descriptor. + */ + +static int /* O - 0 on success, -1 on error */ +mxml_fd_read(_mxml_fdbuf_t *buf) /* I - File descriptor buffer */ +{ + int bytes; /* Bytes read... */ + + + /* + * Range check input... + */ + + if (!buf) + return (-1); + + /* + * Read from the file descriptor... + */ + + while ((bytes = read(buf->fd, buf->buffer, sizeof(buf->buffer))) < 0) +#ifdef EINTR + if (errno != EAGAIN && errno != EINTR) +#else + if (errno != EAGAIN) +#endif /* EINTR */ + return (-1); + + if (bytes == 0) + return (-1); + + /* + * Update the pointers and return success... + */ + + buf->current = buf->buffer; + buf->end = buf->buffer + bytes; + + return (0); +} + + +/* + * 'mxml_fd_write()' - Write a buffer of data to a file descriptor. + */ + +static int /* O - 0 on success, -1 on error */ +mxml_fd_write(_mxml_fdbuf_t *buf) /* I - File descriptor buffer */ +{ + int bytes; /* Bytes written */ + unsigned char *ptr; /* Pointer into buffer */ + + + /* + * Range check... + */ + + if (!buf) + return (-1); + + /* + * Return 0 if there is nothing to write... + */ + + if (buf->current == buf->buffer) + return (0); + + /* + * Loop until we have written everything... + */ + + for (ptr = buf->buffer; ptr < buf->current; ptr += bytes) + if ((bytes = write(buf->fd, ptr, buf->current - ptr)) < 0) + return (-1); + + /* + * All done, reset pointers and return success... + */ + + buf->current = buf->buffer; + + return (0); +} + + +/* + * 'mxml_file_getc()' - Get a character from a file. + */ + +static int /* O - Character or EOF */ +mxml_file_getc(void *p, /* I - Pointer to file */ + int *encoding) /* IO - Encoding */ +{ + int ch, /* Character from file */ + temp; /* Temporary character */ + FILE *fp; /* Pointer to file */ + + + /* + * Read a character from the file and see if it is EOF or ASCII... + */ + + fp = (FILE *)p; + ch = getc(fp); + + if (ch == EOF) + return (EOF); + + switch (*encoding) + { + case ENCODE_UTF8 : + /* + * Got a UTF-8 character; convert UTF-8 to Unicode and return... + */ + + if (!(ch & 0x80)) + { + if (mxml_bad_char(ch)) + { + mxml_error("Bad control character 0x%02x not allowed by XML standard!", + ch); + return (EOF); + } + +#if DEBUG > 1 + printf("mxml_file_getc: %c (0x%04x)\n", ch < ' ' ? '.' : ch, ch); +#endif /* DEBUG > 1 */ + + return (ch); + } + else if (ch == 0xfe) + { + /* + * UTF-16 big-endian BOM? + */ + + ch = getc(fp); + if (ch != 0xff) + return (EOF); + + *encoding = ENCODE_UTF16BE; + + return (mxml_file_getc(p, encoding)); + } + else if (ch == 0xff) + { + /* + * UTF-16 little-endian BOM? + */ + + ch = getc(fp); + if (ch != 0xfe) + return (EOF); + + *encoding = ENCODE_UTF16LE; + + return (mxml_file_getc(p, encoding)); + } + else if ((ch & 0xe0) == 0xc0) + { + /* + * Two-byte value... + */ + + if ((temp = getc(fp)) == EOF || (temp & 0xc0) != 0x80) + return (EOF); + + ch = ((ch & 0x1f) << 6) | (temp & 0x3f); + + if (ch < 0x80) + { + mxml_error("Invalid UTF-8 sequence for character 0x%04x!", ch); + return (EOF); + } + } + else if ((ch & 0xf0) == 0xe0) + { + /* + * Three-byte value... + */ + + if ((temp = getc(fp)) == EOF || (temp & 0xc0) != 0x80) + return (EOF); + + ch = ((ch & 0x0f) << 6) | (temp & 0x3f); + + if ((temp = getc(fp)) == EOF || (temp & 0xc0) != 0x80) + return (EOF); + + ch = (ch << 6) | (temp & 0x3f); + + if (ch < 0x800) + { + mxml_error("Invalid UTF-8 sequence for character 0x%04x!", ch); + return (EOF); + } + + /* + * Ignore (strip) Byte Order Mark (BOM)... + */ + + if (ch == 0xfeff) + return (mxml_file_getc(p, encoding)); + } + else if ((ch & 0xf8) == 0xf0) + { + /* + * Four-byte value... + */ + + if ((temp = getc(fp)) == EOF || (temp & 0xc0) != 0x80) + return (EOF); + + ch = ((ch & 0x07) << 6) | (temp & 0x3f); + + if ((temp = getc(fp)) == EOF || (temp & 0xc0) != 0x80) + return (EOF); + + ch = (ch << 6) | (temp & 0x3f); + + if ((temp = getc(fp)) == EOF || (temp & 0xc0) != 0x80) + return (EOF); + + ch = (ch << 6) | (temp & 0x3f); + + if (ch < 0x10000) + { + mxml_error("Invalid UTF-8 sequence for character 0x%04x!", ch); + return (EOF); + } + } + else + return (EOF); + break; + + case ENCODE_UTF16BE : + /* + * Read UTF-16 big-endian char... + */ + + ch = (ch << 8) | getc(fp); + + if (mxml_bad_char(ch)) + { + mxml_error("Bad control character 0x%02x not allowed by XML standard!", + ch); + return (EOF); + } + else if (ch >= 0xd800 && ch <= 0xdbff) + { + /* + * Multi-word UTF-16 char... + */ + + int lch = (getc(fp) << 8); + lch |= getc(fp); + + if (lch < 0xdc00 || lch >= 0xdfff) + return (EOF); + + ch = (((ch & 0x3ff) << 10) | (lch & 0x3ff)) + 0x10000; + } + break; + + case ENCODE_UTF16LE : + /* + * Read UTF-16 little-endian char... + */ + + ch |= (getc(fp) << 8); + + if (mxml_bad_char(ch)) + { + mxml_error("Bad control character 0x%02x not allowed by XML standard!", + ch); + return (EOF); + } + else if (ch >= 0xd800 && ch <= 0xdbff) + { + /* + * Multi-word UTF-16 char... + */ + + int lch = getc(fp); + lch |= (getc(fp) << 8); + + if (lch < 0xdc00 || lch >= 0xdfff) + return (EOF); + + ch = (((ch & 0x3ff) << 10) | (lch & 0x3ff)) + 0x10000; + } + break; + } + +#if DEBUG > 1 + printf("mxml_file_getc: %c (0x%04x)\n", ch < ' ' ? '.' : ch, ch); +#endif /* DEBUG > 1 */ + + return (ch); +} + + +/* + * 'mxml_file_putc()' - Write a character to a file. + */ + +static int /* O - 0 on success, -1 on failure */ +mxml_file_putc(int ch, /* I - Character to write */ + void *p) /* I - Pointer to file */ +{ + return (putc(ch, (FILE *)p) == EOF ? -1 : 0); +} + + +/* + * 'mxml_get_entity()' - Get the character corresponding to an entity... + */ + +static int /* O - Character value or EOF on error */ +mxml_get_entity(mxml_node_t *parent, /* I - Parent node */ + void *p, /* I - Pointer to source */ + int *encoding, /* IO - Character encoding */ + int (*getc_cb)(void *, int *)) + /* I - Get character function */ +{ + int ch; /* Current character */ + char entity[64], /* Entity string */ + *entptr; /* Pointer into entity */ + + + entptr = entity; + + while ((ch = (*getc_cb)(p, encoding)) != EOF) + if (ch > 126 || (!isalnum(ch) && ch != '#')) + break; + else if (entptr < (entity + sizeof(entity) - 1)) + *entptr++ = ch; + else + { + mxml_error("Entity name too long under parent <%s>!", + parent ? parent->value.element.name : "null"); + break; + } + + *entptr = '\0'; + + if (ch != ';') + { + mxml_error("Character entity \"%s\" not terminated under parent <%s>!", + entity, parent ? parent->value.element.name : "null"); + return (EOF); + } + + if (entity[0] == '#') + { + if (entity[1] == 'x') + ch = strtol(entity + 2, NULL, 16); + else + ch = strtol(entity + 1, NULL, 10); + } + else if ((ch = mxmlEntityGetValue(entity)) < 0) + mxml_error("Entity name \"%s;\" not supported under parent <%s>!", + entity, parent ? parent->value.element.name : "null"); + + if (mxml_bad_char(ch)) + { + mxml_error("Bad control character 0x%02x under parent <%s> not allowed by XML standard!", + ch, parent ? parent->value.element.name : "null"); + return (EOF); + } + + return (ch); +} + + +/* + * 'mxml_load_data()' - Load data into an XML node tree. + */ + +static mxml_node_t * /* O - First node or NULL if the file could not be read. */ +mxml_load_data( + mxml_node_t *top, /* I - Top node */ + void *p, /* I - Pointer to data */ + mxml_load_cb_t cb, /* I - Callback function or MXML_NO_CALLBACK */ + _mxml_getc_cb_t getc_cb, /* I - Read function */ + mxml_sax_cb_t sax_cb, /* I - SAX callback or MXML_NO_CALLBACK */ + void *sax_data) /* I - SAX user data */ +{ + mxml_node_t *node, /* Current node */ + *first, /* First node added */ + *parent; /* Current parent node */ + int ch, /* Character from file */ + whitespace; /* Non-zero if whitespace seen */ + char *buffer, /* String buffer */ + *bufptr; /* Pointer into buffer */ + int bufsize; /* Size of buffer */ + mxml_type_t type; /* Current node type */ + int encoding; /* Character encoding */ + _mxml_global_t *global = _mxml_global(); + /* Global data */ + static const char * const types[] = /* Type strings... */ + { + "MXML_ELEMENT", /* XML element with attributes */ + "MXML_INTEGER", /* Integer value */ + "MXML_OPAQUE", /* Opaque string */ + "MXML_REAL", /* Real value */ + "MXML_TEXT", /* Text fragment */ + "MXML_CUSTOM" /* Custom data */ + }; + + + /* + * Read elements and other nodes from the file... + */ + + if ((buffer = malloc(64)) == NULL) + { + mxml_error("Unable to allocate string buffer!"); + return (NULL); + } + + bufsize = 64; + bufptr = buffer; + parent = top; + first = NULL; + whitespace = 0; + encoding = ENCODE_UTF8; + + if (cb && parent) + type = (*cb)(parent); + else + type = MXML_TEXT; + + while ((ch = (*getc_cb)(p, &encoding)) != EOF) + { + if ((ch == '<' || + (mxml_isspace(ch) && type != MXML_OPAQUE && type != MXML_CUSTOM)) && + bufptr > buffer) + { + /* + * Add a new value node... + */ + + *bufptr = '\0'; + + switch (type) + { + case MXML_INTEGER : + node = mxmlNewInteger(parent, strtol(buffer, &bufptr, 0)); + break; + + case MXML_OPAQUE : + node = mxmlNewOpaque(parent, buffer); + break; + + case MXML_REAL : + node = mxmlNewReal(parent, strtod(buffer, &bufptr)); + break; + + case MXML_TEXT : + node = mxmlNewText(parent, whitespace, buffer); + break; + + case MXML_CUSTOM : + if (global->custom_load_cb) + { + /* + * Use the callback to fill in the custom data... + */ + + node = mxmlNewCustom(parent, NULL, NULL); + + if ((*global->custom_load_cb)(node, buffer)) + { + mxml_error("Bad custom value '%s' in parent <%s>!", + buffer, parent ? parent->value.element.name : "null"); + mxmlDelete(node); + node = NULL; + } + break; + } + + default : /* Ignore... */ + node = NULL; + break; + } + + if (*bufptr) + { + /* + * Bad integer/real number value... + */ + + mxml_error("Bad %s value '%s' in parent <%s>!", + type == MXML_INTEGER ? "integer" : "real", buffer, + parent ? parent->value.element.name : "null"); + break; + } + + bufptr = buffer; + whitespace = mxml_isspace(ch) && type == MXML_TEXT; + + if (!node && type != MXML_IGNORE) + { + /* + * Print error and return... + */ + + mxml_error("Unable to add value node of type %s to parent <%s>!", + types[type], parent ? parent->value.element.name : "null"); + goto error; + } + + if (sax_cb) + { + (*sax_cb)(node, MXML_SAX_DATA, sax_data); + + if (!mxmlRelease(node)) + node = NULL; + } + + if (!first && node) + first = node; + } + else if (mxml_isspace(ch) && type == MXML_TEXT) + whitespace = 1; + + /* + * Add lone whitespace node if we have an element and existing + * whitespace... + */ + + if (ch == '<' && whitespace && type == MXML_TEXT) + { + if (parent) + { + node = mxmlNewText(parent, whitespace, ""); + + if (sax_cb) + { + (*sax_cb)(node, MXML_SAX_DATA, sax_data); + + if (!mxmlRelease(node)) + node = NULL; + } + + if (!first && node) + first = node; + } + + whitespace = 0; + } + + if (ch == '<') + { + /* + * Start of open/close tag... + */ + + bufptr = buffer; + + while ((ch = (*getc_cb)(p, &encoding)) != EOF) + if (mxml_isspace(ch) || ch == '>' || (ch == '/' && bufptr > buffer)) + break; + else if (ch == '<') + { + mxml_error("Bare < in element!"); + goto error; + } + else if (ch == '&') + { + if ((ch = mxml_get_entity(parent, p, &encoding, getc_cb)) == EOF) + goto error; + + if (mxml_add_char(ch, &bufptr, &buffer, &bufsize)) + goto error; + } + else if (mxml_add_char(ch, &bufptr, &buffer, &bufsize)) + goto error; + else if (((bufptr - buffer) == 1 && buffer[0] == '?') || + ((bufptr - buffer) == 3 && !strncmp(buffer, "!--", 3)) || + ((bufptr - buffer) == 8 && !strncmp(buffer, "![CDATA[", 8))) + break; + + *bufptr = '\0'; + + if (!strcmp(buffer, "!--")) + { + /* + * Gather rest of comment... + */ + + while ((ch = (*getc_cb)(p, &encoding)) != EOF) + { + if (ch == '>' && bufptr > (buffer + 4) && + bufptr[-3] != '-' && bufptr[-2] == '-' && bufptr[-1] == '-') + break; + else if (mxml_add_char(ch, &bufptr, &buffer, &bufsize)) + goto error; + } + + /* + * Error out if we didn't get the whole comment... + */ + + if (ch != '>') + { + /* + * Print error and return... + */ + + mxml_error("Early EOF in comment node!"); + goto error; + } + + + /* + * Otherwise add this as an element under the current parent... + */ + + *bufptr = '\0'; + + if (!parent && first) + { + /* + * There can only be one root element! + */ + + mxml_error("<%s> cannot be a second root node after <%s>", + buffer, first->value.element.name); + goto error; + } + + if ((node = mxmlNewElement(parent, buffer)) == NULL) + { + /* + * Just print error for now... + */ + + mxml_error("Unable to add comment node to parent <%s>!", + parent ? parent->value.element.name : "null"); + break; + } + + if (sax_cb) + { + (*sax_cb)(node, MXML_SAX_COMMENT, sax_data); + + if (!mxmlRelease(node)) + node = NULL; + } + + if (node && !first) + first = node; + } + else if (!strcmp(buffer, "![CDATA[")) + { + /* + * Gather CDATA section... + */ + + while ((ch = (*getc_cb)(p, &encoding)) != EOF) + { + if (ch == '>' && !strncmp(bufptr - 2, "]]", 2)) + break; + else if (mxml_add_char(ch, &bufptr, &buffer, &bufsize)) + goto error; + } + + /* + * Error out if we didn't get the whole comment... + */ + + if (ch != '>') + { + /* + * Print error and return... + */ + + mxml_error("Early EOF in CDATA node!"); + goto error; + } + + + /* + * Otherwise add this as an element under the current parent... + */ + + *bufptr = '\0'; + + if (!parent && first) + { + /* + * There can only be one root element! + */ + + mxml_error("<%s> cannot be a second root node after <%s>", + buffer, first->value.element.name); + goto error; + } + + if ((node = mxmlNewElement(parent, buffer)) == NULL) + { + /* + * Print error and return... + */ + + mxml_error("Unable to add CDATA node to parent <%s>!", + parent ? parent->value.element.name : "null"); + goto error; + } + + if (sax_cb) + { + (*sax_cb)(node, MXML_SAX_CDATA, sax_data); + + if (!mxmlRelease(node)) + node = NULL; + } + + if (node && !first) + first = node; + } + else if (buffer[0] == '?') + { + /* + * Gather rest of processing instruction... + */ + + while ((ch = (*getc_cb)(p, &encoding)) != EOF) + { + if (ch == '>' && bufptr > buffer && bufptr[-1] == '?') + break; + else if (mxml_add_char(ch, &bufptr, &buffer, &bufsize)) + goto error; + } + + /* + * Error out if we didn't get the whole processing instruction... + */ + + if (ch != '>') + { + /* + * Print error and return... + */ + + mxml_error("Early EOF in processing instruction node!"); + goto error; + } + + /* + * Otherwise add this as an element under the current parent... + */ + + *bufptr = '\0'; + + if (!parent && first) + { + /* + * There can only be one root element! + */ + + mxml_error("<%s> cannot be a second root node after <%s>", + buffer, first->value.element.name); + goto error; + } + + if ((node = mxmlNewElement(parent, buffer)) == NULL) + { + /* + * Print error and return... + */ + + mxml_error("Unable to add processing instruction node to parent <%s>!", + parent ? parent->value.element.name : "null"); + goto error; + } + + if (sax_cb) + { + (*sax_cb)(node, MXML_SAX_DIRECTIVE, sax_data); + + if (!mxmlRelease(node)) + node = NULL; + } + + if (node) + { + if (!first) + first = node; + + if (!parent) + { + parent = node; + + if (cb) + type = (*cb)(parent); + } + } + } + else if (buffer[0] == '!') + { + /* + * Gather rest of declaration... + */ + + do + { + if (ch == '>') + break; + else + { + if (ch == '&') + if ((ch = mxml_get_entity(parent, p, &encoding, getc_cb)) == EOF) + goto error; + + if (mxml_add_char(ch, &bufptr, &buffer, &bufsize)) + goto error; + } + } + while ((ch = (*getc_cb)(p, &encoding)) != EOF); + + /* + * Error out if we didn't get the whole declaration... + */ + + if (ch != '>') + { + /* + * Print error and return... + */ + + mxml_error("Early EOF in declaration node!"); + goto error; + } + + /* + * Otherwise add this as an element under the current parent... + */ + + *bufptr = '\0'; + + if (!parent && first) + { + /* + * There can only be one root element! + */ + + mxml_error("<%s> cannot be a second root node after <%s>", + buffer, first->value.element.name); + goto error; + } + + if ((node = mxmlNewElement(parent, buffer)) == NULL) + { + /* + * Print error and return... + */ + + mxml_error("Unable to add declaration node to parent <%s>!", + parent ? parent->value.element.name : "null"); + goto error; + } + + if (sax_cb) + { + (*sax_cb)(node, MXML_SAX_DIRECTIVE, sax_data); + + if (!mxmlRelease(node)) + node = NULL; + } + + if (node) + { + if (!first) + first = node; + + if (!parent) + { + parent = node; + + if (cb) + type = (*cb)(parent); + } + } + } + else if (buffer[0] == '/') + { + /* + * Handle close tag... + */ + + if (!parent || strcmp(buffer + 1, parent->value.element.name)) + { + /* + * Close tag doesn't match tree; print an error for now... + */ + + mxml_error("Mismatched close tag <%s> under parent <%s>!", + buffer, parent ? parent->value.element.name : "(null)"); + goto error; + } + + /* + * Keep reading until we see >... + */ + + while (ch != '>' && ch != EOF) + ch = (*getc_cb)(p, &encoding); + + node = parent; + parent = parent->parent; + + if (sax_cb) + { + (*sax_cb)(node, MXML_SAX_ELEMENT_CLOSE, sax_data); + + if (!mxmlRelease(node) && first == node) + first = NULL; + } + + /* + * Ascend into the parent and set the value type as needed... + */ + + if (cb && parent) + type = (*cb)(parent); + } + else + { + /* + * Handle open tag... + */ + + if (!parent && first) + { + /* + * There can only be one root element! + */ + + mxml_error("<%s> cannot be a second root node after <%s>", + buffer, first->value.element.name); + goto error; + } + + if ((node = mxmlNewElement(parent, buffer)) == NULL) + { + /* + * Just print error for now... + */ + + mxml_error("Unable to add element node to parent <%s>!", + parent ? parent->value.element.name : "null"); + goto error; + } + + if (mxml_isspace(ch)) + { + if ((ch = mxml_parse_element(node, p, &encoding, getc_cb)) == EOF) + goto error; + } + else if (ch == '/') + { + if ((ch = (*getc_cb)(p, &encoding)) != '>') + { + mxml_error("Expected > but got '%c' instead for element <%s/>!", + ch, buffer); + mxmlDelete(node); + goto error; + } + + ch = '/'; + } + + if (sax_cb) + (*sax_cb)(node, MXML_SAX_ELEMENT_OPEN, sax_data); + + if (!first) + first = node; + + if (ch == EOF) + break; + + if (ch != '/') + { + /* + * Descend into this node, setting the value type as needed... + */ + + parent = node; + + if (cb && parent) + type = (*cb)(parent); + } + else if (sax_cb) + { + (*sax_cb)(node, MXML_SAX_ELEMENT_CLOSE, sax_data); + + if (!mxmlRelease(node) && first == node) + first = NULL; + } + } + + bufptr = buffer; + } + else if (ch == '&') + { + /* + * Add character entity to current buffer... + */ + + if ((ch = mxml_get_entity(parent, p, &encoding, getc_cb)) == EOF) + goto error; + + if (mxml_add_char(ch, &bufptr, &buffer, &bufsize)) + goto error; + } + else if (type == MXML_OPAQUE || type == MXML_CUSTOM || !mxml_isspace(ch)) + { + /* + * Add character to current buffer... + */ + + if (mxml_add_char(ch, &bufptr, &buffer, &bufsize)) + goto error; + } + } + + /* + * Free the string buffer - we don't need it anymore... + */ + + free(buffer); + + /* + * Find the top element and return it... + */ + + if (parent) + { + node = parent; + + while (parent->parent != top && parent->parent) + parent = parent->parent; + + if (node != parent) + { + mxml_error("Missing close tag under parent <%s>!", + node->value.element.name, + node->parent ? node->parent->value.element.name : "(null)"); + + mxmlDelete(first); + + return (NULL); + } + } + + if (parent) + return (parent); + else + return (first); + + /* + * Common error return... + */ + +error: + + mxmlDelete(first); + + free(buffer); + + return (NULL); +} + + +/* + * 'mxml_parse_element()' - Parse an element for any attributes... + */ + +static int /* O - Terminating character */ +mxml_parse_element( + mxml_node_t *node, /* I - Element node */ + void *p, /* I - Data to read from */ + int *encoding, /* IO - Encoding */ + _mxml_getc_cb_t getc_cb) /* I - Data callback */ +{ + int ch, /* Current character in file */ + quote; /* Quoting character */ + char *name, /* Attribute name */ + *value, /* Attribute value */ + *ptr; /* Pointer into name/value */ + int namesize, /* Size of name string */ + valsize; /* Size of value string */ + + + /* + * Initialize the name and value buffers... + */ + + if ((name = malloc(64)) == NULL) + { + mxml_error("Unable to allocate memory for name!"); + return (EOF); + } + + namesize = 64; + + if ((value = malloc(64)) == NULL) + { + free(name); + mxml_error("Unable to allocate memory for value!"); + return (EOF); + } + + valsize = 64; + + /* + * Loop until we hit a >, /, ?, or EOF... + */ + + while ((ch = (*getc_cb)(p, encoding)) != EOF) + { +#if DEBUG > 1 + fprintf(stderr, "parse_element: ch='%c'\n", ch); +#endif /* DEBUG > 1 */ + + /* + * Skip leading whitespace... + */ + + if (mxml_isspace(ch)) + continue; + + /* + * Stop at /, ?, or >... + */ + + if (ch == '/' || ch == '?') + { + /* + * Grab the > character and print an error if it isn't there... + */ + + quote = (*getc_cb)(p, encoding); + + if (quote != '>') + { + mxml_error("Expected '>' after '%c' for element %s, but got '%c'!", + ch, node->value.element.name, quote); + goto error; + } + + break; + } + else if (ch == '<') + { + mxml_error("Bare < in element %s!", node->value.element.name); + goto error; + } + else if (ch == '>') + break; + + /* + * Read the attribute name... + */ + + name[0] = ch; + ptr = name + 1; + + if (ch == '\"' || ch == '\'') + { + /* + * Name is in quotes, so get a quoted string... + */ + + quote = ch; + + while ((ch = (*getc_cb)(p, encoding)) != EOF) + { + if (ch == '&') + if ((ch = mxml_get_entity(node, p, encoding, getc_cb)) == EOF) + goto error; + + if (mxml_add_char(ch, &ptr, &name, &namesize)) + goto error; + + if (ch == quote) + break; + } + } + else + { + /* + * Grab an normal, non-quoted name... + */ + + while ((ch = (*getc_cb)(p, encoding)) != EOF) + if (mxml_isspace(ch) || ch == '=' || ch == '/' || ch == '>' || + ch == '?') + break; + else + { + if (ch == '&') + if ((ch = mxml_get_entity(node, p, encoding, getc_cb)) == EOF) + goto error; + + if (mxml_add_char(ch, &ptr, &name, &namesize)) + goto error; + } + } + + *ptr = '\0'; + + if (mxmlElementGetAttr(node, name)) + goto error; + + while (ch != EOF && mxml_isspace(ch)) + ch = (*getc_cb)(p, encoding); + + if (ch == '=') + { + /* + * Read the attribute value... + */ + + while ((ch = (*getc_cb)(p, encoding)) != EOF && mxml_isspace(ch)); + + if (ch == EOF) + { + mxml_error("Missing value for attribute '%s' in element %s!", + name, node->value.element.name); + goto error; + } + + if (ch == '\'' || ch == '\"') + { + /* + * Read quoted value... + */ + + quote = ch; + ptr = value; + + while ((ch = (*getc_cb)(p, encoding)) != EOF) + if (ch == quote) + break; + else + { + if (ch == '&') + if ((ch = mxml_get_entity(node, p, encoding, getc_cb)) == EOF) + goto error; + + if (mxml_add_char(ch, &ptr, &value, &valsize)) + goto error; + } + + *ptr = '\0'; + } + else + { + /* + * Read unquoted value... + */ + + value[0] = ch; + ptr = value + 1; + + while ((ch = (*getc_cb)(p, encoding)) != EOF) + if (mxml_isspace(ch) || ch == '=' || ch == '/' || ch == '>') + break; + else + { + if (ch == '&') + if ((ch = mxml_get_entity(node, p, encoding, getc_cb)) == EOF) + goto error; + + if (mxml_add_char(ch, &ptr, &value, &valsize)) + goto error; + } + + *ptr = '\0'; + } + + /* + * Set the attribute with the given string value... + */ + + mxmlElementSetAttr(node, name, value); + } + else + { + mxml_error("Missing value for attribute '%s' in element %s!", + name, node->value.element.name); + goto error; + } + + /* + * Check the end character... + */ + + if (ch == '/' || ch == '?') + { + /* + * Grab the > character and print an error if it isn't there... + */ + + quote = (*getc_cb)(p, encoding); + + if (quote != '>') + { + mxml_error("Expected '>' after '%c' for element %s, but got '%c'!", + ch, node->value.element.name, quote); + ch = EOF; + } + + break; + } + else if (ch == '>') + break; + } + + /* + * Free the name and value buffers and return... + */ + + free(name); + free(value); + + return (ch); + + /* + * Common error return point... + */ + +error: + + free(name); + free(value); + + return (EOF); +} + + +/* + * 'mxml_string_getc()' - Get a character from a string. + */ + +static int /* O - Character or EOF */ +mxml_string_getc(void *p, /* I - Pointer to file */ + int *encoding) /* IO - Encoding */ +{ + int ch; /* Character */ + const char **s; /* Pointer to string pointer */ + + + s = (const char **)p; + + if ((ch = (*s)[0] & 255) != 0 || *encoding == ENCODE_UTF16LE) + { + /* + * Got character; convert UTF-8 to integer and return... + */ + + (*s)++; + + switch (*encoding) + { + case ENCODE_UTF8 : + if (!(ch & 0x80)) + { +#if DEBUG > 1 + printf("mxml_string_getc: %c (0x%04x)\n", ch < ' ' ? '.' : ch, ch); +#endif /* DEBUG > 1 */ + + if (mxml_bad_char(ch)) + { + mxml_error("Bad control character 0x%02x not allowed by XML standard!", + ch); + return (EOF); + } + + return (ch); + } + else if (ch == 0xfe) + { + /* + * UTF-16 big-endian BOM? + */ + + if (((*s)[0] & 255) != 0xff) + return (EOF); + + *encoding = ENCODE_UTF16BE; + (*s)++; + + return (mxml_string_getc(p, encoding)); + } + else if (ch == 0xff) + { + /* + * UTF-16 little-endian BOM? + */ + + if (((*s)[0] & 255) != 0xfe) + return (EOF); + + *encoding = ENCODE_UTF16LE; + (*s)++; + + return (mxml_string_getc(p, encoding)); + } + else if ((ch & 0xe0) == 0xc0) + { + /* + * Two-byte value... + */ + + if (((*s)[0] & 0xc0) != 0x80) + return (EOF); + + ch = ((ch & 0x1f) << 6) | ((*s)[0] & 0x3f); + + (*s)++; + + if (ch < 0x80) + { + mxml_error("Invalid UTF-8 sequence for character 0x%04x!", ch); + return (EOF); + } + +#if DEBUG > 1 + printf("mxml_string_getc: %c (0x%04x)\n", ch < ' ' ? '.' : ch, ch); +#endif /* DEBUG > 1 */ + + return (ch); + } + else if ((ch & 0xf0) == 0xe0) + { + /* + * Three-byte value... + */ + + if (((*s)[0] & 0xc0) != 0x80 || + ((*s)[1] & 0xc0) != 0x80) + return (EOF); + + ch = ((((ch & 0x0f) << 6) | ((*s)[0] & 0x3f)) << 6) | ((*s)[1] & 0x3f); + + (*s) += 2; + + if (ch < 0x800) + { + mxml_error("Invalid UTF-8 sequence for character 0x%04x!", ch); + return (EOF); + } + + /* + * Ignore (strip) Byte Order Mark (BOM)... + */ + + if (ch == 0xfeff) + return (mxml_string_getc(p, encoding)); + +#if DEBUG > 1 + printf("mxml_string_getc: %c (0x%04x)\n", ch < ' ' ? '.' : ch, ch); +#endif /* DEBUG > 1 */ + + return (ch); + } + else if ((ch & 0xf8) == 0xf0) + { + /* + * Four-byte value... + */ + + if (((*s)[0] & 0xc0) != 0x80 || + ((*s)[1] & 0xc0) != 0x80 || + ((*s)[2] & 0xc0) != 0x80) + return (EOF); + + ch = ((((((ch & 0x07) << 6) | ((*s)[0] & 0x3f)) << 6) | + ((*s)[1] & 0x3f)) << 6) | ((*s)[2] & 0x3f); + + (*s) += 3; + + if (ch < 0x10000) + { + mxml_error("Invalid UTF-8 sequence for character 0x%04x!", ch); + return (EOF); + } + +#if DEBUG > 1 + printf("mxml_string_getc: %c (0x%04x)\n", ch < ' ' ? '.' : ch, ch); +#endif /* DEBUG > 1 */ + + return (ch); + } + else + return (EOF); + + case ENCODE_UTF16BE : + /* + * Read UTF-16 big-endian char... + */ + + ch = (ch << 8) | ((*s)[0] & 255); + (*s) ++; + + if (mxml_bad_char(ch)) + { + mxml_error("Bad control character 0x%02x not allowed by XML standard!", + ch); + return (EOF); + } + else if (ch >= 0xd800 && ch <= 0xdbff) + { + /* + * Multi-word UTF-16 char... + */ + + int lch; /* Lower word */ + + + if (!(*s)[0]) + return (EOF); + + lch = (((*s)[0] & 255) << 8) | ((*s)[1] & 255); + (*s) += 2; + + if (lch < 0xdc00 || lch >= 0xdfff) + return (EOF); + + ch = (((ch & 0x3ff) << 10) | (lch & 0x3ff)) + 0x10000; + } + +#if DEBUG > 1 + printf("mxml_string_getc: %c (0x%04x)\n", ch < ' ' ? '.' : ch, ch); +#endif /* DEBUG > 1 */ + + return (ch); + + case ENCODE_UTF16LE : + /* + * Read UTF-16 little-endian char... + */ + + ch = ch | (((*s)[0] & 255) << 8); + + if (!ch) + { + (*s) --; + return (EOF); + } + + (*s) ++; + + if (mxml_bad_char(ch)) + { + mxml_error("Bad control character 0x%02x not allowed by XML standard!", + ch); + return (EOF); + } + else if (ch >= 0xd800 && ch <= 0xdbff) + { + /* + * Multi-word UTF-16 char... + */ + + int lch; /* Lower word */ + + + if (!(*s)[1]) + return (EOF); + + lch = (((*s)[1] & 255) << 8) | ((*s)[0] & 255); + (*s) += 2; + + if (lch < 0xdc00 || lch >= 0xdfff) + return (EOF); + + ch = (((ch & 0x3ff) << 10) | (lch & 0x3ff)) + 0x10000; + } + +#if DEBUG > 1 + printf("mxml_string_getc: %c (0x%04x)\n", ch < ' ' ? '.' : ch, ch); +#endif /* DEBUG > 1 */ + + return (ch); + } + } + + return (EOF); +} + + +/* + * 'mxml_string_putc()' - Write a character to a string. + */ + +static int /* O - 0 on success, -1 on failure */ +mxml_string_putc(int ch, /* I - Character to write */ + void *p) /* I - Pointer to string pointers */ +{ + char **pp; /* Pointer to string pointers */ + + + pp = (char **)p; + + if (pp[0] < pp[1]) + pp[0][0] = ch; + + pp[0] ++; + + return (0); +} + + +/* + * 'mxml_write_name()' - Write a name string. + */ + +static int /* O - 0 on success, -1 on failure */ +mxml_write_name(const char *s, /* I - Name to write */ + void *p, /* I - Write pointer */ + int (*putc_cb)(int, void *)) + /* I - Write callback */ +{ + char quote; /* Quote character */ + const char *name; /* Entity name */ + + + if (*s == '\"' || *s == '\'') + { + /* + * Write a quoted name string... + */ + + if ((*putc_cb)(*s, p) < 0) + return (-1); + + quote = *s++; + + while (*s && *s != quote) + { + if ((name = mxmlEntityGetName(*s)) != NULL) + { + if ((*putc_cb)('&', p) < 0) + return (-1); + + while (*name) + { + if ((*putc_cb)(*name, p) < 0) + return (-1); + + name ++; + } + + if ((*putc_cb)(';', p) < 0) + return (-1); + } + else if ((*putc_cb)(*s, p) < 0) + return (-1); + + s ++; + } + + /* + * Write the end quote... + */ + + if ((*putc_cb)(quote, p) < 0) + return (-1); + } + else + { + /* + * Write a non-quoted name string... + */ + + while (*s) + { + if ((*putc_cb)(*s, p) < 0) + return (-1); + + s ++; + } + } + + return (0); +} + + +/* + * 'mxml_write_node()' - Save an XML node to a file. + */ + +static int /* O - Column or -1 on error */ +mxml_write_node(mxml_node_t *node, /* I - Node to write */ + void *p, /* I - File to write to */ + mxml_save_cb_t cb, /* I - Whitespace callback */ + int col, /* I - Current column */ + _mxml_putc_cb_t putc_cb,/* I - Output callback */ + _mxml_global_t *global)/* I - Global data */ +{ + int i, /* Looping var */ + width; /* Width of attr + value */ + mxml_attr_t *attr; /* Current attribute */ + char s[255]; /* Temporary string */ + + + /* + * Print the node value... + */ + + switch (node->type) + { + case MXML_ELEMENT : + col = mxml_write_ws(node, p, cb, MXML_WS_BEFORE_OPEN, col, putc_cb); + + if ((*putc_cb)('<', p) < 0) + return (-1); + if (node->value.element.name[0] == '?' || + !strncmp(node->value.element.name, "!--", 3) || + !strncmp(node->value.element.name, "![CDATA[", 8)) + { + /* + * Comments, CDATA, and processing instructions do not + * use character entities. + */ + + const char *ptr; /* Pointer into name */ + + + for (ptr = node->value.element.name; *ptr; ptr ++) + if ((*putc_cb)(*ptr, p) < 0) + return (-1); + } + else if (mxml_write_name(node->value.element.name, p, putc_cb) < 0) + return (-1); + + col += strlen(node->value.element.name) + 1; + + for (i = node->value.element.num_attrs, attr = node->value.element.attrs; + i > 0; + i --, attr ++) + { + width = strlen(attr->name); + + if (attr->value) + width += strlen(attr->value) + 3; + + if (global->wrap > 0 && (col + width) > global->wrap) + { + if ((*putc_cb)('\n', p) < 0) + return (-1); + + col = 0; + } + else + { + if ((*putc_cb)(' ', p) < 0) + return (-1); + + col ++; + } + + if (mxml_write_name(attr->name, p, putc_cb) < 0) + return (-1); + + if (attr->value) + { + if ((*putc_cb)('=', p) < 0) + return (-1); + if ((*putc_cb)('\"', p) < 0) + return (-1); + if (mxml_write_string(attr->value, p, putc_cb) < 0) + return (-1); + if ((*putc_cb)('\"', p) < 0) + return (-1); + } + + col += width; + } + + if (node->child) + { + /* + * Write children... + */ + + mxml_node_t *child; /* Current child */ + + + if ((*putc_cb)('>', p) < 0) + return (-1); + else + col ++; + + col = mxml_write_ws(node, p, cb, MXML_WS_AFTER_OPEN, col, putc_cb); + + for (child = node->child; child; child = child->next) + { + if ((col = mxml_write_node(child, p, cb, col, putc_cb, global)) < 0) + return (-1); + } + + /* + * The ? and ! elements are special-cases and have no end tags... + */ + + if (node->value.element.name[0] != '!' && + node->value.element.name[0] != '?') + { + col = mxml_write_ws(node, p, cb, MXML_WS_BEFORE_CLOSE, col, putc_cb); + + if ((*putc_cb)('<', p) < 0) + return (-1); + if ((*putc_cb)('/', p) < 0) + return (-1); + if (mxml_write_string(node->value.element.name, p, putc_cb) < 0) + return (-1); + if ((*putc_cb)('>', p) < 0) + return (-1); + + col += strlen(node->value.element.name) + 3; + + col = mxml_write_ws(node, p, cb, MXML_WS_AFTER_CLOSE, col, putc_cb); + } + } + else if (node->value.element.name[0] == '!' || + node->value.element.name[0] == '?') + { + /* + * The ? and ! elements are special-cases... + */ + + if ((*putc_cb)('>', p) < 0) + return (-1); + else + col ++; + + col = mxml_write_ws(node, p, cb, MXML_WS_AFTER_OPEN, col, putc_cb); + } + else + { + if ((*putc_cb)(' ', p) < 0) + return (-1); + if ((*putc_cb)('/', p) < 0) + return (-1); + if ((*putc_cb)('>', p) < 0) + return (-1); + + col += 3; + + col = mxml_write_ws(node, p, cb, MXML_WS_AFTER_OPEN, col, putc_cb); + } + break; + + case MXML_INTEGER : + if (node->prev) + { + if (global->wrap > 0 && col > global->wrap) + { + if ((*putc_cb)('\n', p) < 0) + return (-1); + + col = 0; + } + else if ((*putc_cb)(' ', p) < 0) + return (-1); + else + col ++; + } + + sprintf(s, "%d", node->value.integer); + if (mxml_write_string(s, p, putc_cb) < 0) + return (-1); + + col += strlen(s); + break; + + case MXML_OPAQUE : + if (mxml_write_string(node->value.opaque, p, putc_cb) < 0) + return (-1); + + col += strlen(node->value.opaque); + break; + + case MXML_REAL : + if (node->prev) + { + if (global->wrap > 0 && col > global->wrap) + { + if ((*putc_cb)('\n', p) < 0) + return (-1); + + col = 0; + } + else if ((*putc_cb)(' ', p) < 0) + return (-1); + else + col ++; + } + + sprintf(s, "%f", node->value.real); + if (mxml_write_string(s, p, putc_cb) < 0) + return (-1); + + col += strlen(s); + break; + + case MXML_TEXT : + if (node->value.text.whitespace && col > 0) + { + if (global->wrap > 0 && col > global->wrap) + { + if ((*putc_cb)('\n', p) < 0) + return (-1); + + col = 0; + } + else if ((*putc_cb)(' ', p) < 0) + return (-1); + else + col ++; + } + + if (mxml_write_string(node->value.text.string, p, putc_cb) < 0) + return (-1); + + col += strlen(node->value.text.string); + break; + + case MXML_CUSTOM : + if (global->custom_save_cb) + { + char *data; /* Custom data string */ + const char *newline; /* Last newline in string */ + + + if ((data = (*global->custom_save_cb)(node)) == NULL) + return (-1); + + if (mxml_write_string(data, p, putc_cb) < 0) + return (-1); + + if ((newline = strrchr(data, '\n')) == NULL) + col += strlen(data); + else + col = strlen(newline); + + free(data); + break; + } + + default : /* Should never happen */ + return (-1); + } + + return (col); +} + + +/* + * 'mxml_write_string()' - Write a string, escaping & and < as needed. + */ + +static int /* O - 0 on success, -1 on failure */ +mxml_write_string( + const char *s, /* I - String to write */ + void *p, /* I - Write pointer */ + _mxml_putc_cb_t putc_cb) /* I - Write callback */ +{ + const char *name; /* Entity name, if any */ + + + while (*s) + { + if ((name = mxmlEntityGetName(*s)) != NULL) + { + if ((*putc_cb)('&', p) < 0) + return (-1); + + while (*name) + { + if ((*putc_cb)(*name, p) < 0) + return (-1); + name ++; + } + + if ((*putc_cb)(';', p) < 0) + return (-1); + } + else if ((*putc_cb)(*s, p) < 0) + return (-1); + + s ++; + } + + return (0); +} + + +/* + * 'mxml_write_ws()' - Do whitespace callback... + */ + +static int /* O - New column */ +mxml_write_ws(mxml_node_t *node, /* I - Current node */ + void *p, /* I - Write pointer */ + mxml_save_cb_t cb, /* I - Callback function */ + int ws, /* I - Where value */ + int col, /* I - Current column */ + _mxml_putc_cb_t putc_cb) /* I - Write callback */ +{ + const char *s; /* Whitespace string */ + + + if (cb && (s = (*cb)(node, ws)) != NULL) + { + while (*s) + { + if ((*putc_cb)(*s, p) < 0) + return (-1); + else if (*s == '\n') + col = 0; + else if (*s == '\t') + { + col += MXML_TAB; + col = col - (col % MXML_TAB); + } + else + col ++; + + s ++; + } + } + + return (col); +} + + +/* + * End of "$Id: mxml-file.c 438 2011-03-24 05:47:51Z mike $". + */ diff --git a/tools/gator/daemon/mxml/mxml-get.c b/tools/gator/daemon/mxml/mxml-get.c new file mode 100644 index 00000000000..a5356d57e18 --- /dev/null +++ b/tools/gator/daemon/mxml/mxml-get.c @@ -0,0 +1,471 @@ +/* + * "$Id: mxml-get.c 427 2011-01-03 02:03:29Z mike $" + * + * Node get functions for Mini-XML, a small XML-like file parsing library. + * + * Copyright 2011 by Michael R Sweet. + * + * These coded instructions, statements, and computer programs are the + * property of Michael R Sweet and are protected by Federal copyright + * law. Distribution and use rights are outlined in the file "COPYING" + * which should have been included with this file. If this file is + * missing or damaged, see the license at: + * + * http://www.minixml.org/ + * + * Contents: + * + * mxmlGetCDATA() - Get the value for a CDATA node. + * mxmlGetCustom() - Get the value for a custom node. + * mxmlGetElement() - Get the name for an element node. + * mxmlGetFirstChild() - Get the first child of an element node. + * mxmlGetInteger() - Get the integer value from the specified node or its + * first child. + * mxmlGetLastChild() - Get the last child of an element node. + * mxmlGetNextSibling() - Get the next node for the current parent. + * mxmlGetOpaque() - Get an opaque string value for a node or its first + * child. + * mxmlGetParent() - Get the parent node. + * mxmlGetPrevSibling() - Get the previous node for the current parent. + * mxmlGetReal() - Get the real value for a node or its first child. + * mxmlGetText() - Get the text value for a node or its first child. + * mxmlGetType() - Get the node type. + * mxmlGetUserData() - Get the user data pointer for a node. + */ + +/* + * Include necessary headers... + */ + +#include "config.h" +#include "mxml.h" + + +/* + * 'mxmlGetCDATA()' - Get the value for a CDATA node. + * + * @code NULL@ is returned if the node is not a CDATA element. + * + * @since Mini-XML 2.7@ + */ + +const char * /* O - CDATA value or NULL */ +mxmlGetCDATA(mxml_node_t *node) /* I - Node to get */ +{ + /* + * Range check input... + */ + + if (!node || node->type != MXML_ELEMENT || + strncmp(node->value.element.name, "![CDATA[", 8)) + return (NULL); + + /* + * Return the text following the CDATA declaration... + */ + + return (node->value.element.name + 8); +} + + +/* + * 'mxmlGetCustom()' - Get the value for a custom node. + * + * @code NULL@ is returned if the node (or its first child) is not a custom + * value node. + * + * @since Mini-XML 2.7@ + */ + +const void * /* O - Custom value or NULL */ +mxmlGetCustom(mxml_node_t *node) /* I - Node to get */ +{ + /* + * Range check input... + */ + + if (!node) + return (NULL); + + /* + * Return the integer value... + */ + + if (node->type == MXML_CUSTOM) + return (node->value.custom.data); + else if (node->type == MXML_ELEMENT && + node->child && + node->child->type == MXML_CUSTOM) + return (node->child->value.custom.data); + else + return (NULL); +} + + +/* + * 'mxmlGetElement()' - Get the name for an element node. + * + * @code NULL@ is returned if the node is not an element node. + * + * @since Mini-XML 2.7@ + */ + +const char * /* O - Element name or NULL */ +mxmlGetElement(mxml_node_t *node) /* I - Node to get */ +{ + /* + * Range check input... + */ + + if (!node || node->type != MXML_ELEMENT) + return (NULL); + + /* + * Return the element name... + */ + + return (node->value.element.name); +} + + +/* + * 'mxmlGetFirstChild()' - Get the first child of an element node. + * + * @code NULL@ is returned if the node is not an element node or if the node + * has no children. + * + * @since Mini-XML 2.7@ + */ + +mxml_node_t * /* O - First child or NULL */ +mxmlGetFirstChild(mxml_node_t *node) /* I - Node to get */ +{ + /* + * Range check input... + */ + + if (!node || node->type != MXML_ELEMENT) + return (NULL); + + /* + * Return the first child node... + */ + + return (node->child); +} + + +/* + * 'mxmlGetInteger()' - Get the integer value from the specified node or its + * first child. + * + * 0 is returned if the node (or its first child) is not an integer value node. + * + * @since Mini-XML 2.7@ + */ + +int /* O - Integer value or 0 */ +mxmlGetInteger(mxml_node_t *node) /* I - Node to get */ +{ + /* + * Range check input... + */ + + if (!node) + return (0); + + /* + * Return the integer value... + */ + + if (node->type == MXML_INTEGER) + return (node->value.integer); + else if (node->type == MXML_ELEMENT && + node->child && + node->child->type == MXML_INTEGER) + return (node->child->value.integer); + else + return (0); +} + + +/* + * 'mxmlGetLastChild()' - Get the last child of an element node. + * + * @code NULL@ is returned if the node is not an element node or if the node + * has no children. + * + * @since Mini-XML 2.7@ + */ + +mxml_node_t * /* O - Last child or NULL */ +mxmlGetLastChild(mxml_node_t *node) /* I - Node to get */ +{ + /* + * Range check input... + */ + + if (!node || node->type != MXML_ELEMENT) + return (NULL); + + /* + * Return the node type... + */ + + return (node->last_child); +} + + +/* + * 'mxmlGetNextSibling()' - Get the next node for the current parent. + * + * @code NULL@ is returned if this is the last child for the current parent. + * + * @since Mini-XML 2.7@ + */ + +mxml_node_t * +mxmlGetNextSibling(mxml_node_t *node) /* I - Node to get */ +{ + /* + * Range check input... + */ + + if (!node) + return (NULL); + + /* + * Return the node type... + */ + + return (node->next); +} + + +/* + * 'mxmlGetOpaque()' - Get an opaque string value for a node or its first child. + * + * @code NULL@ is returned if the node (or its first child) is not an opaque + * value node. + * + * @since Mini-XML 2.7@ + */ + +const char * /* O - Opaque string or NULL */ +mxmlGetOpaque(mxml_node_t *node) /* I - Node to get */ +{ + /* + * Range check input... + */ + + if (!node) + return (NULL); + + /* + * Return the integer value... + */ + + if (node->type == MXML_OPAQUE) + return (node->value.opaque); + else if (node->type == MXML_ELEMENT && + node->child && + node->child->type == MXML_OPAQUE) + return (node->child->value.opaque); + else + return (NULL); +} + + +/* + * 'mxmlGetParent()' - Get the parent node. + * + * @code NULL@ is returned for a root node. + * + * @since Mini-XML 2.7@ + */ + +mxml_node_t * /* O - Parent node or NULL */ +mxmlGetParent(mxml_node_t *node) /* I - Node to get */ +{ + /* + * Range check input... + */ + + if (!node) + return (NULL); + + /* + * Return the node type... + */ + + return (node->parent); +} + + +/* + * 'mxmlGetPrevSibling()' - Get the previous node for the current parent. + * + * @code NULL@ is returned if this is the first child for the current parent. + * + * @since Mini-XML 2.7@ + */ + +mxml_node_t * /* O - Previous node or NULL */ +mxmlGetPrevSibling(mxml_node_t *node) /* I - Node to get */ +{ + /* + * Range check input... + */ + + if (!node) + return (NULL); + + /* + * Return the node type... + */ + + return (node->prev); +} + + +/* + * 'mxmlGetReal()' - Get the real value for a node or its first child. + * + * 0.0 is returned if the node (or its first child) is not a real value node. + * + * @since Mini-XML 2.7@ + */ + +double /* O - Real value or 0.0 */ +mxmlGetReal(mxml_node_t *node) /* I - Node to get */ +{ + /* + * Range check input... + */ + + if (!node) + return (0.0); + + /* + * Return the integer value... + */ + + if (node->type == MXML_REAL) + return (node->value.real); + else if (node->type == MXML_ELEMENT && + node->child && + node->child->type == MXML_REAL) + return (node->child->value.real); + else + return (0.0); +} + + +/* + * 'mxmlGetText()' - Get the text value for a node or its first child. + * + * @code NULL@ is returned if the node (or its first child) is not a text node. + * The "whitespace" argument can be NULL. + * + * @since Mini-XML 2.7@ + */ + +const char * /* O - Text string or NULL */ +mxmlGetText(mxml_node_t *node, /* I - Node to get */ + int *whitespace) /* O - 1 if string is preceded by whitespace, 0 otherwise */ +{ + /* + * Range check input... + */ + + if (!node) + { + if (whitespace) + *whitespace = 0; + + return (NULL); + } + + /* + * Return the integer value... + */ + + if (node->type == MXML_TEXT) + { + if (whitespace) + *whitespace = node->value.text.whitespace; + + return (node->value.text.string); + } + else if (node->type == MXML_ELEMENT && + node->child && + node->child->type == MXML_TEXT) + { + if (whitespace) + *whitespace = node->child->value.text.whitespace; + + return (node->child->value.text.string); + } + else + { + if (whitespace) + *whitespace = 0; + + return (NULL); + } +} + + +/* + * 'mxmlGetType()' - Get the node type. + * + * @code MXML_IGNORE@ is returned if "node" is @code NULL@. + * + * @since Mini-XML 2.7@ + */ + +mxml_type_t /* O - Type of node */ +mxmlGetType(mxml_node_t *node) /* I - Node to get */ +{ + /* + * Range check input... + */ + + if (!node) + return (MXML_IGNORE); + + /* + * Return the node type... + */ + + return (node->type); +} + + +/* + * 'mxmlGetUserData()' - Get the user data pointer for a node. + * + * @since Mini-XML 2.7@ + */ + +void * /* O - User data pointer */ +mxmlGetUserData(mxml_node_t *node) /* I - Node to get */ +{ + /* + * Range check input... + */ + + if (!node) + return (NULL); + + /* + * Return the user data pointer... + */ + + return (node->user_data); +} + + +/* + * End of "$Id: mxml-get.c 427 2011-01-03 02:03:29Z mike $". + */ diff --git a/tools/gator/daemon/mxml/mxml-index.c b/tools/gator/daemon/mxml/mxml-index.c new file mode 100644 index 00000000000..b6efc66f055 --- /dev/null +++ b/tools/gator/daemon/mxml/mxml-index.c @@ -0,0 +1,662 @@ +/* + * "$Id: mxml-index.c 426 2011-01-01 23:42:17Z mike $" + * + * Index support code for Mini-XML, a small XML-like file parsing library. + * + * Copyright 2003-2011 by Michael R Sweet. + * + * These coded instructions, statements, and computer programs are the + * property of Michael R Sweet and are protected by Federal copyright + * law. Distribution and use rights are outlined in the file "COPYING" + * which should have been included with this file. If this file is + * missing or damaged, see the license at: + * + * http://www.minixml.org/ + * + * Contents: + * + */ + +/* + * Include necessary headers... + */ + +#include "config.h" +#include "mxml.h" + + +/* + * Sort functions... + */ + +static int index_compare(mxml_index_t *ind, mxml_node_t *first, + mxml_node_t *second); +static int index_find(mxml_index_t *ind, const char *element, + const char *value, mxml_node_t *node); +static void index_sort(mxml_index_t *ind, int left, int right); + + +/* + * 'mxmlIndexDelete()' - Delete an index. + */ + +void +mxmlIndexDelete(mxml_index_t *ind) /* I - Index to delete */ +{ + /* + * Range check input.. + */ + + if (!ind) + return; + + /* + * Free memory... + */ + + if (ind->attr) + free(ind->attr); + + if (ind->alloc_nodes) + free(ind->nodes); + + free(ind); +} + + +/* + * 'mxmlIndexEnum()' - Return the next node in the index. + * + * Nodes are returned in the sorted order of the index. + */ + +mxml_node_t * /* O - Next node or NULL if there is none */ +mxmlIndexEnum(mxml_index_t *ind) /* I - Index to enumerate */ +{ + /* + * Range check input... + */ + + if (!ind) + return (NULL); + + /* + * Return the next node... + */ + + if (ind->cur_node < ind->num_nodes) + return (ind->nodes[ind->cur_node ++]); + else + return (NULL); +} + + +/* + * 'mxmlIndexFind()' - Find the next matching node. + * + * You should call mxmlIndexReset() prior to using this function for + * the first time with a particular set of "element" and "value" + * strings. Passing NULL for both "element" and "value" is equivalent + * to calling mxmlIndexEnum(). + */ + +mxml_node_t * /* O - Node or NULL if none found */ +mxmlIndexFind(mxml_index_t *ind, /* I - Index to search */ + const char *element, /* I - Element name to find, if any */ + const char *value) /* I - Attribute value, if any */ +{ + int diff, /* Difference between names */ + current, /* Current entity in search */ + first, /* First entity in search */ + last; /* Last entity in search */ + + +#ifdef DEBUG + printf("mxmlIndexFind(ind=%p, element=\"%s\", value=\"%s\")\n", + ind, element ? element : "(null)", value ? value : "(null)"); +#endif /* DEBUG */ + + /* + * Range check input... + */ + + if (!ind || (!ind->attr && value)) + { +#ifdef DEBUG + puts(" returning NULL..."); + printf(" ind->attr=\"%s\"\n", ind->attr ? ind->attr : "(null)"); +#endif /* DEBUG */ + + return (NULL); + } + + /* + * If both element and value are NULL, just enumerate the nodes in the + * index... + */ + + if (!element && !value) + return (mxmlIndexEnum(ind)); + + /* + * If there are no nodes in the index, return NULL... + */ + + if (!ind->num_nodes) + { +#ifdef DEBUG + puts(" returning NULL..."); + puts(" no nodes!"); +#endif /* DEBUG */ + + return (NULL); + } + + /* + * If cur_node == 0, then find the first matching node... + */ + + if (ind->cur_node == 0) + { + /* + * Find the first node using a modified binary search algorithm... + */ + + first = 0; + last = ind->num_nodes - 1; + +#ifdef DEBUG + printf(" find first time, num_nodes=%d...\n", ind->num_nodes); +#endif /* DEBUG */ + + while ((last - first) > 1) + { + current = (first + last) / 2; + +#ifdef DEBUG + printf(" first=%d, last=%d, current=%d\n", first, last, current); +#endif /* DEBUG */ + + if ((diff = index_find(ind, element, value, ind->nodes[current])) == 0) + { + /* + * Found a match, move back to find the first... + */ + +#ifdef DEBUG + puts(" match!"); +#endif /* DEBUG */ + + while (current > 0 && + !index_find(ind, element, value, ind->nodes[current - 1])) + current --; + +#ifdef DEBUG + printf(" returning first match=%d\n", current); +#endif /* DEBUG */ + + /* + * Return the first match and save the index to the next... + */ + + ind->cur_node = current + 1; + + return (ind->nodes[current]); + } + else if (diff < 0) + last = current; + else + first = current; + +#ifdef DEBUG + printf(" diff=%d\n", diff); +#endif /* DEBUG */ + } + + /* + * If we get this far, then we found exactly 0 or 1 matches... + */ + + for (current = first; current <= last; current ++) + if (!index_find(ind, element, value, ind->nodes[current])) + { + /* + * Found exactly one (or possibly two) match... + */ + +#ifdef DEBUG + printf(" returning only match %d...\n", current); +#endif /* DEBUG */ + + ind->cur_node = current + 1; + + return (ind->nodes[current]); + } + + /* + * No matches... + */ + + ind->cur_node = ind->num_nodes; + +#ifdef DEBUG + puts(" returning NULL..."); +#endif /* DEBUG */ + + return (NULL); + } + else if (ind->cur_node < ind->num_nodes && + !index_find(ind, element, value, ind->nodes[ind->cur_node])) + { + /* + * Return the next matching node... + */ + +#ifdef DEBUG + printf(" returning next match %d...\n", ind->cur_node); +#endif /* DEBUG */ + + return (ind->nodes[ind->cur_node ++]); + } + + /* + * If we get this far, then we have no matches... + */ + + ind->cur_node = ind->num_nodes; + +#ifdef DEBUG + puts(" returning NULL..."); +#endif /* DEBUG */ + + return (NULL); +} + + +/* + * 'mxmlIndexGetCount()' - Get the number of nodes in an index. + * + * @since Mini-XML 2.7@ + */ + +int /* I - Number of nodes in index */ +mxmlIndexGetCount(mxml_index_t *ind) /* I - Index of nodes */ +{ + /* + * Range check input... + */ + + if (!ind) + return (0); + + /* + * Return the number of nodes in the index... + */ + + return (ind->num_nodes); +} + + +/* + * 'mxmlIndexNew()' - Create a new index. + * + * The index will contain all nodes that contain the named element and/or + * attribute. If both "element" and "attr" are NULL, then the index will + * contain a sorted list of the elements in the node tree. Nodes are + * sorted by element name and optionally by attribute value if the "attr" + * argument is not NULL. + */ + +mxml_index_t * /* O - New index */ +mxmlIndexNew(mxml_node_t *node, /* I - XML node tree */ + const char *element, /* I - Element to index or NULL for all */ + const char *attr) /* I - Attribute to index or NULL for none */ +{ + mxml_index_t *ind; /* New index */ + mxml_node_t *current, /* Current node in index */ + **temp; /* Temporary node pointer array */ + + + /* + * Range check input... + */ + +#ifdef DEBUG + printf("mxmlIndexNew(node=%p, element=\"%s\", attr=\"%s\")\n", + node, element ? element : "(null)", attr ? attr : "(null)"); +#endif /* DEBUG */ + + if (!node) + return (NULL); + + /* + * Create a new index... + */ + + if ((ind = calloc(1, sizeof(mxml_index_t))) == NULL) + { + mxml_error("Unable to allocate %d bytes for index - %s", + sizeof(mxml_index_t), strerror(errno)); + return (NULL); + } + + if (attr) + ind->attr = strdup(attr); + + if (!element && !attr) + current = node; + else + current = mxmlFindElement(node, node, element, attr, NULL, MXML_DESCEND); + + while (current) + { + if (ind->num_nodes >= ind->alloc_nodes) + { + if (!ind->alloc_nodes) + temp = malloc(64 * sizeof(mxml_node_t *)); + else + temp = realloc(ind->nodes, (ind->alloc_nodes + 64) * sizeof(mxml_node_t *)); + + if (!temp) + { + /* + * Unable to allocate memory for the index, so abort... + */ + + mxml_error("Unable to allocate %d bytes for index: %s", + (ind->alloc_nodes + 64) * sizeof(mxml_node_t *), + strerror(errno)); + + mxmlIndexDelete(ind); + return (NULL); + } + + ind->nodes = temp; + ind->alloc_nodes += 64; + } + + ind->nodes[ind->num_nodes ++] = current; + + current = mxmlFindElement(current, node, element, attr, NULL, MXML_DESCEND); + } + + /* + * Sort nodes based upon the search criteria... + */ + +#ifdef DEBUG + { + int i; /* Looping var */ + + + printf("%d node(s) in index.\n\n", ind->num_nodes); + + if (attr) + { + printf("Node Address Element %s\n", attr); + puts("-------- -------- -------------- ------------------------------"); + + for (i = 0; i < ind->num_nodes; i ++) + printf("%8d %-8p %-14.14s %s\n", i, ind->nodes[i], + ind->nodes[i]->value.element.name, + mxmlElementGetAttr(ind->nodes[i], attr)); + } + else + { + puts("Node Address Element"); + puts("-------- -------- --------------"); + + for (i = 0; i < ind->num_nodes; i ++) + printf("%8d %-8p %s\n", i, ind->nodes[i], + ind->nodes[i]->value.element.name); + } + + putchar('\n'); + } +#endif /* DEBUG */ + + if (ind->num_nodes > 1) + index_sort(ind, 0, ind->num_nodes - 1); + +#ifdef DEBUG + { + int i; /* Looping var */ + + + puts("After sorting:\n"); + + if (attr) + { + printf("Node Address Element %s\n", attr); + puts("-------- -------- -------------- ------------------------------"); + + for (i = 0; i < ind->num_nodes; i ++) + printf("%8d %-8p %-14.14s %s\n", i, ind->nodes[i], + ind->nodes[i]->value.element.name, + mxmlElementGetAttr(ind->nodes[i], attr)); + } + else + { + puts("Node Address Element"); + puts("-------- -------- --------------"); + + for (i = 0; i < ind->num_nodes; i ++) + printf("%8d %-8p %s\n", i, ind->nodes[i], + ind->nodes[i]->value.element.name); + } + + putchar('\n'); + } +#endif /* DEBUG */ + + /* + * Return the new index... + */ + + return (ind); +} + + +/* + * 'mxmlIndexReset()' - Reset the enumeration/find pointer in the index and + * return the first node in the index. + * + * This function should be called prior to using mxmlIndexEnum() or + * mxmlIndexFind() for the first time. + */ + +mxml_node_t * /* O - First node or NULL if there is none */ +mxmlIndexReset(mxml_index_t *ind) /* I - Index to reset */ +{ +#ifdef DEBUG + printf("mxmlIndexReset(ind=%p)\n", ind); +#endif /* DEBUG */ + + /* + * Range check input... + */ + + if (!ind) + return (NULL); + + /* + * Set the index to the first element... + */ + + ind->cur_node = 0; + + /* + * Return the first node... + */ + + if (ind->num_nodes) + return (ind->nodes[0]); + else + return (NULL); +} + + +/* + * 'index_compare()' - Compare two nodes. + */ + +static int /* O - Result of comparison */ +index_compare(mxml_index_t *ind, /* I - Index */ + mxml_node_t *first, /* I - First node */ + mxml_node_t *second) /* I - Second node */ +{ + int diff; /* Difference */ + + + /* + * Check the element name... + */ + + if ((diff = strcmp(first->value.element.name, + second->value.element.name)) != 0) + return (diff); + + /* + * Check the attribute value... + */ + + if (ind->attr) + { + if ((diff = strcmp(mxmlElementGetAttr(first, ind->attr), + mxmlElementGetAttr(second, ind->attr))) != 0) + return (diff); + } + + /* + * No difference, return 0... + */ + + return (0); +} + + +/* + * 'index_find()' - Compare a node with index values. + */ + +static int /* O - Result of comparison */ +index_find(mxml_index_t *ind, /* I - Index */ + const char *element, /* I - Element name or NULL */ + const char *value, /* I - Attribute value or NULL */ + mxml_node_t *node) /* I - Node */ +{ + int diff; /* Difference */ + + + /* + * Check the element name... + */ + + if (element) + { + if ((diff = strcmp(element, node->value.element.name)) != 0) + return (diff); + } + + /* + * Check the attribute value... + */ + + if (value) + { + if ((diff = strcmp(value, mxmlElementGetAttr(node, ind->attr))) != 0) + return (diff); + } + + /* + * No difference, return 0... + */ + + return (0); +} + + +/* + * 'index_sort()' - Sort the nodes in the index... + * + * This function implements the classic quicksort algorithm... + */ + +static void +index_sort(mxml_index_t *ind, /* I - Index to sort */ + int left, /* I - Left node in partition */ + int right) /* I - Right node in partition */ +{ + mxml_node_t *pivot, /* Pivot node */ + *temp; /* Swap node */ + int templ, /* Temporary left node */ + tempr; /* Temporary right node */ + + + /* + * Loop until we have sorted all the way to the right... + */ + + do + { + /* + * Sort the pivot in the current partition... + */ + + pivot = ind->nodes[left]; + + for (templ = left, tempr = right; templ < tempr;) + { + /* + * Move left while left node <= pivot node... + */ + + while ((templ < right) && + index_compare(ind, ind->nodes[templ], pivot) <= 0) + templ ++; + + /* + * Move right while right node > pivot node... + */ + + while ((tempr > left) && + index_compare(ind, ind->nodes[tempr], pivot) > 0) + tempr --; + + /* + * Swap nodes if needed... + */ + + if (templ < tempr) + { + temp = ind->nodes[templ]; + ind->nodes[templ] = ind->nodes[tempr]; + ind->nodes[tempr] = temp; + } + } + + /* + * When we get here, the right (tempr) node is the new position for the + * pivot node... + */ + + if (index_compare(ind, pivot, ind->nodes[tempr]) > 0) + { + ind->nodes[left] = ind->nodes[tempr]; + ind->nodes[tempr] = pivot; + } + + /* + * Recursively sort the left partition as needed... + */ + + if (left < (tempr - 1)) + index_sort(ind, left, tempr - 1); + } + while (right > (left = tempr + 1)); +} + + +/* + * End of "$Id: mxml-index.c 426 2011-01-01 23:42:17Z mike $". + */ diff --git a/tools/gator/daemon/mxml/mxml-node.c b/tools/gator/daemon/mxml/mxml-node.c new file mode 100644 index 00000000000..44af759f9de --- /dev/null +++ b/tools/gator/daemon/mxml/mxml-node.c @@ -0,0 +1,807 @@ +/* + * "$Id: mxml-node.c 436 2011-01-22 01:02:05Z mike $" + * + * Node support code for Mini-XML, a small XML-like file parsing library. + * + * Copyright 2003-2011 by Michael R Sweet. + * + * These coded instructions, statements, and computer programs are the + * property of Michael R Sweet and are protected by Federal copyright + * law. Distribution and use rights are outlined in the file "COPYING" + * which should have been included with this file. If this file is + * missing or damaged, see the license at: + * + * http://www.minixml.org/ + * + * Contents: + * + * mxmlAdd() - Add a node to a tree. + * mxmlDelete() - Delete a node and all of its children. + * mxmlGetRefCount() - Get the current reference (use) count for a node. + * mxmlNewCDATA() - Create a new CDATA node. + * mxmlNewCustom() - Create a new custom data node. + * mxmlNewElement() - Create a new element node. + * mxmlNewInteger() - Create a new integer node. + * mxmlNewOpaque() - Create a new opaque string. + * mxmlNewReal() - Create a new real number node. + * mxmlNewText() - Create a new text fragment node. + * mxmlNewTextf() - Create a new formatted text fragment node. + * mxmlRemove() - Remove a node from its parent. + * mxmlNewXML() - Create a new XML document tree. + * mxmlRelease() - Release a node. + * mxmlRetain() - Retain a node. + * mxml_new() - Create a new node. + */ + +/* + * Include necessary headers... + */ + +#include "config.h" +#include "mxml.h" + + +/* + * Local functions... + */ + +static mxml_node_t *mxml_new(mxml_node_t *parent, mxml_type_t type); + + +/* + * 'mxmlAdd()' - Add a node to a tree. + * + * Adds the specified node to the parent. If the child argument is not + * NULL, puts the new node before or after the specified child depending + * on the value of the where argument. If the child argument is NULL, + * puts the new node at the beginning of the child list (MXML_ADD_BEFORE) + * or at the end of the child list (MXML_ADD_AFTER). The constant + * MXML_ADD_TO_PARENT can be used to specify a NULL child pointer. + */ + +void +mxmlAdd(mxml_node_t *parent, /* I - Parent node */ + int where, /* I - Where to add, MXML_ADD_BEFORE or MXML_ADD_AFTER */ + mxml_node_t *child, /* I - Child node for where or MXML_ADD_TO_PARENT */ + mxml_node_t *node) /* I - Node to add */ +{ +#ifdef DEBUG + fprintf(stderr, "mxmlAdd(parent=%p, where=%d, child=%p, node=%p)\n", parent, + where, child, node); +#endif /* DEBUG */ + + /* + * Range check input... + */ + + if (!parent || !node) + return; + +#if DEBUG > 1 + fprintf(stderr, " BEFORE: node->parent=%p\n", node->parent); + if (parent) + { + fprintf(stderr, " BEFORE: parent->child=%p\n", parent->child); + fprintf(stderr, " BEFORE: parent->last_child=%p\n", parent->last_child); + fprintf(stderr, " BEFORE: parent->prev=%p\n", parent->prev); + fprintf(stderr, " BEFORE: parent->next=%p\n", parent->next); + } +#endif /* DEBUG > 1 */ + + /* + * Remove the node from any existing parent... + */ + + if (node->parent) + mxmlRemove(node); + + /* + * Reset pointers... + */ + + node->parent = parent; + + switch (where) + { + case MXML_ADD_BEFORE : + if (!child || child == parent->child || child->parent != parent) + { + /* + * Insert as first node under parent... + */ + + node->next = parent->child; + + if (parent->child) + parent->child->prev = node; + else + parent->last_child = node; + + parent->child = node; + } + else + { + /* + * Insert node before this child... + */ + + node->next = child; + node->prev = child->prev; + + if (child->prev) + child->prev->next = node; + else + parent->child = node; + + child->prev = node; + } + break; + + case MXML_ADD_AFTER : + if (!child || child == parent->last_child || child->parent != parent) + { + /* + * Insert as last node under parent... + */ + + node->parent = parent; + node->prev = parent->last_child; + + if (parent->last_child) + parent->last_child->next = node; + else + parent->child = node; + + parent->last_child = node; + } + else + { + /* + * Insert node after this child... + */ + + node->prev = child; + node->next = child->next; + + if (child->next) + child->next->prev = node; + else + parent->last_child = node; + + child->next = node; + } + break; + } + +#if DEBUG > 1 + fprintf(stderr, " AFTER: node->parent=%p\n", node->parent); + if (parent) + { + fprintf(stderr, " AFTER: parent->child=%p\n", parent->child); + fprintf(stderr, " AFTER: parent->last_child=%p\n", parent->last_child); + fprintf(stderr, " AFTER: parent->prev=%p\n", parent->prev); + fprintf(stderr, " AFTER: parent->next=%p\n", parent->next); + } +#endif /* DEBUG > 1 */ +} + + +/* + * 'mxmlDelete()' - Delete a node and all of its children. + * + * If the specified node has a parent, this function first removes the + * node from its parent using the mxmlRemove() function. + */ + +void +mxmlDelete(mxml_node_t *node) /* I - Node to delete */ +{ + int i; /* Looping var */ + + +#ifdef DEBUG + fprintf(stderr, "mxmlDelete(node=%p)\n", node); +#endif /* DEBUG */ + + /* + * Range check input... + */ + + if (!node) + return; + + /* + * Remove the node from its parent, if any... + */ + + mxmlRemove(node); + + /* + * Delete children... + */ + + while (node->child) + mxmlDelete(node->child); + + /* + * Now delete any node data... + */ + + switch (node->type) + { + case MXML_ELEMENT : + if (node->value.element.name) + free(node->value.element.name); + + if (node->value.element.num_attrs) + { + for (i = 0; i < node->value.element.num_attrs; i ++) + { + if (node->value.element.attrs[i].name) + free(node->value.element.attrs[i].name); + if (node->value.element.attrs[i].value) + free(node->value.element.attrs[i].value); + } + + free(node->value.element.attrs); + } + break; + case MXML_INTEGER : + /* Nothing to do */ + break; + case MXML_OPAQUE : + if (node->value.opaque) + free(node->value.opaque); + break; + case MXML_REAL : + /* Nothing to do */ + break; + case MXML_TEXT : + if (node->value.text.string) + free(node->value.text.string); + break; + case MXML_CUSTOM : + if (node->value.custom.data && + node->value.custom.destroy) + (*(node->value.custom.destroy))(node->value.custom.data); + break; + default : + break; + } + + /* + * Free this node... + */ + + free(node); +} + + +/* + * 'mxmlGetRefCount()' - Get the current reference (use) count for a node. + * + * The initial reference count of new nodes is 1. Use the @link mxmlRetain@ + * and @link mxmlRelease@ functions to increment and decrement a node's + * reference count. + * + * @since Mini-XML 2.7@. + */ + +int /* O - Reference count */ +mxmlGetRefCount(mxml_node_t *node) /* I - Node */ +{ + /* + * Range check input... + */ + + if (!node) + return (0); + + /* + * Return the reference count... + */ + + return (node->ref_count); +} + + +/* + * 'mxmlNewCDATA()' - Create a new CDATA node. + * + * The new CDATA node is added to the end of the specified parent's child + * list. The constant MXML_NO_PARENT can be used to specify that the new + * CDATA node has no parent. The data string must be nul-terminated and + * is copied into the new node. CDATA nodes use the MXML_ELEMENT type. + * + * @since Mini-XML 2.3@ + */ + +mxml_node_t * /* O - New node */ +mxmlNewCDATA(mxml_node_t *parent, /* I - Parent node or MXML_NO_PARENT */ + const char *data) /* I - Data string */ +{ + mxml_node_t *node; /* New node */ + + +#ifdef DEBUG + fprintf(stderr, "mxmlNewCDATA(parent=%p, data=\"%s\")\n", + parent, data ? data : "(null)"); +#endif /* DEBUG */ + + /* + * Range check input... + */ + + if (!data) + return (NULL); + + /* + * Create the node and set the name value... + */ + + if ((node = mxml_new(parent, MXML_ELEMENT)) != NULL) + node->value.element.name = _mxml_strdupf("![CDATA[%s]]", data); + + return (node); +} + + +/* + * 'mxmlNewCustom()' - Create a new custom data node. + * + * The new custom node is added to the end of the specified parent's child + * list. The constant MXML_NO_PARENT can be used to specify that the new + * element node has no parent. NULL can be passed when the data in the + * node is not dynamically allocated or is separately managed. + * + * @since Mini-XML 2.1@ + */ + +mxml_node_t * /* O - New node */ +mxmlNewCustom( + mxml_node_t *parent, /* I - Parent node or MXML_NO_PARENT */ + void *data, /* I - Pointer to data */ + mxml_custom_destroy_cb_t destroy) /* I - Function to destroy data */ +{ + mxml_node_t *node; /* New node */ + + +#ifdef DEBUG + fprintf(stderr, "mxmlNewCustom(parent=%p, data=%p, destroy=%p)\n", parent, + data, destroy); +#endif /* DEBUG */ + + /* + * Create the node and set the value... + */ + + if ((node = mxml_new(parent, MXML_CUSTOM)) != NULL) + { + node->value.custom.data = data; + node->value.custom.destroy = destroy; + } + + return (node); +} + + +/* + * 'mxmlNewElement()' - Create a new element node. + * + * The new element node is added to the end of the specified parent's child + * list. The constant MXML_NO_PARENT can be used to specify that the new + * element node has no parent. + */ + +mxml_node_t * /* O - New node */ +mxmlNewElement(mxml_node_t *parent, /* I - Parent node or MXML_NO_PARENT */ + const char *name) /* I - Name of element */ +{ + mxml_node_t *node; /* New node */ + + +#ifdef DEBUG + fprintf(stderr, "mxmlNewElement(parent=%p, name=\"%s\")\n", parent, + name ? name : "(null)"); +#endif /* DEBUG */ + + /* + * Range check input... + */ + + if (!name) + return (NULL); + + /* + * Create the node and set the element name... + */ + + if ((node = mxml_new(parent, MXML_ELEMENT)) != NULL) + node->value.element.name = strdup(name); + + return (node); +} + + +/* + * 'mxmlNewInteger()' - Create a new integer node. + * + * The new integer node is added to the end of the specified parent's child + * list. The constant MXML_NO_PARENT can be used to specify that the new + * integer node has no parent. + */ + +mxml_node_t * /* O - New node */ +mxmlNewInteger(mxml_node_t *parent, /* I - Parent node or MXML_NO_PARENT */ + int integer) /* I - Integer value */ +{ + mxml_node_t *node; /* New node */ + + +#ifdef DEBUG + fprintf(stderr, "mxmlNewInteger(parent=%p, integer=%d)\n", parent, integer); +#endif /* DEBUG */ + + /* + * Create the node and set the element name... + */ + + if ((node = mxml_new(parent, MXML_INTEGER)) != NULL) + node->value.integer = integer; + + return (node); +} + + +/* + * 'mxmlNewOpaque()' - Create a new opaque string. + * + * The new opaque node is added to the end of the specified parent's child + * list. The constant MXML_NO_PARENT can be used to specify that the new + * opaque node has no parent. The opaque string must be nul-terminated and + * is copied into the new node. + */ + +mxml_node_t * /* O - New node */ +mxmlNewOpaque(mxml_node_t *parent, /* I - Parent node or MXML_NO_PARENT */ + const char *opaque) /* I - Opaque string */ +{ + mxml_node_t *node; /* New node */ + + +#ifdef DEBUG + fprintf(stderr, "mxmlNewOpaque(parent=%p, opaque=\"%s\")\n", parent, + opaque ? opaque : "(null)"); +#endif /* DEBUG */ + + /* + * Range check input... + */ + + if (!opaque) + return (NULL); + + /* + * Create the node and set the element name... + */ + + if ((node = mxml_new(parent, MXML_OPAQUE)) != NULL) + node->value.opaque = strdup(opaque); + + return (node); +} + + +/* + * 'mxmlNewReal()' - Create a new real number node. + * + * The new real number node is added to the end of the specified parent's + * child list. The constant MXML_NO_PARENT can be used to specify that + * the new real number node has no parent. + */ + +mxml_node_t * /* O - New node */ +mxmlNewReal(mxml_node_t *parent, /* I - Parent node or MXML_NO_PARENT */ + double real) /* I - Real number value */ +{ + mxml_node_t *node; /* New node */ + + +#ifdef DEBUG + fprintf(stderr, "mxmlNewReal(parent=%p, real=%g)\n", parent, real); +#endif /* DEBUG */ + + /* + * Create the node and set the element name... + */ + + if ((node = mxml_new(parent, MXML_REAL)) != NULL) + node->value.real = real; + + return (node); +} + + +/* + * 'mxmlNewText()' - Create a new text fragment node. + * + * The new text node is added to the end of the specified parent's child + * list. The constant MXML_NO_PARENT can be used to specify that the new + * text node has no parent. The whitespace parameter is used to specify + * whether leading whitespace is present before the node. The text + * string must be nul-terminated and is copied into the new node. + */ + +mxml_node_t * /* O - New node */ +mxmlNewText(mxml_node_t *parent, /* I - Parent node or MXML_NO_PARENT */ + int whitespace, /* I - 1 = leading whitespace, 0 = no whitespace */ + const char *string) /* I - String */ +{ + mxml_node_t *node; /* New node */ + + +#ifdef DEBUG + fprintf(stderr, "mxmlNewText(parent=%p, whitespace=%d, string=\"%s\")\n", + parent, whitespace, string ? string : "(null)"); +#endif /* DEBUG */ + + /* + * Range check input... + */ + + if (!string) + return (NULL); + + /* + * Create the node and set the text value... + */ + + if ((node = mxml_new(parent, MXML_TEXT)) != NULL) + { + node->value.text.whitespace = whitespace; + node->value.text.string = strdup(string); + } + + return (node); +} + + +/* + * 'mxmlNewTextf()' - Create a new formatted text fragment node. + * + * The new text node is added to the end of the specified parent's child + * list. The constant MXML_NO_PARENT can be used to specify that the new + * text node has no parent. The whitespace parameter is used to specify + * whether leading whitespace is present before the node. The format + * string must be nul-terminated and is formatted into the new node. + */ + +mxml_node_t * /* O - New node */ +mxmlNewTextf(mxml_node_t *parent, /* I - Parent node or MXML_NO_PARENT */ + int whitespace, /* I - 1 = leading whitespace, 0 = no whitespace */ + const char *format, /* I - Printf-style frmat string */ + ...) /* I - Additional args as needed */ +{ + mxml_node_t *node; /* New node */ + va_list ap; /* Pointer to arguments */ + + +#ifdef DEBUG + fprintf(stderr, "mxmlNewTextf(parent=%p, whitespace=%d, format=\"%s\", ...)\n", + parent, whitespace, format ? format : "(null)"); +#endif /* DEBUG */ + + /* + * Range check input... + */ + + if (!format) + return (NULL); + + /* + * Create the node and set the text value... + */ + + if ((node = mxml_new(parent, MXML_TEXT)) != NULL) + { + va_start(ap, format); + + node->value.text.whitespace = whitespace; + node->value.text.string = _mxml_vstrdupf(format, ap); + + va_end(ap); + } + + return (node); +} + + +/* + * 'mxmlRemove()' - Remove a node from its parent. + * + * Does not free memory used by the node - use mxmlDelete() for that. + * This function does nothing if the node has no parent. + */ + +void +mxmlRemove(mxml_node_t *node) /* I - Node to remove */ +{ +#ifdef DEBUG + fprintf(stderr, "mxmlRemove(node=%p)\n", node); +#endif /* DEBUG */ + + /* + * Range check input... + */ + + if (!node || !node->parent) + return; + + /* + * Remove from parent... + */ + +#if DEBUG > 1 + fprintf(stderr, " BEFORE: node->parent=%p\n", node->parent); + if (node->parent) + { + fprintf(stderr, " BEFORE: node->parent->child=%p\n", node->parent->child); + fprintf(stderr, " BEFORE: node->parent->last_child=%p\n", node->parent->last_child); + } + fprintf(stderr, " BEFORE: node->child=%p\n", node->child); + fprintf(stderr, " BEFORE: node->last_child=%p\n", node->last_child); + fprintf(stderr, " BEFORE: node->prev=%p\n", node->prev); + fprintf(stderr, " BEFORE: node->next=%p\n", node->next); +#endif /* DEBUG > 1 */ + + if (node->prev) + node->prev->next = node->next; + else + node->parent->child = node->next; + + if (node->next) + node->next->prev = node->prev; + else + node->parent->last_child = node->prev; + + node->parent = NULL; + node->prev = NULL; + node->next = NULL; + +#if DEBUG > 1 + fprintf(stderr, " AFTER: node->parent=%p\n", node->parent); + if (node->parent) + { + fprintf(stderr, " AFTER: node->parent->child=%p\n", node->parent->child); + fprintf(stderr, " AFTER: node->parent->last_child=%p\n", node->parent->last_child); + } + fprintf(stderr, " AFTER: node->child=%p\n", node->child); + fprintf(stderr, " AFTER: node->last_child=%p\n", node->last_child); + fprintf(stderr, " AFTER: node->prev=%p\n", node->prev); + fprintf(stderr, " AFTER: node->next=%p\n", node->next); +#endif /* DEBUG > 1 */ +} + + +/* + * 'mxmlNewXML()' - Create a new XML document tree. + * + * The "version" argument specifies the version number to put in the + * ?xml element node. If NULL, version 1.0 is assumed. + * + * @since Mini-XML 2.3@ + */ + +mxml_node_t * /* O - New ?xml node */ +mxmlNewXML(const char *version) /* I - Version number to use */ +{ + char element[1024]; /* Element text */ + + + snprintf(element, sizeof(element), "?xml version=\"%s\" encoding=\"utf-8\"?", + version ? version : "1.0"); + + return (mxmlNewElement(NULL, element)); +} + + +/* + * 'mxmlRelease()' - Release a node. + * + * When the reference count reaches zero, the node (and any children) + * is deleted via mxmlDelete(). + * + * @since Mini-XML 2.3@ + */ + +int /* O - New reference count */ +mxmlRelease(mxml_node_t *node) /* I - Node */ +{ + if (node) + { + if ((-- node->ref_count) <= 0) + { + mxmlDelete(node); + return (0); + } + else + return (node->ref_count); + } + else + return (-1); +} + + +/* + * 'mxmlRetain()' - Retain a node. + * + * @since Mini-XML 2.3@ + */ + +int /* O - New reference count */ +mxmlRetain(mxml_node_t *node) /* I - Node */ +{ + if (node) + return (++ node->ref_count); + else + return (-1); +} + + +/* + * 'mxml_new()' - Create a new node. + */ + +static mxml_node_t * /* O - New node */ +mxml_new(mxml_node_t *parent, /* I - Parent node */ + mxml_type_t type) /* I - Node type */ +{ + mxml_node_t *node; /* New node */ + + +#if DEBUG > 1 + fprintf(stderr, "mxml_new(parent=%p, type=%d)\n", parent, type); +#endif /* DEBUG > 1 */ + + /* + * Allocate memory for the node... + */ + + if ((node = calloc(1, sizeof(mxml_node_t))) == NULL) + { +#if DEBUG > 1 + fputs(" returning NULL\n", stderr); +#endif /* DEBUG > 1 */ + + return (NULL); + } + +#if DEBUG > 1 + fprintf(stderr, " returning %p\n", node); +#endif /* DEBUG > 1 */ + + /* + * Set the node type... + */ + + node->type = type; + node->ref_count = 1; + + /* + * Add to the parent if present... + */ + + if (parent) + mxmlAdd(parent, MXML_ADD_AFTER, MXML_ADD_TO_PARENT, node); + + /* + * Return the new node... + */ + + return (node); +} + + +/* + * End of "$Id: mxml-node.c 436 2011-01-22 01:02:05Z mike $". + */ diff --git a/tools/gator/daemon/mxml/mxml-private.c b/tools/gator/daemon/mxml/mxml-private.c new file mode 100644 index 00000000000..72f3e2320c7 --- /dev/null +++ b/tools/gator/daemon/mxml/mxml-private.c @@ -0,0 +1,331 @@ +/* + * "$Id: mxml-private.c 422 2010-11-07 22:55:11Z mike $" + * + * Private functions for Mini-XML, a small XML-like file parsing library. + * + * Copyright 2003-2010 by Michael R Sweet. + * + * These coded instructions, statements, and computer programs are the + * property of Michael R Sweet and are protected by Federal copyright + * law. Distribution and use rights are outlined in the file "COPYING" + * which should have been included with this file. If this file is + * missing or damaged, see the license at: + * + * http://www.minixml.org/ + * + * Contents: + * + * mxml_error() - Display an error message. + * mxml_integer_cb() - Default callback for integer values. + * mxml_opaque_cb() - Default callback for opaque values. + * mxml_real_cb() - Default callback for real number values. + * _mxml_global() - Get global data. + */ + +/* + * Include necessary headers... + */ + +#include "mxml-private.h" + + +/* + * Some crazy people think that unloading a shared object is a good or safe + * thing to do. Unfortunately, most objects are simply *not* safe to unload + * and bad things *will* happen. + * + * The following mess of conditional code allows us to provide a destructor + * function in Mini-XML for our thread-global storage so that it can possibly + * be unloaded safely, although since there is no standard way to do so I + * can't even provide any guarantees that you can do it safely on all platforms. + * + * This code currently supports AIX, HP-UX, Linux, Mac OS X, Solaris, and + * Windows. It might work on the BSDs and IRIX, but I haven't tested that. + */ + +#if defined(__sun) || defined(_AIX) +# pragma fini(_mxml_fini) +# define _MXML_FINI _mxml_fini +#elif defined(__hpux) +# pragma FINI _mxml_fini +# define _MXML_FINI _mxml_fini +#elif defined(__GNUC__) /* Linux and Mac OS X */ +# define _MXML_FINI __attribute((destructor)) _mxml_fini +#else +# define _MXML_FINI _fini +#endif /* __sun */ + + +/* + * 'mxml_error()' - Display an error message. + */ + +void +mxml_error(const char *format, /* I - Printf-style format string */ + ...) /* I - Additional arguments as needed */ +{ + va_list ap; /* Pointer to arguments */ + char s[1024]; /* Message string */ + _mxml_global_t *global = _mxml_global(); + /* Global data */ + + + /* + * Range check input... + */ + + if (!format) + return; + + /* + * Format the error message string... + */ + + va_start(ap, format); + + vsnprintf(s, sizeof(s), format, ap); + + va_end(ap); + + /* + * And then display the error message... + */ + + if (global->error_cb) + (*global->error_cb)(s); + else + fprintf(stderr, "mxml: %s\n", s); +} + + +/* + * 'mxml_ignore_cb()' - Default callback for ignored values. + */ + +mxml_type_t /* O - Node type */ +mxml_ignore_cb(mxml_node_t *node) /* I - Current node */ +{ + (void)node; + + return (MXML_IGNORE); +} + + +/* + * 'mxml_integer_cb()' - Default callback for integer values. + */ + +mxml_type_t /* O - Node type */ +mxml_integer_cb(mxml_node_t *node) /* I - Current node */ +{ + (void)node; + + return (MXML_INTEGER); +} + + +/* + * 'mxml_opaque_cb()' - Default callback for opaque values. + */ + +mxml_type_t /* O - Node type */ +mxml_opaque_cb(mxml_node_t *node) /* I - Current node */ +{ + (void)node; + + return (MXML_OPAQUE); +} + + +/* + * 'mxml_real_cb()' - Default callback for real number values. + */ + +mxml_type_t /* O - Node type */ +mxml_real_cb(mxml_node_t *node) /* I - Current node */ +{ + (void)node; + + return (MXML_REAL); +} + + +#ifdef HAVE_PTHREAD_H /**** POSIX threading ****/ +# include + +static pthread_key_t _mxml_key = -1; /* Thread local storage key */ +static pthread_once_t _mxml_key_once = PTHREAD_ONCE_INIT; + /* One-time initialization object */ +static void _mxml_init(void); +static void _mxml_destructor(void *g); + + +/* + * '_mxml_destructor()' - Free memory used for globals... + */ + +static void +_mxml_destructor(void *g) /* I - Global data */ +{ + free(g); +} + + +/* + * '_mxml_fini()' - Clean up when unloaded. + */ + +static void +_MXML_FINI(void) +{ + _mxml_global_t *global; /* Global data */ + + + if (_mxml_key != -1) + { + if ((global = (_mxml_global_t *)pthread_getspecific(_mxml_key)) != NULL) + _mxml_destructor(global); + + pthread_key_delete(_mxml_key); + _mxml_key = -1; + } +} + + +/* + * '_mxml_global()' - Get global data. + */ + +_mxml_global_t * /* O - Global data */ +_mxml_global(void) +{ + _mxml_global_t *global; /* Global data */ + + + pthread_once(&_mxml_key_once, _mxml_init); + + if ((global = (_mxml_global_t *)pthread_getspecific(_mxml_key)) == NULL) + { + global = (_mxml_global_t *)calloc(1, sizeof(_mxml_global_t)); + pthread_setspecific(_mxml_key, global); + + global->num_entity_cbs = 1; + global->entity_cbs[0] = _mxml_entity_cb; + global->wrap = 72; + } + + return (global); +} + + +/* + * '_mxml_init()' - Initialize global data... + */ + +static void +_mxml_init(void) +{ + pthread_key_create(&_mxml_key, _mxml_destructor); +} + + +#elif defined(WIN32) && defined(MXML1_EXPORTS) /**** WIN32 threading ****/ +# include + +static DWORD _mxml_tls_index; /* Index for global storage */ + + +/* + * 'DllMain()' - Main entry for library. + */ + +BOOL WINAPI /* O - Success/failure */ +DllMain(HINSTANCE hinst, /* I - DLL module handle */ + DWORD reason, /* I - Reason */ + LPVOID reserved) /* I - Unused */ +{ + _mxml_global_t *global; /* Global data */ + + + (void)hinst; + (void)reserved; + + switch (reason) + { + case DLL_PROCESS_ATTACH : /* Called on library initialization */ + if ((_mxml_tls_index = TlsAlloc()) == TLS_OUT_OF_INDEXES) + return (FALSE); + break; + + case DLL_THREAD_DETACH : /* Called when a thread terminates */ + if ((global = (_mxml_global_t *)TlsGetValue(_mxml_tls_index)) != NULL) + free(global); + break; + + case DLL_PROCESS_DETACH : /* Called when library is unloaded */ + if ((global = (_mxml_global_t *)TlsGetValue(_mxml_tls_index)) != NULL) + free(global); + + TlsFree(_mxml_tls_index); + break; + + default: + break; + } + + return (TRUE); +} + + +/* + * '_mxml_global()' - Get global data. + */ + +_mxml_global_t * /* O - Global data */ +_mxml_global(void) +{ + _mxml_global_t *global; /* Global data */ + + + if ((global = (_mxml_global_t *)TlsGetValue(_mxml_tls_index)) == NULL) + { + global = (_mxml_global_t *)calloc(1, sizeof(_mxml_global_t)); + + global->num_entity_cbs = 1; + global->entity_cbs[0] = _mxml_entity_cb; + global->wrap = 72; + + TlsSetValue(_mxml_tls_index, (LPVOID)global); + } + + return (global); +} + + +#else /**** No threading ****/ +/* + * '_mxml_global()' - Get global data. + */ + +_mxml_global_t * /* O - Global data */ +_mxml_global(void) +{ + static _mxml_global_t global = /* Global data */ + { + NULL, /* error_cb */ + 1, /* num_entity_cbs */ + { _mxml_entity_cb }, /* entity_cbs */ + 72, /* wrap */ + NULL, /* custom_load_cb */ + NULL /* custom_save_cb */ + }; + + + return (&global); +} +#endif /* HAVE_PTHREAD_H */ + + +/* + * End of "$Id: mxml-private.c 422 2010-11-07 22:55:11Z mike $". + */ diff --git a/tools/gator/daemon/mxml/mxml-private.h b/tools/gator/daemon/mxml/mxml-private.h new file mode 100644 index 00000000000..8789e6c52cb --- /dev/null +++ b/tools/gator/daemon/mxml/mxml-private.h @@ -0,0 +1,50 @@ +/* + * "$Id: mxml-private.h 408 2010-09-19 05:26:46Z mike $" + * + * Private definitions for Mini-XML, a small XML-like file parsing library. + * + * Copyright 2003-2010 by Michael R Sweet. + * + * These coded instructions, statements, and computer programs are the + * property of Michael R Sweet and are protected by Federal copyright + * law. Distribution and use rights are outlined in the file "COPYING" + * which should have been included with this file. If this file is + * missing or damaged, see the license at: + * + * http://www.minixml.org/ + */ + +/* + * Include necessary headers... + */ + +#include "config.h" +#include "mxml.h" + + +/* + * Global, per-thread data... + */ + +typedef struct _mxml_global_s +{ + void (*error_cb)(const char *); + int num_entity_cbs; + int (*entity_cbs[100])(const char *name); + int wrap; + mxml_custom_load_cb_t custom_load_cb; + mxml_custom_save_cb_t custom_save_cb; +} _mxml_global_t; + + +/* + * Functions... + */ + +extern _mxml_global_t *_mxml_global(void); +extern int _mxml_entity_cb(const char *name); + + +/* + * End of "$Id: mxml-private.h 408 2010-09-19 05:26:46Z mike $". + */ diff --git a/tools/gator/daemon/mxml/mxml-search.c b/tools/gator/daemon/mxml/mxml-search.c new file mode 100644 index 00000000000..f975af1543c --- /dev/null +++ b/tools/gator/daemon/mxml/mxml-search.c @@ -0,0 +1,287 @@ +/* + * "$Id: mxml-search.c 427 2011-01-03 02:03:29Z mike $" + * + * Search/navigation functions for Mini-XML, a small XML-like file + * parsing library. + * + * Copyright 2003-2010 by Michael R Sweet. + * + * These coded instructions, statements, and computer programs are the + * property of Michael R Sweet and are protected by Federal copyright + * law. Distribution and use rights are outlined in the file "COPYING" + * which should have been included with this file. If this file is + * missing or damaged, see the license at: + * + * http://www.minixml.org/ + * + * Contents: + * + * mxmlFindElement() - Find the named element. + * mxmlFindValue() - Find a value with the given path. + * mxmlWalkNext() - Walk to the next logical node in the tree. + * mxmlWalkPrev() - Walk to the previous logical node in the tree. + */ + +/* + * Include necessary headers... + */ + +#include "config.h" +#include "mxml.h" + + +/* + * 'mxmlFindElement()' - Find the named element. + * + * The search is constrained by the name, attribute name, and value; any + * NULL names or values are treated as wildcards, so different kinds of + * searches can be implemented by looking for all elements of a given name + * or all elements with a specific attribute. The descend argument determines + * whether the search descends into child nodes; normally you will use + * MXML_DESCEND_FIRST for the initial search and MXML_NO_DESCEND to find + * additional direct descendents of the node. The top node argument + * constrains the search to a particular node's children. + */ + +mxml_node_t * /* O - Element node or NULL */ +mxmlFindElement(mxml_node_t *node, /* I - Current node */ + mxml_node_t *top, /* I - Top node */ + const char *name, /* I - Element name or NULL for any */ + const char *attr, /* I - Attribute name, or NULL for none */ + const char *value, /* I - Attribute value, or NULL for any */ + int descend) /* I - Descend into tree - MXML_DESCEND, MXML_NO_DESCEND, or MXML_DESCEND_FIRST */ +{ + const char *temp; /* Current attribute value */ + + + /* + * Range check input... + */ + + if (!node || !top || (!attr && value)) + return (NULL); + + /* + * Start with the next node... + */ + + node = mxmlWalkNext(node, top, descend); + + /* + * Loop until we find a matching element... + */ + + while (node != NULL) + { + /* + * See if this node matches... + */ + + if (node->type == MXML_ELEMENT && + node->value.element.name && + (!name || !strcmp(node->value.element.name, name))) + { + /* + * See if we need to check for an attribute... + */ + + if (!attr) + return (node); /* No attribute search, return it... */ + + /* + * Check for the attribute... + */ + + if ((temp = mxmlElementGetAttr(node, attr)) != NULL) + { + /* + * OK, we have the attribute, does it match? + */ + + if (!value || !strcmp(value, temp)) + return (node); /* Yes, return it... */ + } + } + + /* + * No match, move on to the next node... + */ + + if (descend == MXML_DESCEND) + node = mxmlWalkNext(node, top, MXML_DESCEND); + else + node = node->next; + } + + return (NULL); +} + + +/* + * 'mxmlFindPath()' - Find a node with the given path. + * + * The "path" is a slash-separated list of element names. The name "*" is + * considered a wildcard for one or more levels of elements. For example, + * "foo/one/two", "bar/two/one", "*\/one", and so forth. + * + * The first child node of the found node is returned if the given node has + * children and the first child is a value node. + * + * @since Mini-XML 2.7@ + */ + +mxml_node_t * /* O - Found node or NULL */ +mxmlFindPath(mxml_node_t *top, /* I - Top node */ + const char *path) /* I - Path to element */ +{ + mxml_node_t *node; /* Current node */ + char element[256]; /* Current element name */ + const char *pathsep; /* Separator in path */ + int descend; /* mxmlFindElement option */ + + + /* + * Range check input... + */ + + if (!top || !path || !*path) + return (NULL); + + /* + * Search each element in the path... + */ + + node = top; + while (*path) + { + /* + * Handle wildcards... + */ + + if (!strncmp(path, "*/", 2)) + { + path += 2; + descend = MXML_DESCEND; + } + else + descend = MXML_DESCEND_FIRST; + + /* + * Get the next element in the path... + */ + + if ((pathsep = strchr(path, '/')) == NULL) + pathsep = path + strlen(path); + + if (pathsep == path || (pathsep - path) >= sizeof(element)) + return (NULL); + + memcpy(element, path, pathsep - path); + element[pathsep - path] = '\0'; + + if (*pathsep) + path = pathsep + 1; + else + path = pathsep; + + /* + * Search for the element... + */ + + if ((node = mxmlFindElement(node, node, element, NULL, NULL, + descend)) == NULL) + return (NULL); + } + + /* + * If we get this far, return the node or its first child... + */ + + if (node->child && node->child->type != MXML_ELEMENT) + return (node->child); + else + return (node); +} + + +/* + * 'mxmlWalkNext()' - Walk to the next logical node in the tree. + * + * The descend argument controls whether the first child is considered + * to be the next node. The top node argument constrains the walk to + * the node's children. + */ + +mxml_node_t * /* O - Next node or NULL */ +mxmlWalkNext(mxml_node_t *node, /* I - Current node */ + mxml_node_t *top, /* I - Top node */ + int descend) /* I - Descend into tree - MXML_DESCEND, MXML_NO_DESCEND, or MXML_DESCEND_FIRST */ +{ + if (!node) + return (NULL); + else if (node->child && descend) + return (node->child); + else if (node == top) + return (NULL); + else if (node->next) + return (node->next); + else if (node->parent && node->parent != top) + { + node = node->parent; + + while (!node->next) + if (node->parent == top || !node->parent) + return (NULL); + else + node = node->parent; + + return (node->next); + } + else + return (NULL); +} + + +/* + * 'mxmlWalkPrev()' - Walk to the previous logical node in the tree. + * + * The descend argument controls whether the previous node's last child + * is considered to be the previous node. The top node argument constrains + * the walk to the node's children. + */ + +mxml_node_t * /* O - Previous node or NULL */ +mxmlWalkPrev(mxml_node_t *node, /* I - Current node */ + mxml_node_t *top, /* I - Top node */ + int descend) /* I - Descend into tree - MXML_DESCEND, MXML_NO_DESCEND, or MXML_DESCEND_FIRST */ +{ + if (!node || node == top) + return (NULL); + else if (node->prev) + { + if (node->prev->last_child && descend) + { + /* + * Find the last child under the previous node... + */ + + node = node->prev->last_child; + + while (node->last_child) + node = node->last_child; + + return (node); + } + else + return (node->prev); + } + else if (node->parent != top) + return (node->parent); + else + return (NULL); +} + + +/* + * End of "$Id: mxml-search.c 427 2011-01-03 02:03:29Z mike $". + */ diff --git a/tools/gator/daemon/mxml/mxml-set.c b/tools/gator/daemon/mxml/mxml-set.c new file mode 100644 index 00000000000..b0bd52790b2 --- /dev/null +++ b/tools/gator/daemon/mxml/mxml-set.c @@ -0,0 +1,349 @@ +/* + * "$Id: mxml-set.c 441 2011-12-09 23:49:00Z mike $" + * + * Node set functions for Mini-XML, a small XML-like file parsing library. + * + * Copyright 2003-2011 by Michael R Sweet. + * + * These coded instructions, statements, and computer programs are the + * property of Michael R Sweet and are protected by Federal copyright + * law. Distribution and use rights are outlined in the file "COPYING" + * which should have been included with this file. If this file is + * missing or damaged, see the license at: + * + * http://www.minixml.org/ + * + * Contents: + * + * mxmlSetCDATA() - Set the element name of a CDATA node. + * mxmlSetCustom() - Set the data and destructor of a custom data node. + * mxmlSetElement() - Set the name of an element node. + * mxmlSetInteger() - Set the value of an integer node. + * mxmlSetOpaque() - Set the value of an opaque node. + * mxmlSetReal() - Set the value of a real number node. + * mxmlSetText() - Set the value of a text node. + * mxmlSetTextf() - Set the value of a text node to a formatted string. + * mxmlSetUserData() - Set the user data pointer for a node. + */ + +/* + * Include necessary headers... + */ + +#include "config.h" +#include "mxml.h" + + +/* + * 'mxmlSetCDATA()' - Set the element name of a CDATA node. + * + * The node is not changed if it (or its first child) is not a CDATA element node. + * + * @since Mini-XML 2.3@ + */ + +int /* O - 0 on success, -1 on failure */ +mxmlSetCDATA(mxml_node_t *node, /* I - Node to set */ + const char *data) /* I - New data string */ +{ + /* + * Range check input... + */ + + if (node && node->type == MXML_ELEMENT && + strncmp(node->value.element.name, "![CDATA[", 8) && + node->child && node->child->type == MXML_ELEMENT && + !strncmp(node->child->value.element.name, "![CDATA[", 8)) + node = node->child; + + if (!node || node->type != MXML_ELEMENT || !data || + strncmp(node->value.element.name, "![CDATA[", 8)) + return (-1); + + /* + * Free any old element value and set the new value... + */ + + if (node->value.element.name) + free(node->value.element.name); + + node->value.element.name = _mxml_strdupf("![CDATA[%s]]", data); + + return (0); +} + + +/* + * 'mxmlSetCustom()' - Set the data and destructor of a custom data node. + * + * The node is not changed if it (or its first child) is not a custom node. + * + * @since Mini-XML 2.1@ + */ + +int /* O - 0 on success, -1 on failure */ +mxmlSetCustom( + mxml_node_t *node, /* I - Node to set */ + void *data, /* I - New data pointer */ + mxml_custom_destroy_cb_t destroy) /* I - New destructor function */ +{ + /* + * Range check input... + */ + + if (node && node->type == MXML_ELEMENT && + node->child && node->child->type == MXML_CUSTOM) + node = node->child; + + if (!node || node->type != MXML_CUSTOM) + return (-1); + + /* + * Free any old element value and set the new value... + */ + + if (node->value.custom.data && node->value.custom.destroy) + (*(node->value.custom.destroy))(node->value.custom.data); + + node->value.custom.data = data; + node->value.custom.destroy = destroy; + + return (0); +} + + +/* + * 'mxmlSetElement()' - Set the name of an element node. + * + * The node is not changed if it is not an element node. + */ + +int /* O - 0 on success, -1 on failure */ +mxmlSetElement(mxml_node_t *node, /* I - Node to set */ + const char *name) /* I - New name string */ +{ + /* + * Range check input... + */ + + if (!node || node->type != MXML_ELEMENT || !name) + return (-1); + + /* + * Free any old element value and set the new value... + */ + + if (node->value.element.name) + free(node->value.element.name); + + node->value.element.name = strdup(name); + + return (0); +} + + +/* + * 'mxmlSetInteger()' - Set the value of an integer node. + * + * The node is not changed if it (or its first child) is not an integer node. + */ + +int /* O - 0 on success, -1 on failure */ +mxmlSetInteger(mxml_node_t *node, /* I - Node to set */ + int integer) /* I - Integer value */ +{ + /* + * Range check input... + */ + + if (node && node->type == MXML_ELEMENT && + node->child && node->child->type == MXML_INTEGER) + node = node->child; + + if (!node || node->type != MXML_INTEGER) + return (-1); + + /* + * Set the new value and return... + */ + + node->value.integer = integer; + + return (0); +} + + +/* + * 'mxmlSetOpaque()' - Set the value of an opaque node. + * + * The node is not changed if it (or its first child) is not an opaque node. + */ + +int /* O - 0 on success, -1 on failure */ +mxmlSetOpaque(mxml_node_t *node, /* I - Node to set */ + const char *opaque) /* I - Opaque string */ +{ + /* + * Range check input... + */ + + if (node && node->type == MXML_ELEMENT && + node->child && node->child->type == MXML_OPAQUE) + node = node->child; + + if (!node || node->type != MXML_OPAQUE || !opaque) + return (-1); + + /* + * Free any old opaque value and set the new value... + */ + + if (node->value.opaque) + free(node->value.opaque); + + node->value.opaque = strdup(opaque); + + return (0); +} + + +/* + * 'mxmlSetReal()' - Set the value of a real number node. + * + * The node is not changed if it (or its first child) is not a real number node. + */ + +int /* O - 0 on success, -1 on failure */ +mxmlSetReal(mxml_node_t *node, /* I - Node to set */ + double real) /* I - Real number value */ +{ + /* + * Range check input... + */ + + if (node && node->type == MXML_ELEMENT && + node->child && node->child->type == MXML_REAL) + node = node->child; + + if (!node || node->type != MXML_REAL) + return (-1); + + /* + * Set the new value and return... + */ + + node->value.real = real; + + return (0); +} + + +/* + * 'mxmlSetText()' - Set the value of a text node. + * + * The node is not changed if it (or its first child) is not a text node. + */ + +int /* O - 0 on success, -1 on failure */ +mxmlSetText(mxml_node_t *node, /* I - Node to set */ + int whitespace, /* I - 1 = leading whitespace, 0 = no whitespace */ + const char *string) /* I - String */ +{ + /* + * Range check input... + */ + + if (node && node->type == MXML_ELEMENT && + node->child && node->child->type == MXML_TEXT) + node = node->child; + + if (!node || node->type != MXML_TEXT || !string) + return (-1); + + /* + * Free any old string value and set the new value... + */ + + if (node->value.text.string) + free(node->value.text.string); + + node->value.text.whitespace = whitespace; + node->value.text.string = strdup(string); + + return (0); +} + + +/* + * 'mxmlSetTextf()' - Set the value of a text node to a formatted string. + * + * The node is not changed if it (or its first child) is not a text node. + */ + +int /* O - 0 on success, -1 on failure */ +mxmlSetTextf(mxml_node_t *node, /* I - Node to set */ + int whitespace, /* I - 1 = leading whitespace, 0 = no whitespace */ + const char *format, /* I - Printf-style format string */ + ...) /* I - Additional arguments as needed */ +{ + va_list ap; /* Pointer to arguments */ + + + /* + * Range check input... + */ + + if (node && node->type == MXML_ELEMENT && + node->child && node->child->type == MXML_TEXT) + node = node->child; + + if (!node || node->type != MXML_TEXT || !format) + return (-1); + + /* + * Free any old string value and set the new value... + */ + + if (node->value.text.string) + free(node->value.text.string); + + va_start(ap, format); + + node->value.text.whitespace = whitespace; + node->value.text.string = _mxml_strdupf(format, ap); + + va_end(ap); + + return (0); +} + + +/* + * 'mxmlSetUserData()' - Set the user data pointer for a node. + * + * @since Mini-XML 2.7@ + */ + +int /* O - 0 on success, -1 on failure */ +mxmlSetUserData(mxml_node_t *node, /* I - Node to set */ + void *data) /* I - User data pointer */ +{ + /* + * Range check input... + */ + + if (!node) + return (-1); + + /* + * Set the user data pointer and return... + */ + + node->user_data = data; + return (0); +} + + +/* + * End of "$Id: mxml-set.c 441 2011-12-09 23:49:00Z mike $". + */ diff --git a/tools/gator/daemon/mxml/mxml-string.c b/tools/gator/daemon/mxml/mxml-string.c new file mode 100644 index 00000000000..6be42523f95 --- /dev/null +++ b/tools/gator/daemon/mxml/mxml-string.c @@ -0,0 +1,476 @@ +/* + * "$Id: mxml-string.c 424 2010-12-25 16:21:50Z mike $" + * + * String functions for Mini-XML, a small XML-like file parsing library. + * + * Copyright 2003-2010 by Michael R Sweet. + * + * These coded instructions, statements, and computer programs are the + * property of Michael R Sweet and are protected by Federal copyright + * law. Distribution and use rights are outlined in the file "COPYING" + * which should have been included with this file. If this file is + * missing or damaged, see the license at: + * + * http://www.minixml.org/ + * + * Contents: + * + * _mxml_snprintf() - Format a string. + * _mxml_strdup() - Duplicate a string. + * _mxml_strdupf() - Format and duplicate a string. + * _mxml_vsnprintf() - Format a string into a fixed size buffer. + * _mxml_vstrdupf() - Format and duplicate a string. + */ + +/* + * Include necessary headers... + */ + +#include "config.h" + + +/* + * The va_copy macro is part of C99, but many compilers don't implement it. + * Provide a "direct assignment" implmentation when va_copy isn't defined... + */ + +#ifndef va_copy +# ifdef __va_copy +# define va_copy(dst,src) __va_copy(dst,src) +# else +# define va_copy(dst,src) memcpy(&dst, &src, sizeof(va_list)) +# endif /* __va_copy */ +#endif /* va_copy */ + + +#ifndef HAVE_SNPRINTF +/* + * '_mxml_snprintf()' - Format a string. + */ + +int /* O - Number of bytes formatted */ +_mxml_snprintf(char *buffer, /* I - Output buffer */ + size_t bufsize, /* I - Size of output buffer */ + const char *format, /* I - Printf-style format string */ + ...) /* I - Additional arguments as needed */ +{ + va_list ap; /* Argument list */ + int bytes; /* Number of bytes formatted */ + + + va_start(ap, format); + bytes = vsnprintf(buffer, bufsize, format, ap); + va_end(ap); + + return (bytes); +} +#endif /* !HAVE_SNPRINTF */ + + +/* + * '_mxml_strdup()' - Duplicate a string. + */ + +#ifndef HAVE_STRDUP +char * /* O - New string pointer */ +_mxml_strdup(const char *s) /* I - String to duplicate */ +{ + char *t; /* New string pointer */ + + + if (s == NULL) + return (NULL); + + if ((t = malloc(strlen(s) + 1)) == NULL) + return (NULL); + + return (strcpy(t, s)); +} +#endif /* !HAVE_STRDUP */ + + +/* + * '_mxml_strdupf()' - Format and duplicate a string. + */ + +char * /* O - New string pointer */ +_mxml_strdupf(const char *format, /* I - Printf-style format string */ + ...) /* I - Additional arguments as needed */ +{ + va_list ap; /* Pointer to additional arguments */ + char *s; /* Pointer to formatted string */ + + + /* + * Get a pointer to the additional arguments, format the string, + * and return it... + */ + + va_start(ap, format); + s = _mxml_vstrdupf(format, ap); + va_end(ap); + + return (s); +} + + +#ifndef HAVE_VSNPRINTF +/* + * '_mxml_vsnprintf()' - Format a string into a fixed size buffer. + */ + +int /* O - Number of bytes formatted */ +_mxml_vsnprintf(char *buffer, /* O - Output buffer */ + size_t bufsize, /* O - Size of output buffer */ + const char *format, /* I - Printf-style format string */ + va_list ap) /* I - Pointer to additional arguments */ +{ + char *bufptr, /* Pointer to position in buffer */ + *bufend, /* Pointer to end of buffer */ + sign, /* Sign of format width */ + size, /* Size character (h, l, L) */ + type; /* Format type character */ + int width, /* Width of field */ + prec; /* Number of characters of precision */ + char tformat[100], /* Temporary format string for sprintf() */ + *tptr, /* Pointer into temporary format */ + temp[1024]; /* Buffer for formatted numbers */ + char *s; /* Pointer to string */ + int slen; /* Length of string */ + int bytes; /* Total number of bytes needed */ + + + /* + * Loop through the format string, formatting as needed... + */ + + bufptr = buffer; + bufend = buffer + bufsize - 1; + bytes = 0; + + while (*format) + { + if (*format == '%') + { + tptr = tformat; + *tptr++ = *format++; + + if (*format == '%') + { + if (bufptr && bufptr < bufend) *bufptr++ = *format; + bytes ++; + format ++; + continue; + } + else if (strchr(" -+#\'", *format)) + { + *tptr++ = *format; + sign = *format++; + } + else + sign = 0; + + if (*format == '*') + { + /* + * Get width from argument... + */ + + format ++; + width = va_arg(ap, int); + + snprintf(tptr, sizeof(tformat) - (tptr - tformat), "%d", width); + tptr += strlen(tptr); + } + else + { + width = 0; + + while (isdigit(*format & 255)) + { + if (tptr < (tformat + sizeof(tformat) - 1)) + *tptr++ = *format; + + width = width * 10 + *format++ - '0'; + } + } + + if (*format == '.') + { + if (tptr < (tformat + sizeof(tformat) - 1)) + *tptr++ = *format; + + format ++; + + if (*format == '*') + { + /* + * Get precision from argument... + */ + + format ++; + prec = va_arg(ap, int); + + snprintf(tptr, sizeof(tformat) - (tptr - tformat), "%d", prec); + tptr += strlen(tptr); + } + else + { + prec = 0; + + while (isdigit(*format & 255)) + { + if (tptr < (tformat + sizeof(tformat) - 1)) + *tptr++ = *format; + + prec = prec * 10 + *format++ - '0'; + } + } + } + else + prec = -1; + + if (*format == 'l' && format[1] == 'l') + { + size = 'L'; + + if (tptr < (tformat + sizeof(tformat) - 2)) + { + *tptr++ = 'l'; + *tptr++ = 'l'; + } + + format += 2; + } + else if (*format == 'h' || *format == 'l' || *format == 'L') + { + if (tptr < (tformat + sizeof(tformat) - 1)) + *tptr++ = *format; + + size = *format++; + } + + if (!*format) + break; + + if (tptr < (tformat + sizeof(tformat) - 1)) + *tptr++ = *format; + + type = *format++; + *tptr = '\0'; + + switch (type) + { + case 'E' : /* Floating point formats */ + case 'G' : + case 'e' : + case 'f' : + case 'g' : + if ((width + 2) > sizeof(temp)) + break; + + sprintf(temp, tformat, va_arg(ap, double)); + + bytes += strlen(temp); + + if (bufptr) + { + if ((bufptr + strlen(temp)) > bufend) + { + strncpy(bufptr, temp, (size_t)(bufend - bufptr)); + bufptr = bufend; + } + else + { + strcpy(bufptr, temp); + bufptr += strlen(temp); + } + } + break; + + case 'B' : /* Integer formats */ + case 'X' : + case 'b' : + case 'd' : + case 'i' : + case 'o' : + case 'u' : + case 'x' : + if ((width + 2) > sizeof(temp)) + break; + +#ifdef HAVE_LONG_LONG + if (size == 'L') + sprintf(temp, tformat, va_arg(ap, long long)); + else +#endif /* HAVE_LONG_LONG */ + sprintf(temp, tformat, va_arg(ap, int)); + + bytes += strlen(temp); + + if (bufptr) + { + if ((bufptr + strlen(temp)) > bufend) + { + strncpy(bufptr, temp, (size_t)(bufend - bufptr)); + bufptr = bufend; + } + else + { + strcpy(bufptr, temp); + bufptr += strlen(temp); + } + } + break; + + case 'p' : /* Pointer value */ + if ((width + 2) > sizeof(temp)) + break; + + sprintf(temp, tformat, va_arg(ap, void *)); + + bytes += strlen(temp); + + if (bufptr) + { + if ((bufptr + strlen(temp)) > bufend) + { + strncpy(bufptr, temp, (size_t)(bufend - bufptr)); + bufptr = bufend; + } + else + { + strcpy(bufptr, temp); + bufptr += strlen(temp); + } + } + break; + + case 'c' : /* Character or character array */ + bytes += width; + + if (bufptr) + { + if (width <= 1) + *bufptr++ = va_arg(ap, int); + else + { + if ((bufptr + width) > bufend) + width = bufend - bufptr; + + memcpy(bufptr, va_arg(ap, char *), (size_t)width); + bufptr += width; + } + } + break; + + case 's' : /* String */ + if ((s = va_arg(ap, char *)) == NULL) + s = "(null)"; + + slen = strlen(s); + if (slen > width && prec != width) + width = slen; + + bytes += width; + + if (bufptr) + { + if ((bufptr + width) > bufend) + width = bufend - bufptr; + + if (slen > width) + slen = width; + + if (sign == '-') + { + strncpy(bufptr, s, (size_t)slen); + memset(bufptr + slen, ' ', (size_t)(width - slen)); + } + else + { + memset(bufptr, ' ', (size_t)(width - slen)); + strncpy(bufptr + width - slen, s, (size_t)slen); + } + + bufptr += width; + } + break; + + case 'n' : /* Output number of chars so far */ + *(va_arg(ap, int *)) = bytes; + break; + } + } + else + { + bytes ++; + + if (bufptr && bufptr < bufend) + *bufptr++ = *format; + + format ++; + } + } + + /* + * Nul-terminate the string and return the number of characters needed. + */ + + *bufptr = '\0'; + + return (bytes); +} +#endif /* !HAVE_VSNPRINTF */ + + +/* + * '_mxml_vstrdupf()' - Format and duplicate a string. + */ + +char * /* O - New string pointer */ +_mxml_vstrdupf(const char *format, /* I - Printf-style format string */ + va_list ap) /* I - Pointer to additional arguments */ +{ + int bytes; /* Number of bytes required */ + char *buffer, /* String buffer */ + temp[256]; /* Small buffer for first vsnprintf */ + va_list apcopy; /* Copy of argument list */ + + + /* + * First format with a tiny buffer; this will tell us how many bytes are + * needed... + */ + + va_copy(apcopy, ap); + bytes = vsnprintf(temp, sizeof(temp), format, apcopy); + + if (bytes < sizeof(temp)) + { + /* + * Hey, the formatted string fits in the tiny buffer, so just dup that... + */ + + return (strdup(temp)); + } + + /* + * Allocate memory for the whole thing and reformat to the new, larger + * buffer... + */ + + if ((buffer = calloc(1, bytes + 1)) != NULL) + vsnprintf(buffer, bytes + 1, format, ap); + + /* + * Return the new string... + */ + + return (buffer); +} + + +/* + * End of "$Id: mxml-string.c 424 2010-12-25 16:21:50Z mike $". + */ diff --git a/tools/gator/daemon/mxml/mxml.h b/tools/gator/daemon/mxml/mxml.h new file mode 100644 index 00000000000..79c711f4c80 --- /dev/null +++ b/tools/gator/daemon/mxml/mxml.h @@ -0,0 +1,329 @@ +/* + * "$Id: mxml.h 427 2011-01-03 02:03:29Z mike $" + * + * Header file for Mini-XML, a small XML-like file parsing library. + * + * Copyright 2003-2011 by Michael R Sweet. + * + * These coded instructions, statements, and computer programs are the + * property of Michael R Sweet and are protected by Federal copyright + * law. Distribution and use rights are outlined in the file "COPYING" + * which should have been included with this file. If this file is + * missing or damaged, see the license at: + * + * http://www.minixml.org/ + */ + +/* + * Prevent multiple inclusion... + */ + +#ifndef _mxml_h_ +# define _mxml_h_ + +/* + * Include necessary headers... + */ + +# include +# include +# include +# include +# include + + +/* + * Constants... + */ + +# define MXML_TAB 8 /* Tabs every N columns */ + +# define MXML_NO_CALLBACK 0 /* Don't use a type callback */ +# define MXML_INTEGER_CALLBACK mxml_integer_cb + /* Treat all data as integers */ +# define MXML_OPAQUE_CALLBACK mxml_opaque_cb + /* Treat all data as opaque */ +# define MXML_REAL_CALLBACK mxml_real_cb + /* Treat all data as real numbers */ +# define MXML_TEXT_CALLBACK 0 /* Treat all data as text */ +# define MXML_IGNORE_CALLBACK mxml_ignore_cb + /* Ignore all non-element content */ + +# define MXML_NO_PARENT 0 /* No parent for the node */ + +# define MXML_DESCEND 1 /* Descend when finding/walking */ +# define MXML_NO_DESCEND 0 /* Don't descend when finding/walking */ +# define MXML_DESCEND_FIRST -1 /* Descend for first find */ + +# define MXML_WS_BEFORE_OPEN 0 /* Callback for before open tag */ +# define MXML_WS_AFTER_OPEN 1 /* Callback for after open tag */ +# define MXML_WS_BEFORE_CLOSE 2 /* Callback for before close tag */ +# define MXML_WS_AFTER_CLOSE 3 /* Callback for after close tag */ + +# define MXML_ADD_BEFORE 0 /* Add node before specified node */ +# define MXML_ADD_AFTER 1 /* Add node after specified node */ +# define MXML_ADD_TO_PARENT NULL /* Add node relative to parent */ + + +/* + * Data types... + */ + +typedef enum mxml_sax_event_e /**** SAX event type. ****/ +{ + MXML_SAX_CDATA, /* CDATA node */ + MXML_SAX_COMMENT, /* Comment node */ + MXML_SAX_DATA, /* Data node */ + MXML_SAX_DIRECTIVE, /* Processing directive node */ + MXML_SAX_ELEMENT_CLOSE, /* Element closed */ + MXML_SAX_ELEMENT_OPEN /* Element opened */ +} mxml_sax_event_t; + +typedef enum mxml_type_e /**** The XML node type. ****/ +{ + MXML_IGNORE = -1, /* Ignore/throw away node @since Mini-XML 2.3@ */ + MXML_ELEMENT, /* XML element with attributes */ + MXML_INTEGER, /* Integer value */ + MXML_OPAQUE, /* Opaque string */ + MXML_REAL, /* Real value */ + MXML_TEXT, /* Text fragment */ + MXML_CUSTOM /* Custom data @since Mini-XML 2.1@ */ +} mxml_type_t; + +typedef void (*mxml_custom_destroy_cb_t)(void *); + /**** Custom data destructor ****/ + +typedef void (*mxml_error_cb_t)(const char *); + /**** Error callback function ****/ + +typedef struct mxml_attr_s /**** An XML element attribute value. @private@ ****/ +{ + char *name; /* Attribute name */ + char *value; /* Attribute value */ +} mxml_attr_t; + +typedef struct mxml_element_s /**** An XML element value. @private@ ****/ +{ + char *name; /* Name of element */ + int num_attrs; /* Number of attributes */ + mxml_attr_t *attrs; /* Attributes */ +} mxml_element_t; + +typedef struct mxml_text_s /**** An XML text value. @private@ ****/ +{ + int whitespace; /* Leading whitespace? */ + char *string; /* Fragment string */ +} mxml_text_t; + +typedef struct mxml_custom_s /**** An XML custom value. @private@ ****/ +{ + void *data; /* Pointer to (allocated) custom data */ + mxml_custom_destroy_cb_t destroy; /* Pointer to destructor function */ +} mxml_custom_t; + +typedef union mxml_value_u /**** An XML node value. @private@ ****/ +{ + mxml_element_t element; /* Element */ + int integer; /* Integer number */ + char *opaque; /* Opaque string */ + double real; /* Real number */ + mxml_text_t text; /* Text fragment */ + mxml_custom_t custom; /* Custom data @since Mini-XML 2.1@ */ +} mxml_value_t; + +struct mxml_node_s /**** An XML node. @private@ ****/ +{ + mxml_type_t type; /* Node type */ + struct mxml_node_s *next; /* Next node under same parent */ + struct mxml_node_s *prev; /* Previous node under same parent */ + struct mxml_node_s *parent; /* Parent node */ + struct mxml_node_s *child; /* First child node */ + struct mxml_node_s *last_child; /* Last child node */ + mxml_value_t value; /* Node value */ + int ref_count; /* Use count */ + void *user_data; /* User data */ +}; + +typedef struct mxml_node_s mxml_node_t; /**** An XML node. ****/ + +struct mxml_index_s /**** An XML node index. @private@ ****/ +{ + char *attr; /* Attribute used for indexing or NULL */ + int num_nodes; /* Number of nodes in index */ + int alloc_nodes; /* Allocated nodes in index */ + int cur_node; /* Current node */ + mxml_node_t **nodes; /* Node array */ +}; + +typedef struct mxml_index_s mxml_index_t; + /**** An XML node index. ****/ + +typedef int (*mxml_custom_load_cb_t)(mxml_node_t *, const char *); + /**** Custom data load callback function ****/ + +typedef char *(*mxml_custom_save_cb_t)(mxml_node_t *); + /**** Custom data save callback function ****/ + +typedef int (*mxml_entity_cb_t)(const char *); + /**** Entity callback function */ + +typedef mxml_type_t (*mxml_load_cb_t)(mxml_node_t *); + /**** Load callback function ****/ + +typedef const char *(*mxml_save_cb_t)(mxml_node_t *, int); + /**** Save callback function ****/ + +typedef void (*mxml_sax_cb_t)(mxml_node_t *, mxml_sax_event_t, void *); + /**** SAX callback function ****/ + + +/* + * C++ support... + */ + +# ifdef __cplusplus +extern "C" { +# endif /* __cplusplus */ + +/* + * Prototypes... + */ + +extern void mxmlAdd(mxml_node_t *parent, int where, + mxml_node_t *child, mxml_node_t *node); +extern void mxmlDelete(mxml_node_t *node); +extern void mxmlElementDeleteAttr(mxml_node_t *node, + const char *name); +extern const char *mxmlElementGetAttr(mxml_node_t *node, const char *name); +extern void mxmlElementSetAttr(mxml_node_t *node, const char *name, + const char *value); +extern void mxmlElementSetAttrf(mxml_node_t *node, const char *name, + const char *format, ...) +# ifdef __GNUC__ +__attribute__ ((__format__ (__printf__, 3, 4))) +# endif /* __GNUC__ */ +; +extern int mxmlEntityAddCallback(mxml_entity_cb_t cb); +extern const char *mxmlEntityGetName(int val); +extern int mxmlEntityGetValue(const char *name); +extern void mxmlEntityRemoveCallback(mxml_entity_cb_t cb); +extern mxml_node_t *mxmlFindElement(mxml_node_t *node, mxml_node_t *top, + const char *name, const char *attr, + const char *value, int descend); +extern mxml_node_t *mxmlFindPath(mxml_node_t *node, const char *path); +extern const char *mxmlGetCDATA(mxml_node_t *node); +extern const void *mxmlGetCustom(mxml_node_t *node); +extern const char *mxmlGetElement(mxml_node_t *node); +extern mxml_node_t *mxmlGetFirstChild(mxml_node_t *node); +extern int mxmlGetInteger(mxml_node_t *node); +extern mxml_node_t *mxmlGetLastChild(mxml_node_t *node); +extern mxml_node_t *mxmlGetNextSibling(mxml_node_t *node); +extern const char *mxmlGetOpaque(mxml_node_t *node); +extern mxml_node_t *mxmlGetParent(mxml_node_t *node); +extern mxml_node_t *mxmlGetPrevSibling(mxml_node_t *node); +extern double mxmlGetReal(mxml_node_t *node); +extern int mxmlGetRefCount(mxml_node_t *node); +extern const char *mxmlGetText(mxml_node_t *node, int *whitespace); +extern mxml_type_t mxmlGetType(mxml_node_t *node); +extern void *mxmlGetUserData(mxml_node_t *node); +extern void mxmlIndexDelete(mxml_index_t *ind); +extern mxml_node_t *mxmlIndexEnum(mxml_index_t *ind); +extern mxml_node_t *mxmlIndexFind(mxml_index_t *ind, + const char *element, + const char *value); +extern int mxmlIndexGetCount(mxml_index_t *ind); +extern mxml_index_t *mxmlIndexNew(mxml_node_t *node, const char *element, + const char *attr); +extern mxml_node_t *mxmlIndexReset(mxml_index_t *ind); +extern mxml_node_t *mxmlLoadFd(mxml_node_t *top, int fd, + mxml_type_t (*cb)(mxml_node_t *)); +extern mxml_node_t *mxmlLoadFile(mxml_node_t *top, FILE *fp, + mxml_type_t (*cb)(mxml_node_t *)); +extern mxml_node_t *mxmlLoadString(mxml_node_t *top, const char *s, + mxml_type_t (*cb)(mxml_node_t *)); +extern mxml_node_t *mxmlNewCDATA(mxml_node_t *parent, const char *string); +extern mxml_node_t *mxmlNewCustom(mxml_node_t *parent, void *data, + mxml_custom_destroy_cb_t destroy); +extern mxml_node_t *mxmlNewElement(mxml_node_t *parent, const char *name); +extern mxml_node_t *mxmlNewInteger(mxml_node_t *parent, int integer); +extern mxml_node_t *mxmlNewOpaque(mxml_node_t *parent, const char *opaque); +extern mxml_node_t *mxmlNewReal(mxml_node_t *parent, double real); +extern mxml_node_t *mxmlNewText(mxml_node_t *parent, int whitespace, + const char *string); +extern mxml_node_t *mxmlNewTextf(mxml_node_t *parent, int whitespace, + const char *format, ...) +# ifdef __GNUC__ +__attribute__ ((__format__ (__printf__, 3, 4))) +# endif /* __GNUC__ */ +; +extern mxml_node_t *mxmlNewXML(const char *version); +extern int mxmlRelease(mxml_node_t *node); +extern void mxmlRemove(mxml_node_t *node); +extern int mxmlRetain(mxml_node_t *node); +extern char *mxmlSaveAllocString(mxml_node_t *node, + mxml_save_cb_t cb); +extern int mxmlSaveFd(mxml_node_t *node, int fd, + mxml_save_cb_t cb); +extern int mxmlSaveFile(mxml_node_t *node, FILE *fp, + mxml_save_cb_t cb); +extern int mxmlSaveString(mxml_node_t *node, char *buffer, + int bufsize, mxml_save_cb_t cb); +extern mxml_node_t *mxmlSAXLoadFd(mxml_node_t *top, int fd, + mxml_type_t (*cb)(mxml_node_t *), + mxml_sax_cb_t sax, void *sax_data); +extern mxml_node_t *mxmlSAXLoadFile(mxml_node_t *top, FILE *fp, + mxml_type_t (*cb)(mxml_node_t *), + mxml_sax_cb_t sax, void *sax_data); +extern mxml_node_t *mxmlSAXLoadString(mxml_node_t *top, const char *s, + mxml_type_t (*cb)(mxml_node_t *), + mxml_sax_cb_t sax, void *sax_data); +extern int mxmlSetCDATA(mxml_node_t *node, const char *data); +extern int mxmlSetCustom(mxml_node_t *node, void *data, + mxml_custom_destroy_cb_t destroy); +extern void mxmlSetCustomHandlers(mxml_custom_load_cb_t load, + mxml_custom_save_cb_t save); +extern int mxmlSetElement(mxml_node_t *node, const char *name); +extern void mxmlSetErrorCallback(mxml_error_cb_t cb); +extern int mxmlSetInteger(mxml_node_t *node, int integer); +extern int mxmlSetOpaque(mxml_node_t *node, const char *opaque); +extern int mxmlSetReal(mxml_node_t *node, double real); +extern int mxmlSetText(mxml_node_t *node, int whitespace, + const char *string); +extern int mxmlSetTextf(mxml_node_t *node, int whitespace, + const char *format, ...) +# ifdef __GNUC__ +__attribute__ ((__format__ (__printf__, 3, 4))) +# endif /* __GNUC__ */ +; +extern int mxmlSetUserData(mxml_node_t *node, void *data); +extern void mxmlSetWrapMargin(int column); +extern mxml_node_t *mxmlWalkNext(mxml_node_t *node, mxml_node_t *top, + int descend); +extern mxml_node_t *mxmlWalkPrev(mxml_node_t *node, mxml_node_t *top, + int descend); + + +/* + * Semi-private functions... + */ + +extern void mxml_error(const char *format, ...); +extern mxml_type_t mxml_ignore_cb(mxml_node_t *node); +extern mxml_type_t mxml_integer_cb(mxml_node_t *node); +extern mxml_type_t mxml_opaque_cb(mxml_node_t *node); +extern mxml_type_t mxml_real_cb(mxml_node_t *node); + + +/* + * C++ support... + */ + +# ifdef __cplusplus +} +# endif /* __cplusplus */ +#endif /* !_mxml_h_ */ + + +/* + * End of "$Id: mxml.h 427 2011-01-03 02:03:29Z mike $". + */ -- cgit v1.2.3 From d369859ea66a29a79162f622a3816c4eb9024940 Mon Sep 17 00:00:00 2001 From: Jon Medhurst Date: Thu, 10 Oct 2013 16:48:56 +0100 Subject: gator: Version 5.16 Signed-off-by: Jon Medhurst --- tools/gator/daemon/Android.mk | 104 ++++++------ tools/gator/daemon/Buffer.cpp | 14 +- tools/gator/daemon/Buffer.h | 5 - tools/gator/daemon/CapturedXML.cpp | 8 + tools/gator/daemon/Child.cpp | 3 + tools/gator/daemon/ConfigurationXML.cpp | 38 +++-- tools/gator/daemon/ConfigurationXML.h | 2 + tools/gator/daemon/Hwmon.cpp | 2 +- tools/gator/daemon/KMod.cpp | 4 +- tools/gator/daemon/OlyUtility.cpp | 5 +- tools/gator/daemon/Sender.cpp | 9 +- tools/gator/daemon/SessionData.h | 2 +- tools/gator/daemon/StreamlineSetup.cpp | 7 +- tools/gator/daemon/StreamlineSetup.h | 1 - tools/gator/daemon/events-CCN-504.xml | 122 ++++++++++++++ tools/gator/daemon/events-Cortex-A12.xml | 86 ++++++++++ tools/gator/daemon/events-Linux.xml | 4 +- tools/gator/daemon/events-Mali-400.xml | 180 --------------------- tools/gator/daemon/events-Mali-4xx.xml | 251 +++++++++++++++++++++++++++++ tools/gator/daemon/events-Mali-T6xx_hw.xml | 4 +- tools/gator/daemon/main.cpp | 115 +++++++++++-- 21 files changed, 674 insertions(+), 292 deletions(-) create mode 100644 tools/gator/daemon/events-CCN-504.xml create mode 100644 tools/gator/daemon/events-Cortex-A12.xml delete mode 100644 tools/gator/daemon/events-Mali-400.xml create mode 100644 tools/gator/daemon/events-Mali-4xx.xml (limited to 'tools/gator/daemon') diff --git a/tools/gator/daemon/Android.mk b/tools/gator/daemon/Android.mk index 4798a0a9181..a0429712fa8 100644 --- a/tools/gator/daemon/Android.mk +++ b/tools/gator/daemon/Android.mk @@ -1,52 +1,52 @@ -LOCAL_PATH := $(call my-dir) -include $(CLEAR_VARS) - -XML_H := $(shell cd $(LOCAL_PATH) && make events_xml.h configuration_xml.h) - -LOCAL_CFLAGS += -Wall -O3 -mthumb-interwork -fno-exceptions -DETCDIR=\"/etc\" -Ilibsensors - -LOCAL_SRC_FILES := \ - Buffer.cpp \ - CapturedXML.cpp \ - Child.cpp \ - Collector.cpp \ - ConfigurationXML.cpp \ - Driver.cpp \ - EventsXML.cpp \ - Fifo.cpp \ - Hwmon.cpp \ - KMod.cpp \ - LocalCapture.cpp \ - Logging.cpp \ - main.cpp \ - OlySocket.cpp \ - OlyUtility.cpp \ - Sender.cpp \ - SessionData.cpp \ - SessionXML.cpp \ - StreamlineSetup.cpp \ - libsensors/access.c \ - libsensors/conf-lex.c \ - libsensors/conf-parse.c \ - libsensors/data.c \ - libsensors/error.c \ - libsensors/general.c \ - libsensors/init.c \ - libsensors/sysfs.c \ - mxml/mxml-attr.c \ - mxml/mxml-entity.c \ - mxml/mxml-file.c \ - mxml/mxml-get.c \ - mxml/mxml-index.c \ - mxml/mxml-node.c \ - mxml/mxml-private.c \ - mxml/mxml-search.c \ - mxml/mxml-set.c \ - mxml/mxml-string.c - -LOCAL_C_INCLUDES := $(LOCAL_PATH) - -LOCAL_MODULE := gatord -LOCAL_MODULE_TAGS := optional - -include $(BUILD_EXECUTABLE) +LOCAL_PATH := $(call my-dir) +include $(CLEAR_VARS) + +XML_H := $(shell cd $(LOCAL_PATH) && make events_xml.h configuration_xml.h) + +LOCAL_CFLAGS += -Wall -O3 -mthumb-interwork -fno-exceptions -DETCDIR=\"/etc\" -Ilibsensors + +LOCAL_SRC_FILES := \ + Buffer.cpp \ + CapturedXML.cpp \ + Child.cpp \ + Collector.cpp \ + ConfigurationXML.cpp \ + Driver.cpp \ + EventsXML.cpp \ + Fifo.cpp \ + Hwmon.cpp \ + KMod.cpp \ + LocalCapture.cpp \ + Logging.cpp \ + main.cpp \ + OlySocket.cpp \ + OlyUtility.cpp \ + Sender.cpp \ + SessionData.cpp \ + SessionXML.cpp \ + StreamlineSetup.cpp \ + libsensors/access.c \ + libsensors/conf-lex.c \ + libsensors/conf-parse.c \ + libsensors/data.c \ + libsensors/error.c \ + libsensors/general.c \ + libsensors/init.c \ + libsensors/sysfs.c \ + mxml/mxml-attr.c \ + mxml/mxml-entity.c \ + mxml/mxml-file.c \ + mxml/mxml-get.c \ + mxml/mxml-index.c \ + mxml/mxml-node.c \ + mxml/mxml-private.c \ + mxml/mxml-search.c \ + mxml/mxml-set.c \ + mxml/mxml-string.c + +LOCAL_C_INCLUDES := $(LOCAL_PATH) + +LOCAL_MODULE := gatord +LOCAL_MODULE_TAGS := optional + +include $(BUILD_EXECUTABLE) diff --git a/tools/gator/daemon/Buffer.cpp b/tools/gator/daemon/Buffer.cpp index 56cf42cb774..c7abbf3a182 100644 --- a/tools/gator/daemon/Buffer.cpp +++ b/tools/gator/daemon/Buffer.cpp @@ -14,11 +14,7 @@ #define mask (size - 1) -Buffer::Buffer (const int32_t core, const int32_t buftype, const int size, sem_t *const readerSem) : core(core), buftype(buftype), size(size), readPos(0), writePos(0), commitPos(0), available(true), done(false), buf(new char[size]), -#ifdef GATOR_LIVE - commitTime(gSessionData->mLiveRate), -#endif - readerSem(readerSem) { +Buffer::Buffer (const int32_t core, const int32_t buftype, const int size, sem_t *const readerSem) : core(core), buftype(buftype), size(size), readPos(0), writePos(0), commitPos(0), available(true), done(false), buf(new char[size]), commitTime(gSessionData->mLiveRate), readerSem(readerSem) { if ((size & mask) != 0) { logg->logError(__FILE__, __LINE__, "Buffer size is not a power of 2"); handleException(); @@ -110,13 +106,11 @@ void Buffer::commit (const uint64_t time) { logg->logMessage("Committing data readPos: %i writePos: %i commitPos: %i", readPos, writePos, commitPos); commitPos = writePos; -#ifdef GATOR_LIVE if (gSessionData->mLiveRate > 0) { while (time > commitTime) { commitTime += gSessionData->mLiveRate; } } -#endif if (!done) { frame(); @@ -131,11 +125,7 @@ void Buffer::check (const uint64_t time) { if (filled < 0) { filled += size; } - if (filled >= ((size * 3) / 4) -#ifdef GATOR_LIVE - || (gSessionData->mLiveRate > 0 && time >= commitTime) -#endif - ) { + if (filled >= ((size * 3) / 4) || (gSessionData->mLiveRate > 0 && time >= commitTime)) { commit(time); } } diff --git a/tools/gator/daemon/Buffer.h b/tools/gator/daemon/Buffer.h index c460fb7854a..f820cfd851e 100644 --- a/tools/gator/daemon/Buffer.h +++ b/tools/gator/daemon/Buffer.h @@ -13,8 +13,6 @@ #include #include -#define GATOR_LIVE - class Sender; class Buffer { @@ -56,10 +54,7 @@ private: bool available; bool done; char *const buf; -#ifdef GATOR_LIVE uint64_t commitTime; -#endif - sem_t *const readerSem; }; diff --git a/tools/gator/daemon/CapturedXML.cpp b/tools/gator/daemon/CapturedXML.cpp index af726df71d5..30c4c44c5d9 100644 --- a/tools/gator/daemon/CapturedXML.cpp +++ b/tools/gator/daemon/CapturedXML.cpp @@ -43,6 +43,14 @@ mxml_node_t* CapturedXML::getTree(bool includeTime) { mxmlElementSetAttrf(target, "cores", "%d", gSessionData->mCores); mxmlElementSetAttrf(target, "cpuid", "0x%x", gSessionData->mCpuId); + if (!gSessionData->mOneShot && (gSessionData->mSampleRate > 0)) { + mxmlElementSetAttr(target, "supports_live", "yes"); + } + + if (gSessionData->mLocalCapture) { + mxmlElementSetAttr(target, "local_capture", "yes"); + } + mxml_node_t *counters = NULL; for (x = 0; x < MAX_PERFORMANCE_COUNTERS; x++) { const Counter & counter = gSessionData->mCounters[x]; diff --git a/tools/gator/daemon/Child.cpp b/tools/gator/daemon/Child.cpp index 286c7e7ba39..c0540762698 100644 --- a/tools/gator/daemon/Child.cpp +++ b/tools/gator/daemon/Child.cpp @@ -55,6 +55,9 @@ void handleException() { child->socket->receiveNBytes(&discard, 1); } + // Ensure all data is flushed + child->socket->shutdownConnection(); + // this indirectly calls close socket which will ensure the data has been sent delete sender; } diff --git a/tools/gator/daemon/ConfigurationXML.cpp b/tools/gator/daemon/ConfigurationXML.cpp index fb00202f656..2a5252a5bb0 100644 --- a/tools/gator/daemon/ConfigurationXML.cpp +++ b/tools/gator/daemon/ConfigurationXML.cpp @@ -27,14 +27,7 @@ ConfigurationXML::ConfigurationXML() { char path[PATH_MAX]; - if (gSessionData->mConfigurationXMLPath) { - strncpy(path, gSessionData->mConfigurationXMLPath, PATH_MAX); - } else { - if (util->getApplicationFullPath(path, PATH_MAX) != 0) { - logg->logMessage("Unable to determine the full path of gatord, the cwd will be used"); - } - strncat(path, "configuration.xml", PATH_MAX - strlen(path) - 1); - } + getPath(path); mConfigurationXML = util->readFromDisk(path); for (int retryCount = 0; retryCount < 2; ++retryCount) { @@ -48,12 +41,7 @@ ConfigurationXML::ConfigurationXML() { int ret = parse(mConfigurationXML); if (ret == 1) { - // remove configuration.xml on disk to use the default - if (remove(path) != 0) { - logg->logError(__FILE__, __LINE__, "Invalid configuration.xml file detected and unable to delete it. To resolve, delete configuration.xml on disk"); - handleException(); - } - logg->logMessage("Invalid configuration.xml file detected and removed"); + remove(); // Free the current configuration and reload free((void*)mConfigurationXML); @@ -197,3 +185,25 @@ void ConfigurationXML::getDefaultConfigurationXml(const char * & xml, unsigned i xml = (const char *)configuration_xml; len = configuration_xml_len; } + +void ConfigurationXML::getPath(char* path) { + if (gSessionData->mConfigurationXMLPath) { + strncpy(path, gSessionData->mConfigurationXMLPath, PATH_MAX); + } else { + if (util->getApplicationFullPath(path, PATH_MAX) != 0) { + logg->logMessage("Unable to determine the full path of gatord, the cwd will be used"); + } + strncat(path, "configuration.xml", PATH_MAX - strlen(path) - 1); + } +} + +void ConfigurationXML::remove() { + char path[PATH_MAX]; + getPath(path); + + if (::remove(path) != 0) { + logg->logError(__FILE__, __LINE__, "Invalid configuration.xml file detected and unable to delete it. To resolve, delete configuration.xml on disk"); + handleException(); + } + logg->logMessage("Invalid configuration.xml file detected and removed"); +} diff --git a/tools/gator/daemon/ConfigurationXML.h b/tools/gator/daemon/ConfigurationXML.h index f709ad16825..eba7dc4bac4 100644 --- a/tools/gator/daemon/ConfigurationXML.h +++ b/tools/gator/daemon/ConfigurationXML.h @@ -14,6 +14,8 @@ class ConfigurationXML { public: static void getDefaultConfigurationXml(const char * & xml, unsigned int & len); + static void getPath(char* path); + static void remove(); ConfigurationXML(); ~ConfigurationXML(); diff --git a/tools/gator/daemon/Hwmon.cpp b/tools/gator/daemon/Hwmon.cpp index 94752158436..07925680c1f 100644 --- a/tools/gator/daemon/Hwmon.cpp +++ b/tools/gator/daemon/Hwmon.cpp @@ -73,7 +73,7 @@ HwmonCounter::HwmonCounter(HwmonCounter *next, int key, const sensors_chip_name len = snprintf(NULL, 0, "hwmon_%s_%d", chip_name, feature->number) + 1; name = new char[len]; - len = snprintf(name, len, "hwmon_%s_%d", chip_name, feature->number); + snprintf(name, len, "hwmon_%s_%d", chip_name, feature->number); delete [] chip_name; diff --git a/tools/gator/daemon/KMod.cpp b/tools/gator/daemon/KMod.cpp index 04f33306487..559297fe227 100644 --- a/tools/gator/daemon/KMod.cpp +++ b/tools/gator/daemon/KMod.cpp @@ -13,6 +13,7 @@ #include #include "Collector.h" +#include "ConfigurationXML.h" #include "Counter.h" #include "Logging.h" @@ -73,7 +74,8 @@ void KMod::setupCounter(Counter &counter) { } counter.setCount(count); } else if (counter.getCount() > 0) { - logg->logError(__FILE__, __LINE__, "Event Based Sampling is only supported with kernel versions 3.0.0 and higher with CONFIG_PERF_EVENTS=y, and CONFIG_HW_PERF_EVENTS=y\n"); + ConfigurationXML::remove(); + logg->logError(__FILE__, __LINE__, "Event Based Sampling is only supported with kernel versions 3.0.0 and higher with CONFIG_PERF_EVENTS=y, and CONFIG_HW_PERF_EVENTS=y. The invalid configuration.xml has been removed.\n"); handleException(); } } diff --git a/tools/gator/daemon/OlyUtility.cpp b/tools/gator/daemon/OlyUtility.cpp index b29a1e91cc8..0b22d6ebd02 100644 --- a/tools/gator/daemon/OlyUtility.cpp +++ b/tools/gator/daemon/OlyUtility.cpp @@ -83,7 +83,7 @@ int OlyUtility::getApplicationFullPath(char* fullpath, int sizeOfPath) { } fullpath[length] = 0; - fullpath = getPathPart(fullpath); + getPathPart(fullpath); return 0; } @@ -171,7 +171,7 @@ int OlyUtility::appendToDisk(const char* path, const char* data) { */ #define TRANSFER_SIZE 1024 int OlyUtility::copyFile(const char* srcFile, const char* dstFile) { - char* buffer = (char*)malloc(TRANSFER_SIZE); + char buffer[TRANSFER_SIZE]; FILE * f_src = fopen(srcFile,"rb"); if (!f_src) { return 0; @@ -197,7 +197,6 @@ int OlyUtility::copyFile(const char* srcFile, const char* dstFile) { } fclose(f_src); fclose(f_dst); - free(buffer); return 1; } diff --git a/tools/gator/daemon/Sender.cpp b/tools/gator/daemon/Sender.cpp index 159503f845f..8eb348ff3a0 100644 --- a/tools/gator/daemon/Sender.cpp +++ b/tools/gator/daemon/Sender.cpp @@ -93,8 +93,13 @@ void Sender::writeData(const char* data, int length, int type) { logg->logMessage("Sending data with length %d", length); if (type != RESPONSE_APC_DATA) { // type and length already added by the Collector for apc data - mDataSocket->send((char*)&type, 1); - mDataSocket->send((char*)&length, sizeof(length)); + unsigned char header[5]; + header[0] = type; + header[1] = (length >> 0) & 0xff; + header[2] = (length >> 8) & 0xff; + header[3] = (length >> 16) & 0xff; + header[4] = (length >> 24) & 0xff; + mDataSocket->send((char*)&header, sizeof(header)); } // 100Kbits/sec * alarmDuration sec / 8 bits/byte diff --git a/tools/gator/daemon/SessionData.h b/tools/gator/daemon/SessionData.h index 22a8af05121..e72fa5d7c5e 100644 --- a/tools/gator/daemon/SessionData.h +++ b/tools/gator/daemon/SessionData.h @@ -16,7 +16,7 @@ #define MAX_PERFORMANCE_COUNTERS 50 -#define PROTOCOL_VERSION 14 +#define PROTOCOL_VERSION 16 #define PROTOCOL_DEV 1000 // Differentiates development versions (timestamp) from release versions struct ImageLinkList { diff --git a/tools/gator/daemon/StreamlineSetup.cpp b/tools/gator/daemon/StreamlineSetup.cpp index e196a7dba2c..2faada23f84 100644 --- a/tools/gator/daemon/StreamlineSetup.cpp +++ b/tools/gator/daemon/StreamlineSetup.cpp @@ -255,12 +255,7 @@ void StreamlineSetup::sendCounters() { void StreamlineSetup::writeConfiguration(char* xml) { char path[PATH_MAX]; - if (gSessionData->mConfigurationXMLPath) { - strncpy(path, gSessionData->mConfigurationXMLPath, PATH_MAX); - } else { - util->getApplicationFullPath(path, PATH_MAX); - strncat(path, "configuration.xml", PATH_MAX - strlen(path) - 1); - } + ConfigurationXML::getPath(path); if (util->writeToDisk(path, xml) < 0) { logg->logError(__FILE__, __LINE__, "Error writing %s\nPlease verify write permissions to this path.", path); diff --git a/tools/gator/daemon/StreamlineSetup.h b/tools/gator/daemon/StreamlineSetup.h index a27a7ac97a1..092d956ec99 100644 --- a/tools/gator/daemon/StreamlineSetup.h +++ b/tools/gator/daemon/StreamlineSetup.h @@ -26,7 +26,6 @@ public: StreamlineSetup(OlySocket *socket); ~StreamlineSetup(); private: - int mNumConnections; OlySocket* mSocket; char* readCommand(int*); diff --git a/tools/gator/daemon/events-CCN-504.xml b/tools/gator/daemon/events-CCN-504.xml new file mode 100644 index 00000000000..cfabf65949e --- /dev/null +++ b/tools/gator/daemon/events-CCN-504.xml @@ -0,0 +1,122 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/tools/gator/daemon/events-Cortex-A12.xml b/tools/gator/daemon/events-Cortex-A12.xml new file mode 100644 index 00000000000..20a4772c458 --- /dev/null +++ b/tools/gator/daemon/events-Cortex-A12.xml @@ -0,0 +1,86 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/tools/gator/daemon/events-Linux.xml b/tools/gator/daemon/events-Linux.xml index 05dc613a88a..4a30ad6ec4e 100644 --- a/tools/gator/daemon/events-Linux.xml +++ b/tools/gator/daemon/events-Linux.xml @@ -11,7 +11,7 @@ - - + + diff --git a/tools/gator/daemon/events-Mali-400.xml b/tools/gator/daemon/events-Mali-400.xml deleted file mode 100644 index dceccfb4a67..00000000000 --- a/tools/gator/daemon/events-Mali-400.xml +++ /dev/null @@ -1,180 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/tools/gator/daemon/events-Mali-4xx.xml b/tools/gator/daemon/events-Mali-4xx.xml new file mode 100644 index 00000000000..8772ce410b9 --- /dev/null +++ b/tools/gator/daemon/events-Mali-4xx.xml @@ -0,0 +1,251 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/tools/gator/daemon/events-Mali-T6xx_hw.xml b/tools/gator/daemon/events-Mali-T6xx_hw.xml index 1fd9c4e59d8..8cfe7c3084d 100644 --- a/tools/gator/daemon/events-Mali-T6xx_hw.xml +++ b/tools/gator/daemon/events-Mali-T6xx_hw.xml @@ -5,14 +5,14 @@ - + - + diff --git a/tools/gator/daemon/main.cpp b/tools/gator/daemon/main.cpp index a6ddfe26865..d1b0913aa78 100644 --- a/tools/gator/daemon/main.cpp +++ b/tools/gator/daemon/main.cpp @@ -19,6 +19,8 @@ #include #include #include +#include +#include #include "Child.h" #include "SessionData.h" #include "OlySocket.h" @@ -32,7 +34,7 @@ extern Child* child; static int shutdownFilesystem(); static pthread_mutex_t numSessions_mutex; static int numSessions = 0; -static OlySocket* socket = NULL; +static OlySocket* sock = NULL; static bool driverRunningAtStart = false; static bool driverMountedAtStart = false; @@ -41,11 +43,13 @@ struct cmdline_t { char* module; }; +#define DEFAULT_PORT 8080 + void cleanUp() { if (shutdownFilesystem() == -1) { logg->logMessage("Error shutting down gator filesystem"); } - delete socket; + delete sock; delete util; delete logg; } @@ -100,6 +104,92 @@ static void child_exit(int signum) { } } +static int udpPort(int port) { + int s; + struct sockaddr_in sockaddr; + int on; + + s = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP); + if (s == -1) { + logg->logError(__FILE__, __LINE__, "socket failed"); + handleException(); + } + + on = 1; + if (setsockopt(s, SOL_SOCKET, SO_REUSEADDR, (const char*)&on, sizeof(on)) != 0) { + logg->logError(__FILE__, __LINE__, "setsockopt failed"); + handleException(); + } + + memset((void*)&sockaddr, 0, sizeof(sockaddr)); + sockaddr.sin_family = AF_INET; + sockaddr.sin_port = htons(port); + sockaddr.sin_addr.s_addr = INADDR_ANY; + if (bind(s, (struct sockaddr *)&sockaddr, sizeof(sockaddr)) < 0) { + logg->logError(__FILE__, __LINE__, "socket failed"); + handleException(); + } + + return s; +} + +#define UDP_ANS_PORT 30000 +#define UDP_REQ_PORT 30001 + +typedef struct { + char rviHeader[8]; + uint32_t messageID; + uint8_t ethernetAddress[8]; + uint32_t ethernetType; + uint32_t dhcp; + char dhcpName[40]; + uint32_t ipAddress; + uint32_t defaultGateway; + uint32_t subnetMask; + uint32_t activeConnections; +} RVIConfigureInfo; + +static const char DST_REQ[] = { 'D', 'S', 'T', '_', 'R', 'E', 'Q', ' ', 0, 0, 0, 0x64 }; + +static void* answerThread(void* pVoid) { + const struct cmdline_t * const cmdline = (struct cmdline_t *)pVoid; + RVIConfigureInfo dstAns; + int req = udpPort(UDP_REQ_PORT); + int ans = udpPort(UDP_ANS_PORT); + + // Format the answer buffer + memset(&dstAns, 0, sizeof(dstAns)); + memcpy(dstAns.rviHeader, "STR_ANS ", sizeof(dstAns.rviHeader)); + if (gethostname(dstAns.dhcpName, sizeof(dstAns.dhcpName) - 1) != 0) { + logg->logError(__FILE__, __LINE__, "gethostname failed"); + handleException(); + } + // Subvert the defaultGateway field for the port number + if (cmdline->port != DEFAULT_PORT) { + dstAns.defaultGateway = cmdline->port; + } + // Subvert the subnetMask field for the protocol version + dstAns.subnetMask = PROTOCOL_VERSION; + + for (;;) { + char buf[128]; + struct sockaddr_in sockaddr; + socklen_t addrlen; + int read; + addrlen = sizeof(sockaddr); + read = recvfrom(req, &buf, sizeof(buf), 0, (struct sockaddr *)&sockaddr, &addrlen); + if (read < 0) { + logg->logError(__FILE__, __LINE__, "recvfrom failed"); + handleException(); + } else if ((read == 12) && (memcmp(buf, DST_REQ, sizeof(DST_REQ)) == 0)) { + if (sendto(ans, &dstAns, sizeof(dstAns), 0, (struct sockaddr *)&sockaddr, addrlen) != sizeof(dstAns)) { + logg->logError(__FILE__, __LINE__, "sendto failed"); + handleException(); + } + } + } +} + // retval: -1 = failure; 0 = was already mounted; 1 = successfully mounted static int mountGatorFS() { // If already mounted, @@ -222,14 +312,14 @@ static int shutdownFilesystem() { static struct cmdline_t parseCommandLine(int argc, char** argv) { struct cmdline_t cmdline; - cmdline.port = 8080; + cmdline.port = DEFAULT_PORT; cmdline.module = NULL; char version_string[256]; // arbitrary length to hold the version information int c; // build the version string if (PROTOCOL_VERSION < PROTOCOL_DEV) { - snprintf(version_string, sizeof(version_string), "Streamline gatord version %d (DS-5 v5.%d)", PROTOCOL_VERSION, PROTOCOL_VERSION + 1); + snprintf(version_string, sizeof(version_string), "Streamline gatord version %d (DS-5 v5.%d)", PROTOCOL_VERSION, PROTOCOL_VERSION); } else { snprintf(version_string, sizeof(version_string), "Streamline gatord development version %d", PROTOCOL_VERSION); } @@ -277,7 +367,7 @@ static struct cmdline_t parseCommandLine(int argc, char** argv) { } // Error checking - if (cmdline.port != 8080 && gSessionData->mSessionXMLPath != NULL) { + if (cmdline.port != DEFAULT_PORT && gSessionData->mSessionXMLPath != NULL) { logg->logError(__FILE__, __LINE__, "Only a port or a session xml can be specified, not both"); handleException(); } @@ -339,11 +429,16 @@ int main(int argc, char** argv, char* envp[]) { child->run(); delete child; } else { - socket = new OlySocket(cmdline.port, true); + pthread_t answerThreadID; + if (pthread_create(&answerThreadID, NULL, answerThread, &cmdline)) { + logg->logError(__FILE__, __LINE__, "Failed to create answer thread"); + handleException(); + } + sock = new OlySocket(cmdline.port, true); // Forever loop, can be exited via a signal or exception while (1) { logg->logMessage("Waiting on connection..."); - socket->acceptConnection(); + sock->acceptConnection(); int pid = fork(); if (pid < 0) { @@ -351,14 +446,14 @@ int main(int argc, char** argv, char* envp[]) { logg->logError(__FILE__, __LINE__, "Fork process failed. Please power cycle the target device if this error persists."); } else if (pid == 0) { // Child - socket->closeServerSocket(); - child = new Child(socket, numSessions + 1); + sock->closeServerSocket(); + child = new Child(sock, numSessions + 1); child->run(); delete child; exit(0); } else { // Parent - socket->closeSocket(); + sock->closeSocket(); pthread_mutex_lock(&numSessions_mutex); numSessions++; -- cgit v1.2.3