= CURDATE() ) "; $stmt = $pdo->prepare($sqlList); $stmt->execute(); $mikrotiks = $stmt->fetchAll(PDO::FETCH_ASSOC); $pdo = null; if (empty($mikrotiks)) { echo "[" . date('Y-m-d H:i:s') . "] Tidak ada data MikroTik.\n"; exit(0); } // Fork parallel $child_pids = []; $outfiles = []; foreach ($mikrotiks as $data) { while (count($child_pids) >= $max_processes) { foreach ($child_pids as $key => $pid) { if (pcntl_waitpid($pid, $status, WNOHANG) > 0) { unset($child_pids[$key]); } } usleep(100000); } $safeId = preg_replace('~[^A-Za-z0-9_\-]~', '_', substr($data['board_id'], 0, 64)); $outfile = $tmpDir . "/mt_" . $safeId . "_" . microtime(true) . "_" . mt_rand(1000,9999) . ".json"; $outfiles[] = $outfile; $pid = pcntl_fork(); if ($pid == -1) { echo "[" . date('Y-m-d H:i:s') . "] Gagal fork.\n"; continue; } elseif ($pid) { $child_pids[] = $pid; } else { process_mikrotik_child($data, $timeout_api, $outfile); exit(0); } } foreach ($child_pids as $pid) { pcntl_waitpid($pid, $status); } echo "[" . date('Y-m-d H:i:s') . "] Semua child selesai. Mulai update DB...\n"; // Update DB include "/www/wwwroot/bilspro/config/connect.php"; $pdo->exec("SET time_zone = '+07:00'"); echo "[" . date('Y-m-d H:i:s') . "] Semua child selesai. Mulai update DB masal...\n"; $updated = 0; $skipped = 0; $failed = 0; try { $pdo->beginTransaction(); $updateSql = " UPDATE setting_mikrotik SET pppa = :pppa, hsa = :hsa, uptime = :ut, board_name = :bm, status = :st, last_time = NOW() WHERE board_id = :id "; $statusOnlySql = " UPDATE setting_mikrotik SET status = :st WHERE board_id = :id "; $updateStmt = $pdo->prepare($updateSql); $statusOnlyStmt = $pdo->prepare($statusOnlySql); foreach ($outfiles as $file) { if (!is_file($file)) continue; $json = file_get_contents($file); @unlink($file); $row = json_decode($json, true); if (!$row || !isset($row['board_id'], $row['status'])) { $failed++; continue; } if ($row['status'] === 'Connected') { $params = [ ':pppa' => $row['pppa'] ?? '', ':hsa' => $row['hsa'] ?? '', ':ut' => $row['uptime'] ?? '', ':bm' => $row['board_name'] ?? '', ':st' => 'Connected', ':id' => $row['board_id'], ]; $updateStmt->execute($params); echo "Update count: " . $updateStmt->rowCount() . "\n"; // Cek apakah ada perubahan data asli if ($updateStmt->rowCount() > 0) { echo "[" . date('H:i:s') . "] [UPDATE] {$row['board_id']} - Data diperbarui.\n"; $updated++; } else { echo "[" . date('H:i:s') . "] [SKIP] {$row['board_id']} - Data masih sama.\n"; $skipped++; } } else { $statusOnlyStmt->execute([ ':st' => $row['status'], ':id' => $row['board_id'], ]); if ($statusOnlyStmt->rowCount() > 0) { echo "[" . date('H:i:s') . "] [OFFLINE] {$row['board_id']} - Status menjadi {$row['status']}.\n"; $updated++; } else { $skipped++; } } } // Komit semua perubahan sekaligus $pdo->commit(); echo "[" . date('Y-m-d H:i:s') . "] Transaksi berhasil dicommit ke Database.\n"; } catch (Exception $e) { // Jika ada error fatal, batalkan semua agar data tidak korup if ($pdo->inTransaction()) { $pdo->rollBack(); } echo "[" . date('Y-m-d H:i:s') . "] [CRITICAL ERROR] " . $e->getMessage() . "\n"; } $pdo = null; echo "[" . date('Y-m-d H:i:s') . "] Selesai. OK: {$updated}, SKIP: {$skipped}, FAIL: {$failed}\n"; // ================================ // CHILD: konek RouterOS & tulis file hasil // ================================ function process_mikrotik_child(array $data, int $timeout_api, string $outfile): void { $ip = $data['ip_address']; $username = $data['username']; $password = $data['password']; $port = $data['port_api']; $board_id = $data['board_id']; $API = new RouterosAPI(); $API->debug = false; $API->timeout = $timeout_api; $result = [ 'board_id' => $board_id, 'status' => 'Disconnected', 'pppa' => '', 'hsa' => '', 'board_name' => '', 'uptime' => '', 'ip' => $ip, ]; if ($API->connect2($ip, $username, $password, $port)) { $pppoe_active = []; $pppoe_data = $API->comm("/ppp/active/print"); if (is_array($pppoe_data)) { foreach ($pppoe_data as $item) { $name = $item['name'] ?? ''; $addr = $item['address'] ?? ''; $mac = $item['caller-id'] ?? ''; if ($name !== '' && $addr !== '') { $pppoe_active[] = "{$name}_{$addr}_{$mac}"; } } } $hotspot_active = []; $hotspot_users = $API->comm("/ip/hotspot/user/print"); if (is_array($hotspot_users)) { foreach ($hotspot_users as $item) { $user = $item['name'] ?? ''; $comment = $item['comment'] ?? ''; if ($user !== '' && strpos($comment, 'Login.') === 0) { $hotspot_active[] = "{$user}_" . substr($comment, 6); } } } $sys = $API->comm("/system/resource/print"); $board_name = ''; $uptime = ''; if (is_array($sys) && !empty($sys[0])) { $board_name = $sys[0]['board-name'] ?? ''; $uptime = $sys[0]['uptime'] ?? ''; } $API->disconnect(); $result['status'] = 'Connected'; $result['pppa'] = implode('|', $pppoe_active); $result['hsa'] = implode('|', $hotspot_active); $result['board_name'] = $board_name; $result['uptime'] = $uptime; echo "[" . date('H:i:s') . "] [OK] {$ip}\n"; } else { echo "[" . date('H:i:s') . "] [FAIL] {$ip}\n"; } file_put_contents($outfile, json_encode($result, JSON_UNESCAPED_UNICODE)); }