• Apfeltalk ändert einen Teil seiner Allgemeinen Geschäftsbedingungen (AGB), das Löschen von Useraccounts betreffend.
    Näheres könnt Ihr hier nachlesen: AGB-Änderung
  • Viele hassen ihn, manche schwören auf ihn, wir aber möchten unbedingt sehen, welche Bilder Ihr vor Eurem geistigen Auge bzw. vor der Linse Eures iPhone oder iPad sehen könnt, wenn Ihr dieses Wort hört oder lest. Macht mit und beteiligt Euch an unserem Frühjahrsputz ---> Klick

Perl Skript als Daemon laufen lassen

Cyrics

Neuer Berner Rosenapfel
Registriert
01.04.05
Beiträge
1.973
Hallo,

ich hab ein neues Perl-Problem.
Perl als Skript ist ja recht nett und vor allem effizient, nun hab ich aber eine komplexe Lösung entwickelt, die ich gerne dauerhaft laufen lassen möchte als Daemon, losgelöst von der Shell oder Benutzer.

Daher bin ich auf der Suche nach einem möglichen Ansatz:
mein jetziger sieht so aus:
Code:
#!/usr/bin/perl
use strict;
use locale;
use warnings;

use Proc::Daemon;
Proc::Daemon::Init;
use Proc::PID::File;

print "Programmstart:\n";
my $start = 0;
my $pid;
my $datei = "logdatei.txt";
open (DATEI,">$datei");

if (!defined(@ARGV))
{
	print "Bitte folgende Optionen mitangeben: {start|stop}\n";
	exit (0);
}

if (@ARGV && $ARGV[0] eq "start")
{
	if (Proc::PID::File->running(name=>"daemon"))
	  { print DATEI "Prozess laeuft bereits!";
	    exit (0);
	}
	print DATEI "Prozess wird gestartet...\n";
     
	$pid = Proc::PID::File->running(name => "daemon");
	$start = 1;
	print DATEI "Meine Process-ID ist: $pid\n";
}


$SIG{INT} = sub { 
    $::exit = 1;
    $start = 0;
};

if (@ARGV && $ARGV[0] eq "stop")
  {
    unless ($pid)
      { die "Prozess nicht vollstaendig implementiert!\n" }
    $pid = Proc::PID::File->running(name => "daemon");
    kill(9,$pid) or die "Konnte Daemon nicht stoppen!\n";
    print "Stopsignal wurde gesendet!\n";
    exit;
}

while ($start)
{
       exit if $::exit;
       sleep(5);
       .... # Hier folgt dann das eigentliche Programm
}
close DATEI;

