Synology Hyper Backup - Script pour gérer le NAS de sauvegarde - WOL - Sauvegarde/ Intégrité - Extinction

cadkey

Nouveau membre
5 Juin 2026
12
4
3
Un script PHP hyperbackup.php que j'utilise pour lancer mes sauvegardes une fois par jours, et vérifier l'intégrité de la sauvegarde une fois par mois. La fréquence est à définir dans les deux tâches du plannificateur de tâches, comme indiqué en début de script.
PHP:
<?php

/*
    Sauvegarde hyper backup de la $source vers la $cible sur le réseau local
    La tache hyper backup à executer est identifiée par son nom dans $taskName
    Pour que la tache hyperbackup soit accessible par toutes les interfaces réseau de la $cible, il faut indiquer dans le champ 'cible' de Hyperbackup le nom exacte du NAS CIBLE et non l'ip de l'interface, et activer la découverte Bonjour sur les deux NAS
    Le script est lancé par deux taches via le planificateur de tache:
    - Pour un backup: php /volume1/script/hyperbackup.php backup
    - Pour vérifier l'integrité: php /volume1/script/hyperbackup.php verify
*/

$mode = $argv[1] ?? null; // suivant planificateur de tache, 'backup' ou 'verify'
if ($mode === null) exit(date('d/m/Y H:i:s').' L\'argument de la tâche planifiée n\'est pas valide. Vérifiez'.PHP_EOL);

$log = false; // affiche les logs true/false - les erreurs sont toujours affichées

// Nom de la tâche hyper backup à executer
$taskName = 'Backup'; // nom de la tache réelle

// données des NAS $source et $cible
$source = '920+'; // à modifier suivant configuration
$source_ip = '192.168.1.25'; // à modifier suivant configuration
$source_user = 'hyperbackup'; // user admin complet créé pour hyper backup
$source_pass = 'pass user hyperbackup'; // pass TRES FORT (j'utilise plus de 64 caractères, nombre de combinaisons astronomique, force brute impossible)

$cible = 'ds14'; // à modifier suivant configuration
$cible_mac = 'aa:bb:cc:dd:ee:99'; // à modifier suivant configuration
$cible_ip = '192.168.1.26'; // à modifier suivant configuration
$cible_user = 'user sauvegarde'; // user admin restreint créé pour hyper backup
$cible_pass = 'pas user sauvegarde'; // pass TRES FORT

// données du script
$dsm_timeout = 180;
$backup_timeout = 10800;

$hb_success = ['none', 'done', 'success']; // none sur ma version
$hb_failed = ['failed', 'cancel', 'suspend', 'partial', 'cksum_failed', 'dest_missing', 'failed_checking'];

if ($log) echo date('d/m/Y H:i:s').' Début Hyper Backup mode: '.$mode.PHP_EOL;

// 1. Récupération automatique du task_id Hyper Backup par le nom de la tache hyperbckup
$sid_source = login($source_ip, $source_user, $source_pass);
if (!$sid_source) exit(date('d/m/Y H:i:s ').'Login '.$source.' impossible'.PHP_EOL);
$task = null;
$list = dsm($source_ip, ['api' => 'SYNO.Backup.Task', 'version' => 1, 'method' => 'list'], $sid_source);
$tasks = $list['data']['task_list'] ?? [];
foreach ($tasks as $t) {
    if (trim((string)($t['name'] ?? '')) === trim((string)$taskName)) {
        $task = $t['task_id'];
        break;
    }
}
if (!$task) sortie($source_ip, $sid_source, 'Tâche introuvable : '.$taskName);

// 2. WOL réveil du NAS $cible - ne fonctionne pas sur adaptateur USB/RJ45.
$iface = 'ovs_bond0'; // eth0, eth1 ... interface réseau utilisée par le NAS $source
shell_exec('synonet --wake '.escapeshellarg($cible_mac).' '.escapeshellarg($iface));
sleep(3);
shell_exec('synonet --wake '.escapeshellarg($cible_mac).' '.escapeshellarg($iface)); // commade doublée par sécurité
if ($log) echo date('d/m/Y H:i:s').' WOL -> '.$cible.PHP_EOL;

// 3. Attente NAS $cible prêt
$sid_cible = null;
$limit = time() + $dsm_timeout;

while (time() < $limit) {
    sleep(5);
    $sid_cible = login($cible_ip, $cible_user, $cible_pass);
    if ($sid_cible) break;
    if ($log) echo date('d/m/Y H:i:s').' pas prêt'.PHP_EOL;
}

if (!$sid_cible) sortie($source_ip, $sid_source, 'NAS '.$cible.' injoignable après '.$dsm_timeout.'s');
if ($log) echo date('d/m/Y H:i:s').' NAS cible '.$cible.' prêt'.PHP_EOL;
sleep(12);

