Anzeige:
Ergebnis 1 bis 14 von 14

Thema: Bash: Wie Prozess zur PID finden?

  1. #1
    Registrierter Benutzer
    Registriert seit
    03.09.2006
    Beiträge
    120

    Question Bash: Wie Prozess zur PID finden?

    In einigen meiner Bash-Skripte habe ich drei Funktionen zum sauberen Locken, die ich aus dem Buch "Raffinierte Shell Scripts" habe:

    Code:
    sleeptime="1"           # sleeptime for creating the lockfile
    retries="10"		# default number of retries of creating the lockfile: 10 (should be > locktimeout*sleeptime)
    locktimeout="5"         # default timeout : 5 s. The lockfile will be removed 
                            # by force after locktimeout seconds have passed 
    lockdir="/var/tmp"
    lockfile="$lockdir/lockfile.beispiel" # lockfile name
    
    # ascertain whether we have lockf or lockfile system apps
    check ()
    {
      if [ -z "$(which lockfile | grep -v '^no ')" ] ; then
        echo "$0 failed: 'lockfile' utility not found in PATH." >&2
        exit 1
      fi
    }
    
    # make lockifle
    lock () 
    {
      if [ -f $lockfile ]; then
        if kill -0 $(cat $lockfile) 2> /dev/null; then
          echo "The locking executable with pid $(cat $lockfile) appears to be already running."
          echo "Please check $lockfile if you think this is an error."
          exit 1
        else
          echo "The locking executable with pid $(cat $lockfile) appears to have completed without cleaning up its lockfile."
          echo "Removing that lockfile "$lockfile"."
          rm -f "$lockfile"
        fi
      fi
      # create the lockfile; wait 
      if ! lockfile -$sleeptime -r $retries -l $locktimeout "$lockfile" 2> /dev/null; then
        echo "$0: Failed: Couldn't create lockfile in time" >&2
        exit 1
      fi
      chmod u+rw "$lockfile"
      # store the pid
      echo $$ > "$lockfile"
      chmod u-wx "$lockfile"
    }
    
    # cleanup
    unlock () 
    {
      rm -f "$lockfile"
    }
    Allerdings gibt's damit das Problem, das beim lock() zufällig ein ganz anderer Prozess laufen kann, der die PID hat, die im Lockfile steht.
    Und Gemäß Murphy's Law wird mir das sicherlich irgendwann passieren.
    Man kann das Problem aber beseitigen, indem man den Namen zu der PID überprüft.
    Dabei gibt's aber das Problem, das bei Bash-Skripten am Anfang der Kommandozeile, die ps anzeigt, erstmal /bin/bash steht; ein einfacher String-Vergleich reicht also nicht. Dazu kommt, das am Ende auch etwas stehen kann; beispielsweise
    /dev/ttyS0 | tee data ; } 3>&2 2>&1 1>&3 | tee log.err
    .
    Wie also kann ich beim lock() überprüfen, ob der Prozess, der zu der PID im Lockfile gehört, einen bestimmten Namen hat (enthält); beispielsweise $0 (Name des aktuellen Shellskripts) oder ein String wie "beispiel.sh"?
    Geändert von Linus (07-08-2007 um 21:24 Uhr)

  2. #2
    Registrierter Benutzer
    Registriert seit
    07.05.2007
    Beiträge
    656
    Zitat Zitat von Linus Beitrag anzeigen
    ...Wie also kann ich beim lock() überprüfen, ob der Prozess, der zu der PID im Lockfile gehört, einen bestimmten Namen hat (enthält); beispielsweise $0 (Name des aktuellen Shellskripts) oder ein String wie "beispiel.sh"?
    Schau Dir mal pidof an, damit kannst Du mit dem Namen des Programms die PID-Liste gegen Deine ermittelte PID prüfen.

    HTH
    Jan

  3. #3
    Registrierter Benutzer
    Registriert seit
    03.09.2006
    Beiträge
    120
    Ja, sowas habe ich gesucht; danke
    Inzwischen hat aber auch die Manpage von ps weitergeholfen:

    Print only the name of PID 42:
    ps -p 42 -o comm=

    Damit wird anscheinend immer das Kommando ohne Optionen ausgegeben.
    Allerdings funktioniert das nur ohne vorangestelltes /bin/bash, aber das sollte egal sein, oder?

    Eigentlich fehlt noch die Überprüfung des Users zu der PID um DOS-Atacken lokaler User zu blocken, denn das Skript/Programm kann ja terminieren und ein lokaler User kann ja ein gleichnamiges Skript/Programm laufen lassen und mehr oder minder zufällig unter der PID, die im Lockfile steht; die PID bekommt er ja mit ps oder top oder pidof angezeigt, wenn das ursprüngliche Skript/Programm läuft.

    Mittels

    ps -p 42 -o user=

    überprüfe ich auch das mal.
    Ich poste das Ergebnis (lock()), wenn es fertig ist.

  4. #4
    Registrierter Benutzer
    Registriert seit
    07.05.2007
    Beiträge
    656
    Zitat Zitat von Linus Beitrag anzeigen
    ...Ich poste das Ergebnis (lock()), wenn es fertig ist.
    Ja, gute Idee - man baut sich zwar immer wieder solche Locks, aber eine gute, allgemeingültige Lösung kann man gut brauchen.

    Jan

    P.S.: Und wenn es dann noch auf sh/ksh und unter Solaris, AIX ... funktioniert, das wäre dann sozusagen das Creme-Häufchen obendrauf ;-)

  5. #5
    Registrierter Benutzer
    Registriert seit
    03.09.2006
    Beiträge
    120
    Also ich hab es "fast" fertig, aber es scheitert noch daran, das $0 den Pfad enthält und $(ps -p $(cat $lockfile) -o comm=) nicht. Beispielsweise erhalte ich für $0 und $(ps -p $(cat $lockfile) -o comm=)
    ./testlock.sh und testlock.sh die natürlich verschieden sind

    Wie kann ich $0 und $(ps -p $(cat $lockfile) -o comm=) denn ohne Pfad vergleichen; wie bekomme ich den Pfad bei $0 abgeschnitten?
    Oder hat jemand einen besseren Vorschlag?

  6. #6
    Registrierter Benutzer
    Registriert seit
    07.05.2007
    Beiträge
    656
    Zitat Zitat von Linus Beitrag anzeigen
    ...wie bekomme ich den Pfad bei $0 abgeschnitten?...
    prog_name=`basename $0`

    Jan

  7. #7
    Registrierter Benutzer
    Registriert seit
    03.09.2006
    Beiträge
    120
    Also des Rätsels Lösung ist die Prozssnamen in beiden Fällen mittels ps zu ermitteln.
    Die Lösung ist nun:

    Code:
    #!/bin/bash
    # Testscript (testlock.sh) for locking with lockfile and several checks.
    # Based on scripts from "Linux Server Hacks" and "Wicked Cool Shell Scripts".
    # It checks if the lockfile a) contains a PID of a runnig process, b) if the 
    # name of that process is the name of this script ($0) and c) if the locking
    # process has been started by the same user ($UID).
    # With these checks it should work without problem.
    # For locking from multiple users or scripts it has to be modified and renamed.
    #
    # This script MUST be run without the explicit shell (so NOT /bin/bash testlock.sh).
    #
    # License: GPL
    # Author: verinder at pop.ms (Dr. R. F.)
    # 2007-08-06
    # Version 0.9
    
    ########## Lockfile Part #####################
    
    # parameters
    sleeptime="1"           # sleeptime for creating the lockfile
    retries="10"		# default number of retries of creating the lockfile: 10 (should be > locktimeout*sleeptime)
    locktimeout="5"         # default timeout : 5 s. The lockfile will be removed
                            # by force after locktimeout seconds have passed since the lock-
                            # file was last modified/created. Lockfile is clock skew immune.
    lockdir="/var/tmp"
    this_process="$(ps -p $$ -o comm=)"
    lockfile="$lockdir/lockfile.$this_process" # lockfile name
    
    # ascertain whether we have lockf or lockfile system apps
    check ()
    {
      if [ -z "$(which lockfile | grep -v '^no ')" ] ; then
        echo "$0 failed: 'lockfile' utility not found in PATH." >&2
        exit 1
      fi
    }
    
    
    # make lockifle
    lock () 
    {
      # check if a lockfile is present  
      if [ -f $lockfile ]; then 
        # check the PID in the lockfile
        if kill -0 $(cat $lockfile) 2> /dev/null; then
          echo "The locking executable with pid $(cat $lockfile) appears to be already running."
          locking_process=$(ps -p $(cat $lockfile) -o comm=)
          # check if the process with the found PID has the name of this skript (run this skript always without /bin/bash)
          #if [ "$locking_process" == "$0" ] ; then
          if [ "$locking_process" == $(ps -p $$ -o comm=) ] ; then
            echo "The locking executable has the same name (without the path) as this script"
    	echo "$this_process"
    	echo "."
    	# check if the process with the found UID
    	if [ $(ps -p $(cat $lockfile) -o uid=) == $UID ] ; then
    	  echo "The locking process has been created from the same user $UID which is running this script; exiting."
    	  exit 1
    	else
              echo "The locking process has been created from the different user"
              echo $(ps -p $(cat $lockfile) -o uid=)
    	  echo "; the user (UID) of this script is $UID."
    	  # If you want to (try to) kill the blocking process, uncomment the following line.
    	  kill -9 "$(cat $lockfile)"
    	  # Maybe in the line befor fi you should send an email to root@localhost that a user tried (or maybe caused)
    	  # a DOS attack and that the blocking process (here undocumented because already killed) was killed.
            fi
          else
            echo "The locking executable"
    	echo "$locking_process"
    	echo "DOES NOT has the same name as this script,"
    	echo $(ps -p $$ -o comm=)
    	echo "."
    	echo "Removing that lockfile $lockfile."
    	rm -f "$lockfile"
          fi
        else
          echo "The locking executable with pid $(cat $lockfile) has completed or was killed without cleaning up its lockfile"
          echo "or the locking executable has another name than this script or it is run by an other user;"
          echo "removing that lockfile $lockfile."
          rm -f "$lockfile"
        fi
      fi
      # create the lockfile; wait 
      if ! lockfile -$sleeptime -r $retries -l $locktimeout "$lockfile" 2> /dev/null; then
        echo "$0: Failed: Couldn't create lockfile in time" >&2
        exit 1
      fi
      chmod u+rw "$lockfile"
      # store the pid
      echo $$ > "$lockfile"
      chmod u-wx "$lockfile"
      # A trap to delete the lockfile when the script gets killed by SIGHUP SIGINT or SIGTERM.
      # In many cases, e. g. a kernel hangup, this does not work and the checks above are necessary.
      trap "rm $lockfile; exit" SIGHUP SIGINT SIGTERM
    }
    
    
    # cleanup
    unlock () 
    {
      rm -f "$lockfile"
    }
    
    
    #################### "main" ##############################
    
    echo "Start of main part at"
    date | xargs echo
    
    # variable i
    typeset -i i=0
    
    # lockfile: first check, then set for locking
    check
    lock
    
    echo "lock set; waiting 20 seconds"
    echo "now you, or another user can test with starting this script twice etc."
    sleep 20
    
    unlock
    
    exit 0
    Geändert von Linus (07-08-2007 um 01:21 Uhr)

  8. #8
    Registrierter Benutzer
    Registriert seit
    03.09.2006
    Beiträge
    120
    Zitat Zitat von jan61 Beitrag anzeigen
    prog_name=`basename $0`
    Ok, thx, funktioniert auch, aber ich finde es konsistenter, beide Prozessnamen mittels ps zu ermitteln

  9. #9
    Registrierter Benutzer
    Registriert seit
    03.09.2006
    Beiträge
    120
    Jetzt habe ich noch das Problem, das Skripte, die per inittab gestartet werden, immer explizit mit /bin/bash aufgerufen werden und das das bisherige Skript deshalb als Skript-Namen bash meldet
    Wie bekomme ich den Skript-Namen auch in diesem Fall heraus?

  10. #10
    Registrierter Benutzer
    Registriert seit
    07.05.2007
    Beiträge
    656
    Zitat Zitat von Linus Beitrag anzeigen
    ...Wie bekomme ich den Skript-Namen auch in diesem Fall heraus?
    Bei mir gehts so:
    Code:
    jan@jack:~/tmp> ps -p 8206 -o cmd=
    /bin/bash ./bash_test.sh
    jan@jack:~/tmp> ps -p 8206 -o cmd= | cut -f2- -d" "
    ./bash_test.sh
    Jan

    EDIT: Flexibler ist es, wenn nur dann bearbeitet wird, wenn auch tatsächlich /bin/bash am Anfang steht:
    Code:
    jan@jack:~/tmp> ps -p 8306 -o cmd= | sed 's/^\/bin\/bash //'
    ./bash_test.sh
    Geändert von jan61 (20-08-2007 um 20:39 Uhr) Grund: flexibler mit sed

  11. #11
    Registrierter Benutzer
    Registriert seit
    03.09.2006
    Beiträge
    120
    Aha, danke, aber das funktioniert nicht, wenn statt
    /bin/bash
    nur
    bash
    aufgerufen wird.

  12. #12
    Registrierter Benutzer
    Registriert seit
    07.05.2007
    Beiträge
    656
    Dann nimm halt die 1. Variante. Oder stell besser die sed-Expression auf eine etwas genügsamere Variante um - man sollte schon prüfen, ob man nicht gerade einen "vi DeinProgramm" am Wickel hat. Das ginge z. B. so:
    Code:
    ps -p 8306 -o cmd= | sed 's/^[^ ]*bash //'
    Damit wird abgeschnitten, wenn am Anfang der Kommandozeile irgendwas außer Leerzeichen (vielleicht, muss nicht sein) mit der Zeichenkette "bash" am Ende steht. Das ist auch noch nicht 100% sicher (/pfad/zur/ganz_anderen_bash), aber IMHO hinreichend genau. Auf jeden Fall werden /bin/bash, /usr/bin/bash, bash, ... umweltfreundlich entsorgt.

    Jan

  13. #13
    Registrierter Benutzer
    Registriert seit
    03.09.2006
    Beiträge
    120
    Ok, werde ich mal testen

  14. #14
    Registrierter Benutzer
    Registriert seit
    03.09.2006
    Beiträge
    120
    Ok, nun ist das Beispiel zum Locken so:

    Code:
    #!/bin/bash
    # Testscript (testlock.sh) for locking with lockfile and several checks.
    # Based on scripts from "Linux Server Hacks" and "Wicked Cool Shell Scripts".
    # It checks if the lockfile a) contains a PID of a runnig process, b) if the 
    # name of that process is the name of this script ($0) and c) if the locking
    # process has been started by the same user ($UID).
    # With these checks it should work without problem.
    # For locking from multiple users or scripts it has to be modified and renamed.
    #
    # License: GPL
    # Author: verinder at pop.ms (Dr. R. F.)
    # 2007-08-26
    # Version 0.91
    
    # show every action
    #set -x
    
    ########## Lockfile Part #####################
    
    # parameters
    sleeptime="1"           # sleeptime for creating the lockfile
    retries="10"		# default number of retries of creating the lockfile: 10 (should be > locktimeout*sleeptime)
    locktimeout="5"         # default timeout : 5 s. The lockfile will be removed
                            # by force after locktimeout seconds have passed since the lock-
                            # file was last modified/created. Lockfile is clock skew immune.
    lockdir="/var/tmp"
    
    # Eleminate the optional bash call with sed and get this process name from basename.
    this_process="$(basename "$(ps -p $$ -o cmd= | sed 's/^[^ ]*bash //')")"
    lockfile="$lockdir/lockfile.$this_process" # lockfile name
    
    # ascertain whether we have lockf or lockfile system apps
    check ()
    {
      if [ -z "$(which lockfile | grep -v '^no ')" ] ; then
        echo "$0 failed: 'lockfile' utility not found in PATH." >&2
        exit 1
      fi
    }
    
    
    # make lockifle
    lock () 
    {
      typeset -i pid=0
      # check if a lockfile is present  
      if [ -f "$lockfile" ]; then 
        # check the PID in the lockfile
        pid="$(cat "$lockfile")"
        if [ $pid -eq 0 ]; then 
          echo "Could not read a valid PID from the lockfile."
          echo "Trying to remove that lockfile"
          echo "$lockfile"
          echo "."
          rm -f "$lockfile"
        else
          if kill -0 $pid 2> /dev/null; then
            echo "The locking executable with pid $pid appears to be already running."
            locking_process="$(basename "$(ps -p $pid -o cmd= | sed 's/^[^ ]*bash //')")"
            # check if the process with the found PID has the name of this skript (run this skript always without /bin/bash)
            #if [ "$locking_process" == "$0" ] ; then
            if [ "$locking_process" == "$this_process" ] ; then
              echo "The locking executable has the same name (without the path) as this script"
    	  echo "$this_process"
    	  echo "."
    	  # check if the process with the found UID
    	  if [ $(ps -p $pid -o uid=) == $UID ] ; then
    	    echo "The locking process has been created from the same user $UID which is running this script; exiting."
    	    exit 1
    	  else
                echo "The locking process has been created from the different user"
                echo $(ps -p $pid -o uid=)
    	    echo "; the user (UID) of this script is $UID."
    	    # If you want to (try to) kill the blocking process, uncomment the following 3 lines.
    	    echo "Try to kill this locking process."
      	    kill -9 $pid
    	    rm -f "$lockfile"
                echo "Done killing and lockfile deletion."
    	    # Maybe in the line before the next fi you should send an email to root@localhost that a user tried (or maybe caused)
    	    # a DOS attack and that the blocking process (here undocumented because already killed) was killed.
              fi
            else
              echo "The locking executable"
    	  echo "$locking_process"
    	  echo "DOES NOT has the same name as this script,"
    	  echo "$this_process"
     	  echo "."
      	  echo "Trying to remove that lockfile $lockfile."
    	  rm -f "$lockfile"
            fi
          else
            echo "The locking executable with pid $pid has completed or was killed without cleaning up its lockfile"
            echo "or the locking executable has another name than this script or it is run by an other user;"
            echo "removing that lockfile"
            echo "$lockfile"
            echo "."
            rm -f "$lockfile"
          fi
        fi
      fi
      # (try to) create the lockfile; wait 
      if ! lockfile -$sleeptime -r $retries -l $locktimeout "$lockfile" 2> /dev/null; then
        echo "$0: Failed: Couldn't create lockfile in time" >&2
        exit 1
      fi
      chmod u+rw "$lockfile"
      # store the pid
      echo $$ > "$lockfile"
      chmod u-wx "$lockfile"
      # A trap to delete the lockfile when the script gets killed by SIGHUP SIGINT or SIGTERM.
      # In many cases, e. g. a kernel hangup, this does not work and the checks above are necessary.
      trap "rm $lockfile; exit" SIGHUP SIGINT SIGTERM
    }
    
    
    # cleanup
    unlock () 
    {
      rm -f "$lockfile"
    }
    
    
    #################### "main" ##############################
    
    echo "Start of main part at"
    date | xargs echo
    echo "pid=$$"
    
    # lockfile: first check, then set for locking
    check
    lock
    
    echo "lock set; waiting 20 seconds"
    echo "now you, or another user can test with starting this script twice etc."
    sleep 20
    
    unlock
    
    exit 0

Lesezeichen

Berechtigungen

  • Neue Themen erstellen: Nein
  • Themen beantworten: Nein
  • Anhänge hochladen: Nein
  • Beiträge bearbeiten: Nein
  •