Sehr Umfangreiche Webseite zum Programmieren in C Perl CGI, Skripting, Linux, SystemprogrammierungC C++ C/C++ ANSI C Linux Linuxsystemprogrammierung Unix fork system exec Prozess Prozesse Dämon Daemon Prozess Linuxsystemprogrammieren fork system exec Prozess Prozesse Daemon Dämon 3a. Dämon-Prozesse

3.3. Erstellen von eigenen Dämon - Prozessen           zurück Ein Kapitel tiefer zum Inhaltsverzeichnis

Nach den beiden Kapiteln zuvor werden Sie sich nun Fragen, wie man wohl eigene Dämonen erstellen kann? Wenn Sie mal in der Konsole eingeben ...

pstree | less

... wird Ihnen auffallen, dass alle Dämonprozesse als Elternprozess den init-Prozess haben (wie Sie eigentlich schon wissen sollten). Und Sie wissen auch, wenn sich der Elternprozess vor dem Kindprozess beendet, haben Sie ein Waisenkind, dessen sich der Vater aller Prozesse, init, annimmt.

Nun sehen Sie sich nochmals die Prozesse mit pstree | less an. Sie sehen, dass alle Prozesse einen Anführer haben, einen Dämonprozess. Wie können Sie es nun bewerkstelligen, dass der verwaiste Kindprozess, dessen sich init angenommen hat, zum Häuptling, genauer Sessionsführer wird?

Ein Aufruf der man-Page (deutsch) ...

man 2 setsid

... gibt uns darüber Auskunft. Diese Funktion erzeugt eine neue Sitzung, wenn der aufrufenden Prozess, kein Prozessgruppenführer ist. Somit wird der Aufrufende Prozess der einzige Prozess in der neuen Prozessgruppe und in dieser Sitzung. Hier der Syntax für setsid ...

pid_t setsid(void);

Mehr dazu entnehmen Sie bitte aus der angesprochenen man-Pages. Zum Schluss wechseln Sie noch ins Root-Wurzelverzeichnis und setzen die Dateikreierungsmaske auf 0.

Weitere Vorgänge sind im folgenden Code dokumentiert ...

#include <stdio.h>
#include <unistd.h>
#include <syslog.h>
#include <sys/types.h>

#define CHILD 0
#define ERROR -1

void start_daemon(char *log_name, int facility)
{
 int i;
 pid_t pid;

 /*Elternprozeß beenden, somit haben wir einen Waisen
    dessen sie jetzt vorerst init annimmt*/
 if((pid = fork()) != CHILD) exit(0);
 /*Unser Kindprozess wird zum Session-Führer. Mißlingt dies,
    kann der Fehler daran liegen das der Prozeß bereits Sessionführer
    ist */
 if(setsid() == ERROR)
  {
  fprintf(stderr, "%s kann nicht Session-Führer werden!\n",log_name);
  exit(0);
  }
 /* Gründe für das Root-Wurzelverzeichnis:
     +core-Datei wird im aktuellen Arbeitsverzeichnis hinterlegt
     +Damit bei Beendigung mit umount das Dateisystem sicher abgehängt
      werde kann */
 chdir("/");
 /*Damit wir nicht die Bitmaske vom Elternprozeß erben bzw. diese
    bleiben, stellen wir diese auf 0*/
 umask(0);
 /*Wir schließen alle geöffneten Filedeskriptoren....*/
 for(i=sysconf(_SC_OPEN_MAX); i>0; i--)
  close(i);

 /*Da unser Dämonprozeß selbst kein Terminal für die Ausgabe hat....*/
 openlog(log_name, LOG_PID, facility);
}


int main(int argc, char **argv)
{
 start_daemon("meinDaemon", LOG_USER);
 while(1);
 /*Enlossschleifen: Hier sollte nun der Code für den
    Daemon stehen, was immer er auch tun soll.
    Bei Fehlermeldungen Beispiesweise:
    if(dies_ist_Passiert)
     syslog(LOG_WARNING, "dies_ist_Passiert");
    else if(das_ist_Passiert)
     syslog(LOG_INFO, "das_ist_Passiert");*/
 return 0;
}

Starten Sie nun dieses Programm und geben Sie im Terminal ...

ps xj

