Esempio n. 1
0
static void test_process_start_time(void)
{
    int this_pid, new_pid;
    time_t this_starttime;

    this_pid = getpid();
    this_starttime = GetProcessStartTime(this_pid);

    sleep(1);

    new_pid = fork();

    assert_true(new_pid >= 0);
    if (new_pid == 0)                                           /* child */
    {
        execl("/bin/sleep", "/bin/sleep", "5", NULL);
        assert_true(false);                                  /* unreachable */
    }

    SPAWNED_PID = new_pid;

    time_t newproc_starttime = GetProcessStartTime(new_pid);
    // We might have slipped by a few seconds, but shouldn't be much.
    assert_true(newproc_starttime >= this_starttime + 1 && newproc_starttime <= this_starttime + 5);

    kill(new_pid, SIGKILL);
    wait(NULL);
    SPAWNED_PID = -1;
}
Esempio n. 2
0
static void test_process_start_time(void)
{
    /* Wait a couple of seconds so that process start time differs. */
    printf("Sleeping 2 seconds...\n");
    sleep(2);

    pid_t new_pid = fork();
    assert_true(new_pid >= 0);

    if (new_pid == 0)                                           /* child */
    {
        execl("/bin/sleep", "/bin/sleep", "30", NULL);
        assert_true(false);                                  /* unreachable */
    }

    SPAWNED_PID = new_pid;
    time_t newproc_starttime = GetProcessStartTime(new_pid);

    printf("Spawned a \"sleep\" child with PID %jd and start_time %jd\n",
           (intmax_t) new_pid, (intmax_t) newproc_starttime);

    // We might have slipped by a few seconds, but shouldn't be much.
    assert_int_not_equal(newproc_starttime, PROCESS_START_TIME_UNKNOWN);
    assert_true(newproc_starttime >= THIS_STARTTIME + 1);
    assert_true(newproc_starttime <= THIS_STARTTIME + 15);

    kill(new_pid, SIGKILL);
    wait(NULL);
    SPAWNED_PID = 0;
}
Esempio n. 3
0
int main()
{
    PRINT_TEST_BANNER();

    /* Don't miss the messages about GetProcessStartTime not implemented. */
    LogSetGlobalLevel(LOG_LEVEL_DEBUG);

    THIS_PID       = getpid();
    THIS_STARTTIME = GetProcessStartTime(THIS_PID);

    printf("This parent process has PID %jd and start_time %jd\n",
           (intmax_t) THIS_PID, (intmax_t) THIS_STARTTIME);

    const UnitTest tests[] =
    {
        unit_test(test_process_start_time),
        unit_test(test_process_state),
        unit_test(test_graceful_terminate)
    };

    int ret = run_tests(tests);

    /* Make sure no child is alive. */
    if (SPAWNED_PID > 0)
    {
        kill(SPAWNED_PID, SIGKILL);
    }

    return ret;
}
Esempio n. 4
0
static void test_graceful_terminate(void)
{
    int ret, state;

    pid_t new_pid = fork();
    assert_true(new_pid >= 0);

    if (new_pid == 0)                                           /* child */
    {
        execl("/bin/sleep", "/bin/sleep", "30", NULL);
        assert_true(false);                                  /* unreachable */
    }

    time_t start_time = GetProcessStartTime(new_pid);
    SPAWNED_PID = new_pid;

    printf("Spawned a \"sleep\" child with PID %jd and start_time %jd\n",
           (intmax_t) new_pid, (intmax_t) start_time);

    state = GetProcessState(new_pid);
    assert_int_equal(state, PROCESS_STATE_RUNNING);

    printf("Killing child with wrong start_time, child should not die...\n");

    ret = GracefulTerminate(new_pid, 12345);             /* fake start time */
    assert_false(ret);

    state = GetProcessState(new_pid);
    assert_int_equal(state, PROCESS_STATE_RUNNING);

    printf("Killing child with correct start_time, child should die...\n");

    ret = GracefulTerminate(new_pid, start_time);
    assert_true(ret);

    state = GetProcessState(new_pid);
    assert_int_equal(state, PROCESS_STATE_ZOMBIE);

    wait(NULL);                                               /* reap child */

    state = GetProcessState(new_pid);
    assert_int_equal(state, PROCESS_STATE_DOES_NOT_EXIST);

    printf("Child Dead!\n");
    SPAWNED_PID = 0;

    printf("Killing ourself, should fail...\n");
    ret = GracefulTerminate(THIS_PID, THIS_STARTTIME);
    assert_false(ret);

    printf("Killing ourself without specifying starttime, should fail...\n");
    ret = GracefulTerminate(THIS_PID, PROCESS_START_TIME_UNKNOWN);
    assert_false(ret);
}
Esempio n. 5
0
bool ProcessTimes::GetProcessTimes( const Utils::AutoHandleMgr& ahmProcess_i )
{
    // Get file times for process
    FILETIME ftStartTime = { 0 },
             ftExitTime = { 0 },
             ftKernelModeTime = { 0 },
             ftUserModeTime = { 0 };

    // Get times of process
    const bool bResult = ::GetProcessTimes( ahmProcess_i, 
                                            &ftStartTime, 
                                            &ftExitTime, 
                                            &ftKernelModeTime, 
                                            &ftUserModeTime ) != FALSE;
    // Get formatted
    Utils::GetFormattedTime( GetProcessStartTime(), ftStartTime, true );
    Utils::GetFormattedTime( GetProcessExitTime(), ftExitTime, true );
    Utils::GetFormattedTime( GetProcessKernelTime(), ftKernelModeTime, false );
    Utils::GetFormattedTime( GetProcessUserTime(), ftUserModeTime, false );

    return bResult;
}// End GetProcessTimes
Esempio n. 6
0
static void test_get_start_time_process2(void)
{
    time_t t = GetProcessStartTime(2);
    assert_int_equal(t, PROCESS_START_TIME_UNKNOWN);
}
Esempio n. 7
0
static void test_get_start_time_process1(void)
{
    time_t t = GetProcessStartTime(1);
    assert_int_equal(t, 100);
}
Esempio n. 8
0
#endif
    {
        return true;
    }
    else
    {
        return false;
    }
}

