Config Tree API
API Reference
Manage Config Tree
The Config Tree API is used by apps to read and write their specific configurations. Each app is given an isolated tree. The system utilities store their configuration in the root
tree. Trees are created when they are first accessed by a user or component/process etc. Apps automatically get read access to a tree with the same name as the app. Apps can also be granted read or write access to any other tree with any other name (e.g., a tree named foo
can be shared with appA and appB).
Paths in the tree look like traditional Unix style paths like this:
/path/to/my/value
The path root is the root of the tree where the app has been given access. If the app has permission to access another tree, the path can also include the name of the other tree, followed by a colon.
secondTree:/path/to/my/value
In this case, a value named value
is read from the tree named secondTree
The tree is broken down into stems and leaves.
A stem is a node that has at least one child node. A leaf has no children, but can hold a value.
The config tree supports string, signed integer, boolean, floating point, and empty values. It's recommended to store anything more complex using stems and leaves, which enhances readablity and debugging. It also sidesteps nasty cross platform alignment issues.
Read and Write Transactions
The config tree uses simple transactions to work with its data. Both read and write transactions are supported. Use read transactions to ensure you can atomically read multiple values from your configuration while keeping consistency with third parties trying to write data.
To prevent a single client from locking out other clients, read and write transactions have their own configurable timeout.
During a write transaction, both reading and writing are allowed. If you write a value during a transaction and read from that value again, you will get the same value you wrote. Third party clients will continue to see the old value. It's not until you commit your transaction that third parties will begin to see your updated value.
During read transactions, writes are not permitted and are thrown away.
Transactions are started by creating an iterator. Either a read or write iterator can be created. To end the transaction, you can delete the iterator, cancelling the transaction. Or,for write transactions, you can commit the iterator.
You can have multiple read transactions against the tree. They won't block other transactions from being creating. A read transaction won't block creating a write transaction either. A read transaction only blocks a write transaction from being comitted to the tree.
A write transaction in progress will also block creating another write transaction. If a write transaction is in progress when the request for another write transaction comes in, the secondary request will be blocked. This secondary request will remain blocked until the first transaction has been comitted or has timed out. The transaction timeout default is 30 seconds. You can extend the timeout by setting a value (in seconds) in configTree/transactionTimeout
.
Iterating the Tree
This code sample shows how to iterate a specified node and print its contents:
{do{char stringBuffer[MAX_CFG_STRING] = { 0 };{case LE_CFG_TYPE_STEM:{printf("%s/\n", stringBuffer);if (le_cfg_GoToFirstChild(iteratorRef) == LE_OK){PrintNode(iteratorRef);le_cfg_GoToNode(iteratorRef, "..");}}break;case LE_CFG_TYPE_EMPTY:printf("%s = *empty*\n", stringBuffer);break;case LE_CFG_TYPE_BOOL:printf("%s = %s\n",stringBuffer,break;case LE_CFG_TYPE_INT:break;case LE_CFG_TYPE_FLOAT:break;case LE_CFG_TYPE_STRING:printf("%s = ", stringBuffer);LE_ASSERT(le_cfg_GetString(iteratorRef,"",stringBuffer,sizeof(stringBuffer),"") == LE_OK);printf("%s\n", stringBuffer);break;case LE_CFG_TYPE_DOESNT_EXIST:printf("%s = ** DENIED **\n", stringBuffer);break;}}while (le_cfg_GoToNextSibling(iteratorRef) == LE_OK);}PrintNode(iteratorRef);le_cfg_CancelTxn(iteratorRef);
Writing Configuration Data
This code sample uses a write transaction to update a target's IP address so the data is written atomically.
void SetIp4Static(le_cfg_IteratorRef_t iteratorRef,const char* interfaceNamePtr,const char* ipAddrPtr,const char* netMaskPtr){// Change current tree position to the base ip4 node.char nameBuffer[MAX_CFG_STRING] = { 0 };int r = snprintf(nameBuffer, sizeof(nameBuffer), "/system/%s/ip4", interfaceNamePtr);LE_ASSERT((r >= 0) && (r < sizeof(nameBuffer));le_cfg_GoToNode(iteratorRef, nameBuffer);le_cfg_SetString(iteratorRef, "addr", ipAddrPtr);le_cfg_SetString(iteratorRef, "mask", netMaskPtr);le_cfg_CommitTxn(iteratorRef);}
Reading Configuration Data
This is a code sample of a read transaction.
le_result_t GetIp4Static(le_cfg_IteratorRef_t iteratorRef,const char* interfaceNamePtr,char* ipAddrPtr,size_t ipAddrSize,char* netMaskPtr,size_t netMaskSize){// Change current tree position to the base ip4 node.char nameBuffer[MAX_CFG_STRING] = { 0 };int r = snprintf(nameBuffer, sizeof(nameBuffer), "/system/%s/ip4", interfaceNamePtr);if (r < 0){return LE_FAULT;}else if (r >= sizeof(nameBuffer)){return LE_OVERFLOW;}{LE_WARN("Configuration not found.");return LE_NOT_FOUND;}le_cfg_GoToNode(iteratorRef, nameBuffer);return LE_OK;}
Working without Transactions
It's possible to ignore iterators and transactions entirely (e.g., if all you need to do is read or write some simple values in the tree).
The non-transactional reads and writes work almost identically to the transactional versions. They just don't explictly take an iterator object. The "quick" functions internally use an implicit transaction. This implicit transaction wraps one get or set, and does not protect your code from other activity in the system.
Because these functions don't take an explicit transaction, they can't work with relative paths. If a relative path is given, the path will be considered relative to the tree's root.
Translating this to a "quick" (non-transactional) example looks like this:
void ClearIpInfo(const char* interfaceNamePtr){char pathBuffer[MAX_CFG_STRING] = { 0 };snprintf(pathBuffer, sizeof(pathBuffer), "/system/%s/ip4/", interfaceNamePtr);le_cfg_QuickDeleteNode(pathBuffer);}
- Note
- Because each read is independant, there's no guarantee of consistency between them. If another process changes one of the values while you read/write the other, the two values could be read out of sync.
You'll also need to set Read/Write Access.
Copyright (C) Sierra Wireless Inc.