2016-09-13 2 views
0

Ich habe ein Python-Skript, das Bash-Skripte ausführt. Ich muss in der Lage sein, das Bash-Skript zu töten, wenn es unendlich zu sein scheint, und es muss auch im Chroot-Gefängnis laufen, weil das Skript gefährlich sein könnte. Ich lasse es mit psutil.Popen() laufen und lasse es für zwei Sekunden laufen. Wenn es nicht natürlich endet, sende ich SIGKILL an es und all seine möglichen Kinder.Linux - Gründe für SIGSTOP und wie man damit umgeht?

Das Problem ist, dass das Haupt (Python) -Skript eine SIGSTOP empfängt, wenn ich ein Skript wegen Überstundenausführung abbringe und ein anderes ausführe. Auf meinem lokalen Rechner habe ich eine wirklich dumme Lösung gemacht: Das Python-Skript schrieb seine PID beim Start in eine Datei und dann lief ich ein weiteres Skript, das jede Sekunde SIGCONT an die PID sendete, die in der Datei gespeichert war. Dies hat zwei Probleme: Es ist wirklich dumm, aber noch schlimmer ist, dass es sich weigert, auf dem Server zu arbeiten - SIGCONT macht einfach nichts.

Die Sequenz ist: Python-Skript läuft ein Bash-Skript reagiert für die Jail und das Bash-Skript führt die möglicherweise gefährliche und/oder unendliche Skript. Dieses Skript könnte auch einige Kinder haben.

Die relevanten Teile der Codes:

Haupt Python-Skript

p = psutil.Popen(["bash", mode, script_path, self.TESTENV_ROOT]) 
    start = time.time() 

    while True: 
     if p.status() == psutil.STATUS_ZOMBIE: 
      # process ended naturally 
      duration = time.time() - start 
      self.stdout.write("Script finished, execution time: {}s".format(duration)) 
      break 

     if time.time() > start + run_limit: 
      children = p.children(recursive=True) 
      for child in children: 
       child.kill() 
      p.kill() 
      duration = None 
      self.stdout.write("Script exceeded maximum time ({}s) and was killed.".format(run_limit)) 
      break 

     time.sleep(0.01) 

    os.kill(os.getpid(), 17) # SIGCHLD 
    return duration 

Ausführen von Script in chroot ($ 1 ist das Skript in der chroot-Umgebung ausgeführt werden, ist 2 $ das Gefängnis Pfad)

#!/usr/bin/env bash 

# copy script to chroot environment 
cp "$1" "$2/prepare.sh" 

# run script 
chmod u+x "$2/prepare.sh" 
echo './prepare.sh' | chroot "$2" 
rm "$2/prepare.sh" 

Beispiel prepare.sh Skript

#!/bin/bash 
echo asdf > file 

verbrachte ich einige Zeit versucht, das Problem zu lösen. Ich fand heraus, dass dieses Skript (die nicht chroot-Gefängnis wird mit Bash-Skripte ausgeführt werden) funktioniert perfekt:

import psutil 
import os 
import time 

while True: 
    if os.path.exists("infinite.sh"): 
     p = psutil.Popen(["bash","infinite.sh"]) 
     start = time.time() 

     while True: 
      if p.status() == psutil.STATUS_ZOMBIE: 
       # process ended naturally 
       break 

      if time.time() > start + 2: 
       # process needs too much time and has to be killed 
       children = p.children(recursive=True) 
       for child in children: 
        child.kill() 

       p.kill() 
       break 

     os.remove("infinite.sh") 
     os.kill(os.getpid(), 17) 

Meine Fragen sind:

  • Warum erhalte ich SIGSTOP s? Liegt es am Chroot-Gefängnis?
  • Gibt es einen besseren Weg, mein Problem zu lösen, als das "Wake Up" -Skript auszuführen?

Vielen Dank für Ihre Ideen.

EDIT: Ich fand heraus, dass ich in dem Moment sigstopped bin, in dem ich das erste Skript lief, nachdem ich eine Überstunden getötet habe. Egal ob ich os.system oder psutil.Popen verwende.

EDIT2: Ich habe noch mehr Untersuchung und die kritische Linie ist echo './prepare.sh' | chroot "$2" in der Bash-Skript Kontrolle der Chroot-Gefängnis. Die Frage ist jetzt, was zum Teufel ist los damit?

EDIT3:This könnte ein verwandtes Problem sein, wenn es jemandem hilft.

+0

Können Sie dieses 'prepare.sh' Skript posten? – ElmoVanKielmo

+0

hat meinen Beitrag bearbeitet. – karlosss

Antwort

1

Ok, endlich habe ich die Lösung gefunden. Das Problem war wirklich auf der chroot Linie in dem Bash-Skript:

echo './prepare.sh' | chroot "$2" 

Dies scheint aus irgendeinem Grunde nicht korrekt zu sein. Der richtige Weg, um einen Befehl in chroot laufen soll:

chroot chroot_path shell -c command 

So zum Beispiel:

chroot '/home/chroot_jail' '/bin/sh' -c 'rm -rf /' 

Hope this jemand hilft.

1

Ich bin mir ziemlich sicher, dass Sie dies auf Mac OS und nicht Linux ausführen. Warum?Sie senden Signal 17 zu Ihrem Haupt-Python-Prozess statt mit:

import signal 
signal.SIGCHLD 