static bool WriteLockDataCurrent(CF_DB *dbp, const char *lock_id)
{
    LockData lock_data = {
        .pid = getpid(),
        .time = time(NULL),
        .process_start_time = GetProcessStartTime(getpid()),
    };

    return WriteLockData(dbp, lock_id, &lock_data);
}

/*
 * Much simpler than AcquireLock. Useful when you just want to check
 * if a certain amount of time has elapsed for an action since last
 * time you checked.  No need to clean up after calling this
 * (e.g. like YieldCurrentLock()).
 *
 * WARNING: Is prone to race-conditions, both on the thread and
 *          process level.
 */
Esempio n. 9
0
/*
 * Wait until process specified by #pid is stopped due to SIGSTOP signal.
 *
 * @returns true if process has come to stop during #timeout_ns nanoseconds,
 * false if the process cannot be found or failed to stop during #timeout_ns
 * nanoseconds.
 *
 * FIXME: Only timeouts < 1s are supported
 */
static bool ProcessWaitUntilStopped(pid_t pid, long timeout_ns)
{
    while (timeout_ns > 0)
    {
        switch (GetProcessState(pid))
        {
        case PROCESS_STATE_RUNNING:
            break;                                      /* retry in a while */
        case PROCESS_STATE_STOPPED:
            return true;
        case PROCESS_STATE_ZOMBIE:
            /* There is not much we can do by waiting a zombie process. It
             * will never change to a stopped state. */
            return false;
        case PROCESS_STATE_DOES_NOT_EXIST:
            return false;
        }

        struct timespec ts = {
            .tv_sec = 0,
            .tv_nsec = MIN(SLEEP_POLL_TIMEOUT_NS, timeout_ns),
        };

        while (nanosleep(&ts, &ts) < 0)
        {
            if (errno != EINTR)
            {
                ProgrammingError("Invalid timeout for nanosleep");
            }
        }

        timeout_ns = MAX(0, timeout_ns - SLEEP_POLL_TIMEOUT_NS);
    }

    return false;
}

/*
 * Currently only timeouts < 1s are supported
 */
static bool ProcessWaitUntilExited(pid_t pid, long timeout_ns)
{
    assert(timeout_ns < 1000000000);

    while (timeout_ns > 0)
    {
        switch (GetProcessState(pid))
        {
        case PROCESS_STATE_RUNNING:
            break;                                      /* retry in a while */
        case PROCESS_STATE_DOES_NOT_EXIST:
            return true;
        case PROCESS_STATE_ZOMBIE:
            /* There is not much we can do by waiting a zombie process. It's
               the responsibility of the caller to reap the child so we're
               considering it has already exited.  */
            return true;
        case PROCESS_STATE_STOPPED:
            /* Almost the same case with a zombie process, but it will
             * respond only to signals that can't be caught. */
            return false;
        }

        struct timespec ts = {
            .tv_sec = 0,
            .tv_nsec = MIN(SLEEP_POLL_TIMEOUT_NS, timeout_ns),
        };

        Log(LOG_LEVEL_DEBUG,
            "PID %jd still alive after signalling, waiting for %lu ms...",
            (intmax_t) pid, ts.tv_nsec / 1000000);

        while (nanosleep(&ts, &ts) < 0)
        {
            if (errno != EINTR)
            {
                ProgrammingError("Invalid timeout for nanosleep");
            }
        }

        timeout_ns = MAX(0, timeout_ns - SLEEP_POLL_TIMEOUT_NS);
    }

    return false;
}

