2024年wtmp日志读取,2024年最新C C++研发岗必问30+道高级面试题

img
img

网上学习资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。

需要这份系统化的资料的朋友,可以添加戳这里获取

一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!

因此,我们需要有一个程序,能将wtmp日志解析成上述的格式,才是最终的目标。

使用Go语言读取

auditbeat是elastic开源的一款go语言编写的采集器。其中就有涉及到采集wtmp文件的相关实现。
它首先定义了一个utmp的结构体:

type utmpC struct {
	Type UtType

	// Alignment
	\_ [2]byte

	Pid      int32
	Device   [UT_LINESIZE]byte
	Terminal [4]byte
	Username [UT_NAMESIZE]byte
	Hostname [UT_HOSTSIZE]byte

	ExitStatusTermination int16
	ExitStatusExit        int16

	SessionID int32

	TimeSeconds      int32
	TimeMicroseconds int32

	IP [4]int32

	Unused [20]byte
}

type Utmp struct {
	UtType   UtType
	UtPid    int
	UtLine   string
	UtUser   string
	UtHost   string
	UtTv     time.Time
	UtAddrV6 [4]uint32
}

然后使用ReadNextUtmp函数来遍历wtmp文件:

func ReadNextUtmp(r io.Reader) (\*Utmp, error) {
	utmpC := new(utmpC)

	err := binary.Read(r, byteOrder, utmpC)
	if err != nil {
		return nil, err
	}

	return newUtmp(utmpC), nil
}

newUtmp就是将utmpC转换为utmp格式的一个转换函数。utmpCwtmp存储登录信息的内部二进制格式。
调用逻辑如下:

func readNewInFile(utmpPath string) error{
		f, err := os.Open(utmpPath)
		if err != nil {
			return fmt.Errorf("error opening file %v: %w", utmpFile.Path, err)
		}

		for {
			utmp, err := ReadNextUtmp(f)
			if err != nil && err != io.EOF {
				return fmt.Errorf("error reading entry in UTMP file %v: %w", utmpFile.Path, err)
			}

			if utmp != nil {
				r.log.Debugf("utmp: (ut\_type=%d, ut\_pid=%d, ut\_line=%v, ut\_user=%v, ut\_host=%v, ut\_tv.tv\_sec=%v, ut\_addr\_v6=%v)",
					utmp.UtType, utmp.UtPid, utmp.UtLine, utmp.UtUser, utmp.UtHost, utmp.UtTv, utmp.UtAddrV6)
			} else {
				// Eventually, we have read all UTMP records in the file.
				break
			}
		}
	}
	return nil
}

当然原始代码比这个复杂,我在这里做了一些精简,原始代码里还有一些判断文件滚动的逻辑。具体代码在utmp_c.goutmp.go,感兴趣的可以参考。

使用C语言实现

C语言是提供了utmp相关的系统实现的,这些接口在utmp.h中,主要的接口包含以下这些:

//这个函数相当于上面的ReadNextUtmp,每次获取一条登录信息,如果读到了文件末尾,则返回NULL
//第一次使用该函数会打开文件,文件读完之后可以使用endutent()来关闭文件
struct utmp \*getutent(void);  

//从 utmp 文件中的读写位置逐一往后搜索参数 ut 指定的记录
// 如果ut->ut\_type 为RUN\_LVL, BOOT\_TIME, NEW\_TIME, OLD\_TIME 其中之一则查找与ut->ut\_type 相符的记录
// 若ut->ut\_type为INIT\_PROCESS, LOGIN\_PROCESS, USER\_PROCESS 或DEAD\_PROCESS 其中之一, 则查找与ut->ut\_id相符的记录
struct utmp \*getutid(struct utmp \*ut); 

//从utmp 文件的读写位置逐一往后搜索ut\_type 为USER\_PROCESS 或LOGIN\_PROCESS 的记录, 而且ut\_line 和ut->ut\_line 相符
struct utmp \*getutline(struct utmp \*ut);

//将一个struct utmp结构体写进文件utmp中, 也就是手动写入登录信息
struct utmp \*pututline(struct utmp \*ut);