Ich glaube, Sie haben einen Handler für das Signal 17, die angeblich den inhaftierten Prozess in Reaktion auf dieses Signal respawnen.
Aber signal.SIGCHLD == 17 unter Linux und signal.SIGCHLD == 20 unter Mac OS.

Nun ist die Antwort auf Ihre Frage ist:
signal.SIGSTOP == 17 auf Mac OS.
Ja, sendet Ihr Prozess SIGSTOP sich mit os.kill(os.getpid(), 17)
Mac OS signal man page

EDIT:
es eigentlich auch auf Linux passieren kann, da Linux signal man page sagt, dass POSIX-Standard-Signal 17 sein entweder SIGUSR2, SIGCHLD oder SIGSTOP ermöglicht. Daher empfehle ich dringend die Verwendung von Konstanten aus dem signal Modul der Standardbibliothek anstelle von fest codierten Signalnummern.

+0

Glaubst du wirklich, dass ich nicht weiß, welches System ich benutze? Ich habe die SIGCHLD-Linie auskommentiert, und keine Überraschung, das Verhalten ist immer noch das gleiche. – karlosss

+2

Haben Sie versucht, Konstanten aus 'signal' Bibliothek anstelle von hardcoded Zahlen zu verwenden? War der Effekt gleich? Habe ich dich beleidigt? Ich wollte es wirklich nicht. Einfache Fehler sind Teil unseres (Programmier-) Lebens. Es passiert einfach die ganze Zeit und es gibt keinen Grund, sich darüber zu ärgern. Vielleicht ist es in Ihrem Fall nicht Mac OS, aber Sie senden immer noch eine Singal Nummer 17 und dann erhält Ihr Prozess SIGSTOP. Es gibt zu viel Zufall. Es tut uns leid. Ich versuche nur zu helfen. – ElmoVanKielmo

+0

Nun, es klingt ziemlich komisch von dir, wenn du denkst, dass ich Linux und Mac OS vermischen könnte, aber egal ... :) Ich schaute auf "signal.SIGCHLD" und ging zu der Definition in der "signals" -Bibliothek . Ich habe diese Zeile gefunden: 'SIGCHLD = 17' Also ... irgendwelche anderen Ideen? – karlosss

1

Dieser Thread ist ein bisschen älter ist, aber ich glaube, dass ich die Ursache des Problems kennen (hatte ein ähnliches Problem):

Von here heißt es:

Linux die Standard-Signale unterstützt unten aufgeführt . [...] Zuerst die im ursprünglichen POSIX.1-1990-Standard beschriebenen Signale.

Signal  Value  Action Comment 
    ────────────────────────────────────────────────────────────────────── 
    SIGHUP  1  Term Hangup detected on controlling terminal 
           or death of controlling process 
    SIGINT  2  Term Interrupt from keyboard 
    SIGQUIT  3  Core Quit from keyboard 
    SIGILL  4  Core Illegal Instruction 
    SIGABRT  6  Core Abort signal from abort(3) 
    SIGFPE  8  Core Floating-point exception 
    SIGKILL  9  Term Kill signal 
    SIGSEGV  11  Core Invalid memory reference 
    SIGPIPE  13  Term Broken pipe: write to pipe with no 
           readers; see pipe(7) 
    SIGALRM  14  Term Timer signal from alarm(2) 
    SIGTERM  15  Term Termination signal 
    SIGUSR1 30,10,16 Term User-defined signal 1 
    SIGUSR2 31,12,17 Term User-defined signal 2 
    SIGCHLD 20,17,18 Ign  Child stopped or terminated 
    SIGCONT 19,18,25 Cont Continue if stopped 
    SIGSTOP 17,19,23 Stop Stop process 
    SIGTSTP 18,20,24 Stop Stop typed at terminal 
    SIGTTIN 21,21,26 Stop Terminal input for background process 
    SIGTTOU 22,22,27 Stop Terminal output for background process 

Es zeigt, dass ein Prozess (pro Standardaktion) auch gestoppt wird, wenn es die SIGTSTP, SIGTTIN oder SIGTTOU Signale empfängt.

Diese page erklärt, dass:

[SIGTTIN und SIGTTOU] sind Signale, die Hintergrundprozesse gesendet werden, die sie versuchen, von (SIGTTIN) oder schreiben (SIGTTOU) ihre Steuerung Terminal (oder TTY gelesen).
...
[...] Ändern Terminaleinstellungen [von einem Hintergrundprozess] verursacht SIGTTOU

I sudo strace -tt -o [trace_output_file] -p [pid] verwendet geschickt werden, um zu sehen, welches Signal das Anhalten meines Prozesses ausgelöst.

Wie löst man das Problem? Leider kann ich Ihr reduziertes Beispiel nicht zur Arbeit bringen: Wie sieht Ihr infinite.sh aus? Warum entfernen Sie es während der Ausführung? Ich schlage vor, stdin und stdout umzuleiten. Hast du folgendes versucht?

from subprocess import DEVNULL 
p = psutil.Popen(["bash", mode, script_path, self.TESTENV_ROOT], 
       stdout=DEVNULL, stderr=DEVNULL, STDIN=DEVNULL) 

Sie können natürlich auch subprocess.PIPE verwenden, um die Ausgabe in Ihrem Python-Code zu behandeln oder einfach in eine Datei umleiten. Ich bin mir nicht sicher, wie ich mit nicht autorisierten Versuchen umgehen soll, die Einstellungen von tty zu ändern.

Verwandte Themen