...ein. Sie erkennen nun unseren Dämonprozess am Fragezeichen bei TTY und am Elternprozess PPID der hier 1 (init) ist. Sie haben in diesem Fall einfach einen Dämonprozess gestartet, der nichts tut. Fehlermeldungen oder sonstige Nachrichten die uns der Dämonprozess schickt müssen Sie ebenso selbst mit der Datei syslog.conf abgleichen (siehe Kapitel zuvor). Sie haben hier zwar als Typen LOG_USER verwendet aber Sie sollten schon selbst darauf achten, wo die Nachricht dafür geschrieben wird (/dev/log).

Vor kurzem bekam ich eine Mail, in der jemand die Frage stellte, ob ein Dämon ähnlich wie ein Trojaner funktioniert und wie es mit der Sicherheit dabei aussieht? Im Prinzip kann man sich den Dämon als einen Trojaner vorstellen, da es sich dabei ja auch um einen Prozess im Hintergrund handelt. Aber Sicherheitsprobleme dürften sich da nicht finden. Denn für einen Dämon benötigt man erst mal die root-Rechte (daher auch ein weiterer Grund sich nicht immer als root einzloggen). Selbst wenn man diese theoretisch hat, gibt es noch einig Dinge zu beachten. Dazu finden sie sicherlich auf anderen Webseiten etwas.

Ein weiterer Hinweis zu unserem Programmbeispiel oben. Da ein Dämon kein Terminal zur Verfügung stehen hat, sollten Sie einen Signalhandler einrichten der folgende Signale IGNORIEREN sollte ...

SIGHUP,SIGINT, SIGWINCH

Wie sie Signalhandler einrichten können finden Sie in dem Kapitel der Signale.

cron-Jobs == Dämonprozess?
Wie kann man nun einen Dämonprozeß dauerhaft einrichten?

Entweder Sie schreiben ein Shell-Skript für Ihren Dämon, wie Sie dies in /etc/rc.d/init.d/ oder /etc/init.d/ finden. Oder sie verwenden cron-Jobs. crond ist ebenfalls ein Dämon, mit dem Sie Programme zu einer bestimmten Zeit und Datum ausführen können.

Die Programme oder Skripte die vom cron-Job aufgerufen werden, laufen wiederum als Dämon (egal ob Sie diese als Dämon geschrieben haben oder nicht). Der cron-Dämon wird bereits zum Systemstart mit Hilfe eines Skriptes gestartet.

Was für Programme Sie verwenden bleibt Ihnen überlassen. Häufig verwendet werden dafür Perl-Skripte, Shell-Skripte, C-Programme oder CGI-Programme.

Sie finden die Konfigurations-Files im Verzeichnis /etc. Die Zentrale Datei dazu ist /etc/crontab. Mit diesem Befehl können Sie eigene cron-Jobs komfortabel erstellen.

Im Internet finden sie Tausende Seiten die Dokumentieren wie Sie einen cron-Job einrichten können. Wem das Errichten von cron-Jobs in der Text-Konsole zu lau ist, der kann sich ja für KDE kcron oder für andere Window-Manager Vcron ansehen.

3.4. Resourcen-Limits von Prozessen           zurück Ein Kapitel höher zum Inhaltsverzeichnis

Sie werden die Limits der Ressourcen von Prozessen wahrscheinlich nie verändern müssen. Falls doch, dann dient diese Kapitel dazu, damit Sie wenigstens wissen wie. Bedenken Sie aber bitte, falls Sie die Ressourcen eines Prozesses verändern und Sie schon am Limit sind, ob überhaupt noch ein Sinnvolles Arbeiten Möglich ist. Dies ist Abhängig vom System. Diese Funktionen dienen aber 'nur' für das Verändern des Prozesses der gerade läuft. Alle anderen bleiben davon unberührt. Wollen Sie die in diesem Kapitel gezeigten Ressourcen dauerhaft ändern, kommen um das Neukompilieren des Kernels nicht herum. Natürlich müssen Sie erst mal einige Konstanten verändern.

Um unter Linux/Unix geltende Ressourcen eines Prozesses abzufragen oder zu ändern gibt es die beiden Funktionen ...

#include <sys/time.h>
#include <sys/resource.h>

int getrlimit(int resource, struct rlimit *limit_zeiger);

int setrlimit(int resource, struct rlimit *limit_zeiger);

Folgende vordefinierten Konstanten stehen dabei für die int-Variable ressource zur Verfügung (Systemabhängig) ...

