diff --git a/asynnet.c b/asynnet.c new file mode 100644 index 00000000..3de2be75 --- /dev/null +++ b/asynnet.c @@ -0,0 +1,576 @@ +/** + * Asynchronous networking for SICS and other programs. This module centrally manages + * a number of network connections for a client program. It is a layer between the + * program and the network which manages non blocking network I/O. To this purpose, the + * client program has to call ANETprocess at convenient intervalls. This module + * has a couple of features: + * - Connections are abstracted to handles which are guranteed to be unique + * rather then socket numbers. Socket numbers may be reused by the OS. + * - This module allows upper level code to figure out if a connection is still + * connected or not. + * - This module introduces a buffer layer between the socket and the application. + * Thus the upper layer does not have to worry much about I/O blocking. This + * is taken care of by this module both for reading and writing. + * - All I/O is non blocking. + * - This module can detect if a client is hanging and close the connection then. + * Hanging is detected by not being able to write to the client for some period + * of time. + * + * copyright: see file COPYRIGHT + * + * Mark Koennecke, January 2009 + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "asynnet.h" +#include "rwpuffer.h" + +/*--------------------------------------------------------------------------*/ +#define SERVERSOCKET 0 +#define DATASOCKET 1 +#define MAXCONNECTIONS 1024 +#define RBUFFERSIZE 262144 /* 256kb */ +#define WBUFFERSIZE 2*262144 /* 512kb */ +/*--------------------------------------------------------------------------*/ +typedef struct { + int socket; + int handle; + int type; + prwBuffer readBuffer; + prwBuffer writeBuffer; + time_t lastOpenForWrite; + ANETcallback readCallback; + void *userData; + ANETkill killUser; +} SocketDescriptor, *pSocketDescriptor; + +static SocketDescriptor connections[MAXCONNECTIONS]; +static int noConnections = 0; +static unsigned int handleID = 0; +/*------------------------------------------------------------------------*/ +static int SocketCompare(const void *s1, const void *s2) +{ + pSocketDescriptor socke1, socke2; + + socke1 = (pSocketDescriptor) s1; + socke2 = (pSocketDescriptor) s2; + return socke1->handle - socke2->handle; +} + +/*------------------------------------------------------------------------*/ +static void sortConnections() +{ + qsort(connections, noConnections, + sizeof(SocketDescriptor), SocketCompare); +} + +/*------------------------------------------------------------------------*/ +static pSocketDescriptor findSocketDescriptor(int handle) +{ + SocketDescriptor key; + pSocketDescriptor result; + + key.handle = handle; + result = bsearch(&key, connections, noConnections, + sizeof(SocketDescriptor), SocketCompare); + return result; +} + +/*------------------------------------------------------------------------*/ +static ANETlog logOutput = NULL; +static void *logUserData = NULL; +/*------------------------------------------------------------------------*/ +static void anetLog(int level, char *fmt, ...) +{ + va_list ap; + char buf[256]; + char *text = NULL; + int l; + + if (logOutput == NULL) { + return; + } + + va_start(ap, fmt); + l = vsnprintf(buf, sizeof buf, fmt, ap); + va_end(ap); + if (l < sizeof buf) { + text = buf; + logOutput(level, text, logUserData); + } else { + /* assuming we have a C99 conforming snprintf and need a larger buffer */ + text = calloc(l, 1); + va_start(ap, fmt); + vsnprintf(text, l, fmt, ap); + va_end(ap); + logOutput(level, text, logUserData); + free(text); + } +} + +/*============= public interface =========================================*/ +void ANETsetLog(ANETlog lcb, void *userData) +{ + logOutput = lcb; + logUserData = userData; +} + +/*------------------------------------------------------------------------*/ +int ANETopenServerPort(int iPort, ANETcallback cb, void *userData) +{ + SocketDescriptor socke; + int i = 1, status; + struct sockaddr_in addresse; + + assert(iPort > 0); + assert(cb != NULL); + + memset(&socke, 0, sizeof(SocketDescriptor)); + socke.socket = socket(AF_INET, SOCK_STREAM, 0); + if (socke.socket < 0) { + anetLog(ANETERROR, "Failed to open server port: socket: %d", iPort); + return ANETOPENFAIL; + } + status = + setsockopt(socke.socket, SOL_SOCKET, SO_REUSEADDR, &i, sizeof(int)); + if (status < 0) { + anetLog(ANETERROR, + "Failed to open server port: setsockopt: %d, errno = %d", + iPort, errno); + return ANETOPENFAIL; + } + memset(&addresse, 0, sizeof(struct sockaddr_in)); + addresse.sin_family = AF_INET; + addresse.sin_addr.s_addr = htonl(INADDR_ANY); + addresse.sin_port = htons(iPort); + status = bind(socke.socket, (struct sockaddr *) &addresse, + sizeof(struct sockaddr_in)); + if (status < 0) { + anetLog(ANETERROR, "Failed to open server port: bind: %d, errno = %d", + iPort, errno); + return ANETOPENFAIL; + } + status = listen(socke.socket, 8); + if (status < 0) { + anetLog(ANETERROR, "Failed to open server port: listen: %d", iPort); + return ANETOPENFAIL; + } + socke.type = SERVERSOCKET; + socke.handle = handleID; + handleID++; + socke.readCallback = cb; + socke.userData = userData; + socke.lastOpenForWrite = time(NULL); + connections[noConnections] = socke; + noConnections++; + sortConnections(); + anetLog(ANETCON, "Opened server port %d", iPort); + return socke.handle; +} + +/*-----------------------------------------------------------------------*/ +int ANETregisterSocket(int socket) +{ + SocketDescriptor socke; + int flags, status; + + if (noConnections >= MAXCONNECTIONS) { + anetLog(ANETERROR, "Maximum number of connections exceeded"); + return ANETOUTOFSOCKETS; + } + + memset(&socke, 0, sizeof(SocketDescriptor)); + flags = fcntl(socket, F_GETFL, 0); + status = fcntl(socket, F_SETFL, flags | O_NONBLOCK); + if (status < 0) { + return ANETSOCKERROR; + } + socke.readBuffer = MakeRWPuffer(RBUFFERSIZE); + socke.writeBuffer = MakeRWPuffer(WBUFFERSIZE); + if (socke.readBuffer == NULL || socke.writeBuffer == NULL) { + return ANETMEM; + } + socke.socket = socket; + socke.handle = handleID; + handleID++; + socke.type = DATASOCKET; + socke.lastOpenForWrite = time(NULL); + connections[noConnections] = socke; + noConnections++; + sortConnections(); + return socke.handle; +} + +/*-------------------------------------------------------------------------*/ +int ANETconnect(char *name, int iPort) +{ + struct in_addr addr; + struct sockaddr_in addresse; + struct hostent *host; + int socke, status; + + /* check for aaa.bbb.ccc.ddd first */ + addr.s_addr = inet_addr(name); + if (addr.s_addr < 0) { + host = gethostbyname(name); + if (host == NULL) { + anetLog(ANETERROR, "Failed to locate host: %s", name); + return ANETOPENFAIL; + } + memcpy(&addr, host->h_addr_list, sizeof(struct in_addr)); + } + memset(&addresse, 0, sizeof(struct sockaddr_in)); + addresse.sin_family = AF_INET; + addresse.sin_port = iPort; + addresse.sin_addr = addr; + socke = socket(AF_INET, SOCK_STREAM, 0); + status = connect(socke, (struct sockaddr *) &addresse, + sizeof(struct sockaddr_in)); + if (status < 0) { + anetLog(ANETERROR, "Failed to open socket to %s:%d", name, iPort); + return ANETOPENFAIL; + } + anetLog(ANETCON, "Opened socket %d to %s:%d", socke, name, iPort); + return ANETregisterSocket(socke); +} + +/*--------------------------------------------------------------------------*/ +void ANETclose(int handle) +{ + pSocketDescriptor socke = NULL; + + socke = findSocketDescriptor(handle); + if (socke == NULL) { + return; + } + close(socke->socket); + anetLog(ANETCON, "Closed socket %d", socke->socket); + if (socke->readBuffer != NULL) { + KillRWBuffer(socke->readBuffer); + } + if (socke->writeBuffer != NULL) { + KillRWBuffer(socke->writeBuffer); + } + if (socke->userData && socke->killUser) { + socke->killUser(socke->userData); + } + if (noConnections > 1) { + *socke = connections[noConnections - 1]; + noConnections--; + sortConnections(); + } else { + noConnections = 0; + } +} + +/*--------------------------------------------------------------------------*/ +static int anetWrite(SocketDescriptor con) +{ + int status, length; + void *pPtr; + + con.lastOpenForWrite = time(NULL); + pPtr = GetRWBufferData(con.writeBuffer, &length); + if (length > 0) { + status = send(con.socket, pPtr, length, 0); + if (status < 0) { + if (errno == EAGAIN) { + return 1; + } + ANETclose(con.handle); + return 0; + } + RemoveRWBufferData(con.writeBuffer, status); + } + return 1; +} + +/*--------------------------------------------------------------------------*/ +static int anetRead(SocketDescriptor con) +{ + int socke, handle, status; + unsigned int len; + struct sockaddr addresse; + char buffer[8192]; + + switch (con.type) { + case SERVERSOCKET: + len = sizeof(struct sockaddr); + socke = accept(con.socket, &addresse, &len); + if (socke < 0) { + return 1; + } + handle = ANETregisterSocket(socke); + if (handle > 0) { + status = con.readCallback(handle, con.userData); + if (status != 1) { + ANETclose(handle); + return 0; + } + } + anetLog(ANETCON, "Accepted socket %d on port %d, handle %d", + socke, con.socket, handle); + break; + case DATASOCKET: + memset(buffer, 0, 8192); + status = recv(con.socket, buffer, 8192, 0); + if (status < 0) { + if (errno == EAGAIN) { + return 1; + } + ANETclose(con.handle); + return 0; + } else if (status == 0) { + /* this means EOF */ + ANETclose(con.handle); + return 0; + } else { + status = StoreRWBuffer(con.readBuffer, buffer, status); + if (status != 1) { + anetLog(ANETERROR, "Read buffer overrun at handle %d, socket %d", + con.handle, con.socket); + } + if (con.readCallback != NULL) { + con.readCallback(con.handle, con.userData); + } + } + break; + } + return 1; +} + +/*---------------------------------------------------------------------------*/ +void ANETprocess(void) +{ + int i, status, count = 0, socke = 0; + fd_set readMask, writeMask; + struct timeval tmo = { 0, 10 }; + + FD_ZERO(&readMask); + FD_ZERO(&writeMask); + for (i = 0; i < noConnections; i++) { + socke = connections[i].socket; + FD_SET(socke, &readMask); + FD_SET(socke, &writeMask); + if (socke > count) { + count = socke; + } + } + + count++; + status = select(count, &readMask, &writeMask, NULL, &tmo); + if (status < 0) { + if (errno == EINTR) { + return; + } + return; + } + + /** + * I always jump out of this loop when a socket is created or closed + * because then the order in the connections array is no longer valid. + * Try again the next time round. + */ + for (i = 0; i < noConnections; i++) { + socke = connections[i].socket; + if (FD_ISSET(socke, &readMask)) { + if (!anetRead(connections[i])) { + return; + } + } + if (FD_ISSET(socke, &writeMask)) { + if (!anetWrite(connections[i])) { + return; + } + } else { + /* + * if I could not write to the socket for three minutes, + * the socket is considered broken and is closed + */ + if (time(NULL) > connections[i].lastOpenForWrite + 180 && + connections[i].type == DATASOCKET) { + ANETclose(connections[i].handle); + return; + } + } + } +} + +/*--------------------------------------------------------------------------*/ +int ANETvalidHandle(int handle) +{ + pSocketDescriptor con = NULL; + + con = findSocketDescriptor(handle); + if (con != NULL) { + return ANETOK; + } else { + return 0; + } +} + +/*---------------------------------------------------------------------------*/ +int ANETinfo(int handle, char *hostname, int hostnameLen) +{ + pSocketDescriptor con = NULL; + struct sockaddr_in sin; + struct hostent *host; + socklen_t len; + + con = findSocketDescriptor(handle); + if (con == NULL) { + return ANETDISCONNECTED; + } else { + if (getpeername(con->socket, (struct sockaddr *) &sin, &len) < 0) { + return ANETSOCKERROR; + } + if ((host = gethostbyaddr((char *) &sin.sin_addr, + sizeof(sin.sin_addr), AF_INET)) == NULL) { + return ANETSOCKERROR; + } + memset(hostname, 0, hostnameLen); + strncpy(hostname, host->h_name, hostnameLen); + } + return 1; +} + +/*---------------------------------------------------------------------------*/ +int ANETwrite(int handle, void *buffer, int count) +{ + pSocketDescriptor con = NULL; + int status; + + con = findSocketDescriptor(handle); + if (con == NULL) { + return ANETDISCONNECTED; + } else { + status = StoreRWBuffer(con->writeBuffer, buffer, count); + /* + first try if ANETprocess can write some and free the buffer + before giving up + */ + if (status != 1) { + ANETprocess(); + status = StoreRWBuffer(con->writeBuffer, buffer, count); + } + if (status != 1) { + anetLog(ANETERROR, "write buffer overrun on handle %d, socket %d", + con->handle, con->socket); + return ANETWRITEBUFFERFULL; + } + } + return ANETOK; +} + +/*---------------------------------------------------------------------------*/ +int ANETread(int handle, void *buffer, int bufferLength) +{ + pSocketDescriptor con = NULL; + int status, length, len; + void *data = NULL; + + con = findSocketDescriptor(handle); + if (con == NULL) { + return ANETDISCONNECTED; + } else { + data = GetRWBufferData(con->readBuffer, &length); + if (length == 0) { + len = 0; + } else if (length >= bufferLength) { + len = bufferLength; + } else { + len = length; + } + if (len > 0) { + memcpy(buffer, data, len); + } + } + return len; +} + +/*---------------------------------------------------------------------------*/ +void *ANETreadPtr(int handle, int *length) +{ + pSocketDescriptor con = NULL; + void *data = NULL; + + con = findSocketDescriptor(handle); + if (con == NULL) { + return NULL; + } else { + data = GetRWBufferData(con->readBuffer, length); + return data; + } +} + +/*---------------------------------------------------------------------------*/ +void ANETreadConsume(int handle, int count) +{ + pSocketDescriptor con = NULL; + + con = findSocketDescriptor(handle); + if (con == NULL) { + return; + } else { + RemoveRWBufferData(con->readBuffer, count); + } +} + +/*---------------------------------------------------------------------------*/ +void ANETsetReadCallback(int handle, ANETcallback cb, + void *userData, ANETkill killUser) +{ + pSocketDescriptor con = NULL; + + con = findSocketDescriptor(handle); + if (con == NULL) { + return; + } else { + con->readCallback = cb; + con->userData = userData; + con->killUser = killUser; + } +} + +/*----------------------------------------------------------------------------*/ +int ANETreadTillTerm(int handle, + ANETtermCallback tcb, void *termData, + ANETwait wcb, void *waitData, char **buffer) +{ + pSocketDescriptor con = NULL; + char *data; + int length, status; + + while (wcb(waitData) > 0) { + ANETprocess(); + con = findSocketDescriptor(handle); + if (con == NULL) { + return ANETDISCONNECTED; + } + data = GetRWBufferData(con->readBuffer, &length); + if (length > 0) { + status = tcb(data, length, termData); + if (status > 0) { + *buffer = malloc(status * sizeof(char)); + if (*buffer != NULL) { + memcpy(*buffer, data, status); + } + RemoveRWBufferData(con->readBuffer, status); + return ANETOK; + } + } + } + return ANETTIMEOUT; +} diff --git a/asynnet.h b/asynnet.h new file mode 100644 index 00000000..46a1b556 --- /dev/null +++ b/asynnet.h @@ -0,0 +1,209 @@ +/** + * Asynchronous networking for SICS and other programs. This module centrally manages + * a number of network connections for a client program. It is a layer between the + * program and the network which manages non blocking network I/O. To this purpose, the + * client program has to call ANETprocess at convenient intervalls. This module + * has a couple of features: + * - Connections are abstracted to handles which are guranteed to be unique + * rather then socket numbers. Socket numbers may be reused by the OS. + * - This module allows upper level code to figure out if a connection is still + * connected or not. + * - This module introduces a buffer layer between the socket and the application. + * Thus the upper layer does not have to worry much about I/O blocking. This + * is taken care of by this module both for reading and writing. + * - All I/O is non blocking. + * - This module can detect if a client is hanging and close the connection then. + * Hanging is detected by not being able to write to the client for some period + * of time. + * + * copyright: see file COPYRIGHT + * + * Mark Koennecke, January 2009 + */ +#ifndef ASYNNET_H_ +#define ASYNNET_H_ +/*=================== error codes ========================================*/ +#define ANETOK 1 +#define ANETDISCONNECTED -10000 +#define ANETWRITEBUFFERFULL -10001 +#define ANETTIMEOUT -10002 +#define ANETOPENFAIL -10003 +#define ANETSOCKERROR -10004 +#define ANETMEM -10005 +#define ANETOUTOFSOCKETS -10006 +/*================== log levels ==========================================*/ +#define ANETNONE 0 +#define ANETERROR 1 +#define ANETIO 2 +#define ANETCON 3 +/*================== callback functions ==================================*/ +/** + * \brief Callback called when a connection has been accepted on a + * port or data is ready. + * \param handle The handle of the new network connection + * \return 1 if the new connection can be accepted or 0 if not. + */ +typedef int (*ANETcallback) (int handle, void *userData); +/** + * \brief a callback which is called in order to determine if a + * a terminator is present in the data. + * \param data The data to inspect + * \param length The length of the data to inspect + * \param userData An opaque pointer passed through to this + * function + * \return -1 when no terminator is in the data, an integer pointing + * to after the terminator in data (in bytes). + */ +typedef int (*ANETtermCallback) (void *data, int length, void *userData); +/** + * \brief a callback function for waiting on some event. + * This is typically called to do something else while waiting + * for data to arrive. It can also return a negative return value, + * which will effectively implement a timeout. + * \param userData An opaque pointer passed through to this callback + * \return 1 to continue waiting, -1 for a timeout. + */ +typedef int (*ANETwait) (void *userData); +/** + * \brief callback to log events in ANET + * \param level The level of the logging message + * \param txt The logging data + * \param userData An opaque pointer passed through to ANETlog + */ +typedef void (*ANETlog) (int level, char *txt, void *userData); +/** + * \brief a callback for killing userdata associated with a read callback. + * This is called in ANETclose, if defined. + * \param userData The user data to kill. + */ +typedef void (*ANETkill) (void *userData); +/*===================== open/close functions =============================*/ +/** + * * \brief open a server port + * \param iPort The port number at which to listen for + * connections. + * \param cb A callback which will be called whenever a new connection + * has been accepted on this port. + * \prama userData An opaque pointer to be passed as an argument to the + * callback function. + * \return A handle for the server port or a negative error code. + */ +int ANETopenServerPort(int iPort, ANETcallback cb, void *userData); +/** + * \brief open a client connection to a server. + * \param name the computer name of the server + * \param iPort The port number at which the server is listening + * \return A handle to the open port or a negative error code. + */ +int ANETconnect(char *name, int iPort); +/** + * \brief register a socket to be managed by this module. The socket + * may have been obtained by any means. + * \param socket The file descriptor of the socket + * \return A handle to use for this socket later on. + */ +int ANETregisterSocket(int socket); +/** + * \brief close a connection + * \param handle The handle of the connection + */ +void ANETclose(int handle); +/** + * \brief This function drives I/O processing, i.e. reading and writing. + * This has to be called by the client of this module at regular intervalls. + */ +void ANETprocess(void); +/** + * \brief tests if a handle is still a valid connection + * \param handle The handle to test. + * \return 1 if this is still a connected socket, 0 else. + */ +int ANETvalidHandle(int handle); +/** + * \brief figure out to which host we are connected. + * \param handle The connection handle + * \param hostname a buffer to copy the hostname into + * \param hostNameLen the length of the hostname buffer + * \return 1 on success, a negative error code else. + */ +int ANETinfo(int handle, char *hostname, int hostNameLen); +/*=================== I/O functions =========================================== + * For reading there are possibilities: + * - Raw reading happens through the combination of + * ANETread and ANETreadConsume. + * - Another way for raw reading is to register a read callback which is + * called anytime new data arrives. + * - The usual case is to wait for a line of terminated command input. This is + * done through ANETreadTillterm. + * ==========================================================================*/ +/** + * \brief write to the network + * \param handle The handle for the connection + * \param buffer A pointer to the data to write + * \param count The number of bytes to write. + * \return 1 on success, 0 on failure + */ +int ANETwrite(int handle, void *buffer, int count); +/** + * \brief copy at max bufferLength bytes into buffer. The data is not deleted from + * the read buffer yet. + * \param handle The handle of the connection to read from + * \param buffer a pointer to an area for holding the data + * \param bufferLength The maximum number of bytes which can be copied into + * the buffer. + * \return The number of bytes copied. Can be 0 if no data is available. On + * errors a negative error code is returned. + */ +int ANETread(int handle, void *buffer, int bufferLength); +/** + * \brief Get a pointer to the data which has been read up to now. + * Do not mess with the data!! Else the result may be badly defined! + * \param handle The handle for the connection + * \param length will be set to the length of the data read so far. + * \return NULL when the socket is disconnected, a pointer else. + */ +void *ANETreadPtr(int handle, int *length); +/** + * \brief remove count bytes from the read buffer. + * \param handle The handle for the connection. + * \param count The number of bytes which can be removed from the + * read buffer. + */ +void ANETreadConsume(int handle, int count); +/** + * \brief set a callback to be called when data is available at the port. + * \param handle The handle of the connection + * \param cb The callback function to call + * \param userData An opaque pointer passed on as a parameter to the + * callback function. + */ +void ANETsetReadCallback(int handle, ANETcallback cb, void *userData, + ANETkill killUser); +/** + * \brief wait for terminated data to arrive. + * \param handle The connection handle to read from + * \param tcb a callback function which determines if a terminator is in the + * data. + * \param termData An opaque data pointer passed on to tcb + * \param wcb A callback function called while waiting for data + * to arrive. + * \param waitData An opaque pointer passed on to wcb + * \param buffer A newly allocated buffer holding the data as read + * including the terminator. + * \return 1 on success, a negative error code else. + */ +int ANETreadTillTerm(int handle, + ANETtermCallback tcb, void *termData, + ANETwait wcb, void *waitData, char **buffer); +/** + * Note to Markus: suitable callbacks for the standard case: waiting for \n and + * TaskYield for waiting to a timeout will become part of nread.h, .c. + */ +/*========================== system ====================================*/ +/** + * \brief install a logging function + * \param lcb The logging function to install + * \param userData An opaque pointer with data for lcb + */ +void ANETsetLog(ANETlog lcb, void *userData); +#endif /*ASYNNET_H_ */ diff --git a/protocol.c b/protocol.c index 2a80b99a..71f58e13 100644 --- a/protocol.c +++ b/protocol.c @@ -476,7 +476,7 @@ void sycformat(char *tag, OutCode msgFlag, pDynString msgString, int SCWriteSycamore(SConnection * pCon, char *pBuffer, int iOut) { int iRet; - char pBueffel[MAXMSG], *pBufferFrom, *pBufferTo; + char pBueffel[MAXMSG]; long taskID = 0; /* char pPrefix[40];*/ pDynString pMsg = NULL; @@ -487,16 +487,7 @@ int SCWriteSycamore(SConnection * pCon, char *pBuffer, int iOut) if (strlen(pBuffer) == 0) { return 0; } - /* Strip \r and \n */ - for (pBufferFrom = pBufferTo = pBuffer;; pBufferFrom++) { - if (*pBufferFrom == '\r' || *pBufferFrom == '\n') - continue; - *pBufferTo = *pBufferFrom; - if (*pBufferTo == '\0') - break; - pBufferTo++; - } - + if (!SCVerifyConnection(pCon)) { return 0; } diff --git a/scriptcontext.c b/scriptcontext.c index d02fad14..4f3e5a9f 100644 --- a/scriptcontext.c +++ b/scriptcontext.c @@ -1005,9 +1005,17 @@ static int SctMakeController(SConnection * con, SicsInterp * sics, return 0; } - parent = FindHdbParent(NULL, argv[1], &nodeName, con); - if (parent == NULL) - return 0; /* error message already written */ + /* + * Install into the Hipadaba when full path given + */ + if(strstr(argv[1],"/") != NULL){ + parent = FindHdbParent(NULL, argv[1], &nodeName, con); + if (parent == NULL) + return 0; /* error message already written */ + } else { + nodeName = argv[1]; + parent = NULL; + } controller = calloc(1, sizeof(*controller)); assert(controller); @@ -1022,7 +1030,9 @@ static int SctMakeController(SConnection * con, SicsInterp * sics, ccmd->pPrivate = controller; ccmd->KillPrivate = SctKillController; - AddHipadabaChild(parent, controller->node, con); + if(parent != NULL){ + AddHipadabaChild(parent, controller->node, con); + } controller->devser = DevMake(con, argc - 2, argv + 2); if (!controller->devser) diff --git a/sicshipadaba.c b/sicshipadaba.c index e14e815b..82d3cdcb 100644 --- a/sicshipadaba.c +++ b/sicshipadaba.c @@ -3310,7 +3310,7 @@ static int GetSICSHdbProperty(SConnection * pCon, SicsInterp * pSics, } status = GetHdbProperty(targetNode, argv[2], buffer, 511); if (status != 1) { - SCPrintf(pCon, eValue, "ERROR: property %s not found", argv[2]); + SCPrintf(pCon, eError, "ERROR: property %s not found", argv[2]); return 0; } SCPrintf(pCon, eValue, "%s.%s = %s", argv[1], argv[2], buffer);