Esempio n. 1
0
//--------------------------------------------------------------------------------------------------
static void OnTransactionTimeout
(
    le_timer_Ref_t timerRef   ///< The timer that expired.
)
//--------------------------------------------------------------------------------------------------
{
    // Extract the iterator reference out of the timer object.  Then perform a sanity check to make
    // sure everything is going to plan.
    ni_IteratorRef_t iteratorRef = (ni_IteratorRef_t)le_timer_GetContextPtr(timerRef);
    LE_ASSERT(iteratorRef->timerRef == timerRef);

    // For clearer message reporting, figure out if this is a read or write transaction.
    char* iterTypePtr = "Read";

    if (ni_IsWriteable(iteratorRef))
    {
        iterTypePtr = "Write";
    }

    if (iteratorRef->isTerminated)
    {
        LE_DEBUG("Previously terminated iterator, <%p> timed out.", iteratorRef);
        return;
    }

    // Report the failure in the log, and close the client session.  Once the session is closed all
    // of that user's resources within the configTree will be naturally cleaned up.
    LE_EMERG("%s transaction <%p> timer expired, for user %s, <%d>.",
             iterTypePtr,
             iteratorRef->reference,
             tu_GetUserName(iteratorRef->userRef),
             tu_GetUserId(iteratorRef->userRef));

    tu_TerminateConfigClient(iteratorRef->sessionRef, "Transaction timeout.");
}
Esempio n. 2
0
//--------------------------------------------------------------------------------------------------
le_result_t fwDaemons_SigChildHandler
(
    pid_t pid,              ///< [IN] Pid of the process that produced the SIGCHLD.
    int status              ///< [IN] Status of the process.
)
{
    // See which daemon produced this signal.
    DaemonObj_t* daemonObjPtr = NULL;

    int i;
    for (i = 0; i < NUM_ARRAY_MEMBERS(FrameworkDaemons); i++)
    {
        if (FrameworkDaemons[i].pid == pid)
        {
            daemonObjPtr = &(FrameworkDaemons[i]);

            // Check status of process and handle SIGCONT and SIGSTOP signals.
            if ( WIFSTOPPED(status) || WIFCONTINUED(status) )
            {
                // The framework dameon was either stopped or continued which should not happen kill
                // the process now.
                kill_Hard(pid);

                // Return LE_OK here, when the process actually dies we'll get another SIGCHLD.
                return LE_OK;
            }

            // Mark this daemon as dead.
            daemonObjPtr->pid = -1;
            kill_Died(pid);

            break;
        }
    }

    if (daemonObjPtr == NULL)
    {
        return LE_NOT_FOUND;
    }

    if (ShutdownIndex >= 0)
    {
        // We are in the midst of a shutdown sequence, continue the shutdown sequence.
        ShutdownIndex = ShutdownNextDaemon(ShutdownIndex);

        return LE_OK;
    }
    else
    {
        // This was an unexpected error from one of the framework daemons.
        LE_EMERG("The framework daemon '%s' has experienced a problem.",
                le_path_GetBasenamePtr(daemonObjPtr->path, "/"));

        return LE_FAULT;
    }
}
Esempio n. 3
0
    //----------------------------------------------------------------------------------------------
    static void CheckGuardBands
    (
        MemBlock_t* blockHeaderPtr  // Pointer to the per-block overhead area of the memory block.
    )
    {
        int i;

        // There's a guard band at the start of the data section.
        uint32_t* guardBandWordPtr = (uint32_t*)(blockHeaderPtr->data);
        for (i = 0; i < NUM_GUARD_BAND_WORDS; i++, guardBandWordPtr++)
        {
            if (*guardBandWordPtr != GUARD_WORD)
            {
                LE_EMERG("Memory corruption detected at address %p before object allocated"
                                                                                " from pool '%s'.",
                         guardBandWordPtr,
                         blockHeaderPtr->poolPtr->name);
                LE_FATAL("Guard band value should have been %d, but was found to be %d.",
                         GUARD_WORD,
                         *guardBandWordPtr);
            }
        }

        // There's another guard band at the end of the data section.
        guardBandWordPtr = (uint32_t*)(   ((uint8_t*)blockHeaderPtr)
                                        + blockHeaderPtr->poolPtr->blockSize
                                        - GUARD_BAND_SIZE);
        for (i = 0; i < NUM_GUARD_BAND_WORDS; i++, guardBandWordPtr++)
        {
            if (*guardBandWordPtr != GUARD_WORD)
            {
                LE_EMERG("Memory corruption detected at address %p at end of object allocated"
                                                                                " from pool '%s'.",
                         guardBandWordPtr,
                         blockHeaderPtr->poolPtr->name);
                LE_FATAL("Guard band value should have been %d, but was found to be %d.",
                         GUARD_WORD,
                         *guardBandWordPtr);
            }
        }
    }
