2009-10-29 5 views
15

Ho una procedura che desidero avviare solo se diversi test sono stati completati correttamente.C'è un buon modo per rilevare un attacco NFS stante

Un test di cui ho bisogno è che tutte le mie montature NFS sono vive e vegete.

Posso fare di meglio che l'approccio forza bruta:


mount | sed -n "s/^.* on \(.*\) type nfs .*$/\1/p" | 
while read mount_point ; do 
    timeout 10 ls $mount_point >& /dev/null || echo "stale $mount_point" ; 
done 

Qui timeout è un programma di utilità che verrà eseguito il comando in background, e sarà ucciderlo dopo un certo tempo, se non SIGCHLD è stato catturato prima del limite di tempo, restituendo successo/fallimento in modo ovvio.


In inglese: Parse l'uscita di mount, controllo (delimitata da un timeout) ogni punto di attivazione NFS. Opzionalmente (non nel codice sopra) che si rompe sul primo attacco obsoleto.

risposta

3

È possibile scrivere un programma in C e verificare ESTALE.

#include <sys/types.h> 
#include <sys/stat.h> 
#include <unistd.h> 
#include <iso646.h> 
#include <errno.h> 
#include <stdio.h> 
#include <stdlib.h> 

int main(){ 
    struct stat st; 
    int ret; 
    ret = stat("/mnt/some_stale", &st); 
    if(ret == -1 and errno == ESTALE){ 
     printf("/mnt/some_stale is stale\n"); 
     return EXIT_SUCCESS; 
    } else { 
     return EXIT_FAILURE; 
    } 
} 
+0

Da 'man 3 errno': ESTALE riservati . Questo significa che dovrei cercare un'altra soluzione? –

+0

Penso che dipenda dal tuo kernel. – Teddy

+0

Sì, hai ragione: in una versione successiva della mia distro 'man 3 errno' dire:" 'ESTALE' Gestione file stantio (POSIX.1)) Questo errore può verificarsi per NFS e per altri file system". E anche se sono andato con l'approccio della forza bruta, descritto nella mia domanda, accetterò questa risposta. –

7

Un mio collega corse nello script. Questo non evita un approccio di "forza bruta", ma se posso in Bash:

while read _ _ mount _; do 
    read -t1 < <(stat -t "$mount") || echo "$mount timeout"; 
done < <(mount -t nfs) 

mount possiamo elencare NFS monta direttamente. read -t (una shell incorporata) può timeout un comando. stat -t (uscita tersa) si blocca ancora come un ls *. ls produce output non necessari, rischia di falsi positivi su elenchi di elenchi enormi/lenti e richiede l'accesso alle autorizzazioni, il che potrebbe anche provocare un falso positivo se non li possiede.

while read _ _ mount _; do 
    read -t1 < <(stat -t "$mount") || lsof -b 2>/dev/null|grep "$mount"; 
done < <(mount -t nfs) 

Stiamo usando con lsof -b (non bloccante, in modo che non si bloccherà anche) al fine di determinare la fonte dei si blocca.

Grazie per il puntatore!

  • test -d (integrato nella shell) sarebbe lavorare anziché stat (uno standard esterno) pure, ma read -t ritorni successo solamente se non timeout e legge una linea di ingresso. Dal momento che lo test -d non utilizza lo stdout, sarebbe necessario un controllo di errorlevel (($? > 128)) - non vale il colpo di leggibilità, IMO.
+0

mentre l'ultimo esempio consente il comando (senza appendere alla statistica) l'lsof -b 2 sembra semplicemente saltare tutti i test delle statistiche e non restituire nulla. –

+0

