C 从终点站向上看

C 从终点站向上看,c,linux,process,ssh,signals,C,Linux,Process,Ssh,Signals,专家们, 我有一个通过ssh连接到服务器的客户端(它得到了一个tty分配)。我有一个进程a正在服务器上运行。现在,每当客户机断开连接时,我都需要了解消失的tty 我在想,既然SSHD知道会话正在消亡(在超时或简单退出之后),它就可以生成一个信号来处理会话 有没有其他方法可以让A获得关于tty的信息,这些信息会像在SIGHUP上收听tty一样消失?我正在Linux上用C编写代码 感谢您的帮助。您面临两个问题,都很困难。简洁的回答是“你不能”;较长的答案是“如果不做重大修改,你就不能” 信号传递的信

专家们, 我有一个通过ssh连接到服务器的客户端(它得到了一个tty分配)。我有一个进程a正在服务器上运行。现在,每当客户机断开连接时,我都需要了解消失的tty

我在想,既然SSHD知道会话正在消亡(在超时或简单退出之后),它就可以生成一个信号来处理会话

有没有其他方法可以让A获得关于tty的信息,这些信息会像在SIGHUP上收听tty一样消失?我正在Linux上用C编写代码


感谢您的帮助。

您面临两个问题,都很困难。简洁的回答是“你不能”;较长的答案是“如果不做重大修改,你就不能”

  • 信号传递的信息很少,除了它发生的事实。如果您使用and
    sau SIGINFO
    ,您可以找到发送信号的进程的进程ID,但在您的方案中,这将是
    sshd
    ,这并没有太大的帮助。因此,很难(几乎不可能)通过信号获得关于哪个终端的信息。显然,可以定义其他方案,但必须将信息写入文件或类似文件

  • 您必须修改
    sshd
    ,以记录有关它将哪个终端分配(或被分配)给其子进程的信息,然后安排它在子进程终止时将该信息发送给您的进程A。这充其量也会很棘手

  • 仅这两个因素就相当困难。如果你仍然想这样做,那么我会尝试的方法是让
    sshd
    运行你设计的一个特殊过程,这反过来会分叉,孩子运行
    sshd
    否则会运行的过程。父级(a)记录子级连接到哪个终端,并且(b)等待子级终止。当它这样做时,它将终端信息写入进程A将找到它的某个地方,然后退出。您仍然需要修改
    sshd
    ,并且您必须设计一种机制,使父进程知道作为子进程运行什么(但这可能不是很难;您保持参数列表不变,只是让
    sshd
    执行您的监视进程,而不是指定为
    argv[0]的任何进程)
    …父级使用
    argv[0]
    作为
    execvp()
    的文件参数


    此方案最大限度地减少了对
    sshd
    的更改(但仍需要非标准版本)。您必须仔细编写父代码,并且必须与进程A配合。所有这些都非常重要。

    POSIX.1提供了一个工具,其中列出了当前登录的用户、他们的终端和其他信息。在Linux中,这与utmp相同;有关更多信息,请参阅

    OpenSSH确实维护utmp记录

    下面是一个简单的示例,其中列出了当前从远程计算机登录的所有用户、他们使用的终端以及用户拥有的初始进程组:

    #define  _POSIX_C_SOURCE 200809L
    #include <unistd.h>
    #include <stdlib.h>
    #include <stdio.h>
    #include <utmpx.h>
    
    int main(void)
    {
        struct utmpx *entry;
    
        setutxent();
        while ((entry = getutxent()))
            if (entry->ut_type == USER_PROCESS && entry->ut_host[0] != '\0')
                printf("%s is logged in on /dev/%s from %s and owns process group %d\n", 
                       entry->ut_user, entry->ut_line, entry->ut_host,
                       (int)getpgid(entry->ut_pid));
    
        return 0;
    }
    
    #定义POSIX_C_SOURCE200809L
    #包括
    #包括
    #包括
    #包括
    内部主(空)
    {
    结构utmpx*条目;
    setutxent();
    而((entry=getutxent())
    如果(条目->ut\U类型==用户\U进程和条目->ut\U主机[0]!='\0')
    printf(“%s从%s登录到/dev/%s并拥有进程组%d\n”,
    条目->ut\U用户,条目->ut\U行,条目->ut\U主机,
    (int)getpgid(entry->ut_pid));
    返回0;
    }
    
    在您的情况下,我希望进程A维护一个远程连接用户的列表,并定期执行如上所述的类似循环,以更新已知条目的状态并添加新条目;删除不再可见的条目

    然后,新条目与“登录”事件匹配,不再显示的条目与“注销”事件匹配(并在循环后删除),所有其他事件都是“仍然登录”用户

    就所用CPU时间和所用I/O而言,上面的循环相当轻量级是二进制格式的,如果经常访问,通常在页面缓存中。条目相对较小,即使在有很多用户的服务器上,文件读取的大小也不超过1兆字节。不过,我不会在一个严格的循环中执行


    就我个人而言,我通常会等待
    UTMPX\u文件上的
    WRITE
    事件(
    /var/run/utmp
    在大多数Linux机器上),然后在每个事件后重新读取记录。这样,服务将在大部分时间阻塞inotify文件描述符上的
    read(),并几乎立即对任何登录/注销事件做出反应。

    谢谢。但是,真正的问题是我不能有一个特殊的过程。当我通过ssh登录时,我启动一个特殊的shell,它处理来自ssh客户端的请求。这个shell使用套接字与a通信以获取一些信息。因此每个ssh客户端都有自己的shell launched和用户选择是否通过客户端外壳获取重定向的标准输出。如果用户选择通过客户端外壳获取标准输出,我需要处理客户端终止。即进程A不应再将标准输出重定向到客户端外壳。这就是我遇到问题的地方,因此出现了上述问题。然后是与将必须安排A知道它何时终止。一种可能是让shell创建并打开FIFO进行写入,而进程A打开FIFO进行读取。进程A可以安排在FIFO的另一端关闭时被告知,然后删除FIFO。这听起来是个好主意。让我试试。但是如果客户端终止abru突然(例如网络问题),这个shell会被删除吗?(记住当用户使用特定的loginid登录时,我在passwd文件中启动这个shell)。如果shell被杀死,除非FIFO被明确关闭,否则FIFO仍然保留,并且A不会得到任何正确的通知?通过。。