#!/usr/bin/php
<?php
/**
* ============================================================
* daemon_watchman_sub.php
* - 2중 감시용 서브 워치맨
* - 단 하나만 감시함: daemon_watchman_main.php
* - main 데몬이 죽으면 즉시 살림
* - PID 파일 사용 없음 (DB 기반)
* - 관제: daemon_record 테이블
* - 루프: 3초
* ============================================================
*/
error_reporting(E_ALL);
ini_set('display_errors', 1);
date_default_timezone_set('Asia/Seoul');
// ------------------------------------------------------------
// 1. 기본 설정
// ------------------------------------------------------------
$THIS_FILE = __FILE__;
$DAEMON_ID = pathinfo($THIS_FILE, PATHINFO_FILENAME); // daemon_watchman_sub
$WATCH_DIR = "/home/www/DATA/UPBIT/daemon/watchman";
$TARGET_MAIN = "daemon_watchman_trading_main.php";
$TARGET_PATH = $WATCH_DIR . "/" . $TARGET_MAIN;
// ------------------------------------------------------------
// 2. DB 연결 함수
// ------------------------------------------------------------
function get_db() {
require "/home/www/DB/db_upbit.php"; // $db_upbit
$pdo = $db_upbit;
$pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
return $pdo;
}
$pdo = get_db();
// ------------------------------------------------------------
// 3. 관제 기록 (daemon_record)
// ------------------------------------------------------------
function report_status($pdo, $DAEMON_ID) {
$stmt = $pdo->prepare("
INSERT INTO daemon_record (
d_id, d_category, d_pid, d_status,
d_heartbeat, d_ip, d_start_time, d_memo, d_kill_flag
)
VALUES (
:id, 'WATCHMAN', :pid, 'RUNNING',
NOW(), 'CLI', NOW(), 'SUB WATCHMAN', 0
)
ON DUPLICATE KEY UPDATE
d_pid = VALUES(d_pid),
d_status = 'RUNNING',
d_heartbeat = NOW()
");
$stmt->execute([
':id' => $DAEMON_ID,
':pid' => getmypid()
]);
}
// ------------------------------------------------------------
// 4. 프로세스 생존 여부 체크
// ------------------------------------------------------------
function is_alive($pid) {
return ($pid && posix_kill((int)$pid, 0));
}
// ------------------------------------------------------------
// 5. main 데몬 실행
// ------------------------------------------------------------
function run_main_daemon($path) {
$cmd = "php {$path} > /dev/null 2>&1 & echo $!";
$pid = trim(shell_exec($cmd));
return ctype_digit($pid) ? (int)$pid : null;
}
// ------------------------------------------------------------
// 6. DB에서 대상 데몬 PID 가져오기
// ------------------------------------------------------------
function get_target_pid($pdo, $target_id) {
$stmt = $pdo->prepare("SELECT d_pid FROM daemon_record WHERE d_id = :id LIMIT 1");
$stmt->execute([':id' => $target_id]);
$pid = $stmt->fetchColumn();
return $pid ? (int)$pid : null;
}
// ------------------------------------------------------------
// INIT
// ------------------------------------------------------------
echo "==================================================\n";
echo "[{$DAEMON_ID}] SUB WATCHMAN STARTED\n";
echo "TARGET: {$TARGET_MAIN}\n";
echo "==================================================\n";
// ------------------------------------------------------------
// MAIN LOOP — 3초 감시
// ------------------------------------------------------------
while (true) {
try {
$pdo = get_db(); // ensure connection
// 워치맨 상태 저장
report_status($pdo, $DAEMON_ID);
// main 데몬 PID 조회
$main_pid = get_target_pid($pdo, "daemon_watchman_main");
$alive = is_alive($main_pid);
if (!$alive) {
echo "[SUB] MAIN DEAD — RESTARTING...\n";
// 재실행
$new_pid = run_main_daemon($TARGET_PATH);
if ($new_pid) {
// DB 갱신
$stmt = $pdo->prepare("
INSERT INTO daemon_record (
d_id, d_category, d_pid, d_status,
d_heartbeat, d_ip, d_start_time, d_memo, d_kill_flag
)
VALUES (
'daemon_watchman_main', 'WATCHMAN', :pid, 'RUNNING',
NOW(), 'CLI', NOW(), 'MAIN WATCHMAN', 0
)
ON DUPLICATE KEY UPDATE
d_pid = VALUES(d_pid),
d_status = 'RUNNING',
d_heartbeat = NOW()
");
$stmt->execute([':pid' => $new_pid]);
echo "[SUB] MAIN RESTARTED | PID={$new_pid}\n";
} else {
echo "[SUB] MAIN RESTART FAILED\n";
}
}
} catch (Exception $e) {
echo "[ERROR] " . $e->getMessage() . "\n";
sleep(2);
}
sleep(3);
}
?>