Unix & macOS 编写 who 命令

作者:高玉涵
时间:2022.3.14 09:47
博客:blog.csdn.net/cg_i
环境:
 HP-UX B.11.31 U ia64
 macOS 12.2.1

活动老,学到老。—— 俗语

介绍

 在使用 Unix 的时候,经常需要知道有哪些用户正在使用系统,系统是否很繁忙,某人是否正在使用系统等。为了回答这些问题,可以使用 who 命令,所有的多用户系统都会有这个命令。这个命令会显示系统中活动用户的情况。接下来的问题是,who 命令是如何工作的呢?

 本文分析 Unix 中的 who 命令,通过分析来学习 who 命令是如何工作的。除此之外,还将学习如何从 Unix 的联机帮助中得到有用的信息。

对以下 3 个问题的解答来学习 who 命令:
  1. who 命令能做些什么?
  2. who 命令是如何工作的?
  3. 如何编写 who?
命令也是程序

 在开始之前,需要声明的是,在 Unix 系统中,几乎所有的命令都是人为编写的程序,如 who 和 ls,而且它们中的大多数都是用 C 语言写的。当在命令行中输入 ls,Shell(命令解释器)就知道你想运行名字为 ls 的程序。如果对 ls 所提供的功能还不满意,完全可以编写和使用自己的 ls 命令。

 在 Unix 系统中增加新的命令是一件很容易的事。把程序的可执行文件放到以下任意一个目录就可以了:/bin、/usr/bin、/usr/local/bin,这些目录里面存放着很多系统命令。Unix 系统中一开始并没有这么多命令,一些人编写程序用来解决某个特定的问题,而其他人也觉得这个程序很有用,随着越来越多的使用,这个程序就逐渐成了 Unix 的标准命令。说不定哪一天,你编写的程序也会成为标准命令。

问题 1: who 命令能做些什么

 如果想要知道都有谁在使用系统,只有输入 who 命令,输出如下:

  • HP-UX
$who
jstest     pts/1        Mar 14 08:30
jstest     pts/2        Mar 14 08:45
uatvlog    pts/5        Mar 14 09:10
jstest     pts/6        Mar 14 09:18
jstest     pts/7        Mar 14 08:56
jstest     pts/8        Mar 14 08:59
jstest     pts/9        Mar 14 09:19
jstest     pts/10       Mar 14 09:19
uattest    pts/13       Mar 14 09:25
jstest     pts/14       Mar 14 09:34
jstest     pts/15       Mar 14 09:35
jstest     pts/16       Mar 14 09:35
uatvlog    pts/18       Mar 14 09:40

 每一行代表一个已经登录的用户,第 1 列是用户名,第 2 列是终端名,第 3 列是登录时间,某些版本的 who 命令会给出第 4 列(用户的登录地址)我的版本默认状态下不给出第 4 列的内容。

阅读手册

 通过直接运行命令,可以了解 who 的大致功能,要进一步了解 who 的用法,需要借助联机帮助。

 每一个 Unix 在发售的时候都会带上大量的有关各个命令使用方法的文档。以前,这些文档是打印出来的,现在有电子版的可供使用。查看联机帮助的命令是 man,如要查看 who 的帮助,可输入:

$man who
who(1)                                                               who(1)

 NAME
      who - who is on the system

 SYNOPSIS
      who [-muTlHqpdbrtasARW] [file]

      who am i

      who am I

 DESCRIPTION
      The who command can list the user's name, terminal line, login time,
      elapsed time since input activity occurred on the line, the user's
      host name, and the process-ID of the command interpreter (shell) for
      each current system user.  It examines the utmps database to obtain
      the information.  If file is given, that file is examined, file should
      be a utmp like file.

      The who command with the am i or am I option identifies the invoking
      user.

      Except for the default -s option, the general format for output
      entries is:

           name [state] line time activity pid [comment] [exit]

      With options, who can list logins, logoffs, reboots, and changes to
      the system clock, as well as other processes spawned by the init
      process.

    Options
           -m             Output only information about the current
                          terminal.  This option is equivalent to the am i
                          and am I options described above.

           -u             Lists only those users who are currently logged
                          in.  name is the user's login name.  line is the
                          name of the line as found in the directory /dev.
                          The time field indicates when the user logged in.

                          activity is the number of hours and minutes since
                          input activity last occurred on that particular
