终端关闭时终止sudo python脚本 [英] Terminate sudo python script when the terminal closes

查看:142
本文介绍了终端关闭时终止sudo python脚本的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

如何判断运行我的python脚本的终端是否关闭?如果用户关闭终端,我想安全地结束我的python脚本.我可以使用处理程序捕获SIGHUP,但是当脚本以sudo运行时无法捕获.当我使用sudo启动脚本并关闭终端时,python脚本将继续运行.

示例脚本:

import signal
import time
import sys

def handler(signum, frame):
    fd = open ("tmp.txt", "a")
    fd.write(str(signum) + " handled\n")
    fd.close()
    sys.exit(0)


signal.signal(signal.SIGHUP, handler)
signal.signal(signal.SIGINT, handler)
signal.signal(signal.SIGTERM, handler)

time.sleep(50)

有时,当以sudo运行时,脚本将执行处理程序,但通常不会执行.在没有sudo的情况下运行时,脚本始终会写入文件.我正在Raspberry Pi上运行它.我在LXTerminal和gnome终端中看到相同的内容.该示例脚本将在50秒后结束,但是我冗长的代码在无限循环中运行

最终目标是在Raspberry Pi上安装.desktop启动器以进行蓝牙扫描并查找设备.蓝牙扫描需要sudo,因为它使用4.0 BLE.我不确定bluez为什么需要sudo但它确实需要.在pi上键入sudo时,它永远不会要求输入密码对我来说很好.问题是关闭终端后,扫描过程仍在运行.扫描是通过在终端中运行的python脚本完成的.

解决方案

sudo是为tty上其他进程的子级而获得的SIGHUP语义设计的.在这种情况下,当父进程退出时,所有进程都会从内核获取自己的SIGHUP.

xterm -e sudo cmd直接在伪终端上运行sudo.这会产生与sudo预期不同的SIGHUP语义.只有sudo从内核接收到SIGHUP,并且不会中继它,因为它期望只有在其子进程也拥有自己的进程时才从内核获取SIGHUP(因为sudo的父进程(例如bash)可以这样做).

在上游报告了该问题,并且该问题现在标记为在sudo 1.8.15及更高版本中已修复.

解决方法:

xterm -e 'sudo ./sig-counter; true'

# or for uses that don't implicitly use a shell:
xterm -e sh -c 'sudo some-cmd; true'

如果您的-c参数是单个命令,则bash通过执行它来进行优化.跟踪另一个命令(在本例中为琐碎的true),使bash坚持不动,并在小时候运行sudo.我进行了测试,使用这种方法,当您关闭xterm时,sig-counter会从内核中获取一个SIGHUP. (对于任何其他终端仿真器,它应该是相同的.)

我已经测试过了,它可以与bash和dash一起使用.附带一个方便但无需退出的信号接收程序的源,您可以跟踪该程序以查看其收到的所有信号.


此答案其余部分的某些部分可能会稍有不同步.在弄清楚sudo是控制过程而sudo是壳差异的孩子之后,我经历了一些理论和测试方法.


POSIX表示伪终端的原因是这样的:应将SIGHUP信号发送到控制过程,如果有的话,则以伪终端的从属方为控制终端."

close()的POSIX措辞意味着只能有一个以pty作为控制终端的处理过程.

当bash是pty的从属方的控制进程时,它执行的操作会使所有其他进程收到SIGHUP.这是sudo期望的语义.

ssh localhost,然后中止与~.的连接或杀死您的ssh客户端.

$ ssh localhost
ssh$ sudo ~/.../sig-counter  # without exec
   # on session close: gets a SIGHUP and a SIGCONT from the kernel

$ ssh localhost
ssh$ exec sudo ~/src/experiments-sys/sig-counter
   # on session close: gets only a SIGCONT SI_USER relayed from sudo

$ ssh -t localhost sudo ~/src/experiments-sys/sig-counter
   # on session close: gets only a SIGCONT SI_USER relayed from sudo