/* A timeout (in nanoseconds) to wait for process to stop (pause) or exit.
 * Note that it's important that it does not overflow 32 bits; no more than
 * nine 9s in a row, i.e. one second. */
#define STOP_WAIT_TIMEOUT 999999999L

/*
 * Safely kill process by checking that the process is the right one by matching
 * process start time.
 *
 * The algorithm:
 *
 *   1. Check that the process has the same start time as stored in lock. If it
 *      is not, return, as we know for sure this is a wrong process. (This step
 *      is an optimization to avoid sending SIGSTOP/SIGCONT to wrong processes).
 *
 *   2. Send SIGSTOP to the process.
 *
 *   3. Poll process state until it is stopped.
 *
 *   Now the process is stopped, so we may examine it and not be afraid that it
 *   will exit and another one with the same PID will appear.
 *
 *   4. Check that the process has the same start time as provided.
 *      If it is, send the signal to the process.
 *
 *   5. Send SIGCONT to the process, so it may continue.
 *
 *
 * Returns 0 on success, -1 on error. Error code is signalled through errno.
 *
 * ERRORS
 *
 *  EINVAL An invalid signal was specified.
 *  EPERM The process does not have permission to send the signal.
 *  ESRCH The pid does not exist or its start time does not match expected one.
 */
static int SafeKill(pid_t pid, time_t expected_start_time, int signal)
{
    /* Preliminary check: in case process start time is different already, we
     * are sure we don't want to STOP it or kill it. */

    time_t pid_start_time = GetProcessStartTime(pid);

    if (pid_start_time != expected_start_time)
    {
        errno = ESRCH;
        return -1;
    }

    /* Now to avoid race conditions we need to stop process so it won't exit
     * voluntarily while we are working on it */

    if (kill(pid, SIGSTOP) < 0)
    {
        return -1;
    }

    if (!ProcessWaitUntilStopped(pid, STOP_WAIT_TIMEOUT))
    {
        /* Ensure the process is started again in case of timeout or error, so
         * we don't leave SIGSTOP'ed processes around on overloaded or
         * misconfigured machine */
        kill(pid, SIGCONT);
        errno = ESRCH;
        return -1;
    }

    /* Here process has stopped, so we may interrogate it without race conditions */

    pid_start_time = GetProcessStartTime(pid);

    if (pid_start_time != expected_start_time)
    {
        /* This is a wrong process, let it continue */
        kill(pid, SIGCONT);
        errno = ESRCH;
        return -1;
    }

    /* We've got a right process, signal it and let it continue */

    int ret = kill(pid, signal);
    int saved_errno = errno;

    /*
     * We don't check return value of SIGCONT, as the process may have been
     * terminated already by previous kill. Moreover, what would we do with the
     * return code?
     */
    kill(pid, SIGCONT);

    errno = saved_errno;
    return ret;
}
Esempio n. 10
0
/*
 * Wait until process specified by #pid is stopped due to SIGSTOP signal.
 *
 * @returns true if process has come to stop during #timeout_ns nanoseconds,
 * false if the process cannot be found or failed to stop during #timeout_ns
 * nanoseconds.
 *
 * FIXME: Only timeouts < 1s are supported
 */
static bool ProcessWaitUntilStopped(pid_t pid, long timeout_ns)
{
    while (timeout_ns > 0)
    {
        switch (GetProcessState(pid))
        {
        case PROCESS_STATE_RUNNING:
            break;
        case PROCESS_STATE_STOPPED:
            return true;
        case PROCESS_STATE_DOES_NOT_EXIST:
            return false;
        default:
            ProgrammingError("Unexpected value returned from GetProcessState");
        }

        struct timespec ts = {
            .tv_sec = 0,
            .tv_nsec = MIN(SLEEP_POLL_TIMEOUT_NS, timeout_ns),
        };

        while (nanosleep(&ts, &ts) < 0)
        {
            if (errno != EINTR)
            {
                ProgrammingError("Invalid timeout for nanosleep");
            }
        }

        timeout_ns = MAX(0, timeout_ns - SLEEP_POLL_TIMEOUT_NS);
    }

    return false;
}

/*
 * FIXME: Only timeouts < 1s are supported
 */