......

 所有命令的联机帮助都有相同的基本格式,从第 1 行可知这是关于哪个命令的帮助,还可以知道这个帮助是位于哪一节的。在这个例子中,从第 1 行的内容 who(1),可以知道这是 who 命令的帮助,它的小节编号是 1 。Unix 的联机帮助分为很多节,如第 1 小节中是关于用户命令的帮助,第 2 小节中是关于系统调用的帮助,第 5 小节中是关于配置文件的帮助。你可查看一下 Unix 系统的联机帮助,了解其他节讲述的内容。

 名字(NAME)部分包含命令的名字以及对这个命令的简短说明。

 概要(SYNOPSYS)部分给出了命令的用法说明,包括命令格式、参数和选项列表。选项指的是一个短线后面紧跟着一个或多个英文字母,如 -a、-Bc,命令的选项影响该命令所进行的操作。

 在联机帮助中,方括号([-a])表示该选项不是一个必要的部分。帮助中指出 who 的写法可以是 who,或者 who -a,或者 who - 加上 muTlHqpdbrtasARW 这些字母的任意组合,在命令的未尾还可以有一个文件参数。

 从帮助中可以知道 who 命令还有其他 3 种形式:

who am i
who am I
whoami

 从联机帮助中还可以获得上述形式的进一步帮助。

 描述(DESCRIPTION)部分是关于命令功能的详细阐述,根据命令和平台的不同,描述的内容也不同,有的简洁、精确、有的包含了大量的例子。不管怎么样,它描述了命令的所有功能,而且是这个命令的权威性解释。

 选项(OPTIONS)部分给出了命令行中每一个选项的说明。早期的 Unix 命令的功能都很简单,每个命令只有一两个选项,但随着时间的推移,命令的功能越来越多,基本上每个选项用来实现一个功能,所以选项也越来越多,像 who 命令就是有很多选项。

 参阅(SEE ALSO)部分包含与这个命令相关的其他主题。有些帮助还有 BUG 部分。

问题 2: who 命令是如何工作的

 前面看到 who 命令可以显示出当前系统中已经登录的用户信息,联机帮助中描述了 who 的功能和用法,现在的问题是:who 是如何实现这些功能的?

 你可能会认为,像 who 这样的系统程序一定会用到一些特殊的系统调用,需要高级管理员的权限,要编写这样的程序得要花很多钱来购买系统开发工具,包括光盘、参考书等。

 实际上,所需的资料都在系统中,你要知道的仅仅是如何找到这些资料。

  1. 从 Unix 中学习 Unix

 以下 4 项技巧有助于你的学习:

  • 阅读联机帮助
  • 搜索联机帮助
  • 阅读 .h 文件
  • 从参阅部分(SEE ALSO)得到启示
  1. 阅读联机帮助

 以 who 为例,在命令行输入如下命令:

$man who

 翻到描述部分,可以看到如下内容:

DESCRIPTION
      The who command can list the user's name, terminal line, login time,
      elapsed time since input activity occurred on the line, the user's
      host name, and the process-ID of the command interpreter (shell) for
      each current system user.  It examines the utmps database to obtain
      the information.  If file is given, that file is examined, file should
      be a utmp like file.

      The who command with the am i or am I option identifies the invoking
      user.

      Except for the default -s option, the general format for output
      entries is:

           name [state] line time activity pid [comment] [exit]

      With options, who can list logins, logoffs, reboots, and changes to
      the system clock, as well as other processes spawned by the init
      process.

 上述描述说明,注意这一句It examines the utmps database to obtain the information.(已登录用户的信息存放在 uptmps 数据库里)继续翻到 FILES 部分找到,在 /etc/utmp 这个中,who 通过读取该文件获得信息,可以通过搜索联机帮助来了解这个文件的结构信息。

 FILES
      /etc/inittab
      /etc/utmp
      /var/adm/wtmp
      /var/adm/wtmps
  1. 搜索联机帮助

 使用带有选项 -k 的 man 命令可以根据关键字搜索联机帮助。如果要查找 “utmp” 的信息,在命令行输入如下命令:

$man -k utmp
/usr/share/lib/whatis: No such file or directory

 报上述错误,执行 man man阅读帮助手册,得知 -k 选项必须配合 /usr/share/lib/whatis(索引库)文件方能使用。

......
Searching for Entry Names by Keyword (first form)
      The first form above searches the one-line descriptions of individual
      entries for specified keywords.  Arguments are as follows:

           -k keyword     -k followed by one or more keywords causes man to
                          print the one-line description of each manual
                          entry whose one-line description contains text
                          matching one or more of the specified keywords
                          (similar to the behavior of grep(1)).  Keywords
                          are separated by blanks (space or tab).

                          Before this option can be used, file
                          /usr/share/lib/whatis must exist.
                          /usr/share/lib/whatis can be created by running
                          catman(1M).
......

 即而在命令行输入如下命令,以期能得到创建索引库的方法:

$man whatis
No manual entry for whatis.

囧囧囧… 提示找不到对应的手册。无奈之下救助于网络搜索,得知,是由于系统没有安装扩展帮助手册导致。而最先前的问题,找到的都是关于在 Linux 下通过 makewhatis 命令创建索引库,我并没有搜索到在 HP-UX 下创建 mandb 的方法。 上述问题的解决,都依赖于安装对应的软件包,而这些我都没有,网络也未能找到有关的 HP-UX 资源,这里只好作罢 ╮( ̄▽  ̄)╭ 希望,观看此文的高人前辈指点一二,定感激不尽!

 先前阅读 who 命令手册里,提到过 utmp 这个文件,那试着用 man utmp 能否找到有用的信息呢?报着试试看的心理,在命令行里输入以下命令:

$man utmp
utmp(4)                                                             utmp(4)
                               TO BE OBSOLETED

 NAME
      utmp, wtmp, btmp - user login record format

 SYNOPSIS
      #include <sys/types.h>
      #include <utmp.h>

 DESCRIPTION
      These files, which hold user and accounting information for such
      commands as last, who, write, and login (see last(1), who(1),
      write(1), and login(1)), have the following structure as defined by
      <utmp.h>:

      #define UTMP_FILE   "/etc/utmp"
      #define WTMP_FILE   "/var/adm/wtmp"
      #define BTMP_FILE   "/var/adm/btmp"
      #define ut_name     ut_user

      struct  utmp {
        char   ut_user[8];           /* User login name */
        char   ut_id[4];             /* /etc/inittab id(usually line#)*/
        char   ut_line[12]           /* device name (console, lnxx) */
        pid_t  ut_pid;               /* process id */
        short  ut_type;              /* type of entry */
        struct exit_status
            short  e_termination;    /* Process termination status*/
            short  e_exit;           /* Process exit status*/
            } ut_exit;               /* The exit status of a process*/
                                     /* marked as DEAD_PROCESS.*/
        unsigned short ut_reserved1; /* Reserved for future use*/
        time_t  ut_time;             /* time entry was made*/
        char    ut_host[16];         /* host name,if remote*/
        unsigned long ut_addr;       /* host Internet addr, if remote*/
      };

      /*  Definitions for ut_type  */
      #define EMPTY           0
      #define RUN_LVL         1
      #define BOOT_TIME       2
      #define OLD_TIME        3
      #define NEW_TIME        4
      #define INIT_PROCESS    5      /* Process spawned by "init" */
      #define LOGIN_PROCESS   6      /* getty process awaiting login */
      #define USER_PROCESS    7      /* A user process */
      #define DEAD_PROCESS    8
      #define ACCOUNTING      9
      #define UTMAXTYPE       ACCOUNTING  /* Max. legal value of ut_type */

      /*  Special strings or formats used in the "ut_line" field */
      /*  when  accounting for something other than a process  */
      /*  No string for the ut_line field can be more than */

 Hewlett-Packard Company            - 1 -       HP-UX 11i Version 3 Feb 2007

 utmp(4)                                                             utmp(4)
                               TO BE OBSOLETED

      /*  11 chars + a NULL in length  */