$ xterm -e sudo ./sig-counter
           # on close: gets only a SIGCONT SI_USER relayed from sudo


测试此过程非常棘手,因为xterm在退出和关闭pty之前还自行发送了SIGHUP.其他终端仿真器(gnome-terminal,konsole)可能会或可能不会这样做.我必须自己编写一个信号测试程序,不仅要在第一次SIGHUP之后就死掉.

除非xterm以root身份运行,否则它无法将信号发送到sudo,因此sudo仅从内核获取信号. (因为这是tty的控制过程,而不是在sudo下运行的过程.)

sudo手册页说:

除非命令正在新的pty中运行, SIGHUP,SIGINT和SIGQUIT信号不会被中继,除非它们是由用户进程而不是内核发送的.否则, 命令将收到SIGINT 每次用户输入Control-C两次.

在我看来,sudo的SIGHUP双信号回避逻辑被设计为作为交互式shell的子代运行.当不涉及任何交互式外壳程序时(在交互式外壳程序的exec sudo之后,或者首先不涉及任何外壳程序时),只有父进程(sudo)才能得到SIGHUP.

sudo的行为对于SIGINT和SIGQUIT都是好的,即使在不涉及任何shell的xterm中也是如此:在xterm中按下^ C或^ \之后,sig-counter恰好接收到一个SIGINT或SIGQUIT. sudo收到一个并且不中继它.在两个过程中都是si_code=SI_KERNEL.


已在Ubuntu 15.04,sudo --version:1.8.9p5上进行了测试. xterm -v: XTerm(312).

###### No sudo
$ pkill sig-counter; xterm -e ./sig-counter &

$ strace -p $(pidof sig-counter)
Process 19446 attached
   quit xterm (ctrl-left click -> quit)
rt_sigtimedwait(~[TERM RTMIN RT_1], {si_signo=SIGHUP, si_code=SI_USER, si_pid=19444, si_uid=1000}, NULL, 8) = 1  # from xterm
rt_sigtimedwait(~[TERM RTMIN RT_1], {si_signo=SIGHUP, si_code=SI_KERNEL}, NULL, 8) = 1    # from the kernel
rt_sigtimedwait(~[TERM RTMIN RT_1], {si_signo=SIGCONT, si_code=SI_KERNEL}, NULL, 8) = 18   # from the kernel
   sig-counter is still running, because it only exits on SIGTERM

 #### with sudo, attaching to sudo and sig-counter after the fact
 # Then send SIGUSR1 to sudo
 # Then quit xterm

 $ sudo pkill sig-counter; xterm -e sudo ./sig-counter &
 $ sudo strace -p 20398  # sudo's pid