//打开文件utmp,并且将文件指针指向文件的最开始,相当于fseek到文件开始位置
void setutent(void);

//关闭文件utmp
void endutent(void);

//设定utmp文件所在的路径,默认的路径为宏 \_PATH\_UTMP,利用该函数,可以控制读哪个文件
int utmpname(const char \*file);

上面这些接口中反复出现的结构体struct utmp,其实和上文中go语言实现里的utmpC是一个东西,只不过这里是C语言的定义方式,其结构体如下:

/\* The structure describing an entry in the user accounting database. \*/
struct utmp
{
  short int ut_type;        /\* Type of login. \*/
  pid\_t ut_pid;         /\* Process ID of login process. \*/
  char ut_line[UT_LINESIZE];    /\* Devicename. \*/
  char ut_id[4];        /\* Inittab ID. \*/
  char ut_user[UT_NAMESIZE];    /\* Username. \*/
  char ut_host[UT_HOSTSIZE];    /\* Hostname for remote login. \*/
  struct exit\_status ut_exit;   /\* Exit status of a process marked
 as DEAD\_PROCESS. \*/
/\* 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. \*/
#ifdef \_\_WORDSIZE\_TIME64\_COMPAT32
  int32\_t ut_session;       /\* Session ID, used for windowing. \*/
  struct
  {
    int32\_t tv_sec;     /\* Seconds. \*/
    int32\_t tv_usec;        /\* Microseconds. \*/
  } ut_tv;          /\* Time entry was made. \*/
#else
  long int ut_session;      /\* Session ID, used for windowing. \*/
  struct timeval ut_tv;     /\* Time entry was made. \*/
#endif

  int32\_t ut_addr_v6[4];    /\* Internet address of remote host. \*/
  char __unused[20];        /\* Reserved for future use. \*/
};

这里需要说明的是,ut_type解析出来是数字,它其实是一个enum,对应关系如下:

#define EMPTY 0 /\* No valid user accounting information. \*/

#define RUN\_LVL 1 /\* The system's runlevel. \*/
#define BOOT\_TIME 2 /\* Time of system boot. \*/
#define NEW\_TIME 3 /\* Time after system clock changed. \*/
#define OLD\_TIME 4 /\* Time when system clock changed. \*/

#define INIT\_PROCESS 5 /\* Process spawned by the init process. \*/
#define LOGIN\_PROCESS 6 /\* Session leader of a logged in user. \*/
#define USER\_PROCESS 7 /\* Normal process. \*/
#define DEAD\_PROCESS 8 /\* Terminated process. \*/

#define ACCOUNTING 9

/\* Old Linux name for the EMPTY type. \*/
#define UT\_UNKNOWN EMPTY

有了以上知识储备,就可以使用C语言获取wtmp文件内容了:

#include <utmp.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h> 

char \*ntop(int32\_t ip_addr)
{
    int addr_1 = ip_addr % 256;        
    ip_addr = ip_addr / 256;
        int addr_2 = ip_addr % 256;         
        ip_addr  = ip_addr / 256;
        int addr_3 = ip_addr % 256;        
        ip_addr  = ip_addr  / 256;
        int addr_4 = ip_addr % 256;


![img](https://img-blog.csdnimg.cn/img_convert/24f60687a0c1ed7b6875407ccbbabcf1.png)
![img](https://img-blog.csdnimg.cn/img_convert/be9393cee3d4c5a2eb80af2903292ea5.png)

**既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,涵盖了95%以上C C++开发知识点,真正体系化!**

**由于文件比较多,这里只是将部分目录截图出来,全套包含大厂面经、学习笔记、源码讲义、实战项目、大纲路线、讲解视频,并且后续会持续更新**

**[如果你需要这些资料,可以戳这里获取](https://bbs.csdn.net/topics/618668825)**

-zIXoUPEa-1715587407318)]

**既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,涵盖了95%以上C C++开发知识点,真正体系化!**

**由于文件比较多,这里只是将部分目录截图出来,全套包含大厂面经、学习笔记、源码讲义、实战项目、大纲路线、讲解视频,并且后续会持续更新**

**[如果你需要这些资料,可以戳这里获取](https://bbs.csdn.net/topics/618668825)**

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值