Frage Ist es möglich, dass ein Programm SIGTERM erfasst, das vom Systemaufruf exit generiert wurde?


Das Programm, das ich habe, ist wie folgt:

#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <signal.h>

int main()
{
    struct sigaction new_sa;
    struct sigaction old_sa;
    sigfillset(&new_sa.sa_mask);
    new_sa.sa_handler = SIG_IGN;
    new_sa.sa_flags = 0;
    int input;

    if (sigaction(SIGTERM, &new_sa, &old_sa) == 0 && old_sa.sa_handler != SIG_IGN) {
        new_sa.sa_handler = NULL;
        sigaction(SIGTERM, &new_sa, 0);
    }

    printf("Pgm is running\n");
    while (1) {
        printf("Enter input\n");
        scanf("%d", &input);
        if (!input) {
            /* I actually call a libraries API which calls exit()
             * Just calling exit here for simpilicity */
            exit(1);
        }
    }
}

Ich möchte SIGTERM, das von einem Systemausgangsaufruf generiert wurde, behandeln / ignorieren. Ist es möglich? Es gibt keine Möglichkeit für mich, den Aufruf von exit zu vermeiden, da es tatsächlich ein Bibliotheksaufruf ist, der versucht, das Programm zu verlassen, das ich vermeiden möchte.


5
2017-11-26 10:22


Ursprung


Antworten:


Sie können sicherlich fangen SIGTERM. Aber das ist nicht das Problem hier. Sie möchten die exit() Anruf.

Dies ist in keiner tragbaren oder standardkonformen Weise möglich. Das exit() ist eine der Funktionen, die definiert ist nicht zurück zu seinem Anrufer. In der Regel geschieht dies mit dem __attribute__((noreturn)) in gcc und C11 hat das Makro eingeführt _Noreturn für den gleichen Zweck.

Der Versuch, von einer solchen Funktion zurückzukehren, wie exit()ist undefiniertes Verhalten.

Es gibt wenige Möglichkeiten, die ich mir vorstellen kann:

  • Kompiliere und ersetze mit deiner Funktion: gcc -Dexit=not_exit file.c
  • Schreibe eine Hook-Funktion für exit(). Sehen Hier zum Beispiel. Die Implementierung einer Hook-Funktion funktioniert möglicherweise gar nicht mehr keine Rückkehr hat bei den meisten libc-Implementierungen vor C11's existiert _Noreturn.
  • Verwenden Sie GNUs ld wie von @evading vorgeschlagen. Ich glaube, das entspricht dem oben genannten, aber der Linker macht die Hälfte der Arbeit für Sie.

Ändern <stdlib.h> entfernen _Noreturn (oder sein Äquivalent) Attribut für die exit() Funktion könnte es funktionieren lassen. Keines davon funktioniert garantiert. Wir sind schon gut in UB Land.

Andere Option ist eine Installation atexit() Handler, der nützlich sein könnte, wenn Sie vorher etwas tun möchten exit().

Ein vernünftigerer Ansatz wäre es, die Bibliothek zu modifizieren, wenn Sie nicht anrufen exit() sondern gib stattdessen einen Fehlercode zurück. Meiner Meinung nach ist entweder die Bibliothek schlecht entworfen, dass sie zufällig von alleine austritt oder wahrscheinlich gibt es einen guten Grund, dass die Bibliothek (aufgrund eines nicht behebbaren Fehlers) beendet wird und Ihre Anwendung nicht weiter fortgesetzt werden soll, was Sie versuchen machen.


2
2017-11-26 12:12



Mit Blick auf die glibc Quelle Für den Ausgang sieht es so aus, als ob es nicht möglich ist. Aber es könnte sein, wenn Sie eine andere C std-Bibliothek verwenden.

Sie können Sachen at_exit tun, aber Sie können es nicht verhindern.

[bearbeiten]

Alles, was hier unten steht, ist anscheinend nicht anwendbar für den Ausstieg aus den Gründen in diese Frage.

Wenn Sie gnu ld verwenden, könnte es möglich sein, sie zu überschreiben __run_exit_handlers, oder exit in diesem Fall, mit dem Gnu ld --wrap Option, aber ich habe es nicht versucht.

Wenn Sie gnu ld können Sie tun --wrap exit und dann implementieren __wrap_exit() in deinem Code. Wenn du anrufen willst exit Danach können Sie darauf zugreifen __real_exit(). Dies ist ein Gnu-ld-Feature und ich bin mir nicht sicher, wie universell es ist.


2
2017-11-26 10:36



Eine Möglichkeit besteht darin, den Bibliotheksaufruf in einem Kindprozess mit auszuführen fork. Dies verhindert nicht den Aufruf an exit aber möglicherweise eine Problemumgehung, wenn Sie den Fehler selbst behandeln möchten.

Nach einem Tutorial Fork, Exec und Prozesssteuerung. Ich verwende Pipes, um das Ergebnis des Bibliotheksrückrufs zu senden, wenn der Aufruf erfolgreich ist.

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/wait.h>

int main(){
  int pipefd[2];
  pipe(pipefd); //Create pipe for communication
  pid_t pid = fork();  
  if (pid == 0){ //Child process
    close(pipefd[0]); // close the read-end of the pipe,
    printf("Run library here\n");
    //    exit(3); if failed
    int val=4; //Result of library call send to parent
    write(pipefd[1], &val, sizeof(val));
    close(pipefd[1]); // close the write-end of the pipe
    exit(EXIT_SUCCESS);
  }
  else if (pid < 0){           
    printf("Failed to fork\n");
  }
  else{//Parent process
    close(pipefd[1]);
    printf("Parent process\n");
    int status=0;
    int pid=wait(&status);
    printf("The pid %d finished with status %d\n",pid,status);        
    if (status==EXIT_SUCCESS){//The library call was successful
      int val=2;
      read(pipefd[0],&val,sizeof(int)); //Read data from pipe and do something with it
      printf("Value %d received\n",val);
    }
    close(pipefd[0]);
  }
  return 0;
} 

0
2017-11-26 11:36