Standard input
unsigned short ut_reserved1; /* Reserved for future use*/
        time_t  ut_time;             /* time entry was made*/
        char    ut_host[16];         /* host name,if remote*/
        unsigned long ut_addr;       /* host Internet addr, if remote*/
      };

      /*  Definitions for ut_type  */
      #define EMPTY           0
      #define RUN_LVL         1
      #define BOOT_TIME       2
      #define OLD_TIME        3
      #define NEW_TIME        4
      #define INIT_PROCESS    5      /* Process spawned by "init" */
      #define LOGIN_PROCESS   6      /* getty process awaiting login */
      #define USER_PROCESS    7      /* A user process */
      #define DEAD_PROCESS    8
      #define ACCOUNTING      9
      #define UTMAXTYPE       ACCOUNTING  /* Max. legal value of ut_type */

      /*  Special strings or formats used in the "ut_line" field */
      /*  when  accounting for something other than a process  */
      /*  No string for the ut_line field can be more than */

 Hewlett-Packard Company            - 1 -       HP-UX 11i Version 3 Feb 2007

 utmp(4)                                                             utmp(4)
                               TO BE OBSOLETED

      /*  11 chars + a NULL in length  */
      #define RUNLVL_MSG      "run-level %c"
      #define BOOT_MSG        "system boot"
      #define OTIME_MSG       "old time"
      #define NTIME_MSG       "new time"

      File utmp contains a record of all users logged onto the system.  File
      btmp contains bad login entries for each invalid logon attempt.  File
      wtmp contains a record of all logins and logouts.

      Note that wtmp and btmp tend to grow without bound, and should be
      checked regularly.  Information that is no longer useful should be
      removed periodically to prevent it from becoming too large.  Also note
      that wtmp and btmp are not created by the programs that maintain them.
      Thus, if these files are removed, record-keeping is turned off.

 FILES
      /etc/utmp
      /var/adm/wtmp
      /var/adm/btmp

 AUTHOR
      utmp, wtmp, and btmp were developed by HP and the University of
      California, Berkeley.

 SEE ALSO
      last(1), login(1), who(1), write(1), acctcon(1M), fwtmp(1M),
      utmpd(1M), getut(3C), getuts(3C), getutx(3C).

 STANDARDS CONFORMANCE
      <utmp.h>: XPG2

Standard input: END

 可以看到 utmp 结构所保存的详细登录记录。各个平台上的 utmp 结构可能不会完全相同,具体的内容由 utmp.h 来决定,它位于 /usr/include 目录里。虽然没有看过 who 的源代码,但从联机帮助以及头文件,可以知道 who 的工作原理,who 通过读文件来获得需要的信息,而每个登录的用户在文件中都有对应的记录。who 的工作流程可以用图 1.1 来表示。

在这里插入图片描述

(图 1.1)

 文件中的结构数组存放登录用户的信息,所以直接的想法就是把记录一个一个的读出来并显示出来,是不是就这么简单呢?是的,以前的确是这样做的(参见1)细心的朋友可能注意到了第 2 行这句 TO BE OBSOLETED(被弃用)Orz !是的,这个已不再使用,那现行用的又是那个呢?

  • macOS

 自 1969 年,贝尔实验室的计算机科学家发明了 Unix,至今在商业计算和研究机构得到很大的发展,出现了不同的方向,如 System V、BSD 乃至当今最流行的 GNU/Linux。虽然各种 Unix/Linux 不尽相同,但它们都尊循着 POSIX 标准中形式化的方法描述了 Unix 的系统接口。而我 MacBook Pro 上运行的 macOS 本身就是 Unix, 尝试着在它那里搜索答案。在命令行里输入以下命令:

$man -k utmp
utmp(5), wtmp(5), lastlog(5) - login records (DEPRECATED)
utmpx(5)                 - user accounting database
getlastlogx(3), getlastlogxbyname(3), getutmp(3), getutmpx(3), utmpxname(3) - user accounting database functions
utmp(5), wtmp(5), lastlog(5) - login records (DEPRECATED)
utmpx(5)                 - user accounting database

 以上的输出是 macOS 平台上的输出, 不同版本的 Unix 输出可能会有所不同,其中每一行都包含帮助的主题、标题和一段简短的描述。我们忽略所有带有 (DEPRECATED) 的行,注意这一行:

utmpx(5) - user accounting database

 看起来好像就是所需要的。这一行里的“5”是小节编号,说明该帮助是位于第 5 节,在查看该帮助内容的时候,注意别把小节编号漏了:

$man 5 utmpx
UTMPX(5)                                   File Formats Manual                                  UTMPX(5)

NAME
     utmpx – user accounting database

SYNOPSIS
     #include <utmpx.h>

DESCRIPTION
     In contrast to utmp and wtmp, the extended databases in utmpx and wtmpx reserve more space for
     logging hostnames, and also information on a process' ID, termination signal and exit status.

     The ⟨utmpx.h⟩ header defines the structures and functions for logging user.  Currently logged in
     users are tracked in /var/run/utmpx.  The interface to the utmpx file is described in endutxent(3).
     The file is not automatically created if they do not exist; it must be created manually.

     Traditionally, separate files would be used to store the running log of the logins and logouts
     (wtmpx), and the last login of each user (lastlogx).  With the availability of the Apple system log
     facility asl(3), these separate files can be replace with log entries, which are automatically
     generated when utmpx entries are written.  The API to access the logins and logouts is described in
     endutxent_wtmp(3) while the last login info is accessible with getlastlogx(3).

     For compatibility, changes to utmpx are reflected in utmp(3) (in the utmp, wtmp and lastlog files),
     but not the other way around.

FILES
     /var/run/utmpx  The utmpx file.

SEE ALSO
     asl(3), endutxent(3), endutxent_wtmp(3), getlastlogx(3), utmp(5)

macOS 12.2                                  January 31, 2007                                  macOS 12.2
(END)

 到此已经离目标不远了,联机帮助说明读取的是 /var/run/utmpx 这个文件, 头文件是 utmpx.h 。接下来的问题是:utmpx.h 这个文件在哪里?

  1. 阅读 .h 文件

 macOS 默认是没有这些头文件的,需要在命令行界面下输入 xcode-select --install来安装命令行开发工具,此过程需要联网,进度根据您的网速,我安装成功大约耗时 20 分钟左右。安装成功后,头文件保存位置在 /Library/Developer/CommandLineTools/SDKs/MacOSX.sdk/usr/include (HP-UX: /usr/include)在命令行界面下输入以下命令:

$cd /Library/Developer/CommandLineTools/SDKs/MacOSX.sdk/usr/include
more utmpx.h
......
struct utmpx {
    char ut_user[_UTX_USERSIZE];    /* login name */
    char ut_id[_UTX_IDSIZE];    /* id */
    char ut_line[_UTX_LINESIZE];    /* tty name */
    pid_t ut_pid;           /* process id creating the entry */
    short ut_type;          /* type of this entry */
    struct timeval ut_tv;       /* time entry was created */
    char ut_host[_UTX_HOSTSIZE];    /* host name */
    __uint32_t ut_pad[16];      /* reserved for future use */
};