static bool ProcessWaitUntilExited(pid_t pid, long timeout_ns)
{
    while (timeout_ns > 0)
    {
        if (kill(pid, 0) < 0 && errno == ESRCH)
        {
            return true;
        }

        struct timespec ts = {
            .tv_sec = 0,
            .tv_nsec = MIN(SLEEP_POLL_TIMEOUT_NS, timeout_ns),
        };

        while (nanosleep(&ts, &ts) < 0)
        {
            if (errno != EINTR)
            {
                ProgrammingError("Invalid timeout for nanosleep");
            }
        }

        timeout_ns = MAX(0, timeout_ns - SLEEP_POLL_TIMEOUT_NS);
    }

    return false;
}

/* A timeout to wait for process to stop (pause) or exit.  Note that
 * it's important that it not over-flow 32 bits; no more than nine 9s
 * in a row ! */
#define STOP_WAIT_TIMEOUT 999999999L

/*
 * Safely kill process by checking that the process is the right one by matching
 * process start time.
 *
 * The algorithm:
 *
 *   1. Check that the process has the same start time as stored in lock. If it
 *      is not, return, as we know for sure this is a wrong process. (This step
 *      is an optimization to avoid sending SIGSTOP/SIGCONT to wrong processes).
 *
 *   2. Send SIGSTOP to the process.
 *
 *   3. Poll process state until it is stopped.
 *
 *   Now the process is stopped, so we may examine it and not be afraid that it
 *   will exit and another one with the same PID will appear.
 *
 *   4. Check that the process has the same start time as provided.
 *      If it is, send the signal to the process.
 *
 *   5. Send SIGCONT to the process, so it may continue.
 *
 *
 * Returns 0 on success, -1 on error. Error code is signalled through errno.
 *
 * ERRORS
 *
 *  EINVAL An invalid signal was specified.
 *  EPERM The process does not have permission to send the signal.
 *  ESRCH The pid does not exist or its start time does not match expected one.
 */
static int SafeKill(pid_t pid, time_t expected_start_time, int signal)
{
    /* Preliminary check: in case process start time is different already, we
     * are sure we don't want to STOP it or kill it. */

    time_t pid_start_time = GetProcessStartTime(pid);

    if (pid_start_time != expected_start_time)
    {
        errno = ESRCH;
        return -1;
    }

    /* Now to avoid race conditions we need to stop process so it won't exit
     * voluntarily while we are working on it */

    if (kill(pid, SIGSTOP) < 0)
    {
        return -1;
    }

    if (!ProcessWaitUntilStopped(pid, STOP_WAIT_TIMEOUT))
    {
        /* Ensure the process is started again in case of timeout or error, so
         * we don't leave SIGSTOP'ed processes around on overloaded or
         * misconfigured machine */
        kill(pid, SIGCONT);
        errno = ESRCH;
        return -1;
    }

    /* Here process has stopped, so we may interrogate it without race conditions */

    pid_start_time = GetProcessStartTime(pid);

    if (pid_start_time != expected_start_time)
    {
        /* This is a wrong process, let it continue */
        kill(pid, SIGCONT);
        errno = ESRCH;
        return -1;
    }

    /* We've got a right process, signal it and let it continue */

    int ret = kill(pid, signal);
    int saved_errno = errno;

    /*
     * We don't check return value of SIGCONT, as the proces may have been
     * terminated already by previous kill. Moreover, what would we do with the
     * return code?
     */
    kill(pid, SIGCONT);

    errno = saved_errno;
    return ret;
}

static int Kill(pid_t pid, time_t process_start_time, int signal)
{
    if (process_start_time == PROCESS_START_TIME_UNKNOWN)
    {
        /* We don't know when the process has started, do a plain kill(2) */
        return kill(pid, signal);
    }
    else
    {
        return SafeKill(pid, process_start_time, signal);
    }
}

int GracefulTerminate(pid_t pid, time_t process_start_time)
{
    if (Kill(pid, process_start_time, SIGINT) < 0)
    {
        return errno == ESRCH;
    }

    if (ProcessWaitUntilExited(pid, STOP_WAIT_TIMEOUT))
    {
        return true;
    }

    if (Kill(pid, process_start_time, SIGTERM) < 0)
    {
        return errno == ESRCH;
    }

    if (ProcessWaitUntilExited(pid, STOP_WAIT_TIMEOUT))
    {
        return true;
    }

    if (Kill(pid, process_start_time, SIGKILL) < 0)
    {
        return errno == ESRCH;
    }

    return true;
}
Esempio n. 11
0
static void test_get_start_time_process2(void)
{
    time_t t2 = GetProcessStartTime(2);
    assert_int_equal(t2, 347151599);
}