restart_syscall(<... resuming interrupted call ...>) = ? 
ERESTART_RESTARTBLOCK (Interrupted by signal)
--- SIGUSR1 {si_signo=SIGUSR1, si_code=SI_USER, si_pid=20540, si_uid=0} ---
write(7, "\n", 1)                       = 1   # FD 7 is the write end of a pipe. sudo's FD 6 is the other end.  Some kind of deadlock-avoidance?
rt_sigreturn()                          = -1 EINTR (Interrupted system call)
poll([{fd=6, events=POLLIN}], 1, 4294967295) = 1 ([{fd=6, revents=POLLIN}])
read(6, "\n", 1)                        = 1
kill(20399, SIGUSR1)                    = 0   ##### Passes it on to child
read(6, 0x7fff67d916ab, 1)              = -1 EAGAIN (Resource temporarily unavailable)
poll([{fd=6, events=POLLIN}], 1, 4294967295

     ####### close xterm
--- SIGHUP {si_signo=SIGHUP, si_code=SI_KERNEL} ---
rt_sigreturn()                          = -1 EINTR (Interrupted system call)
--- SIGCONT {si_signo=SIGCONT, si_code=SI_KERNEL} ---   ### sudo gets both SIGHUP and SIGCONT
write(7, "\22", 1)                      = 1
rt_sigreturn()                          = -1 EINTR (Interrupted system call)
poll([{fd=6, events=POLLIN}], 1, 4294967295) = 1 ([{fd=6, revents=POLLIN}])
read(6, "\22", 1)                       = 1
kill(20399, SIGCONT)                    = 0   ## but only passes on SIGCONT
read(6, 0x7fff67d916ab, 1)              = -1 EAGAIN (Resource temporarily unavailable)
poll([{fd=6, events=POLLIN}], 1, 4294967295
## keeps running after xterm closes

 $ sudo strace -p $(pidof sig-counter)  # in another window
rt_sigtimedwait(~[RTMIN RT_1], {si_signo=SIGUSR1, si_code=SI_USER, si_pid=20398, si_uid=0}, NULL, 8) = 10
rt_sigtimedwait(~[RTMIN RT_1], {si_signo=SIGCONT, si_code=SI_USER, si_pid=20398, si_uid=0}, NULL, 8) = 18
## keeps running after xterm closes

sudo下运行的命令仅在xterm关闭时才会看到SIGCONT.

请注意,单击xterm标题栏上的窗口管理器的关闭按钮只会使xterm手动发送SIGHUP.通常,这将导致xterm内部的进程关闭,在这种情况下,xterm会在此之后退出.同样,这只是xterm的行为.


这是bash在收到SIGHUP时所执行的操作,产生了sudo预期的行为:

Process 26121 attached
wait4(-1, 0x7ffc9b8c78c0, WSTOPPED|WCONTINUED, NULL) = ? ERESTARTSYS (To be restarted if SA_RESTART is set)
--- SIGHUP {si_signo=SIGHUP, si_code=SI_KERNEL} ---
--- SIGCONT {si_signo=SIGCONT, si_code=SI_KERNEL} ---
   ... write .bash history ...
kill(4294941137, SIGHUP)                = -1 EPERM (Operation not permitted)  # This is kill(-26159), which signals all processes in that process group
rt_sigprocmask(SIG_BLOCK, [CHLD TSTP TTIN TTOU], [CHLD], 8) = 0
ioctl(255, SNDRV_TIMER_IOCTL_SELECT or TIOCSPGRP, [26121]) = -1 ENOTTY (Inappropriate ioctl for device) # tcsetpgrp()
rt_sigprocmask(SIG_SETMASK, [CHLD], NULL, 8) = 0
setpgid(0, 26121)                       = -1 EPERM (Operation not permitted)
rt_sigaction(SIGHUP, {SIG_DFL, [], SA_RESTORER, 0x7f3b25ebf2f0}, {0x45dec0, [HUP INT ILL TRAP ABRT BUS FPE USR1 SEGV USR2 PIPE ALRM TERM XCPU XFSZ VTALRM SYS], SA_RESTORER, 0x7f3b25ebf2f0}, 8) = 0
kill(26121, SIGHUP)                     = 0 ## exit in a way that lets bash's parent see that SIGHUP killed it.
--- SIGHUP {si_signo=SIGHUP, si_code=SI_USER, si_pid=26121, si_uid=1000} ---
+++ killed by SIGHUP +++

我不确定这是哪一部分完成的.可能真正的退出是窍门,或者它是在启动命令之前所做的事情,因为killtcsetpgrp()都失败了.


我自己尝试的第一次尝试是:

xterm -e sudo strace -o /dev/pts/11 sleep 60

(其中pts/11是另一个终端.)sleep在第一个SIGHUP之后退出,因此没有sudo的测试仅显示xterm手动发送的SIGHUP.

sig-counter.c:

// sig-counter.c.
// http://stackoverflow.com/questions/32511170/terminate-sudo-python-script-when-the-terminal-closes
// gcc -Wall -Os -std=gnu11 sig-counter.c -o sig-counter
#include <stdio.h>
#include <signal.h>
#include <unistd.h>
#include <errno.h>

#define min(x, y) ({                \
    typeof(x) _min1 = (x);          \
    typeof(y) _min2 = (y);          \
    (void) (&_min1 == &_min2);      \
    _min1 < _min2 ? _min1 : _min2; })

