SgtIgrams: PHP gestarteter prozess lässt sich nicht beenden!

Ahoi!

ich habe das problem das ich ein prozess folgendermaßen starte und nichtmehr beendet bekomme!!

$descriptorspec = array(
    0 => array('pipe', 'r'),
    1 => array('file', 'out.log', 'a'),
    2 => array('file', 'err.log', 'w')
);


$cmd = "sudo python /var/www/test.py &> /dev/null &";



$cwd = '/tmp';
$env = array('some_option' => 'aeiou');

$process = proc_open($cmd, $descriptorspec, $pipes, $cwd, $env);

zu beachten ist natürlich das ich ihn mit sudo starte.. gestartet wird er auch definitiv da ich damit LEDs ansteuer und eine PID wird auch rausgeworfen mit "proc_get_status"

ich habe echt meiner meinung nach alles durchprobiert:

$array = proc_get_status($process);
print_r($array);
    $return_value = proc_close($process);
proc_terminate($process);
$cmd = "sudo kill -9 ".$array['pid'];
exec($cmd);
posix_kill($array['pid'], 15);

EDIT: nur ums gesagt zu haben.. zu testzwecken hab ich auf der maschine den user www-data zu den sudoers hinzugefügt... aber wie gesagt.. gestartet wird der prozess einwandfrei.