Resource Standard Bedeutung
RLIMIT_CORE SVR4,BSD Maximal Grösse der core-Datei in Bytes. Der Wert 0 bedeutet das keine core-Datei angelegt wird.
RLIMIT_CPU SVR4,BSD Limit für CPU-Zeit in Sekunden. Liefert Signal SIGXCPU, wenn das Soft-Limit überschritten wurde, an den Prozess.
RLIMIT_DATA SVR4, BSD Maximale Grösse des Gesamten Datensegmentes (Datensegment,BSS - Segment und Heap)
RLIMIT_FSIZE SVR4, BSD Maximale Grösse einer Datei die beschrieben werden kann in Bytes. Wird das Soft-Limit überschritten wird das Signal SIGXFSZ ausgelöst.
RLIMIT_MEMLOCK BSD, LINUX Max. Speichergrösse die mit unlock gesperrt werden kann.
RLIMIT_NOFILE SVR4 Max. Anzahl von gleichzeitig geöffnete Dateien.
RLIMIT_NPROC BSD Max. Anzahl von Kindprozessen pro User-ID.
RLIMIT_OFILE BSD Max. Anzahl von gleichzeitig geöffnete Dateien.
RLIMIT_RSS BSD Maximale resident set size in Byte. Bei Speicherengpässen entzieht der Kern den Prozessen, die den resident set size überschreiten, den zuviel angeforderten Speicher.
RLIMIT_STACK SVR4, BSD Max Grösse des Stacks in Byte
RLIMIT_VMEM SVR4 Max. Grösse des Memory Mapped-Adressraums.


Die Struktur rlimit hat folgendes Aussehen ...

struct rlimit {
          rlim_t rlim_cur;   //Soft-Limit == aktuelles Limit
          rlim_t rlim_max;   //Hard-Limit == maximaler Wert für rlim_cur
               };

Mit den Soft-Limits können Sie die Ressourcen gleich bis auf die Hard-Limits ändern. Sollten die beiden Struktur-Variablen rlim_cur oder/und rlim_max den Wert RLIM_INFINITY haben, so bedeutet dies, dass überhaupt kein Limit gesetzt (unbegrenzt) ist. Nun wollen wir mal anhand eines Beispiels sehen wie Sie die Ressourcen-Limits des aktuellen Prozesses auslesen können ...

#include <stdio.h>
#include <sys/time.h>
#include <sys/resource.h>
/*keinerlei Limits (unbegrenzt)*/
#define NO_LIMIT RLIM_INFINITY

enum{ERROR=-1, SUCCESS};

int limit_wert[] = {RLIMIT_CORE, RLIMIT_CPU, RLIMIT_DATA, RLIMIT_FSIZE,
                    RLIMIT_MEMLOCK, RLIMIT_NOFILE, RLIMIT_NPROC, RLIMIT_OFILE,
                    RLIMIT_RSS, RLIMIT_STACK, /*RLIMIT_VMEM*/};

char *limit_name[] = {"RLIMIT_CORE","RLIMIT_CPU","RLIMIT_DATA","RLIMIT_FSIZE",
                      "RLIMIT_MEMLOCK","RLIMIT_NOFILE","RLIMIT_NPROC","RLIMIT_OFILE",
                      "RLIMIT_RSS","RLIMIT_STACK",/*"RLIMIT_VMEM",*/NULL};

void read_limit(int resource, char *name)
{
   struct rlimit limit;

   /*Wir Erfragen nach dem Limit 'resource'*/
   if(getrlimit(resource, &limit) == ERROR)
     {
       printf("Fehler bei getrlimit...\n");
       exit(0);
     }

   printf("%15s | Soft-Limit : ",name);

   if(limit.rlim_cur == NO_LIMIT)
       printf("unbegrenzt |");
   else
       printf("%12ld |",limit.rlim_cur);

   printf("Hard-Limit : ");
   if(limit.rlim_max == NO_LIMIT)
       printf("unbegrenzt |");
   else
       printf("%12ld|",limit.rlim_max);

   if(limit.rlim_cur == limit.rlim_max)
       printf("(Soft-Limit = maximal)\n");
   else
       printf("(Soft-Limt = erweiterbar\n");
}


int main()
{
   int i;

   for(i=0; limit_name[i]!= NULL; i++)
       read_limit(limit_wert[i], limit_name[i]);
   return 0;
}

Da ich auf Linux programmiere und mir somit die meisten dieser Ressourcen-Konstanten zur Verfügung stehen, müssen Sie den Code Ihren Bedürfnissen anpassen. Falls das Programm portabel gehalten werden soll muss natürlich bedingt compiliert werden ...

#ifdef RLIMIT_OFILE
   read_limit(RLIMIT_OFILE,"RLIMIT_OFILE");