int sigcounts[64];
static const int sigcount_size = sizeof(sigcounts)/sizeof(sigcounts[0]);

void handler(int sig_num)
{
    sig_num = min(sig_num, sigcount_size);
    sigcounts[sig_num]++;
}

int main(void)
{
    sigset_t sigset;
    sigfillset(&sigset);
    // sigdelset(&sigset, SIGTERM);

    if (sigprocmask(SIG_BLOCK, &sigset, NULL))
        perror("sigprocmask: ");

    const struct timespec timeout = { .tv_sec = 60 };
    int sig;
    do {
        // synchronously receive signals, instead of installing a handler
        siginfo_t siginfo;
        int ret = sigtimedwait(&sigset, &siginfo, &timeout);
        if (-1 == ret) {
            if (errno == EAGAIN) break; // exit after 60 secs with no signals
            else continue;
        }
        sig = siginfo.si_signo;
//      switch(siginfo.si_code) {
//      case SI_USER:  // printf some stuff about the signal... just use strace

        handler(sig);
    } while (sig != SIGTERM );

    //sigaction(handler, ...);
    //sleep(60);
    for (int i=0; i<sigcount_size ; i++) {
        if (sigcounts[i]) {
            printf("counts[%d] = %d\n", i, sigcounts[i]);
        }
    }
}

我的第一个尝试是perl,但是安装信号处理程序并没有阻止信号处理程序返回后perl在SIGHUP上退出.我看到消息出现在xterm关闭之前.

cmd=perl\ -e\ \''use strict; use warnings; use sigtrap qw/handler signal_handler normal-signals/; sleep(60); sub signal_handler { print "Caught a signal $!"; }'\';
xterm -e "$cmd" &

显然,perl信号处理相当复杂,因为perl必须推迟它们,直到它们不在没有适当锁定的地方为止.

C语言中的Unix syscalls是进行系统编程的默认"方式,因此避免了任何可能的混乱. strace通常是一种便宜的方法,可避免实际编写用于处理内容的日志记录/打印代码. :P

How can I tell if the terminal running my python script was closed? I want to safely end my python script if the user closes the terminal. I can catch SIGHUP with a handler, but not when the script is run as sudo. When I start the script with sudo and close the terminal, the python script keeps running.

Example script:

import signal
import time
import sys

def handler(signum, frame):
    fd = open ("tmp.txt", "a")
    fd.write(str(signum) + " handled\n")
    fd.close()
    sys.exit(0)


signal.signal(signal.SIGHUP, handler)
signal.signal(signal.SIGINT, handler)
signal.signal(signal.SIGTERM, handler)

time.sleep(50)

Sometimes the script will execute the handler when run as sudo, but more often it doesn't. The script always writes to the file when ran without sudo. I am running it on a Raspberry Pi. I see the same thing in a LXTerminal and a gnome-terminal. This example script will end after 50 seconds, but my lengthy code runs in an infinite loop

The ultimate goal is to have a .desktop launcher on a Raspberry Pi to do bluetooth scanning and find devices. The bluetooth scanning requires sudo because it use 4.0 BLE. I'm not sure why bluez requires sudo but it does. When type sudo on the pi, it never asks for a password which is fine with me. The problem is that after closing the terminal, the scan process is still running. The scanning is done by a python script that runs in a terminal.

解决方案

sudo is designed for the SIGHUP semantics you get when it's a child of some other process on the tty. In that case, all processes get their own SIGHUP from the kernel when the parent exits.