grüße

  • sgtigrams
  1. Moin!

    ich habe das problem das ich ein prozess folgendermaßen starte und nichtmehr beendet bekomme!!

    Prozesse, die von PHP mit proc_open() gestartet werden, werden in einem Prozess von sh gewrappt. Die PID, die du abfragst, ist die Shell, nicht der eigentliche Prozess. Deswegen killst du auch nur diese Shell, nicht den Prozess.

    Das ganze ist in https://bugs.php.net/bug.php?id=39992 etwas genauer aufgedröselt, und definitiv auch mit PHP 5.6 noch immer ein Problem.

    Symfony hat eine Wrapper-Klasse für derartige Prozesse, und damit auch ein Problem. Deswegen gibts auf Github dazu dieses "issue": https://github.com/symfony/symfony/issues/5759

    Der "Fix" ist, das Kommando mit vorangestelltem "exec" auszuführen. Das vermeidet anscheinend die Wrapper-Shell, die abfragbare PID ist dann die vom tatsächlichen Prozess, und der lässt sich dann auch auf normalem Weg beenden.

    $cmd = "sudo python /var/www/test.py &> /dev/null &";

    $cmd = "exec sudo python /var/www/test.py &> /dev/null &";

    Ich bin mir unsicher wegen sudo, würde auch "sudo exec" in Erwägung ziehen, wenn nötig. Oder am besten ganz auf sudo verzichten. :)

    Grüße Sven

    1. Hallo und guten Abend,

      Der "Fix" ist, das Kommando mit vorangestelltem "exec" auszuführen. Das vermeidet anscheinend die Wrapper-Shell, die abfragbare PID ist dann die vom tatsächlichen Prozess, und der lässt sich dann auch auf normalem Weg beenden.

      Außerdem kann man dem Prozess selber eine Abbruchbedingung mitgeben.
      Innerhalb der zweifellos irgendwo im Prozess vorhandenen Endlosschleife baut man einfach ein (gelegentliches, je nach Aufgabe) Abfragen auf die Existenz eines Runfiles ein. Wenn das nicht mehr da ist, muss der Prozess sich (geordnet) beeneden.

      Und ein Tipp aus der Praxis: Doppelt genäht hilft hier doch meistens besser ;-)

      Grüße
      TS

      --
      es wachse der Freifunk
      https://harz.freifunk.net
  2. Hallo

    ich habe z.Zt. (fast?) das gleiche Problem: ich möchte über eine "Webseite" ein Programm steuern, das LEDs blinken lässt. Meine Umgebung ist ein Raspbery PI mit nginx als Webserver.

    Den Prozess, bei mir ein C-Programm, starte ich mit

    echo(shell_exec('nohup /home/pi/lauflicht/lauflicht 2> /dev/null > /dev/null & echo $!'));
    

    So wird an die WWW-Seite bzw. an die Callback-Funktion im HTTP-Request die Prozessid des Programms gesendet.

    Meine Versuche, den Prozess mit "kill" zu beeinfluseen, sind gescheitert, daher habe ich mir ein C-Programm geschrieben, das das Signal (per kill aus der C-Bibliothek) an den Prozess sendet:

    echo(shell_exec('/home/pi/lauflicht/send_sig 2 '.$_GET['pid']));
    

    Gruß Jürgen

    1. yeah, hab meine alten benutzerdaten wiedergefunden :D

      jürgen, da es sich bei mir auch um leds per gpios meines raspberrys handelt wäre es sehr interessant sich mal mit dir auszutauschen.

      hast du skype/ts oder sowas in die richtung?

      grüße -sgtigram(s)

      1. Hallo,

        jürgen, da es sich bei mir auch um leds per gpios meines raspberrys handelt wäre es sehr interessant sich mal mit dir auszutauschen.

        hast du skype/ts oder sowas in die richtung?

        nee, aber ich kann meine Quelltexte mal hier reinstellen:

        <?php
        if(isset($_GET['cmd'])) {
        	if($_GET['cmd']=='init') {
        // LED-Steuerprogramm starten und PID ausgeben
        		echo(shell_exec('nohup /home/pi/lauflicht/lauflicht 2> /dev/null > /dev/null & echo $!'));
        	}
        	else if($_GET['cmd']=='quit') {
        		if(isset($_GET['pid'])) {
        // LED-Steuerprogramm über PID beenden
        			echo(shell_exec('/home/pi/lauflicht/send_sig 2 '.$_GET['pid']));
        		}
        		else {
        			echo 'Paramter pid fehlt';
        		}
        	}
        	else if($_GET['cmd']=='next') {
        		if(isset($_GET['pid'])) {
        // Signal SIGUSR1 (10) an LED-Steuerprogramm senden, um Lichtmuster zu ändern
        			echo(shell_exec('/home/pi/lauflicht/send_sig 10 '.$_GET['pid']));
        		}
        		else {
        			echo 'Paramter pid fehlt';
        		}
        	}
        	else {
        		echo 'Falscher Parameter';
        	}
        }
        else {
        	echo 'Kein Parameter';
        }
        ?>
        
        // lauflicht.cpp
        // g++ lauflicht.cpp -o lauflicht -lwiringPi
        // sudo chown root lauflicht
        // sudo chmod 4755 lauflicht
        
        // WiringPi-Api einbinden
        #include <wiringPi.h>
        
        // C-Standardbibliotheken einbinden
        #include <stdio.h>
        #include <stdlib.h>
        #include <signal.h>
        #include <time.h>
        #include <math.h>
        
        // Die vier LEDs
        #define LED1 15  // GPIO 14, PIN 8
        #define LED2 16  // GPIO 15, PIN 10
        #define LED3  1  // GPIO 18, PIN 12
        #define LED4  4  // GPIO 23, PIN 16
        
        int leds[4] = {LED1,LED2,LED3,LED4};
        
        // Die Muster
        #define NM 6
        #define NME 16
        
        int muster[NM][NME] = { 
        	{0b1111,    -1,    -1,    -1,    -1,    -1,    -1,    -1,    -1,    -1,    -1,    -1,    -1,    -1,    -1,    -1},
        
        	{0b0001,0b0001,0b0011,0b0011,0b0111,0b0111,0b1111,0b1111,0b1111,0b1111,0b1111,0b1111,0b0000,0b0000,    -1,    -1},
        	{0b0001,0b0000,0b0011,0b0000,0b0111,0b0000,0b1111,0b0000,0b1111,0b0000,0b1111,0b0000,0b1111,0b0000,    -1,    -1},
        	{0b0001,0b0010,0b0100,0b1000,0b0011,0b0110,0b1100,0b1001,0b0111,0b1110,0b1101,0b1011,0b1111,0b1111,0b0000,    -1},
        	{0b0001,0b0011,0b0111,0b1111,0b1111,0b1110,0b1100,0b1000,0b0000,    -1,    -1,    -1,    -1,    -1,    -1,    -1},
        	{0b1000,0b0100,0b0010,0b0001,0b1001,0b0101,0b0011,0b1011,0b0111,0b1111,0b1111,0b0000,    -1,    -1,    -1,    -1}
        										};
        
        int mnr = 0;
        int weiter = 1;
        
        void mnr_up(int sig) {
        	mnr++;
        	if(mnr >= NM ) mnr = 0;
        }
        
        void mnr_down(int sig) {
        	mnr--;
        	if(mnr < 0 ) mnr = NM - 1;
        }
        
        void ende(int sig) {
        	weiter = 0;
        }
        
        void alle_leds(int n) {
        	int i,bit,mask,zuf1,zuf2;
        	// Muster anlegen
        	for(i=0,mask=1;i<4;i++,mask*=2) {
        		bit = n & mask;
        		digitalWrite(leds[i],bit);
        	}
        	// Flackern
        	for(i=0;i<5;i++) {
        		zuf1 = (int)(((float)rand()*4.0)/RAND_MAX);
        		zuf2 = (int)(((float)rand()*10.0)/RAND_MAX);
        		digitalWrite(leds[zuf1],0);
        		delay(10+zuf2);
        		mask = pow(2,zuf1);
        		bit = n & mask;
        		digitalWrite(leds[zuf1],bit);
        		delay(90-zuf2);
        	}
        }
        
        int main( int argc, char **argv ) {
        	int i;
        
        // Übergabeparameter für Musternummer
        	if(argc == 2) {
        		mnr = atoi(argv[1]) ;
        		if(mnr < 0) mnr = 0;
        		if(mnr >= NM ) mnr = NM-1;
        	}
        
        // Starte die WiringPi-Api (wichtig)
        	if (wiringPiSetup() == -1)
        		return 1;
        		
        // Signalhandler anlegen
        	signal(SIGUSR1,mnr_up); // 10
        	signal(SIGUSR2,mnr_down); // 12
        	signal(SIGINT,ende); // 2
        
        // Schalte alle 4 PINs auf Ausgang
        	for(i=0;i<4;i++) pinMode(leds[i], OUTPUT);
        	
        	// Zufalsszahlen initialisieren
        	srand(time(NULL));
        	
        // Musterschleife
        	while(weiter) {
        		for(i=0;i<NME;i++) {
        			if(muster[mnr][i]==-1) break;
        			alle_leds(muster[mnr][i]);
        		}
        	}
        
        // Alles auf 0
        	alle_leds(0);
        
        // Schalte alle 4 PINs auf Eingang
        	for(i=0;i<4;i++) pinMode(leds[i], INPUT);
        }
        
        // send_sig.cpp
        // C-Standardbibliotheken einbinden
        #include <stdio.h>
        #include <stdlib.h>
        #include <signal.h>
        
        int main( int argc, char **argv ) {
        	int sig,ret;
        	pid_t pid;
        
        	// Übergabeparameter für Musternummer
        	if(argc == 3) {
        		sig = atoi(argv[1]) ;
        		pid = atoi(argv[2]) ;
        		printf("Sende Signal %d an PID %d\n",sig,pid);
        		ret = kill(pid,sig);
        		printf("Rückgabe: %d\n",ret);
        	}
        	else {
        		printf("Usage: send_sig sig pid\n");
        	}
        }
        
        <!DOCTYPE html>
        <html lang="de">
        	<head>
        		<meta charset="UTF-8" />
        		<meta name="viewport" content="width=device-width" />
        		<title>Lauflicht</title>
        	<style>
        
        	</style>
        	</head>
        
        	<body>
        		<h1>Lauflicht</h1>
        		<p>
        			<button type="button" onclick="start()">Start</button>
        			<button type="button" onclick="next()">Next</button>
        			<button type="button" onclick="quit()">Quit</button>
        		</p>
        		<p>PID: <span id="opid"></span></p>
        		
        		<script>
        			var hr = function(url,cbf) {
        				var req = new XMLHttpRequest();
        				req.onreadystatechange = function() {
        					if (req.readyState == 4) {
        						var status = req.status;
        						if(status == 200) {
        							req.onreadystatechange = null;
        							var res = req.responseText;
        							if(typeof(cbf)=="function") cbf(res);
        						}
        					}
        				}
        				req.open('GET', url, true);
        				req.send(null);
        			}
        			var pid=-1;
        			var opid = document.getElementById("opid");
        			function start() {
        				if(pid==-1) hr("lauflicht.php?cmd=init",function(res) {
        					pid = res;
        					opid.innerHTML = pid;
        				});
        			}
        			function next() {
        				if(pid>-1) hr("lauflicht.php?cmd=next&pid="+pid,null);
        			}
        			function quit() {
        				if(pid>-1) hr("lauflicht.php?cmd=quit&pid="+pid,function(res) {
        					pid = -1
        					opid.innerHTML = "";
        				});
        			}
        		</script>
        	</body>
        </html>
        

        Wie du siehst, steuere ich die PINS über wiringPi an und programmiere das über C, PHP und im Browser dann HTML und Javascript für das Aufrufen des PHP-Scriptes über HTTP-Requests.

        Gruß Jürgen

  3. Moin!

    Python kennt seine PID:

    import os
    pid = str(os.getpid())
    

    Die PID in eine Datei wegschreiben und diese auszulesen um den Prozess zu killen könnte also auch gehen.

    Jörg Reinholz