Come sapete, '<(...)' viene eseguito in una sotto-shell, e se 'stat (1)' si blocca a causa di un vecchio NFS, la sotto-shell non verrà terminata correttamente. Vedi [check-nfs.sh] (https://gist.github.com/cinsk/840ed553905cb6e8f0ae) per il miglioramento di questo. – cinsk

5

ho messo del tempo, ma qui è quello che ho trovato che funziona in Python:

import signal, os, subprocess 
class Alarm(Exception): 
    pass 

def alarm_handler(signum, frame): 
    raise Alarm 

pathToNFSMount = '/mnt/server1/' # or you can implement some function 
           # to find all the mounts... 

signal.signal(signal.SIGALRM, alarm_handler) 
signal.alarm(3) # 3 seconds 
try: 
    proc = subprocess.call('stat '+pathToNFSMount, shell=True, stderr=subprocess.PIPE, stdout=subprocess.PIPE) 
    stdoutdata, stderrdata = proc.communicate() 
    signal.alarm(0) # reset the alarm 
except Alarm: 
    print "Oops, taking too long!" 

Osservazione:

  1. credito al answer here.
  2. Si potrebbe anche usare schema alternativo:

    os.fork() e os.stat()

verificare se la forcella finito, se è scaduta si può ucciderlo. Dovrai lavorare con time.time() e così via.

3

Scrivere un programma in C che controlli ESTALE è una buona opzione se non ti dispiace aspettare che il comando finisca a causa del file system stantio. Se si desidera implementare un'opzione "timeout", il modo migliore che ho trovato per implementarlo (in un programma C) è quello di eseguire il fork di un processo figlio che tenta di aprire il file. Quindi si controlla se il processo figlio ha terminato la lettura di un file con successo nel filesystem entro un intervallo di tempo allocato.

Ecco una piccola prova del programma di concept C per fare questo:

#include <stdlib.h> 
#include <stdio.h> 
#include <stdint.h> 
#include <unistd.h> 
#include <errno.h> 
#include <fcntl.h> 
#include <sys/wait.h> 


void readFile(); 
void waitForChild(int pid); 


int main(int argc, char *argv[]) 
{ 
    int pid; 

    pid = fork(); 

    if(pid == 0) { 
    // Child process. 
    readFile(); 
    } 
    else if(pid > 0) { 
    // Parent process. 
    waitForChild(pid); 
    } 
    else { 
    // Error 
    perror("Fork"); 
    exit(1); 
    } 

    return 0; 
} 

void waitForChild(int child_pid) 
{ 
    int timeout = 2; // 2 seconds timeout. 
    int status; 
    int pid; 

    while(timeout != 0) { 
    pid = waitpid(child_pid, &status, WNOHANG); 
    if(pid == 0) { 
     // Still waiting for a child. 
     sleep(1); 
     timeout--; 
    } 
    else if(pid == -1) { 
     // Error 
     perror("waitpid()"); 
     exit(1); 
    } 
    else { 
     // The child exited. 
     if(WIFEXITED(status)) { 
     // Child was able to call exit(). 
     if(WEXITSTATUS(status) == 0) { 
      printf("File read successfully!\n"); 
      return; 
     } 
     } 
     printf("File NOT read successfully.\n"); 
     return; 
    } 
    } 

    // The child did not finish and the timeout was hit. 
    kill(child_pid, 9); 
    printf("Timeout reading the file!\n"); 
} 

void readFile() 
{ 
    int fd; 

    fd = open("/path/to/a/file", O_RDWR); 
    if(fd == -1) { 
    // Error 
    perror("open()"); 
    exit(1); 
    } 
    else { 
    close(fd); 
    exit(0); 
    } 
} 
1

Un altro modo, utilizzando script di shell. Funziona bene per me:

#!/bin/bash 
# Purpose: 
# Detect Stale File handle and remove it 
# Script created: July 29, 2015 by Birgit Ducarroz 
# Last modification: -- 
# 

# Detect Stale file handle and write output into a variable and then into a file 
mounts=`df 2>&1 | grep 'Stale file handle' |awk '{print ""$2"" }' > NFS_stales.txt` 
# Remove : ‘ and ’ characters from the output 
sed -r -i 's/://' NFS_stales.txt && sed -r -i 's/‘//' NFS_stales.txt && sed -r -i 's/’//' NFS_stales.txt 

# Not used: replace space by a new line 
# stales=`cat NFS_stales.txt && sed -r -i ':a;N;$!ba;s/ /\n /g' NFS_stales.txt` 

# read NFS_stales.txt output file line by line then unmount stale by stale. 
# IFS='' (or IFS=) prevents leading/trailing whitespace from being trimmed. 
# -r prevents backslash escapes from being interpreted. 
# || [[ -n $line ]] prevents the last line from being ignored if it doesn't end with a \n (since read returns a non-zero exit code when it encounters EOF). 

while IFS='' read -r line || [[ -n "$line" ]]; do 
    echo "Unmounting due to NFS Stale file handle: $line" 
    umount -fl $line 
done < "NFS_stales.txt" 
#EOF 
3

ho scritto https://github.com/acdha/mountstatus che utilizza un approccio simile a quello che UndeadKernel detto, che ho trovato ad essere l'approccio più robusto: si tratta di un demone che analizza periodicamente tutti i filesystem montati dal forking un bambino processo che tenta di elencare la directory di primo livello e SIGKILL se non riesce a rispondere in un determinato timeout, con entrambi i successi e gli errori registrati su syslog. Ciò evita problemi con alcune implementazioni client (ad esempio Linux precedenti) che non attivano mai timeout per determinate classi di errori, server NFS parzialmente responsive ma ad es. non risponderà a chiamate effettive come listdir, ecc.

Non li pubblico ma il Makefile incluso utilizza fpm per creare pacchetti rpm e deb con uno script Upstart.

+0

Insieme al porting del progetto su Rust e aggiungendo alcune altre funzionalità, ora ci sono pacchetti deb & rpm: https://github.com/acdha/mountstatus/releases –

1

Oltre alle risposte precedenti, che pende in alcune circostanze, i controlli questo frammento Tutti i supporti adatti, uccide con KILL del segnale, e viene testato con CIFS troppo:

grep -v tracefs /proc/mounts | cut -d' ' -f2 | \ 
    while read m; do \ 
    timeout --signal=KILL 1 ls -d $m > /dev/null || echo "$m"; \ 
    done