xterm -e sudo cmd runs sudo directly on the pseudo-terminal. This produces different SIGHUP semantics than sudo is expecting. Only sudo receives a SIGHUP from the kernel, and doesn't relay it because it's expecting that it gets a SIGHUP from the kernel only when its child process also got its own (because of something sudo's parent (e.g. bash) does).

I reported the issue upstream, and it's now marked as fixed in sudo 1.8.15 and onwards.

Workaround:

xterm -e 'sudo ./sig-counter; true'

# or for uses that don't implicitly use a shell:
xterm -e sh -c 'sudo some-cmd; true'

If your -c argument is a single command, bash optimizes by execing it. Tacking another command (the trivial true in this case), gets bash to stick around and run sudo as a child. I tested, and with this method, sig-counter gets one SIGHUP from the kernel when you close xterm. (It should be the same for any other terminal emulator.)

I've tested this, and it works with bash and dash. Source included for a handy-dandy signal-receiving-without-exiting program which you can strace to see all the signals it receives.


Some parts of the rest of this answer may be slightly out of sync. I went through a few theories and testing methods before figuring out the sudo as controlling process vs. sudo as child of a shell difference.


POSIX says that close() on the master end of a pseudo-terminal causes this: "a SIGHUP signal shall be sent to the controlling process, if any, for which the slave side of the pseudo-terminal is the controlling terminal."

The POSIX wording for close() implies there can be only one processing process that has the pty as its controlling terminal.

When bash is the controlling process for the slave side of a pty, it does something that causes all other processes to receive a SIGHUP. This is the semantics sudo is expecting.

ssh localhost, then abort the connection with ~. or kill your ssh client.

$ ssh localhost
ssh$ sudo ~/.../sig-counter  # without exec
   # on session close: gets a SIGHUP and a SIGCONT from the kernel

$ ssh localhost
ssh$ exec sudo ~/src/experiments-sys/sig-counter
   # on session close: gets only a SIGCONT SI_USER relayed from sudo

$ ssh -t localhost sudo ~/src/experiments-sys/sig-counter
   # on session close: gets only a SIGCONT SI_USER relayed from sudo

$ xterm -e sudo ./sig-counter
           # on close: gets only a SIGCONT SI_USER relayed from sudo


Testing this was tricky, because xterm also sends a SIGHUP on its own, before exiting and closing the pty. Other terminal emulators (gnome-terminal, konsole) may or may not do this. I had to write a signal-testing program myself to not just die after the first SIGHUP.

Unless xterm is running as root, it can't send signals to sudo, so sudo only gets the signals from the kernel. (Because it is the controlling process for the tty, and the process running under sudo isn't.)

The sudo man page says:

Unless the command is being run in a new pty, the SIGHUP, SIGINT and SIGQUIT signals are not relayed unless they are sent by a user process, not the kernel. Otherwise, the command would receive SIGINT twice every time the user entered control-C.

It looks to me like sudo's double-signal avoidance logic for SIGHUP was designed for running as a child of an interactive shell. When there's no interactive shell involved (after exec sudo from an interactive shell, or when there was no shell involved in the first place), only the parent process (sudo) gets a SIGHUP.

sudo's behaviour is good for SIGINT and SIGQUIT, even in an xterm with no shell involved: after pressing ^C or ^\ in the xterm, sig-counter receives exactly one SIGINT or SIGQUIT. sudo receives one and doesn't relay it. si_code=SI_KERNEL in both processes.


Tested on Ubuntu 15.04, sudo --version: 1.8.9p5. xterm -v: XTerm(312).

###### No sudo
$ pkill sig-counter; xterm -e ./sig-counter &

$ strace -p $(pidof sig-counter)
Process 19446 attached
   quit xterm (ctrl-left click -> quit)