#if !defined(_POSIX_C_SOURCE) || defined(_DARWIN_C_SOURCE)
struct lastlogx {
    struct timeval ll_tv;       /* time entry was created */
    char ll_line[_UTX_LINESIZE];    /* tty name */
    char ll_host[_UTX_HOSTSIZE];    /* host name */
};
#endif /* !_POSIX_C_SOURCE || _DARWIN_C_SOURCE */

__BEGIN_DECLS

void    endutxent(void);

#if !defined(_POSIX_C_SOURCE) || defined(_DARWIN_C_SOURCE)
void    endutxent_wtmp(void) __OSX_AVAILABLE_STARTING(__MAC_10_5, __IPHONE_2_0);
struct lastlogx *
    getlastlogx(uid_t, struct lastlogx *) __OSX_AVAILABLE_STARTING(__MAC_10_5, __IPHONE_2_0);
struct lastlogx *
    getlastlogxbyname(const char*, struct lastlogx *)__OSX_AVAILABLE_STARTING(__MAC_10_5, __IPHONE_2_0);
struct utmp;    /* forward reference */
void    getutmp(const struct utmpx *, struct utmp *) __OSX_AVAILABLE_BUT_DEPRECATED(__MAC_10_5, __MAC_10_9, __IPHONE_2_0, __IPHONE_7_0) __WATCHOS_PROHIBITED __TVOS_PROHIBITED;
void    getutmpx(const struct utmp *, struct utmpx *) __OSX_AVAILABLE_BUT_DEPRECATED(__MAC_10_5, __MAC_10_9, __IPHONE_2_0, __IPHONE_7_0) __WATCHOS_PROHIBITED __TVOS_PROHIBITED;
#endif /* !_POSIX_C_SOURCE || _DARWIN_C_SOURCE */

struct utmpx *
    getutxent(void);

#if !defined(_POSIX_C_SOURCE) || defined(_DARWIN_C_SOURCE)
struct utmpx *
    getutxent_wtmp(void) __OSX_AVAILABLE_STARTING(__MAC_10_5, __IPHONE_2_0);
#endif /* !_POSIX_C_SOURCE || _DARWIN_C_SOURCE */

struct utmpx *
    getutxid(const struct utmpx *);
struct utmpx *
    getutxline(const struct utmpx *);
struct utmpx *
    pututxline(const struct utmpx *);
void    setutxent(void);

#if !defined(_POSIX_C_SOURCE) || defined(_DARWIN_C_SOURCE)
void    setutxent_wtmp(int) __OSX_AVAILABLE_STARTING(__MAC_10_5, __IPHONE_2_0);
int utmpxname(const char *) __OSX_AVAILABLE_STARTING(__MAC_10_5, __IPHONE_2_0);
int wtmpxname(const char *) __OSX_AVAILABLE_STARTING(__MAC_10_5, __IPHONE_2_0);
#endif /* !_POSIX_C_SOURCE || _DARWIN_C_SOURCE */
......

 略过所有介绍性的内容,直接来看 utmpx 结构保存的登录记录,还有定义的一组函数。

问题3: 如何编写 who

 其实只使用 getutxent() 这个函数就够了,如果没有打开数据库的话,这个函数会自动打开,访问结束后用 endutxent() 关闭数据库。文章未尾,我给出了 C 读取旧版 utmp 的方法,记录的展示未精心设计,仅是为了显示它们的区别(参见 2)。

#include <stdio.h>
#include <utmpx.h>

int main(void)
{
	struct utmpx *tmp;
	while( (tmp = getutxent()) != NULL )
	{
		time_t t = tmp->ut_tv.tv_sec;
		struct tm *p = localtime(&t);
		char buf[55];
		strftime(buf, sizeof(buf), "%b %d %H %M", p);
		
		if( tmp->ut_type == USER_PROCESS )
			printf("%s %s %s %s\n", tmp->ut_user, tmp->ut_line, buf, tmp->ut_host);
	}
	endutxent();
	return 0;
}

 将上述代码编译、运行:

  • macOS