#endif
#ifdef RLIMIT_NPROC
   read_limit(RLIMIT_NPROC,"RLIMIT_NPROC");
#endif

Man muss auch noch dazu anmerken, das dieses Kapitel nicht dem POSIX.1-Standard entspricht.

Jetzt haben Sie eine Funktion zum Auslesen der Ressourcen geschrieben. Jetzt benötigen Sie noch eine zum Ändern der Ressourcen. Dabei gibt es folgende Regeln zu beachten ...

Hier nun das Programm mit setrlimit. Auch hier gilt wieder, wenn Sie das Programm portabel halten wollen, müssen Sie die einzelnen Limits bedingt Compilieren, da nicht alle Limtits auf jedem System vorhanden sind ...

#include <stdio.h>
#include <sys/time.h>
#include <sys/resource.h>
/*keinerlei Limits (unbegrenzt)*/
#define NO_LIMIT RLIM_INFINITY

enum{ERROR=-1, SUCCESS};

int limit_wert[] = {RLIMIT_CORE, RLIMIT_CPU, RLIMIT_DATA, RLIMIT_FSIZE,
                    RLIMIT_MEMLOCK, RLIMIT_NOFILE, RLIMIT_NPROC, RLIMIT_OFILE,
                    RLIMIT_RSS, RLIMIT_STACK, /*RLIMIT_VMEM*/};

char *limit_name[] = {"RLIMIT_CORE","RLIMIT_CPU","RLIMIT_DATA","RLIMIT_FSIZE",
                      "RLIMIT_MEMLOCK","RLIMIT_NOFILE","RLIMIT_NPROC","RLIMIT_OFILE",
                      "RLIMIT_RSS","RLIMIT_STACK",/*"RLIMIT_VMEM",*/NULL};

void read_limit(int resource, char *name)
{
   struct rlimit limit;

   /*Wir Erfragen nach dem Limit 'resource'*/
   if(getrlimit(resource, &limit) == ERROR)
     {
       printf("Fehler bei getrlimit...\n");
       exit(0);
     }

   printf("%15s | Soft-Limit : ",name);

   if(limit.rlim_cur == NO_LIMIT)
       printf("unbegrenzt |");
   else
       printf("%12ld |",limit.rlim_cur);

   printf("Hard-Limit : ");
   if(limit.rlim_max == NO_LIMIT)
       printf("unbegrenzt |");
   else
       printf("%12ld|",limit.rlim_max);

   if(limit.rlim_cur == limit.rlim_max)
       printf("(Soft-Limit = maximal)\n");
   else
       printf("(Soft-Limt = erweiterbar\n");
}


/*Funktion zum setzen eines Limits. Ist beschränkt auf Soft-Limits*/
void set_limit(int resource, char *name)
{
   struct rlimit limit;
   long neuer_wert;

   printf("\nWir verändern nur Soft-Limits!!\n\n");
   printf("Welchen Wert soll der Soft-Limit von %s bekommen : ",name);
   scanf("%ld",&neuer_wert);

   if(getrlimit(resource, &limit) == ERROR)
     {
       printf("Fehler bei getrlimit...\n");
       exit(0);
     }

   limit.rlim_cur=neuer_wert;
   if(setrlimit(resource, &limit) == ERROR)
     {
       printf("Fehler bei setrlimit...\n");
       exit(0);
     }

   printf("\nLese das Soft-Limit von %s nochmals....\n",name);
   read_limit(resource, name);
   printf("\n\n");
}


int main()
{
   int i,x;

   printf("\n");
   while(1)
     {
       for(i=0; limit_name[i] != NULL; i++)
         {
           printf("%2d - %-15s ",i,limit_name[i]);
           if(!(i%3))
             printf("\n");
         }
       printf("\n\nWelches Limit wollen sie verändern (99=ENDE): ");
       scanf("%d",&x);
       if(x==99)
           break;
       read_limit(limit_wert[x], limit_name[x]);
       set_limit(limit_wert[x], limit_name[x]);
     }
   return 0;
}

Sollten Sie die Hard-Limits ändern wollen, so müssen Sie im Programm anstatt ...

limit.rlim_cur=neuer_wert;

...folgendes schreiben....

limit.rlim_max=neuer_wert;

Ich wiederhole es nochmals, auch wenn Sie das Limit ändern, gilt dies nur für den aktuell laufenden Prozess. Wird das Programm beendet und wieder neu gestartet, sind alle Limits wieder im Urzustand.

Weiter mit 4.1. signal - Einrichten von Signalhandlern