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); }
void test_kill_simple_process(void) { InitTime(); InitFakeProcess(12345, 100, false, false, true); int res = GracefulTerminate(1, 12345); assert_true(res); FakeProcessDoSignals(); assert_false(exists); assert_int_equal(exit_signal, SIGINT); }
void TimeOut() { alarm(0); if (ALARM_PID != -1) { Log(LOG_LEVEL_VERBOSE, "Time out of process %jd", (intmax_t)ALARM_PID); GracefulTerminate(ALARM_PID, PROCESS_START_TIME_UNKNOWN); } else { Log(LOG_LEVEL_VERBOSE, "%s> Time out", VPREFIX); } }
void test_kill_anothers_process(void) { /* This process is not owned by killer */ InitTime(); InitFakeProcess(12345, 100, true, true, false); int res = GracefulTerminate(1, 12345); assert_true(res); FakeProcessDoSignals(); assert_true(exists); assert_false(was_stopped); }
void test_kill_no_sigint_sigterm(void) { /* This process only can be killed by SIGKILL */ InitTime(); InitFakeProcess(12345, 100, true, true, true); int res = GracefulTerminate(1, 12345); assert_true(res); FakeProcessDoSignals(); assert_false(exists); assert_int_equal(exit_signal, SIGKILL); }
void test_kill_no_sigint(void) { /* This process blocks SIGINT */ InitTime(); InitFakeProcess(12345, 100, true, false, true); int res = GracefulTerminate(1, 12345); assert_true(res); FakeProcessDoSignals(); assert_false(exists); assert_int_equal(exit_signal, SIGTERM); }
void test_kill_long_reacting_signal(void) { /* This process is very slow in reaction. It should not be left stopped though */ InitTime(); InitFakeProcess(12345, 2000000000, false, false, true); int res = GracefulTerminate(1, 12345); assert_true(res); FakeProcessDoSignals(); assert_true(exists); /* We should not kill this process */ assert_false(stopped); /* It should either be running or waiting to process SIGCONT */ }
void TimeOut() { alarm(0); if (ALARM_PID != -1) { CfOut(OUTPUT_LEVEL_VERBOSE, "", "Time out of process %jd\n", (intmax_t)ALARM_PID); GracefulTerminate(ALARM_PID); } else { CfOut(OUTPUT_LEVEL_VERBOSE, "", "%s> Time out\n", VPREFIX); } }
void test_kill_wrong_process(void) { InitTime(); InitFakeProcess(66666, 100, false, false, true); int res = GracefulTerminate(1, 12345); assert_true(res); FakeProcessDoSignals(); assert_true(exists); assert_false(stopped); assert_false(was_stopped); /* We should not touch this process at all */ assert_int_equal(signal_time, (time_t)-1); /* No pending signals either */ }
CfLock AcquireLock(char *operand, char *host, time_t now, Attributes attr, Promise *pp, int ignoreProcesses) { unsigned int pid; int i, err, sum = 0; time_t lastcompleted = 0, elapsedtime; char *promise, cc_operator[CF_BUFSIZE], cc_operand[CF_BUFSIZE]; char cflock[CF_BUFSIZE], cflast[CF_BUFSIZE], cflog[CF_BUFSIZE]; char str_digest[CF_BUFSIZE]; CfLock this; unsigned char digest[EVP_MAX_MD_SIZE + 1]; this.last = (char *) CF_UNDEFINED; this.lock = (char *) CF_UNDEFINED; this.log = (char *) CF_UNDEFINED; if (now == 0) { return this; } this.last = NULL; this.lock = NULL; this.log = NULL; /* Indicate as done if we tried ... as we have passed all class constraints now but we should only do this for level 0 promises. Sub routine bundles cannot be marked as done or it will disallow iteration over bundles */ if (pp->done) { return this; } if (CF_STCKFRAME == 1) { *(pp->donep) = true; /* Must not set pp->done = true for editfiles etc */ } HashPromise(operand, pp, digest, CF_DEFAULT_DIGEST); strcpy(str_digest, HashPrint(CF_DEFAULT_DIGEST, digest)); /* As a backup to "done" we need something immune to re-use */ if (THIS_AGENT_TYPE == cf_agent) { if (IsItemIn(DONELIST, str_digest)) { CfOut(cf_verbose, "", " -> This promise has already been verified"); return this; } PrependItem(&DONELIST, str_digest, NULL); } /* Finally if we're supposed to ignore locks ... do the remaining stuff */ if (IGNORELOCK) { this.lock = xstrdup("dummy"); return this; } promise = BodyName(pp); snprintf(cc_operator, CF_MAXVARSIZE - 1, "%s-%s", promise, host); strncpy(cc_operand, operand, CF_BUFSIZE - 1); CanonifyNameInPlace(cc_operand); RemoveDates(cc_operand); free(promise); CfDebug("AcquireLock(%s,%s), ExpireAfter=%d, IfElapsed=%d\n", cc_operator, cc_operand, attr.transaction.expireafter, attr.transaction.ifelapsed); for (i = 0; cc_operator[i] != '\0'; i++) { sum = (CF_MACROALPHABET * sum + cc_operator[i]) % CF_HASHTABLESIZE; } for (i = 0; cc_operand[i] != '\0'; i++) { sum = (CF_MACROALPHABET * sum + cc_operand[i]) % CF_HASHTABLESIZE; } snprintf(cflog, CF_BUFSIZE, "%s/cf3.%.40s.runlog", CFWORKDIR, host); snprintf(cflock, CF_BUFSIZE, "lock.%.100s.%s.%.100s_%d_%s", pp->bundle, cc_operator, cc_operand, sum, str_digest); snprintf(cflast, CF_BUFSIZE, "last.%.100s.%s.%.100s_%d_%s", pp->bundle, cc_operator, cc_operand, sum, str_digest); CfDebug("LOCK(%s)[%s]\n", pp->bundle, cflock); // Now see if we can get exclusivity to edit the locks CFINITSTARTTIME = time(NULL); WaitForCriticalSection(); /* Look for non-existent (old) processes */ lastcompleted = FindLock(cflast); elapsedtime = (time_t) (now - lastcompleted) / 60; if (elapsedtime < 0) { CfOut(cf_verbose, "", " XX Another cf-agent seems to have done this since I started (elapsed=%jd)\n", (intmax_t) elapsedtime); ReleaseCriticalSection(); return this; } if (elapsedtime < attr.transaction.ifelapsed) { CfOut(cf_verbose, "", " XX Nothing promised here [%.40s] (%jd/%u minutes elapsed)\n", cflast, (intmax_t) elapsedtime, attr.transaction.ifelapsed); ReleaseCriticalSection(); return this; } /* Look for existing (current) processes */ if (!ignoreProcesses) { lastcompleted = FindLock(cflock); elapsedtime = (time_t) (now - lastcompleted) / 60; if (lastcompleted != 0) { if (elapsedtime >= attr.transaction.expireafter) { CfOut(cf_inform, "", "Lock %s expired (after %jd/%u minutes)\n", cflock, (intmax_t) elapsedtime, attr.transaction.expireafter); pid = FindLockPid(cflock); if (pid == -1) { CfOut(cf_error, "", "Illegal pid in corrupt lock %s - ignoring lock\n", cflock); } #ifdef MINGW // killing processes with e.g. task manager does not allow for termination handling else if (!NovaWin_IsProcessRunning(pid)) { CfOut(cf_verbose, "", "Process with pid %d is not running - ignoring lock (Windows does not support graceful processes termination)\n", pid); LogLockCompletion(cflog, pid, "Lock expired, process not running", cc_operator, cc_operand); unlink(cflock); } #endif /* MINGW */ else { CfOut(cf_verbose, "", "Trying to kill expired process, pid %d\n", pid); err = GracefulTerminate(pid); if (err || errno == ESRCH) { LogLockCompletion(cflog, pid, "Lock expired, process killed", cc_operator, cc_operand); unlink(cflock); } else { ReleaseCriticalSection(); FatalError("Unable to kill expired cfagent process %d from lock %s, exiting this time..\n", pid, cflock); } } } else { ReleaseCriticalSection(); CfOut(cf_verbose, "", "Couldn't obtain lock for %s (already running!)\n", cflock); return this; } } WriteLock(cflock); } ReleaseCriticalSection(); this.lock = xstrdup(cflock); this.last = xstrdup(cflast); this.log = xstrdup(cflog); /* Keep this as a global for signal handling */ strcpy(CFLOCK, cflock); strcpy(CFLAST, cflast); strcpy(CFLOG, cflog); return this; }