rt_sigtimedwait(~[TERM RTMIN RT_1], {si_signo=SIGHUP, si_code=SI_USER, si_pid=19444, si_uid=1000}, NULL, 8) = 1  # from xterm
rt_sigtimedwait(~[TERM RTMIN RT_1], {si_signo=SIGHUP, si_code=SI_KERNEL}, NULL, 8) = 1    # from the kernel
rt_sigtimedwait(~[TERM RTMIN RT_1], {si_signo=SIGCONT, si_code=SI_KERNEL}, NULL, 8) = 18   # from the kernel
   sig-counter is still running, because it only exits on SIGTERM

 #### with sudo, attaching to sudo and sig-counter after the fact
 # Then send SIGUSR1 to sudo
 # Then quit xterm

 $ sudo pkill sig-counter; xterm -e sudo ./sig-counter &
 $ sudo strace -p 20398  # sudo's pid
restart_syscall(<... resuming interrupted call ...>) = ? 
ERESTART_RESTARTBLOCK (Interrupted by signal)
--- SIGUSR1 {si_signo=SIGUSR1, si_code=SI_USER, si_pid=20540, si_uid=0} ---
write(7, "\n", 1)                       = 1   # FD 7 is the write end of a pipe. sudo's FD 6 is the other end.  Some kind of deadlock-avoidance?
rt_sigreturn()                          = -1 EINTR (Interrupted system call)
poll([{fd=6, events=POLLIN}], 1, 4294967295) = 1 ([{fd=6, revents=POLLIN}])
read(6, "\n", 1)                        = 1
kill(20399, SIGUSR1)                    = 0   ##### Passes it on to child
read(6, 0x7fff67d916ab, 1)              = -1 EAGAIN (Resource temporarily unavailable)
poll([{fd=6, events=POLLIN}], 1, 4294967295

     ####### close xterm
--- SIGHUP {si_signo=SIGHUP, si_code=SI_KERNEL} ---
rt_sigreturn()                          = -1 EINTR (Interrupted system call)
--- SIGCONT {si_signo=SIGCONT, si_code=SI_KERNEL} ---   ### sudo gets both SIGHUP and SIGCONT
write(7, "\22", 1)                      = 1
rt_sigreturn()                          = -1 EINTR (Interrupted system call)
poll([{fd=6, events=POLLIN}], 1, 4294967295) = 1 ([{fd=6, revents=POLLIN}])
read(6, "\22", 1)                       = 1
kill(20399, SIGCONT)                    = 0   ## but only passes on SIGCONT
read(6, 0x7fff67d916ab, 1)              = -1 EAGAIN (Resource temporarily unavailable)
poll([{fd=6, events=POLLIN}], 1, 4294967295
## keeps running after xterm closes

 $ sudo strace -p $(pidof sig-counter)  # in another window
rt_sigtimedwait(~[RTMIN RT_1], {si_signo=SIGUSR1, si_code=SI_USER, si_pid=20398, si_uid=0}, NULL, 8) = 10
rt_sigtimedwait(~[RTMIN RT_1], {si_signo=SIGCONT, si_code=SI_USER, si_pid=20398, si_uid=0}, NULL, 8) = 18
## keeps running after xterm closes

The command running under sudo only sees a SIGCONT when the xterm closes.

Note that clicking the window-manager's close button on xterm's titlebar just makes xterm send a SIGHUP manually. Often this will cause the process inside xterm to close, in which case xterm exits after that. Again, this is just xterm's behaviour.


This is what bash does when it gets SIGHUP, producing the behaviour sudo expects:

Process 26121 attached
wait4(-1, 0x7ffc9b8c78c0, WSTOPPED|WCONTINUED, NULL) = ? ERESTARTSYS (To be restarted if SA_RESTART is set)
--- SIGHUP {si_signo=SIGHUP, si_code=SI_KERNEL} ---
--- SIGCONT {si_signo=SIGCONT, si_code=SI_KERNEL} ---
   ... write .bash history ...
kill(4294941137, SIGHUP)                = -1 EPERM (Operation not permitted)  # This is kill(-26159), which signals all processes in that process group
rt_sigprocmask(SIG_BLOCK, [CHLD TSTP TTIN TTOU], [CHLD], 8) = 0
ioctl(255, SNDRV_TIMER_IOCTL_SELECT or TIOCSPGRP, [26121]) = -1 ENOTTY (Inappropriate ioctl for device) # tcsetpgrp()
rt_sigprocmask(SIG_SETMASK, [CHLD], NULL, 8) = 0
setpgid(0, 26121)                       = -1 EPERM (Operation not permitted)
rt_sigaction(SIGHUP, {SIG_DFL, [], SA_RESTORER, 0x7f3b25ebf2f0}, {0x45dec0, [HUP INT ILL TRAP ABRT BUS FPE USR1 SEGV USR2 PIPE ALRM TERM XCPU XFSZ VTALRM SYS], SA_RESTORER, 0x7f3b25ebf2f0}, 8) = 0
kill(26121, SIGHUP)                     = 0 ## exit in a way that lets bash's parent see that SIGHUP killed it.
--- SIGHUP {si_signo=SIGHUP, si_code=SI_USER, si_pid=26121, si_uid=1000} ---
+++ killed by SIGHUP +++

I'm not sure which part of this gets the job done. Probably the actual exiting is the trick, or something it did before launching the command, since kill and tcsetpgrp() both failed.


My first attempt at trying it myself was:

xterm -e sudo strace -o /dev/pts/11 sleep 60

(where pts/11 is another terminal.) sleep exits after the first SIGHUP, so testing without sudo just shows the SIGHUP sent manually by xterm.

sig-counter.c:

// sig-counter.c.
// http://stackoverflow.com/questions/32511170/terminate-sudo-python-script-when-the-terminal-closes
// gcc -Wall -Os -std=gnu11 sig-counter.c -o sig-counter
#include <stdio.h>
#include <signal.h>
#include <unistd.h>
#include <errno.h>

#define min(x, y) ({                \
    typeof(x) _min1 = (x);          \
    typeof(y) _min2 = (y);          \
    (void) (&_min1 == &_min2);      \
    _min1 < _min2 ? _min1 : _min2; })

