Esempio n. 1
0
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]);
				}
			}
		}
	}
}
Esempio n. 2
0
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;
	}
}