void caerConfigInit(const char *configFile, int argc, char *argv[]) { // If configFile is NULL, no config file will be accessed at all, // and neither will it be written back at shutdown. if (configFile != NULL) { // Let's try to open the file for reading, or create it. int configFileFd = open(configFile, O_RDONLY | O_CREAT, S_IWUSR | S_IRUSR | S_IRGRP); if (configFileFd >= 0) { // File opened for reading successfully. // This means it exists and we can access it, so let's remember // it for writing the config later at shutdown (if permitted). caerConfigFilePath = realpath(configFile, NULL); // Determine if there is actual content to parse first. struct stat configFileStat; fstat(configFileFd, &configFileStat); if (configFileStat.st_size > 0) { sshsNodeImportSubTreeFromXML(sshsGetNode(sshsGetGlobal(), "/"), configFileFd, true); } close(configFileFd); // Ensure configuration is written back at shutdown. atexit(&caerConfigShutDownWriteBack); } else { caerLog(CAER_LOG_EMERGENCY, "Config", "Could not create and/or read from the configuration file '%s'. Error: %d.", configFile, errno); exit(EXIT_FAILURE); } } else { caerLog(CAER_LOG_EMERGENCY, "Config", "No configuration file defined, using default values for everything."); } // Override with command line arguments if requested. if (argc > 1) { // Format: -o node key type value (5 arguments). Equal to caerctl format. for (size_t i = 1; i < (size_t) argc; i += 5) { if ((i + 4) < (size_t) argc && caerStrEquals(argv[i], "-o")) { sshsNode node = sshsGetNode(sshsGetGlobal(), argv[i + 1]); if (node == NULL) { caerLog(CAER_LOG_EMERGENCY, "Config", "SSHS Node %s doesn't exist.", argv[i + 1]); continue; } if (!sshsNodeStringToNodeConverter(node, argv[i + 2], argv[i + 3], argv[i + 4])) { caerLog(CAER_LOG_EMERGENCY, "Config", "Failed to convert attribute %s of type %s with value %s.", argv[i + 2], argv[i + 3], argv[i + 4]); } } } } }
static void caerConfigServerHandleRequest(int connectedClientSocket, uint8_t action, uint8_t type, const uint8_t *extra, size_t extraLength, const uint8_t *node, size_t nodeLength, const uint8_t *key, size_t keyLength, const uint8_t *value, size_t valueLength) { UNUSED_ARGUMENT(extra); caerLog(CAER_LOG_DEBUG, "Config Server", "Handling request: action=%" PRIu8 ", type=%" PRIu8 ", extraLength=%zu, nodeLength=%zu, keyLength=%zu, valueLength=%zu.", action, type, extraLength, nodeLength, keyLength, valueLength); // Interpretation of data is up to each action individually. sshs configStore = sshsGetGlobal(); switch (action) { case NODE_EXISTS: { // We only need the node name here. Type is not used (ignored)! bool result = sshsExistsNode(configStore, (const char *) node); // Send back result to client. Format is the same as incoming data. const uint8_t *sendResult = (const uint8_t *) ((result) ? ("true") : ("false")); size_t sendResultLength = (result) ? (5) : (6); caerConfigSendResponse(connectedClientSocket, NODE_EXISTS, BOOL, sendResult, sendResultLength); break; } case ATTR_EXISTS: { bool nodeExists = sshsExistsNode(configStore, (const char *) node); // Only allow operations on existing nodes, this is for remote // control, so we only manipulate what's already there! if (!nodeExists) { // Send back error message to client. caerConfigSendError(connectedClientSocket, "Node doesn't exist. Operations are only allowed on existing data."); break; } // This cannot fail, since we know the node exists from above. sshsNode wantedNode = sshsGetNode(configStore, (const char *) node); // Check if attribute exists. bool result = sshsNodeAttributeExists(wantedNode, (const char *) key, type); // Send back result to client. Format is the same as incoming data. const uint8_t *sendResult = (const uint8_t *) ((result) ? ("true") : ("false")); size_t sendResultLength = (result) ? (5) : (6); caerConfigSendResponse(connectedClientSocket, ATTR_EXISTS, BOOL, sendResult, sendResultLength); break; } case GET: { bool nodeExists = sshsExistsNode(configStore, (const char *) node); // Only allow operations on existing nodes, this is for remote // control, so we only manipulate what's already there! if (!nodeExists) { // Send back error message to client. caerConfigSendError(connectedClientSocket, "Node doesn't exist. Operations are only allowed on existing data."); break; } // This cannot fail, since we know the node exists from above. sshsNode wantedNode = sshsGetNode(configStore, (const char *) node); // Check if attribute exists. Only allow operations on existing attributes! bool attrExists = sshsNodeAttributeExists(wantedNode, (const char *) key, type); if (!attrExists) { // Send back error message to client. caerConfigSendError(connectedClientSocket, "Attribute of given type doesn't exist. Operations are only allowed on existing data."); break; } union sshs_node_attr_value result = sshsNodeGetAttribute(wantedNode, (const char *) key, type); char *resultStr = sshsHelperValueToStringConverter(type, result); if (resultStr == NULL) { // Send back error message to client. caerConfigSendError(connectedClientSocket, "Failed to allocate memory for value string."); } else { caerConfigSendResponse(connectedClientSocket, GET, type, (const uint8_t *) resultStr, strlen(resultStr) + 1); free(resultStr); } // If this is a string, we must remember to free the original result.str // too, since it will also be a copy of the string coming from SSHS. if (type == STRING) { free(result.string); } break; } case PUT: { bool nodeExists = sshsExistsNode(configStore, (const char *) node); // Only allow operations on existing nodes, this is for remote // control, so we only manipulate what's already there! if (!nodeExists) { // Send back error message to client. caerConfigSendError(connectedClientSocket, "Node doesn't exist. Operations are only allowed on existing data."); break; } // This cannot fail, since we know the node exists from above. sshsNode wantedNode = sshsGetNode(configStore, (const char *) node); // Check if attribute exists. Only allow operations on existing attributes! bool attrExists = sshsNodeAttributeExists(wantedNode, (const char *) key, type); if (!attrExists) { // Send back error message to client. caerConfigSendError(connectedClientSocket, "Attribute of given type doesn't exist. Operations are only allowed on existing data."); break; } // Put given value into config node. Node, attr and type are already verified. const char *typeStr = sshsHelperTypeToStringConverter(type); if (!sshsNodeStringToNodeConverter(wantedNode, (const char *) key, typeStr, (const char *) value)) { // Send back error message to client. caerConfigSendError(connectedClientSocket, "Impossible to convert value according to type."); break; } // Send back confirmation to the client. caerConfigSendResponse(connectedClientSocket, PUT, BOOL, (const uint8_t *) "true", 5); break; } case GET_CHILDREN: { bool nodeExists = sshsExistsNode(configStore, (const char *) node); // Only allow operations on existing nodes, this is for remote // control, so we only manipulate what's already there! if (!nodeExists) { // Send back error message to client. caerConfigSendError(connectedClientSocket, "Node doesn't exist. Operations are only allowed on existing data."); break; } // This cannot fail, since we know the node exists from above. sshsNode wantedNode = sshsGetNode(configStore, (const char *) node); // Get the names of all the child nodes and return them. size_t numNames; const char **childNames = sshsNodeGetChildNames(wantedNode, &numNames); // No children at all, return empty. if (childNames == NULL) { // Send back error message to client. caerConfigSendError(connectedClientSocket, "Node has no children."); break; } // We need to return a big string with all of the child names, // separated by a NUL character. size_t namesLength = 0; for (size_t i = 0; i < numNames; i++) { namesLength += strlen(childNames[i]) + 1; // +1 for terminating NUL byte. } // Allocate a buffer for the names and copy them over. char namesBuffer[namesLength]; for (size_t i = 0, acc = 0; i < numNames; i++) { size_t len = strlen(childNames[i]) + 1; memcpy(namesBuffer + acc, childNames[i], len); acc += len; } caerConfigSendResponse(connectedClientSocket, GET_CHILDREN, STRING, (const uint8_t *) namesBuffer, namesLength); break; } case GET_ATTRIBUTES: { bool nodeExists = sshsExistsNode(configStore, (const char *) node); // Only allow operations on existing nodes, this is for remote // control, so we only manipulate what's already there! if (!nodeExists) { // Send back error message to client. caerConfigSendError(connectedClientSocket, "Node doesn't exist. Operations are only allowed on existing data."); break; } // This cannot fail, since we know the node exists from above. sshsNode wantedNode = sshsGetNode(configStore, (const char *) node); // Get the keys of all the attributes and return them. size_t numKeys; const char **attrKeys = sshsNodeGetAttributeKeys(wantedNode, &numKeys); // No attributes at all, return empty. if (attrKeys == NULL) { // Send back error message to client. caerConfigSendError(connectedClientSocket, "Node has no attributes."); break; } // We need to return a big string with all of the attribute keys, // separated by a NUL character. size_t keysLength = 0; for (size_t i = 0; i < numKeys; i++) { keysLength += strlen(attrKeys[i]) + 1; // +1 for terminating NUL byte. } // Allocate a buffer for the keys and copy them over. char keysBuffer[keysLength]; for (size_t i = 0, acc = 0; i < numKeys; i++) { size_t len = strlen(attrKeys[i]) + 1; memcpy(keysBuffer + acc, attrKeys[i], len); acc += len; } caerConfigSendResponse(connectedClientSocket, GET_ATTRIBUTES, STRING, (const uint8_t *) keysBuffer, keysLength); break; } case GET_TYPES: { bool nodeExists = sshsExistsNode(configStore, (const char *) node); // Only allow operations on existing nodes, this is for remote // control, so we only manipulate what's already there! if (!nodeExists) { // Send back error message to client. caerConfigSendError(connectedClientSocket, "Node doesn't exist. Operations are only allowed on existing data."); break; } // This cannot fail, since we know the node exists from above. sshsNode wantedNode = sshsGetNode(configStore, (const char *) node); // Check if any keys match the given one and return its types. size_t numTypes; enum sshs_node_attr_value_type *attrTypes = sshsNodeGetAttributeTypes(wantedNode, (const char *) key, &numTypes); // No attributes for specified key, return empty. if (attrTypes == NULL) { // Send back error message to client. caerConfigSendError(connectedClientSocket, "Node has no attributes with specified key."); break; } // We need to return a big string with all of the attribute types, // separated by a NUL character. size_t typesLength = 0; for (size_t i = 0; i < numTypes; i++) { const char *typeString = sshsHelperTypeToStringConverter(attrTypes[i]); typesLength += strlen(typeString) + 1; // +1 for terminating NUL byte. } // Allocate a buffer for the types and copy them over. char typesBuffer[typesLength]; for (size_t i = 0, acc = 0; i < numTypes; i++) { const char *typeString = sshsHelperTypeToStringConverter(attrTypes[i]); size_t len = strlen(typeString) + 1; memcpy(typesBuffer + acc, typeString, len); acc += len; } caerConfigSendResponse(connectedClientSocket, GET_TYPES, STRING, (const uint8_t *) typesBuffer, typesLength); break; } default: // Unknown action, send error back to client. caerConfigSendError(connectedClientSocket, "Unknown action."); break; } }