int sigcounts[64];
static const int sigcount_size = sizeof(sigcounts)/sizeof(sigcounts[0]);

void handler(int sig_num)
{
    sig_num = min(sig_num, sigcount_size);
    sigcounts[sig_num]++;
}

int main(void)
{
    sigset_t sigset;
    sigfillset(&sigset);
    // sigdelset(&sigset, SIGTERM);

    if (sigprocmask(SIG_BLOCK, &sigset, NULL))
        perror("sigprocmask: ");

    const struct timespec timeout = { .tv_sec = 60 };
    int sig;
    do {
        // synchronously receive signals, instead of installing a handler
        siginfo_t siginfo;
        int ret = sigtimedwait(&sigset, &siginfo, &timeout);
        if (-1 == ret) {
            if (errno == EAGAIN) break; // exit after 60 secs with no signals
            else continue;
        }
        sig = siginfo.si_signo;
//      switch(siginfo.si_code) {
//      case SI_USER:  // printf some stuff about the signal... just use strace

        handler(sig);
    } while (sig != SIGTERM );

    //sigaction(handler, ...);
    //sleep(60);
    for (int i=0; i<sigcount_size ; i++) {
        if (sigcounts[i]) {
            printf("counts[%d] = %d\n", i, sigcounts[i]);
        }
    }
}

My first attempt at this was perl, but installing a signal handler wasn't stopping perl from exitting on SIGHUP after the signal handler returned. I saw the message appear right before xterm closed.

cmd=perl\ -e\ \''use strict; use warnings; use sigtrap qw/handler signal_handler normal-signals/; sleep(60); sub signal_handler { print "Caught a signal $!"; }'\';
xterm -e "$cmd" &

Apparently perl signal handling is fairly complicated because perl has to defer them until it's not in the middle of something that doesn't do proper locking.

Unix syscalls in C is the "default" way to do systems programming, so that takes out any possible confusion. strace is often a cheap way to avoid actually writing logging / printing code for playing around with stuff. :P

这篇关于终端关闭时终止sudo python脚本的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