文章目录

wtop

原理

/var/run/utmputmp
           #define UT_LINESIZE      32
           #define UT_NAMESIZE      32
           #define UT_HOSTSIZE     256

           struct exit_status {              /* Type for ut_exit, below */
               short e_termination;          /* Process termination status */
               short e_exit;                 /* Process exit status */
           };

           struct utmp {
               short   ut_type;              /* Type of record */
               pid_t   ut_pid;               /* PID of login process */
               char    ut_line[UT_LINESIZE]; /* Device name of tty - "/dev/" */
               char    ut_id[4];             /* Terminal name suffix,
                                                or inittab(5) ID */
               char    ut_user[UT_NAMESIZE]; /* Username */
               char    ut_host[UT_HOSTSIZE]; /* Hostname for remote login, or
                                                kernel version for run-level
                                                messages */
               struct  exit_status ut_exit;  /* Exit status of a process
                                                marked as DEAD_PROCESS; not
                                                used by Linux init(1) */
               /* The ut_session and ut_tv fields must be the same size when
                  compiled 32- and 64-bit.  This allows data files and shared
                  memory to be shared between 32- and 64-bit applications. */
           #if __WORDSIZE == 64 && defined __WORDSIZE_COMPAT32
               int32_t ut_session;           /* Session ID (getsid(2)),
                                                used for windowing */
               struct {
                   int32_t tv_sec;           /* Seconds */
                   int32_t tv_usec;          /* Microseconds */
               } ut_tv;                      /* Time entry was made */
           #else
                long   ut_session;           /* Session ID */
                struct timeval ut_tv;        /* Time entry was made */
           #endif

               int32_t ut_addr_v6[4];        /* Internet address of remote
                                                host; IPv4 address uses
                                                just ut_addr_v6[0] */
               char __unused[20];            /* Reserved for future use */
           };

Go代码

package main

import (
	"encoding/binary"
	"fmt"
	"io"
	"os"
)

const (
	UT_LINESIZE = 32
	UT_NAMESIZE = 32
	UT_HOSTSIZE = 256
)

type ExitStatus struct {
	Termination int16
	Exit        int16
}

type TimeVal struct {
	Sec  int32
	Usec int32
}

type Utmp struct {
	// https://man7.org/linux/man-pages/man5/utmp.5.html
	Type     int16             /* Type of record */
	_        [2]byte           /* 内存对齐,不可少*/
	Pid      int32             /* PID of login process */
	Device   [UT_LINESIZE]byte /* Device name of tty - "/dev/" */
	Id       [4]byte           /* Terminal name suffix, or inittab(5) ID */
	User     [UT_NAMESIZE]byte /* Username */
	Host     [UT_HOSTSIZE]byte /* Hostname for remote login, or kernel version for run-level messages */
	Exit     ExitStatus        /* Exit status of a process marked as DEAD_PROCESS; not used by Linux init(1) */
	Session  int32             /* Session ID (getsid(2)), used for windowing */
	Time     TimeVal           /* Time entry was made */
	AddrV6   [16]byte          /* Internet address of remote host; IPv4 address uses just ut_addr_v6[0] */
	Reserved [20]byte
}

func main() {
	f, err := os.Open("/var/run/utmp")
	if err != nil {
		panic("open /var/run/tmp failed: %s" + err.Error())
	}
	var utmps []*Utmp
	for {
		utmp := new(Utmp)
		err = binary.Read(f, binary.LittleEndian, utmp)
		if err == io.EOF {
			break
		} else if err != nil {
			panic("read /var/run/tmp failed: " + err.Error())
		}
		// #define USER_PROCESS	7	/* Normal process.  */
		if utmp.Type != 7 || utmp.User[0] == 0 {
			// 忽略特殊用户,参考 procps-ng/contrib/utmp.c 源码
			continue
		}
		utmps = append(utmps, utmp)
	}

	fmt.Printf("%s\t%s\t%s\n", "USER", "TTY", "FROM")
	for _, utmp := range utmps {
		fmt.Printf("%s\t%s\t%s\n", string(utmp.User[:]), string(utmp.Device[:]), string(utmp.Host[:]))
	}
}

测试验证

wgo