Esempio n. 4
0
//--------------------------------------------------------------------------------------------------
void comp1_Foo(void)
{
    LE_DEBUG("comp1 %d msg", LE_LOG_DEBUG);
    LE_INFO("comp1 %d msg", LE_LOG_INFO);
    LE_WARN("comp1 %d msg", LE_LOG_WARN);
    LE_ERROR("comp1 %d msg", LE_LOG_ERR);
    LE_CRIT("comp1 %d msg", LE_LOG_CRIT);
    LE_EMERG("comp1 %d msg", LE_LOG_EMERG);

    le_log_TraceRef_t trace1 = le_log_GetTraceRef("key 1");
    le_log_TraceRef_t trace2 = le_log_GetTraceRef("key 2");

    LE_TRACE(trace1, "Trace msg in %s", STRINGIZE(LE_COMPONENT_NAME));
    LE_TRACE(trace2, "Trace msg in %s", STRINGIZE(LE_COMPONENT_NAME));
}
Esempio n. 5
0
//--------------------------------------------------------------------------------------------------
void tu_TerminateConfigAdminClient
(
    le_msg_SessionRef_t sessionRef,  ///< [IN] The session that needs to close.
    const char* killMessage          ///< [IN] The reason the session needs to close.
)
//--------------------------------------------------------------------------------------------------
{
    tu_UserRef_t userRef = GetUserInfo(sessionRef, NULL);

    LE_EMERG("A fatal error occured.  Killing admin sesssion <%p> for user %s, <%u>.  Reason: %s",
             sessionRef,
             tu_GetUserName(userRef),
             tu_GetUserId(userRef),
             killMessage);

    le_msg_CloseSession(sessionRef);
}
//--------------------------------------------------------------------------------------------------
static void LoadSimFromSecStore
(
    le_sim_Id_t simId
)
{
    uint32_t attemptCounter = SECSTORE_ATTEMPT_MAX;
    le_result_t result;
    le_sim_States_t simState;

    LE_DEBUG("Start reading SIM-%d information in secure storage",simId);

    do
    {
        simState = le_sim_GetState(simId);

        switch (simState)
        {
            case LE_SIM_INSERTED:
            {
                // Set the secure storage path for the SIM
                char secStorePath[LE_SECSTORE_MAX_NAME_BYTES];
                snprintf(secStorePath, sizeof(secStorePath), "%s/%d/%s",
                         SECSTORE_NODE_SIM, simId, SECSTORE_NODE_PIN);

                char simPin[LE_SIM_PIN_MAX_BYTES] = {0};
                size_t simSize = LE_SIM_PIN_MAX_BYTES;

                // Read PIN code stored in secure storage
                result = le_secStore_Read(secStorePath, (uint8_t *)simPin, &simSize);
                if (LE_NOT_FOUND == result)
                {
                    LE_ERROR("SIM PIN code isn't found in the secure storage");
                    return;
                }
                else if (LE_OVERFLOW == result)
                {
                    LE_WARN("PIN string too large for SIM-%d", simId);
                    return;
                }
                else if (LE_OK != result)
                {
                    LE_ERROR("Unable to retrieve PIN for SIM-%d, error %s",
                             simId, LE_RESULT_TXT(result));
                    return;
                }
                if (0 == strncmp(simPin, "", sizeof(simPin)))
                {
                    LE_WARN("PIN not set for SIM-%d", simId);
                    return;
                }
                if (LE_OK != (result = le_sim_EnterPIN(simId, simPin)))
                {
                    LE_ERROR("Error.%d Failed to enter SIM pin for SIM-%d", result, simId);
                    return;
                }
                LE_DEBUG("Sim-%d is unlocked", simId);

                attemptCounter = 1;
                break;
            }
            case LE_SIM_BLOCKED:
            {
                LE_EMERG("Be careful the sim-%d is BLOCKED, need to enter PUK code",simId);
                attemptCounter = 1;
                break;
            }
            case LE_SIM_BUSY:
                if (attemptCounter==1)
                {
                    LE_WARN("Could not load the configuration because "
                            "the SIM is still busy after %d attempts", SECSTORE_ATTEMPT_MAX);
                }
                else
                {
                    LE_WARN("Sim-%d was busy when loading configuration,"
                            "retry in 1 seconds",simId);
                }
                sleep(1); // Retry in 1 second.
                break;
            case LE_SIM_READY:
                LE_DEBUG("Sim-%d is ready",simId);
                attemptCounter = 1;
                break;
            case LE_SIM_ABSENT:
                LE_WARN("Sim-%d is absent",simId);
                attemptCounter = 1;
                break;
            case LE_SIM_POWER_DOWN:
                LE_WARN("Sim-%d is powered down",simId);
                break;
            case LE_SIM_STATE_UNKNOWN:
                break;
        }
    } while (--attemptCounter);

    LE_DEBUG("Load SIM information is done");
}
Esempio n. 7
0
//--------------------------------------------------------------------------------------------------
void le_mem_Release
(
    void*   objPtr  ///< [IN] Pointer to the object to be released.
)
{
    MemBlock_t* blockPtr;

    // Get the block from the object pointer.
    #ifdef USE_GUARD_BAND
        uint8_t* dataPtr = objPtr;
        dataPtr -= GUARD_BAND_SIZE;
        blockPtr = CONTAINER_OF(dataPtr, MemBlock_t, data);
    #else
        blockPtr = CONTAINER_OF(objPtr, MemBlock_t, data);
    #endif

    #ifdef USE_GUARD_BAND
        CheckGuardBands(blockPtr);
    #endif

    Lock();

    switch (blockPtr->refCount)
    {
        case 1:
        {
            MemPool_t* poolPtr = blockPtr->poolPtr;

            // The reference count has reached zero.
            blockPtr->refCount = 0;

            // Call the destructor, if there is one.
            if (poolPtr->destructor)
            {
                // Make sure that the destructor is not called with the mutex locked, because
                // it is not a recursive mutex and therefore will deadlock if locked again by
                // the same thread.  Also, fetch the destructor function address before unlocking
                // the mutex so that we don't touch the pool object while the mutex is unlocked.
                le_mem_Destructor_t destructor = poolPtr->destructor;
                Unlock();
                destructor(objPtr);

                // Re-lock the mutex now so that it is safe to access the pool object again.
                Lock();
            }

            #ifndef LE_MEM_VALGRIND
                // Release the memory back into the pool.
                // Note that we don't do this before calling the destructor because the destructor
                // still needs to access it, but after it goes back on the free list, it could get
                // reallocated by another thread (or even the destructor itself) and have its
                // contents clobbered.
                le_sls_Stack(&(poolPtr->freeList), &(blockPtr->link));
            #else
                free(blockPtr);
            #endif

            poolPtr->numBlocksInUse--;

            break;
        }

        case 0:
            LE_EMERG("Releasing free block.");
            LE_FATAL("Free block released from pool %p (%s).",
                     blockPtr->poolPtr,
                     blockPtr->poolPtr->name);

        default:
            blockPtr->refCount--;
    }

    Unlock();
}
Esempio n. 8
0
//--------------------------------------------------------------------------------------------------
static void ReadMeta
(
    void
)
{
    /* Copy the meta file from sfs to a temporary location for faster access. */
    char tmpFilePath[SECSTOREADMIN_MAX_PATH_BYTES] = "/tmp/tempMetaFile_secStoreTool_Deleteme";

    le_result_t result = secStoreAdmin_CopyMetaTo(tmpFilePath);

    if (result != LE_OK)
    {
        LE_EMERG("Could not copy the meta file to path %s.  Result code %s.",
                 tmpFilePath,
                 LE_RESULT_TXT(result));

        /* Delete the temp file. */
        if (unlink(tmpFilePath) != 0)
        {
            INTERNAL_ERR("Could not delete %s. %m.", tmpFilePath);
        }

        InternalErr();
    }

    /* Open the temp file. */
    FILE* tmpFilePtr;

    do
    {
        tmpFilePtr = fopen(tmpFilePath, "r");
    }
    while ( (tmpFilePtr == NULL) && (errno == EINTR) );

    if (tmpFilePtr == NULL)
    {
        LE_EMERG("Could not open temp file %s. %m.", tmpFilePath);

        /* Delete the temp file. */
        if (unlink(tmpFilePath) != 0)
        {
            INTERNAL_ERR("Could not delete %s. %m.", tmpFilePath);
        }

        InternalErr();
    }

    /* Read the temp file. */

    // Buffer to store a line read from a meta file.
    // This buffer should accomodate either a link path or a sfs item path.
    // The max limit for a link path should be more than enough for sfs item path as well.
    char lineBuf[SECSTOREADMIN_MAX_PATH_BYTES] = {0};
    bool printedNewLine = true;
    while (fgets(lineBuf, sizeof(lineBuf), tmpFilePtr) != NULL)
    {
        // Remove the trailing newline char.
        size_t len = strlen(lineBuf);

        if (lineBuf[len - 1] == '\n')
        {
            lineBuf[len - 1] = '\0';
        }

        printf("%s", lineBuf);

        if (printedNewLine)
        {
            printf(" ");
            printedNewLine = false;
        }
        else
        {
            printf("\n");
            printedNewLine = true;
        }
    }

    // check if fgets encountered error before reaching the end of the file.
    if (ferror(tmpFilePtr))
    {
        fprintf(stderr, "Error reading temp file %s. %m.\n", tmpFilePath);
    }

    /* Close the temp file. */
    int r;
    do
    {
        r = fclose(tmpFilePtr);
    }
    while ( (r != 0) && (errno == EINTR) );

    if (r != 0)
    {
        LE_EMERG("Could not close %s. %m.", tmpFilePath);

        /* Delete the temp file. */
        if (unlink(tmpFilePath) != 0)
        {
            INTERNAL_ERR("Could not delete %s. %m.", tmpFilePath);
        }

        InternalErr();
    }

    /* Delete the temp file. */
    if (unlink(tmpFilePath) != 0)
    {
        INTERNAL_ERR("Could not delete %s. %m.", tmpFilePath);
    }
}
Esempio n. 9
0
//--------------------------------------------------------------------------------------------------
static void LoadSimFromConfigDb
(
    le_sim_Id_t simId
)
{
    uint32_t attemptCounter = CONFIGDB_ATTEMPT_MAX;
    // Get the configuration path for the SIM.
    char configPath[LIMIT_MAX_PATH_BYTES];
    snprintf(configPath, sizeof(configPath), "%s/%d",
             CFG_MODEMSERVICE_SIM_PATH,
             simId);

    LE_DEBUG("Start reading SIM-%d information in ConfigDB",simId);

    le_result_t result;
    le_sim_States_t simState;

    do
    {
        simState = le_sim_GetState(simId);

        switch (simState)
        {
            case LE_SIM_INSERTED:
            {
                // Check that the app has a configuration value.
                le_cfg_IteratorRef_t simCfg = le_cfg_CreateReadTxn(configPath);

                char simPin[LIMIT_MAX_PATH_BYTES] = {0};

                result = le_cfg_GetString(simCfg,CFG_NODE_PIN,simPin,sizeof(simPin),"");
                if ( result != LE_OK )
                {
                    LE_WARN("PIN string too large for SIM-%d",simId);
                    le_cfg_CancelTxn(simCfg);
                    return;
                }
                if ( strncmp(simPin,"",sizeof(simPin))==0 )
                {
                    LE_WARN("PIN not set for SIM-%d",simId);
                    le_cfg_CancelTxn(simCfg);
                    return;
                }
                if ( (result = le_sim_EnterPIN(simId,simPin)) != LE_OK )
                {
                    LE_ERROR("Error.%d Failed to enter SIM pin for SIM-%d",result,simId);
                    le_cfg_CancelTxn(simCfg);
                    return;
                }
                LE_DEBUG("Sim-%d is unlocked", simId);

                le_cfg_CancelTxn(simCfg);
                attemptCounter = 1;
                break;
            }
            case LE_SIM_BLOCKED:
            {
                LE_EMERG("Be carefull the sim-%d is BLOCKED, need to enter PUK code",simId);
                attemptCounter = 1;
                break;
            }
            case LE_SIM_BUSY:
                if (attemptCounter==1)
                {
                    LE_WARN("Could not load the configuration because "
                            "the SIM is still busy after %d attempts", CONFIGDB_ATTEMPT_MAX);
                }
                else
                {
                    LE_WARN("Sim-%d was busy when loading configuration,"
                            "retry in 1 seconds",simId);
                }
                sleep(1); // Retry in 1 second.
                break;
            case LE_SIM_READY:
                LE_DEBUG("Sim-%d is ready",simId);
                attemptCounter = 1;
                break;
            case LE_SIM_ABSENT:
                LE_WARN("Sim-%d is absent",simId);
                attemptCounter = 1;
                break;
            case LE_SIM_STATE_UNKNOWN:
                break;
        }
    } while (--attemptCounter);

    LE_DEBUG("Load SIM information is done");
}
Esempio n. 10
0
//--------------------------------------------------------------------------------------------------
static inline le_result_t StartProc
(
    proc_Ref_t procRef,             ///< [IN] The process to start.
    const char* workingDirPtr,      ///< [IN] The path to the process's working directory, relative
                                    ///       to the sandbox directory.
    uid_t uid,                      ///< [IN] The user ID to start the process as.
    gid_t gid,                      ///< [IN] The primary group ID for this process.
    const gid_t* groupsPtr,         ///< [IN] List of supplementary groups for this process.
    size_t numGroups,               ///< [IN] The number of groups in the supplementary groups list.
    const char* sandboxDirPtr       ///< [IN] The path to the root of the sandbox this process is to
                                    ///       run in.  If NULL then process will be unsandboxed.
)
{
    if (procRef->pid != -1)
    {
        LE_ERROR("Process '%s' (PID: %d) cannot be started because it is already running.",
                 procRef->name, procRef->pid);
        return LE_FAULT;
    }

    // Create a pipe for parent/child synchronization.
    int syncPipeFd[2];
    LE_FATAL_IF(pipe(syncPipeFd) == -1, "Could not create synchronization pipe.  %m.");

    // @Note The current IPC system does not support forking so any reads to the config DB must be
    //       done in the parent process.

    // Get the environment variables from the config tree for this process.
    EnvVar_t envVars[LIMIT_MAX_NUM_ENV_VARS];
    int numEnvVars = GetEnvironmentVariables(procRef, envVars, LIMIT_MAX_NUM_ENV_VARS);

    if (numEnvVars == LE_FAULT)
    {
        LE_ERROR("Error getting environment variables.  Process '%s' cannot be started.",
                 procRef->name);
        return LE_FAULT;
    }

    // Get the command line arguments from the config tree for this process.
    char argsBuffers[LIMIT_MAX_NUM_CMD_LINE_ARGS][LIMIT_MAX_ARGS_STR_BYTES];
    char* argsPtr[NUM_ARGS_PTRS];

    if (GetArgs(procRef, argsBuffers, argsPtr) != LE_OK)
    {
        LE_ERROR("Could not get command line arguments, process '%s' cannot be started.",
                 procRef->name);
        return LE_FAULT;
    }

    // Get the smack label for the process.
    // Must get the label here because smack_GetAppLabel uses the config and we can not
    // use any IPC after a fork.
    char smackLabel[LIMIT_MAX_SMACK_LABEL_BYTES];
    appSmack_GetLabel(app_GetName(procRef->appRef), smackLabel, sizeof(smackLabel));

    // Create pipes for the process's standard error and standard out streams.
    int stderrPipe[2];
    int stdoutPipe[2];
    CreatePipe(procRef, stderrPipe, STDERR_FILENO);
    CreatePipe(procRef, stdoutPipe, STDOUT_FILENO);

    // Create the child process
    pid_t pID = fork();

    if (pID < 0)
    {
        LE_EMERG("Failed to fork.  %m.");
        return LE_FAULT;
    }

    if (pID == 0)
    {
        // Wait for the parent to allow us to continue by blocking on the read pipe until it
        // is closed.
        fd_Close(syncPipeFd[WRITE_PIPE]);

        ssize_t numBytesRead;
        int dummyBuf;
        do
        {
            numBytesRead = read(syncPipeFd[READ_PIPE], &dummyBuf, 1);
        }
        while ( ((numBytesRead == -1)  && (errno == EINTR)) || (numBytesRead != 0) );

        LE_FATAL_IF(numBytesRead == -1, "Could not read synchronization pipe.  %m.");

        // The parent has allowed us to continue.

        // Redirect the process's standard streams.
        RedirectStdStream(stderrPipe, STDERR_FILENO);
        RedirectStdStream(stdoutPipe, STDOUT_FILENO);

        // Set the process's SMACK label.
        smack_SetMyLabel(smackLabel);

        // Set the umask so that files are not accidentally created with global permissions.
        umask(S_IRWXG | S_IRWXO);

        // Unblock all signals that might have been blocked.
        sigset_t sigSet;
        LE_ASSERT(sigfillset(&sigSet) == 0);
        LE_ASSERT(pthread_sigmask(SIG_UNBLOCK, &sigSet, NULL) == 0);

        SetEnvironmentVariables(envVars, numEnvVars);

        // Setup the process environment.
        if (sandboxDirPtr != NULL)
        {
            // Sandbox the process.
            sandbox_ConfineProc(sandboxDirPtr, uid, gid, groupsPtr, numGroups, workingDirPtr);
        }
        else
        {
            ConfigNonSandboxedProcess(workingDirPtr);
        }

        // Launch the child program.  This should not return unless there was an error.
        LE_INFO("Execing '%s'", argsPtr[0]);

        // Close all non-standard file descriptors.
        fd_CloseAllNonStd();

        execvp(argsPtr[0], &(argsPtr[1]));

        // The program could not be started.  Log an error message.
        log_ReInit();
        LE_FATAL("Could not exec '%s'.  %m.", argsPtr[0]);
    }

    procRef->pid = pID;
    procRef->paused = false;

    // Don't need this end of the pipe.
    fd_Close(syncPipeFd[READ_PIPE]);

    // Set the scheduling priority for the child process while the child process is blocked.
    SetSchedulingPriority(procRef);

    // Send standard pipes to the log daemon so they will show up in the logs.
    SendStdPipeToLogDaemon(procRef, stderrPipe, STDERR_FILENO);
    SendStdPipeToLogDaemon(procRef, stdoutPipe, STDOUT_FILENO);

    // Set the resource limits for the child process while the child process is blocked.
    if (resLim_SetProcLimits(procRef) != LE_OK)
    {
        LE_ERROR("Could not set the resource limits.  %m.");

        kill_Hard(procRef->pid);
    }

    LE_INFO("Starting process %s with pid %d", procRef->name, procRef->pid);

    // Unblock the child process.
    fd_Close(syncPipeFd[WRITE_PIPE]);

    return LE_OK;
}
Esempio n. 11
0
//--------------------------------------------------------------------------------------------------
le_result_t fwDaemons_SigChildHandler
(
    pid_t pid               ///< [IN] Pid of the process that produced the SIGCHLD.
)
{
    // See which daemon produced this signal.
    DaemonObj_t* daemonObjPtr = NULL;

    int i;
    for (i = 0; i < NUM_ARRAY_MEMBERS(FrameworkDaemons); i++)
    {
        if (FrameworkDaemons[i].pid == pid)
        {
            daemonObjPtr = &(FrameworkDaemons[i]);

            // Mark this daemon as dead.
            daemonObjPtr->pid = -1;
            kill_Died(pid);

            break;
        }
    }

    if (daemonObjPtr == NULL)
    {
        return LE_NOT_FOUND;
    }

    // This child process is a framework daemon.
    // Reap the child now.
    int status = wait_ReapChild(pid);

    if (ShutdownIndex >= 0)
    {
        // We are in the midst of a shutdown sequence, continue the shutdown sequence.
        ShutdownIndex = ShutdownNextDaemon(ShutdownIndex);

        return LE_OK;
    }
    else
    {
        const char* daemonName = le_path_GetBasenamePtr(daemonObjPtr->path, "/");

        if (WIFEXITED(status))
        {
            // This was an unexpected error from one of the framework daemons.
            LE_EMERG("Framework daemon '%s' has exited with code %d.",
                     daemonName,
                     WEXITSTATUS(status));
        }
        else if (WIFSIGNALED(status))
        {
            // This was an unexpected error from one of the framework daemons.
            LE_EMERG("Framework daemon '%s' has been killed by a signal: %d.",
                     daemonName,
                     WTERMSIG(status));
        }
        else
        {
            // This was an unexpected error from one of the framework daemons.
            LE_EMERG("Framework daemon '%s' has died for an unknown reason (status = 0x%x).",
                     daemonName,
                     status);
        }

        return LE_FAULT;
    }
}