Richtiges SignalHandling für SIGTERM



  • Guten Abend,

    Ich habe einen Daemon, der in seiner Mainloop select() ausführt. Nun möchte ich, dass beim Eintreffen des Signals SIGTERM, mein Programm sauber beendet wird. Also in anderen Worten mein Cleanupcode ausgeführt wird. Da dieser Cleanupcode alles andere als "AsyncSignalSafe" ist, darf ich ihn ja nicht im SignalHandler verwenden. Deswegen habe ich mir einen Weg überlegt um diesen Code irgendwo ausserhalb des Signalhandlers auszulagern: Da mein Programm sowieso schon select() benutzt hab ich mir gedacht, erstelle einfach eine Pipe, deren Ende in der Mainloop mithilfe von select() auf lesbarkeit überprüft wird. Der SIGTERM-Signalhandler schreibt einfach ein Byte an das schreibbare ende, was ja unproblematisch ist, da write() "AsyncSignalSafe" ist.
    Soweit zur Theorie. Ich hab dann schnell folgendes Testprogrämmchen in die Tastatur gehackt, das sich aber ein wenig anders verhält als erwartet:

    #include <stdio.h>
    #include <stdlib.h>
    #include <string.h>
    #include <signal.h>
    #include <sys/select.h>
    #include <unistd.h>
    
    static int sigterm_rd_fd;
    static int sigterm_wr_fd;
    
    void got_sigterm(int signum)
    {
    	signum = signum; /* shut up gcc */
    	/* this is async-signal-safe */
    	write(sigterm_wr_fd, "", 1);
    	return;
    }
    
    int main(void)
    {
    	struct sigaction new_sa;
    	struct sigaction old_sa;
    	struct timeval tv;
    	fd_set rd_set;
    	char dummy;
    	int retval;
    	int fd[2];
    
    	/* make pipe ready */
    	if (pipe(fd) < 0) {
    		perror("pipe()");
    		exit(1);
    	}
    	sigterm_rd_fd = fd[0];
    	sigterm_wr_fd = fd[1];
    
    	/* install signal handler for SIGTERM */
    	sigemptyset(&new_sa.sa_mask);
    	new_sa.sa_handler = got_sigterm;
    	if (sigaction(SIGTERM, &new_sa, &old_sa) < 0) {
    		perror("sigaction()");
    		exit(1);
    	}
    
    	for (;;) {
    		pause();
    
    		/* check if sigterm_rd_fd is readable */
    		FD_ZERO(&rd_set);
    		FD_SET(sigterm_rd_fd, &rd_set);
    		tv.tv_sec  = 0;
    		tv.tv_usec = 0;
    		retval = select(sigterm_rd_fd + 1, &rd_set, NULL, NULL, &tv);
    		if (retval < 0) {
    			perror("select()");
    			exit(1);
    		}
    		if (retval > 0) {
    			if (FD_ISSET(sigterm_rd_fd, &rd_set)) {
    				read(sigterm_rd_fd, &dummy, 1);
    				/* here we know that SIGTERM occured */
    				printf("SIGTERM occured :)\n");
    			} else {
    				printf("something strange happened...\n");
    			}
    		} else {
    			printf("some other sort of signal occured");
    		}
    	}
    	return 0;
    }
    

    Wie man sieht, wird pause() in einer Endlosschleife ausgeführt, und das SIGTERM das Programm nicht beendet. Es ist also von der Semantik her ein wenig anders als mein Problem oben es erfordert. Aber abgesehen davon schein ich wohl das Verhalten von sigaction() falsch verstanden zu haben

    # ./test &
    [4711]
    # kill 4711
    SIGTERM occured :)
    # kill 4711
    [1]+  Terminated   ./test
    #
    

    Meiner Erwartung nach müsste eigentlich wieder "SIGTERM occured" auf dem Terminal ausgegeben werden, da sigaction() doch die "reliable" (also beständige) "SignalHandlerInstallierungsFunktion" ist.
    Und noch eine zweite Frage brennt mir auf der Brust: Ist mein Vorgehen überhaupt sinnvoll? Können RaceConditions enstehen? Oder ist alles total für'n Arsch?

    vielen Dank im voraus

    mfg



  • Du musst auch sa_flags setzen (in struct sigaction). Da ist wahrscheinlich zufällig SA_ONESHOT eingestellt.



  • Du musst auch sa_flags setzen (in struct sigaction). Da ist wahrscheinlich zufällig SA_ONESHOT eingestellt.

    Hmm. Das Manual sagt nichts zu SA_ONESHOT. Werde nachher (bin grad nicht zu Hause) mal sa_flags einfach auf 0 setzen. Danke für den Hinweis

    mfg



  • Du musst auch sa_flags setzen (in struct sigaction). Da ist wahrscheinlich zufällig SA_ONESHOT eingestellt.

    Danke das sa_flags auf 0 setzen hat geholfen!

    Bleibt noch die Frage, ob es irgendwelche race-conditions gibt bei meinem Vorgehen...

    mfg


Anmelden zum Antworten