Für das Programm sind dann zwei externe Module nötig. Das wären Proc::Daemon und Proc::pID::File (beides externe Links auf CPAN.org

Problem an dem Skript ist derzeit folgendes:
Es wird nur sehr unregelmässig (auf 4 Systemen getestet, auf einem klappts) eine daemon.pid in /var/run angelegt.
Soweit ich es verstanden habe, müsste diese pid-File aufgrund der Initialisierung des Daemons (Proc::Daemon::Init) angelegt werden.
In dieser steht dann die aktuelle PID des Daemons drin. Diese wird ausgelesen und z.B. für die Beenden-Prozedure, die durch den Programmaufruf + stop eingeleitet wird, dann beendet. Jedoch hat dies noch nie geklappt.

Habt ihr irgendwelche Ideen wie man mein Wirrwarr ordnen kann? Was hab ich vergessen bzw. wo ist der eigentliche Fehler in meiner Logik?

PS: als initialisierte Daemon werden auch irgendwie alle Kanäle geschlossen. Ich hab nicht mehr die Möglichkeit über FileHandler oder die STDOUT, STDIN, STDERR fehler auszugeben oder sonst etwas nach außen dringen lassen. Wie kann ich meinen Daemon wieder gesprächig kriegen?

Änderung betreffend PS: Ich denke die Lösung für Phänomen gefunden zu haben: Siehe Link: Wheel::Run and Proc::Daemon troubles - another solution
 
Zuletzt bearbeitet:

tjp

Altgelds Küchenapfel
Registriert
07.07.04
Beiträge
4.059
PS: als initialisierte Daemon werden auch irgendwie alle Kanäle geschlossen. Ich hab nicht mehr die Möglichkeit über FileHandler oder die STDOUT, STDIN, STDERR fehler auszugeben oder sonst etwas nach außen dringen lassen. Wie kann ich meinen Daemon wieder gesprächig kriegen?
Der Sinn und Zweck eines Daemons ist es, daß er kein "Controlling Terminal" mehr hat, damit hat er natürlich auch keine Möglichkeit mehr auf STDOUT, STDIN, STDERR zuzugreifen. Das übliche Prozedere für die Fehlermeldungen eines Daemons ist es, die Fehlermeldungen in ein Log-File wegzuschreiben, dafür nutzt man syslog.

Den Pfusch aus dem Link läßt Du besser sein, entweder Daemon ->syslog oder Prozeß als root -> STDOUT & Co.
 

Cyrics

Neuer Berner Rosenapfel
Registriert
01.04.05
Beiträge
1.973
Vielen Dank für den Hinweis mit syslog. Das ist genau das was ich suche.

Jedoch besteht immer noch das Problem mit dem Daemon allgemein. Er wird manchmal erzeugt und manchmal nicht. Meine Annahme war es, dass durch Proc::Daemon::Init die PID-File in /var/run angelegt wird. Dies geschah bis jetzt aber nur auf einem von 4 Testsystemen. Woran kann dies liegen? Der Daemon wurde jeweils mit root-Rechten gestartet um Berechtigungsproblemen aus dem Weg zu gehen.
Ohne diese PID-File scheint eine Steuerung des Daemon unmöglich. Ich würde sogar behaupten, es gibt diesen dann gar nicht.
Das erfahr ich jedoch erst, wenn ich mit syslog eine Ausgabe erreiche.

Links für mich:
http://pronix.linuxdelta.de/C/Linuxprogrammierung/Linuxsystemprogrammieren_C_Kurs_Kapitel3.shtml
http://galileocomputing.de/openbook/unix_guru/node172.html
http://www.thomas-fahle.de/pub/perl/UNIX/Sys_Syslog.html
 

tjp

Altgelds Küchenapfel
Registriert
07.07.04
Beiträge
4.059
Er wird manchmal erzeugt und manchmal nicht. Meine Annahme war es, dass durch Proc:: Daemon::Init die PID-File in /var/run angelegt wird.
Nein, das Anlegen der PID-Files wird üblicherweise durch die Start-Skripte der Daemons durchgeführt und nicht durch die Daemons selbst.
Ohne diese PID-File scheint eine Steuerung des Daemon unmöglich.
Nein, ein Daemon wird üblicherweise nur über Signale (andere IPC-Methoden gehen natürlich auch, sind aber in den meisten Fällen zu viel des Guten) gesteuert. Bei Starten des Daemons wird eine Konfigurationsdatei eingelesen, wenn man während der Laufzeit will, daß das nochmals erfolgt, dann schickt man das Signal SIGHUP. Damit das funktioniert muß man aus dem daemon heraus einen Signal-Handler installieren, andernfalls beendet SIGHUP das Programm.

Für das Beenden des Daemons schickt man ihm SIGTERM. Wenn das Programm geordnet heruntergefahren werden soll, muß ein Signal-Handler geschrieben werden.
 

Cyrics

Neuer Berner Rosenapfel
Registriert
01.04.05
Beiträge
1.973
Nach etwas Arbeit hab ich denke ich einen funktionierenden Daemon auf die Beine stellen können. Einen richtigen Signal-Handler gibt es nicht. Es gibt nur Start oder Stop. Aber das reicht vollkommen.

Vielleicht steigt ja einer hinter und kann noch mögliche Tipps geben.
Code:
#!/usr/bin/perl
use strict;
use locale;
use warnings;

###################################################################
#                                                                 #
# Initialisierung aller noetigen Pakete, Variablen (siehe strict) #
#                                                                 #
###################################################################

use Sys::Syslog qw/:DEFAULT setlogsock/;
use Proc::Daemon;
use Proc::PID::File;

my $start = 0;
my $pid = $$;
my $facility = 'local5';
my $type = 'unix';
my $ident = $0;
my ($sek, $min, $std, $tag, $mon, $jahr, $wtag, $jtag, $isdat) = localtime (time);

my $logstring = "";
my $prioritaet = 'info';

######################################
#				     #
# Initialisierung der Syslog-Kanaele #
#				     #
######################################

setlogsock($type) or die "Syslog-Fehler: Setlogsock falsch initialisiert.\n";
openlog($ident,'pid,cons',$facility) or die "Syslog-Fehler: Konnte Syslog-Kanal nicht oeffnen.\n";

##############################################################################################
#											     #
# Daemonsteuerung: Testweiseueberpruefung der richtigen Parameter, Steuerung durch Parameter #
#											     #
##############################################################################################


#########################################
#	undefinierte Parameter		# 
#########################################

if (!defined(@ARGV)) {
	die "Daemon-Fehler: Noetige Start-Parameter: {start|stop}\n";
}
elsif ($ARGV[0] ne "start" && $ARGV[0] ne "stop") {
	die "Daemon-Fehler: Fehlerhafte Parameter-Uebergabe! Noetige Start-Parameter: {start|stop}\n";
}
elsif (defined($ARGV[1])) {
	print "2.Parameter wird ignoriert.\n";
}
#########################################
#	Start-Signal Verarbeitung	#
#########################################

if (@ARGV && $ARGV[0] eq "start") {
	if ($start == 1 || stat('/var/run/daemon.pid')) {
		$pid = Proc::PID::File->running(name=>"daemon"); 
		die "Daemon-Fehler: Daemon laeuft bereits! (PID: $pid)\n";
	}
	else {
		print "Daemon ($ident) wird gestartet!\n";
		Proc::Daemon::Init;
		$pid = Proc::PID::File->running(name=>"daemon");
		$start = 1;
	}
}

#########################################
#	Moeglicher Signal-Handler	#
#########################################

$SIG{INT} = sub { 
	$::exit = 1;
	$start = 0;
};

#########################################
#	Stop-Signal Verarbeitung	#
#########################################

if (@ARGV && $ARGV[0] eq "stop") {
	$pid = Proc::PID::File->running(name=>"daemon");
	unless ($pid) { 
		die "Daemon-Fehler: Keine PID-Datei gefunden!\n";
	}
	kill(2,$pid) or die "Daemon-Fehler: Stoppen des Daemons fehlgeschlagen!\n";
    	print "Beende Daemon ($ident) PID: $pid\n";
    	exit 0;
}

#############################################
#					    #
# Whileschleife - Ende der Daemon-Steuerung #
#					    #
#############################################


while ($start == 1)
{
	($sek, $min, $std, $tag, $mon, $jahr, $wtag, $jtag, $isdat) = localtime (time);
	$logstring = "Der Daemon schreibt! Uhrzeit: $std:$min:$sek";
	syslog($prioritaet,$logstring) or die "Syslog-Fehler: Syslog-Kanal nicht nutzbar!\n";
	exit if $::exit;
	sleep(3);
}

#########################################
#	Schliessung des Syslog-Kanals	#
#########################################

closelog();
 
  • Like
Reaktionen: 1 Person

Cyrics

Neuer Berner Rosenapfel
Registriert
01.04.05
Beiträge
1.973
Vielen Dank für die Literatur, jedoch soll das Skript Systemunabhaengig funktionieren. Zumindest in der Unix/Linux-Welt.
Das oben gepostete Skript laeuft sowohl unter OS X, als auch Gentoo, als auch Suse als auch Debian.
 

Cyrics

Neuer Berner Rosenapfel
Registriert
01.04.05
Beiträge
1.973
Ich will noch hinzufügen, dass durch den Daemon jegliche Filehandler sowohl Uni- als auch Bidirektional nach /dev/null geleitet werden.
Sie muessen also nach der "Daemonisierung" neu initialisiert werden. Jedoch dürfen diese Filehandlers keine 'Bareword Filehandler' sein. Ich übersetze das einfach mal als bloße nackte Filehandler. Also damit ist das klassische open(Filehandler,">datei") close Filehandler etc.gemeint.

Es gibt als Alternative indirekte Filehandler:
open my $Filehandler, '>', datei;

damit kann man dann auch als Daemon arbeiten.
Das hab ich jedoch leider erst spät mitbekommen, weil der Daemon jegliche Meldung auf STDERR nicht zu lies.
Nur falls jemand vor einem ähnlichen Problem steht...

PS: $Filehandler kann dann normal genutzt werden wie z.B. der 'Bareword Filehandler' Filehandler.
Also: $input = <$FileHandler> für das Einlesen z.B.
 

tjp

Altgelds Küchenapfel
Registriert
07.07.04
Beiträge
4.059
Vielen Dank für die Literatur, jedoch soll das Skript Systemunabhaengig funktionieren. Zumindest in der Unix/Linux-Welt.
Das oben gepostete Skript laeuft sowohl unter OS X, als auch Gentoo, als auch Suse als auch Debian.
Es ist trotzdem nicht sauber, Du solltest ordentlich trennen zwischen Daemon und Startup-Skript. Jedes System bringt seinen eigene Startup-Skripte mit, solche Shell-Skripte sollte man für den eigenen Daemon mitliefern. Da macht mehr Arbeit, weil man für jede Plattform solche Skripte braucht, integriert aber den Daemon perfekt in das jeweilige OS.
 

Cyrics

Neuer Berner Rosenapfel
Registriert
01.04.05
Beiträge
1.973
ich glaube ich hab jetzt nicht ganz verstanden worauf du hinaus willst.
Also... mein Daemon soll sich selbst als Startskript eintragen? Das liesse sich schnell über eine einfache Installationsroutine realisieren bei der alle nötigen Parameter gesetzt werden. Dazu könnte man dann die zwei integrierten Pakete in Abhängigkeit ob man eine neue chroot-Umgebung für den Daemon festlegt kompiliert werden.

Oder möchtest du mir mitteilen, dass ich den Daemon händisch über fork(), die neue PID annehmen, kill, chroot etc. initialisieren soll? Das hab ich mir nämlich gespart gehabt, weil mein Programm nun lockere 600 Zeilen Code hat, und das mit einem Befehlsaufruf von Proc::Daemon::Init in meinen Augen schneller und sauberer realisiert ist.

Du siehst... du musst irgendwie den Begriff Startup-Skripte näher definieren. Ich schätze du meinst die Initialisierung des Daemons im Skript, und die soll ich Systemabhängig abändern, also einfach mehrere Versionen einbetten und die jeweilige durch das System wählen lassen?
Das wäre natürlich richtig, und eine sehr saubere Lösung, für meinen Zweck jedoch vollkommen ungeeignet, weil mein Skript auf einem SLES 10-Server laufen soll. Dieses nutzt ein Programm, mit dem mein Daemon kommunizieren wird. Dieses Programm läuft nach meinem Wissen jedoch nur unter Suse, aber gibt es sicher über Umwege auch für die anderen bekannteren Plattformen... also mein Einsatzgebiet ist sehr beschränkt und wird es auch für die nächste Zeit bleiben, zumindest für die Initialisierung des Daemons.

PS: Tut mir leid, wollte nicht soviel schreiben. Ich denke du meintest den letzten Punkt: Startup-Skript = Initialisierung des Daemons. Dann hast du natürlich recht. Schönen Abend noch.
 

MacMark

Jakob Lebel
Registriert
01.01.05
Beiträge
4.874
Was soll dieser Eiertanz? Definiert einen Launch-Dämon, der das Skript aufruft, und die Sache ist gegessen.
 

Cyrics

Neuer Berner Rosenapfel
Registriert
01.04.05
Beiträge
1.973
Was soll dieser Eiertanz? Definiert einen Launch-Dämon, der das Skript aufruft, und die Sache ist gegessen.

keiner liest meine Beiträge :(
Aber wahrscheinlich ist es mein Fehler... ich werd mal die Verschiebung des Themas beantragen. Das kann hier ja so nicht weiter gehen. Mein Skript wird einfach zu sehr im Zusammenhang mit OS X gesehen. Unter OS X würde ich es mit launchd machen. Danke MacMark, aber das hab ich ja schon 5 Beiträge weiter oben geschrieben.
 

tjp

Altgelds Küchenapfel
Registriert
07.07.04
Beiträge
4.059
keiner liest meine Beiträge :(
Dasselbe könnte man Dir vorhalten. Die PID-Files werden niemals von Daemon selbst geschrieben sondern von Startup "Skript" (im Falle von MacOS X ist das launchd-Skript). Der ganze chroot, PID etc. Kram macht man nicht vom Daemon aus. Die Trennung ist sinnvoll, weil es bei jedem Daemon so ist. Eine Notwendigkeit dafür gibt es nicht, aber Du solltest das als guten Ratschlag verstehen. Anderfalls wundert man sich nur darüber, warum ein Dienst so von dem Üblichem abweicht.
 

MacMark

Jakob Lebel
Registriert
01.01.05
Beiträge
4.874
Er ist sich nicht klar darüber, was ein Dämon ist :p
 

Cyrics

Neuer Berner Rosenapfel
Registriert
01.04.05
Beiträge
1.973
Er ist sich nicht klar darüber, was ein Dämon ist :p

das würde ich jetzt auch langsam von mir behaupten... ich glaube wir sprechen von zwei total unterschiedlichen Anforderungsbereichen bzw. Daemons... ihr fragt euch jetzt sicher welche zwei Daemons es gibt... es gibt die, von denen ihr redet, und es gibt die, von denen ich rede.
Eure Daemons würde ich als Metadaemon bezeichnen. Denn als solche hab ich sie kennen gelernt. launchd z.B.. Es ist ein daemon, der Anfragen erwartet, und je nach Ereignis welches eintrifft, schaut es in die Konfigurationsdatei und ordnet es dem bestimmten Skript zu. Es startet nämlich dann 'meinen' Daemon, wenn ich das so sagen kann. Jetzt versteh ich auch langsam, was mit Startup-Skript gemeint ist.

Ich hab denke ich verstanden worauf ihr hinaus wollt. Launchd ist auf jeden Fall nicht mein Partner.

Mein Daemon bleibt erstmal unabhängig und macht sein eigenes Ding. Das System kann ihn jederzeit beenden mit einem einfachen SIGINT.
Ich muss mal gucken inwiefern ich es in Abhängigkeit mit einem Metadaemon zum Laufen kriege.
 

MacMark

Jakob Lebel
Registriert
01.01.05
Beiträge
4.874
Launchd ist noch anders und noch mehr als Du denkst.

Und wenn Du unter OS X ein Skript oder Programm als Dämon laufen lassen willst, dann sollte er von launchd verwaltet werden. Dazu ist lediglich eine Konfigurationsdatei passend zu plazieren. Alles andere ist unter OS X Version 10.4 und folgende der falsche Weg.