//-------------------------------------------------------------------------------------------------- static void ResetApp ( void ) { le_cfg_ConnectService(); le_cfgAdmin_ConnectService(); // Get a write iterator to the application node. le_cfg_IteratorRef_t cfgIter = le_cfg_CreateWriteTxn("/apps"); le_cfg_GoToNode(cfgIter, AppName); // Check if this is a temporary configuration that was previously created by this or a similar // tool. if (le_cfg_IsEmpty(cfgIter, CFG_DEBUG_TOOL)) { fprintf(stderr, "This application already has its original configuration.\n"); exit(EXIT_FAILURE); } // Blow away what's in there now. le_cfg_GoToNode(cfgIter, "/apps"); le_cfg_DeleteNode(cfgIter, AppName); le_cfg_CommitTxn(cfgIter); // NOTE: Currently there is a bug in the config DB where deletions and imports cannot be done in // the same transaction so we must do it in two transactions. cfgInstall_Add(AppName); }
//-------------------------------------------------------------------------------------------------- static void SetObject9InstanceForApp ( const char* appName, ///< The name of the application in question. assetData_InstanceDataRef_t instanceRef ///< The instance of object 9 to link to. Pass NULL if ///< the link is to be cleared. ) //-------------------------------------------------------------------------------------------------- { le_cfg_IteratorRef_t iterRef = le_cfg_CreateWriteTxn(CFG_OBJECT_INFO_PATH); if (instanceRef != NULL) { int instanceId; LE_ASSERT(assetData_GetInstanceId(instanceRef, &instanceId) == LE_OK); le_cfg_GoToNode(iterRef, appName); le_cfg_SetInt(iterRef, "oiid", instanceId); LE_DEBUG("Application '%s' mapped to instance %d.", appName, instanceId); } else { le_cfg_DeleteNode(iterRef, appName); } le_cfg_CommitTxn(iterRef); }
//-------------------------------------------------------------------------------------------------- void userAddRemove_Add ( const char* appName ) //-------------------------------------------------------------------------------------------------- { le_result_t result; uid_t uid; gid_t gid; char userName[256] = "app"; result = le_utf8_Append(userName, appName, sizeof(userName), NULL); LE_FATAL_IF(result != LE_OK, "App name '%s' is too long.", appName); LE_INFO("Creating user '%s' for application '%s'.", userName, appName); // Start a read transaction and go to node /apps/app-name le_cfg_ConnectService(); le_cfg_IteratorRef_t i = le_cfg_CreateReadTxn("/apps"); le_cfg_GoToNode(i, appName); // If the node doesn't exist, bail out. if (!le_cfg_NodeExists(i, "")) { fprintf(stderr, "** ERROR: App '%s' doesn't exist in the system configuration.\n", appName); } else { result = user_Create(userName, &uid, &gid); if (result == LE_OK) { printf("Created user '%s' (uid %u, gid %u).\n", userName, uid, gid); // TODO: Groups configuration. le_cfg_CancelTxn(i); exit(EXIT_SUCCESS); } else if (result == LE_DUPLICATE) { // TODO: Verify correct groups configuration. printf("User '%s' already exists (uid %u, gid %u).\n", userName, uid, gid); le_cfg_CancelTxn(i); exit(EXIT_SUCCESS); } else { fprintf(stderr, "** ERROR: user_Create() failed for user '%s'.\n", userName); } } le_cfg_CancelTxn(i); exit(EXIT_FAILURE); }
//-------------------------------------------------------------------------------------------------- LE_SHARED appCfg_Iter_t appCfg_FindApp ( const char* appName ///< [IN] Name of the app to find. ) { AppsIter_t* iterPtr = appCfg_CreateAppsIter(); le_cfg_GoToNode(iterPtr->cfgIter, appName); if (le_cfg_NodeExists(iterPtr->cfgIter, "") == false) { appCfg_DeleteIter(iterPtr); return NULL; } return iterPtr; }
//-------------------------------------------------------------------------------------------------- static le_result_t GetEnvironmentVariables ( proc_Ref_t procRef, ///< [IN] The process to get the environment variables for. EnvVar_t envVars[], ///< [IN] The list of environment variables. size_t maxNumEnvVars ///< [IN] The maximum number of items envVars can hold. ) { le_cfg_IteratorRef_t procCfg = le_cfg_CreateReadTxn(procRef->cfgPathRoot); le_cfg_GoToNode(procCfg, CFG_NODE_ENV_VARS); if (le_cfg_GoToFirstChild(procCfg) != LE_OK) { LE_WARN("No environment variables for process '%s'.", procRef->name); le_cfg_CancelTxn(procCfg); return LE_NOT_FOUND; } int i; for (i = 0; i < maxNumEnvVars; i++) { if ( (le_cfg_GetNodeName(procCfg, "", envVars[i].name, LIMIT_MAX_ENV_VAR_NAME_BYTES) != LE_OK) || (le_cfg_GetString(procCfg, "", envVars[i].value, LIMIT_MAX_PATH_BYTES, "") != LE_OK) ) { LE_ERROR("Error reading environment variables for process '%s'.", procRef->name); le_cfg_CancelTxn(procCfg); return LE_FAULT; } if (le_cfg_GoToNextSibling(procCfg) != LE_OK) { break; } else if (i >= maxNumEnvVars-1) { LE_ERROR("There were too many environment variables for process '%s'.", procRef->name); le_cfg_CancelTxn(procCfg); return LE_FAULT; } } le_cfg_CancelTxn(procCfg); return i + 1; }
//-------------------------------------------------------------------------------------------------- LE_SHARED appCfg_Iter_t appCfg_CreateAppProcIter ( appCfg_Iter_t appIterRef ///< [IN] Apps iterator ) { CheckFor(appIterRef, ITER_TYPE_APP); char pathStr[LE_CFG_STR_LEN_BYTES] = ""; le_cfg_GetPath(appIterRef->cfgIter, "", pathStr, sizeof(pathStr)); AppsIter_t* iterPtr = le_mem_ForceAlloc(AppIterPool); iterPtr->type = ITER_TYPE_PROC; iterPtr->cfgIter = le_cfg_CreateReadTxn(pathStr); le_cfg_GoToNode(iterPtr->cfgIter, CFG_PROCS_LIST); iterPtr->atFirst = true; return iterPtr; }
//-------------------------------------------------------------------------------------------------- static void SetRLimit ( pid_t pid, // The pid of the process to set the limit for. le_cfg_IteratorRef_t procCfg, // The iterator for the process. This is a iterator is owned by // the caller and should not be deleted in this function. const char* resourceName, // The resource name in the config tree. int resourceID, // The resource ID that setrlimit() expects. rlim_t defaultValue // The default value for this resource limit. ) { // Initialize the limit to the defaults. struct rlimit lim = {defaultValue, defaultValue}; // Move the iterator to the resource limit to read. le_cfg_GoToNode(procCfg, resourceName); if (le_cfg_NodeExists(procCfg, "") == false) { LE_WARN("No resource limit %s. Using the default value %lu.", resourceName, defaultValue); } else { // Try reading the resource limit from the config tree. if (GetCfgResourceLimit(procCfg, &(lim.rlim_max)) != LE_OK) { LE_ERROR("Configured resource limit %s is invalid. Using the default value %lu.", resourceName, defaultValue); } else { // Check that the limit does not exceed the maximum. if ( (resourceID == RLIMIT_NOFILE) && (lim.rlim_max > MAX_LIMIT_NUM_FD) ) { LE_ERROR("Resource limit %s is greater than the maximum allowed limit (%d). Using the \ maximum allowed value.", resourceName, MAX_LIMIT_NUM_FD); lim.rlim_max = MAX_LIMIT_NUM_FD; } } // Move the iterator back to where it was. LE_ASSERT(le_cfg_GoToParent(procCfg) == LE_OK); }
//-------------------------------------------------------------------------------------------------- rlim_t resLim_GetSandboxedAppTmpfsLimit ( app_Ref_t appRef ///< [IN] The application to set resource limits for. ) { // Get the resource limit from the config tree. Zero means unlimited for tmpfs mounts and is // not allowed. // Determine the file system limit to set. rlim_t fileSysLimit = DEFAULT_LIMIT_FILE_SYSTEM_SIZE; // Create a config iterator to get the file system limit from the config tree. le_cfg_IteratorRef_t appCfg = le_cfg_CreateReadTxn(app_GetConfigPath(appRef)); le_cfg_GoToNode(appCfg, CFG_NODE_LIMIT_FILE_SYSTEM_SIZE); if (le_cfg_NodeExists(appCfg, "") == false) { LE_WARN("No resource limit %s. Assuming the default value %d.", CFG_NODE_LIMIT_FILE_SYSTEM_SIZE, DEFAULT_LIMIT_FILE_SYSTEM_SIZE); } else if ( (GetCfgResourceLimit(appCfg, &fileSysLimit) != LE_OK) || (fileSysLimit == 0) ) { // Use the default limit. LE_ERROR("Configured resource limit %s is invalid. Assuming the default value %d.", CFG_NODE_LIMIT_FILE_SYSTEM_SIZE, DEFAULT_LIMIT_FILE_SYSTEM_SIZE); fileSysLimit = DEFAULT_LIMIT_FILE_SYSTEM_SIZE; } le_cfg_CancelTxn(appCfg); return fileSysLimit; }
//-------------------------------------------------------------------------------------------------- static assetData_InstanceDataRef_t GetObject9InstanceForApp ( const char* appName, ///< Name of the application in question. bool mapIfNotFound ///< If an instance was created, should a mapping be created for it? ) //-------------------------------------------------------------------------------------------------- { LE_DEBUG("Getting object 9 instance for application '%s'.", appName); // Attempt to read the mapping from the configuration. assetData_InstanceDataRef_t instanceRef = NULL; le_cfg_IteratorRef_t iterRef = le_cfg_CreateReadTxn(CFG_OBJECT_INFO_PATH); le_cfg_GoToNode(iterRef, appName); int instanceId = le_cfg_GetInt(iterRef, "oiid", -1); le_cfg_CancelTxn(iterRef); if (instanceId != -1) { LE_DEBUG("Was mapped to instance, %d.", instanceId); // Looks like there was a mapping. Try to get that instance and make sure it's not taken // by another application. If the instance was taken by another application, remap this // application to a new instance and update the mapping. if (LE_OK == assetData_GetInstanceRefById(LWM2M_NAME, LWM2M_SOFTWARE_UPDATE, instanceId, &instanceRef)) { char newName[MAX_APP_NAME_BYTES] = ""; LE_ASSERT(assetData_client_GetString(instanceRef, O9F_PKG_NAME, newName, sizeof(newName)) == LE_OK); if (strcmp(newName, appName) != 0) { LE_DEBUG("Instance has been taken by '%s', creating new.", newName); LE_ASSERT(assetData_CreateInstanceById(LWM2M_NAME, 9, -1, &instanceRef) == LE_OK); LE_ASSERT(assetData_client_SetString(instanceRef, O9F_PKG_NAME, appName) == LE_OK); if (mapIfNotFound) { LE_DEBUG("Recording new instance id."); SetObject9InstanceForApp(appName, instanceRef); } } else { LE_DEBUG("Instance is existing and has been reused."); } } else { LE_DEBUG("No instance found, creating new as mapped."); LE_ASSERT(assetData_CreateInstanceById(LWM2M_NAME, 9, instanceId, &instanceRef) == LE_OK); LE_ASSERT(assetData_client_SetString(instanceRef, O9F_PKG_NAME, appName) == LE_OK); } } else { LE_DEBUG("No instance mapping found, creating new."); // A mapping was not found. So create a new object, and let the data store assign an // instance Id. If desired, at this point record the instance mapping for later use. LE_ASSERT(assetData_CreateInstanceById(LWM2M_NAME, 9, -1, &instanceRef) == LE_OK); LE_ASSERT(assetData_client_SetString(instanceRef, O9F_PKG_NAME, appName) == LE_OK); if (mapIfNotFound) { LE_DEBUG("Recording new instance id."); SetObject9InstanceForApp(appName, instanceRef); } } return instanceRef; }
//-------------------------------------------------------------------------------------------------- static void Load ( void ) //-------------------------------------------------------------------------------------------------- { le_result_t result; // Connect to the Configuration API server. le_cfg_ConnectService(); // Initialize the "User API". user_Init(); // Start a read transaction on the root of the "system" configuration tree. le_cfg_IteratorRef_t i = le_cfg_CreateReadTxn("system:"); // Tell the Service Directory to delete all existing bindings. SendUnbindAllRequest(); // Iterate over the users collection. le_cfg_GoToNode(i, "/users"); result = le_cfg_GoToFirstChild(i); while (result == LE_OK) { uid_t uid; if (GetUserUid(i, &uid) == LE_OK) { // For each user, iterate over the bindings collection, sending the binding to // the Service Directory. le_cfg_GoToNode(i, "bindings"); result = le_cfg_GoToFirstChild(i); while (result == LE_OK) { SendBindRequest(uid, i); result = le_cfg_GoToNextSibling(i); } // Go back up to the user name node. le_cfg_GoToNode(i, "../.."); } // Move on to the next user. result = le_cfg_GoToNextSibling(i); } // Iterate over the apps collection. le_cfg_GoToNode(i, "/apps"); result = le_cfg_GoToFirstChild(i); while (result == LE_OK) { uid_t uid; if (GetAppUid(i, &uid) == LE_OK) { // For each app, iterate over the bindings collection, sending the binding to // the Service Directory. le_cfg_GoToNode(i, "bindings"); result = le_cfg_GoToFirstChild(i); while (result == LE_OK) { SendBindRequest(uid, i); result = le_cfg_GoToNextSibling(i); } // Go back up to the app's node. le_cfg_GoToNode(i, "../.."); } // Move on to the next app. result = le_cfg_GoToNextSibling(i); } exit(EXIT_SUCCESS); }
//-------------------------------------------------------------------------------------------------- static void ConfigureGdb ( void ) { le_cfg_ConnectService(); le_cfgAdmin_ConnectService(); // Get a write iterator to the application node. le_cfg_IteratorRef_t cfgIter = le_cfg_CreateWriteTxn("/apps"); le_cfg_GoToNode(cfgIter, AppName); // Check if this is a temporary configuration that was previously created by this or a similar // tool. if (!le_cfg_IsEmpty(cfgIter, CFG_DEBUG_TOOL)) { char debugTool[LIMIT_MAX_PATH_BYTES]; // Don't need to check return code because the value is just informative and does not matter // if it is truncated. le_cfg_GetString(cfgIter, CFG_DEBUG_TOOL, debugTool, sizeof(debugTool), ""); fprintf(stderr, "This application has already been configured for %s debug mode.\n", debugTool); exit(EXIT_FAILURE); } // Write into the config's debug tool node to indicate that this configuration has been modified. le_cfg_SetString(cfgIter, CFG_DEBUG_TOOL, "gdb"); // Add 512K to the maxFileSytemBytes so that we can debug this app in sandboxed mode uint32_t maxBytes; maxBytes = le_cfg_GetInt(cfgIter, "maxFileSystemBytes", DEFAULT_LIMIT_MAX_FILE_SYSTEM_BYTES); maxBytes += ADD_FILE_SYSTEM_BYTES; // add an additional 512KBytes LE_INFO("Resetting maxFileSystemBytes to %d bytes", maxBytes); le_cfg_SetInt(cfgIter, "maxFileSystemBytes", maxBytes); // Add gdbserver and libs to the app's 'requires/files' section. le_cfg_GoToNode(cfgIter, "requires/files"); AddImportFiles(cfgIter, &GdbFilesImports, NUM_ARRAY_MEMBERS(GdbFilesImports)); // Add /proc to the app's dirs section. le_cfg_GoToParent(cfgIter); le_cfg_GoToNode(cfgIter, "dirs"); AddImportFiles(cfgIter, &GdbDirsImports, NUM_ARRAY_MEMBERS(GdbDirsImports)); // Delete the list of processes. le_cfg_GoToParent(cfgIter); le_cfg_GoToParent(cfgIter); int i; for (i = 0; i < NumProcs; i++) { char nodePath[LIMIT_MAX_PATH_BYTES]; int n = snprintf(nodePath, sizeof(nodePath), "procs/%s", ProcNames[i]); INTERNAL_ERR_IF(n >= sizeof(nodePath), "Node name is too long."); INTERNAL_ERR_IF(n < 0, "Format error. %m"); le_cfg_DeleteNode(cfgIter, nodePath); } le_cfg_CommitTxn(cfgIter); }
//-------------------------------------------------------------------------------------------------- static le_result_t GetArgs ( proc_Ref_t procRef, ///< [IN] The process to get the args for. char argsBuffers[LIMIT_MAX_NUM_CMD_LINE_ARGS][LIMIT_MAX_ARGS_STR_BYTES], ///< [OUT] A pointer to /// an array of buffers /// used to store /// arguments. char* argsPtr[NUM_ARGS_PTRS] ///< [OUT] An array of pointers that will point to the valid /// arguments list. The list is terminated by NULL. ) { // Get a config iterator to the arguments list. le_cfg_IteratorRef_t procCfg = le_cfg_CreateReadTxn(procRef->cfgPathRoot); le_cfg_GoToNode(procCfg, CFG_NODE_ARGS); if (le_cfg_GoToFirstChild(procCfg) != LE_OK) { LE_ERROR("No arguments for process '%s'.", procRef->name); le_cfg_CancelTxn(procCfg); return LE_FAULT; } size_t ptrIndex = 0; size_t bufIndex = 0; // Record the executable path. if (le_cfg_GetString(procCfg, "", argsBuffers[bufIndex], LIMIT_MAX_ARGS_STR_BYTES, "") != LE_OK) { LE_ERROR("Error reading argument '%s...' for process '%s'.", argsBuffers[bufIndex], procRef->name); le_cfg_CancelTxn(procCfg); return LE_FAULT; } argsPtr[ptrIndex++] = argsBuffers[bufIndex++]; // Record the process name in the list. argsPtr[ptrIndex++] = procRef->name; // Record the arguments in the caller's list of buffers. while(1) { if (le_cfg_GoToNextSibling(procCfg) != LE_OK) { // Terminate the list. argsPtr[ptrIndex] = NULL; break; } else if (bufIndex >= LIMIT_MAX_NUM_CMD_LINE_ARGS) { LE_ERROR("Too many arguments for process '%s'.", procRef->name); le_cfg_CancelTxn(procCfg); return LE_FAULT; } if (le_cfg_IsEmpty(procCfg, "")) { LE_ERROR("Empty node in argument list for process '%s'.", procRef->name); le_cfg_CancelTxn(procCfg); return LE_FAULT; } if (le_cfg_GetString(procCfg, "", argsBuffers[bufIndex], LIMIT_MAX_ARGS_STR_BYTES, "") != LE_OK) { LE_ERROR("Argument too long '%s...' for process '%s'.", argsBuffers[bufIndex], procRef->name); le_cfg_CancelTxn(procCfg); return LE_FAULT; } // Point to the string. argsPtr[ptrIndex++] = argsBuffers[bufIndex++]; } le_cfg_CancelTxn(procCfg); return LE_OK; }
// ------------------------------------------------------------------------------------------------- static le_result_t HandleImportJSONIteration ( le_cfg_IteratorRef_t iterRef, ///< Dump the JSON data into this iterator. json_t* nodePtr ///< From this JSON object. ) // ------------------------------------------------------------------------------------------------- { // Get value json_t* value = json_object_get(nodePtr, JSON_FIELD_VALUE); // Check type const char* typeStr = json_string_value(json_object_get(nodePtr, JSON_FIELD_TYPE)); le_cfg_nodeType_t type = GetNodeTypeFromString(typeStr); switch (type) { case LE_CFG_TYPE_BOOL: le_cfg_SetBool(iterRef, "", json_is_true(value)); break; case LE_CFG_TYPE_STRING: le_cfg_SetString(iterRef, "", json_string_value(value)); break; case LE_CFG_TYPE_INT: le_cfg_SetInt(iterRef, "", json_integer_value(value)); break; case LE_CFG_TYPE_FLOAT: le_cfg_SetFloat(iterRef, "", json_real_value(value)); break; case LE_CFG_TYPE_STEM: { // Iterate on children json_t* childrenPtr = json_object_get(nodePtr, JSON_FIELD_CHILDREN); json_t* childPtr; int i; json_array_foreach(childrenPtr, i, childPtr) { // Get name const char* name = json_string_value(json_object_get(childPtr, JSON_FIELD_NAME)); // Is node exist with this name? le_cfg_nodeType_t existingType = le_cfg_GetNodeType(iterRef, name); switch (existingType) { case LE_CFG_TYPE_DOESNT_EXIST: case LE_CFG_TYPE_STEM: case LE_CFG_TYPE_EMPTY: // Not existing, already a stem or empty node, nothing to do break; default: // Issue with node creation fprintf(stderr, "Node conflict when importing, at node %s", name); return LE_NOT_POSSIBLE; break; } // Iterate to this child le_cfg_GoToNode(iterRef, name); // Iterate le_result_t subResult = HandleImportJSONIteration(iterRef, childPtr); if (subResult != LE_OK) { // Something went wrong return subResult; } // Go back to parent le_cfg_GoToParent(iterRef); } } break; default: return LE_FAULT; } return LE_OK; }
// ------------------------------------------------------------------------------------------------- static int HandleCopy ( void ) // ------------------------------------------------------------------------------------------------- { // Create a temp file to export the tree to. char tempFilePath[] = "/tmp/configExport-XXXXXX"; int tempFd; do { tempFd = mkstemp(tempFilePath); } while ((tempFd == -1) && (errno == EINTR)); if (tempFd == -1) { fprintf(stderr, "Could not create temp file. Reason, %s (%d).", strerror(errno), errno); return 1; } // Unlink the file now so that we can make sure that it will end up being deleted, no matter how // we exit. if (unlink(tempFilePath) == -1) { printf("Could not unlink temporary file. Reason, %s (%d).", strerror(errno), errno); } // Create a transaction and export the data from the config tree. le_cfg_IteratorRef_t iterRef = le_cfg_CreateWriteTxn(NodePath); le_result_t result = le_cfgAdmin_ExportTree(iterRef, tempFilePath, ""); if (result != LE_OK) { fprintf(stderr, "An I/O error occurred while updating the config tree. " "Tree has been left untouched.\n"); goto txnDone; } if (DeleteAfterCopy != false) { // Since this is a rename, then delete the node at the original location. le_cfg_DeleteNode(iterRef, ""); } // Now, move the iterator to the node's new name, then attempt to reload the data. le_cfg_GoToNode(iterRef, ".."); result = le_cfgAdmin_ImportTree(iterRef, tempFilePath, NodeDestPath); if (result != LE_OK) { switch (result) { case LE_FAULT: fprintf(stderr, "An I/O error occurred while updating the config tree. " "Tree has been left untouched.\n"); break; case LE_FORMAT_ERROR: fprintf(stderr, "Import/export corruption detected. Tree has been left untouched.\n"); break; default: fprintf(stderr, "An unexpected error has occurred: %s, (%d).\n", LE_RESULT_TXT(result), result); break; } } txnDone: // Make sure that the change was successful, and either commit or discard any changes that were // made. if (result == LE_OK) { le_cfg_CommitTxn(iterRef); } else { le_cfg_CancelTxn(iterRef); } // Was the operation successful? int exitResult = (result == LE_OK) ? EXIT_SUCCESS : EXIT_FAILURE; // Finally, clean up our temp file and report our results. int closeRetVal; do { closeRetVal = close(tempFd); } while ((closeRetVal == -1) && (errno == EINTR)); if (closeRetVal == -1) { fprintf(stderr, "Could not close temp file. Reason, %s (%d).", strerror(errno), errno); exitResult = EXIT_FAILURE; } return exitResult; }