// 4a. Backup
if ($mode === 'backup') {
    $r = dsm($source_ip, ['api' => 'SYNO.Backup.Task', 'version' => 1, 'method' => 'backup', 'task_id' => $task], $sid_source);
    if (!($r['success'] ?? false)) {
        logout($source_ip, $sid_source);
        echo date('d/m/Y H:i:s').' Impossible de démarrer la tâche #'.$task.' '.$taskName.'. Allonger le sleep() précédent'.PHP_EOL;
        goto shutdown;
    }
    if ($log) echo date('d/m/Y H:i:s').' Backup démarré'.PHP_EOL;
    $limit = time() + $backup_timeout;
    $ok = false;
    while (time() < $limit) {
        sleep(20);
        $r = dsm($source_ip, ['api' => 'SYNO.Backup.Task', 'version' => 1, 'method' => 'status', 'task_id' => $task], $sid_source);
        $status = ($r['success'] ?? false) ? ($r['data']['status'] ?? null) : null;
        if (in_array($status, $hb_success, true)) {
            $ok = true;
            break;
        }
        if (in_array($status, $hb_failed, true)) break;
    }
    logout($source_ip, $sid_source);
    if ($ok && $log) echo date('d/m/Y H:i:s').' Backup vers '.$cible.' réussie'.PHP_EOL;
    elseif (!$ok) echo date('d/m/Y H:i:s').' Backup vers '.$cible.' échouée/expirée'.PHP_EOL;
}

// 4b. Vérification intégrité
if ($mode === 'verify') {
    if ($log) echo date('d/m/Y H:i:s').' Vérification intégrité démarrée'.PHP_EOL;
    shell_exec('/var/packages/HyperBackup/target/bin/detect_monitor --task-id '.escapeshellarg($task).' --trigger --full --guard 2>&1');
    $limit = time() + $backup_timeout;
    while (time() < $limit) {
        sleep(60);
        $json = trim(shell_exec('/usr/syno/bin/synowebapi --exec api=SYNO.Backup.Task method=list version=1 2>/dev/null'));
        $data = json_decode($json, true);
        $task_data = null;
        if (is_array($data) && isset($data['data']['task_list'])) {
            foreach ($data['data']['task_list'] as $t) if ($t['task_id'] == $task) {
                $task_data = $t;
                break;
            }
        }
        $status = $task_data ? $task_data['state'] : 'error_detect';
        if ($log) echo date('d/m/Y H:i:s ').$source.' status: '.$status.PHP_EOL;
        if ($status !== 'error_detect') break;
    }
    if ($status !== 'backupable') echo date('d/m/Y H:i:s').' Vérification échouée, status: '.$status.PHP_EOL;
    elseif ($log) echo date('d/m/Y H:i:s').' Vérification réussie'.PHP_EOL;
}

// 5. Shutdown NAS $cible, même si backup KO
shutdown:
logout($cible_ip, $sid_cible);
$sid_cible = login($cible_ip, $cible_user, $cible_pass);
$r = $sid_cible ? dsm($cible_ip, ['api' => 'SYNO.Core.System', 'version' => 1, 'method' => 'shutdown'], $sid_cible) : null;
if ($r['success'] ?? false) {
    if ($log) echo date('d/m/Y H:i:s').' Shutdown envoyé à NAS '.$cible.PHP_EOL;
    $limit = time() + 120;
    while (time() < $limit) {
        sleep(3);
        $ping = (string) shell_exec('ping -c 1 -W 1 '.escapeshellarg($cible_ip).' 2>&1');
        if (!str_contains($ping, 'bytes from')) {
            if ($log) echo date('d/m/Y H:i:s').' cible NAS '.$cible.' éteint, FIN'.PHP_EOL;
            break;
        }
    }
}
else echo date('d/m/Y H:i:s').' Shutdown échoué, FIN'.PHP_EOL;

function dsm($ip, $p, $sid = null) {
    if ($sid) $p['_sid'] = $sid;
    $ep = str_contains($p['api'], 'Auth') ? 'auth.cgi' : 'entry.cgi';
    $url = 'http://'.$ip.'/webapi/'.$ep.'?'.http_build_query($p);
    $ctx = stream_context_create(['http' => ['timeout' => 10, 'ignore_errors' => true]]);
    $body = @file_get_contents($url, false, $ctx);
    return $body ? json_decode($body, true) : null;
}

function login($ip, $user, $pass) {
    $r = dsm($ip, ['api' => 'SYNO.API.Auth', 'version' => 6, 'method' => 'login', 'account' => $user, 'passwd' => $pass, 'session' => 'HBBackup', 'format' => 'sid']);
    return ($r['success'] ?? false) ? $r['data']['sid'] : null;
}

function logout($ip, $sid) {
    dsm($ip, ['api' => 'SYNO.API.Auth', 'version' => 6, 'method' => 'logout', 'session' => 'HBBackup'], $sid);
}

function sortie($ip, $sid, $text) {
    logout($ip, $sid);
    exit(date('d/m/Y H:i:s ').$text.PHP_EOL);
}

?>

En activité chez moi depuis plusieurs semaines.
En pratique je définis les tâches ainsi pour logger les erreurs et le debug ($log = true):
php /volume1/script/hyperbackup.php backup >> /volume1/script/hyperbackup.log
php /volume1/script/hyperbackup.php verify >> /volume1/script/hyperbackup.log