gcc who1.c -o who1
./who1
gaoyuhan console Mar 15 14 17 
gaoyuhan ttys000 Mar 15 14 54 
gaoyuhan ttys001 Mar 15 16 05 
  • HP-UX
yztest pts/1 Mar 15 11 41 11.#.###.2
root pts/3 Mar 15 11 57 11.#.###.38

 代码是不是很简单(对比旧方法),同样的代码两种平台上,编译后都能正常运行,还能自动适应平台间的差异(HP-UX 下会输出第 4 列 IP 地址)。

引用
  1. Perl读取wtmpx日志文件
  2. 访问 utmp (HP-UX 通过)
/*
 * who1.c - a first version of the who program
 * 	open, read UTMP file, and show results
 */
#include <stdio.h>
#include <stdlib.h>
#include <utmpx.h>
#include <fcntl.h>
#include <unistd.h>
 
#define SHOWHOST	/* include remote machine on output */
void show_info( struct utmpx *utbufp );
 
int main()
{
	struct utmpx current_record;	/* read info into here */
	int utmpfd;						/* read from this descriptor */
	int reclen = sizeof(current_record);
	
	if( (utmpfd = open(_PATH_UTMPX, O_RDONLY)) == -1 )
	{
		perror(_PATH_UTMPX);	/* _PATH_UTMPX is in utmpx.h */
		exit(EXIT_FAILURE);
	}
	
	while( read(utmpfd, &current_record, reclen) == reclen )
	{
			show_info(&current_record);
	}		
	close(utmpfd);
	return EXIT_SUCCESS;	/* went ok */
}

/*
 * show info()
 * displays contents of the utmp struct in human readable form
 * * note * these sizes should not be hardwired
 */
void show_info( struct utmpx *utbufp )
{
	printf("%s", utbufp->ut_user);		/* the logname */
	printf(" ");							/* a space */
	printf("%s",utbufp->ut_line);		/* the tty */
	printf(" ");							/* a space */
	printf("%10ld", (utbufp->ut_tv).tv_sec);		/* login time */
	printf(" ");							/* a space */
#ifdef SHOWHOST
	printf("(%s)", utbufp->ut_host);		/* the host */
#endif
	printf("\n");							/* newline */
}

 将上述代码编译、运行:

cc who2.c -o who2
./who2
system boot 1640247484 ()
 run-level 3 1640247484 ()
vxenablevxen  1640247484 ()
bcheckrcbrc1  1640247487 ()
cat  1640247487 ()
clu_spawcspd  1640247487 ()
rm  1640247487 ()
sh  1640247487 ()
fsdaemonfs01  1640247489 ()
fsdaemonfs02  1640247489 ()
fsdaemonfs03  1640247493 ()
sh  1640247493 ()
rc  1640247569 ()
getty console 1640247699 ()
krsd  1640247569 ()
sfd  1640247569 ()
esmd  1640247569 ()
cimservecim1  1640247569 ()
sh  1640247569 ()
p_clientems4  1640247569 ()
iocdsfd  1640247569 ()
clu_dsf_cdin  1640247575 ()
utild  1640247569 ()
init.ohah1  1640247569 ()
init.tfahtfa  1640247569 ()
yztest pts/0 1647245407 ()
yztest pts/1 1647315682 (11.1.215.2)
yztest  1647295070 ()
fts_cs pts/2 1645671079 ()
root pts/3 1647316658 (11.1.215.38)
yztest pts/4 1646352277 ()
yztest pts/5 1646299034 ()
root pts/6 1646287693 ()
yztest pts/7 1645698635 ()
fts_cs pts/8 1645688929 ()
yztest pts/9 1645690790 ()
fts_cs pts/10 1645691774 ()
yztest pts/11 1645692783 ()
  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值