summaryrefslogtreecommitdiffhomepage
path: root/libbb/utmp.c
blob: d6ba336e3e36725544ce41ab8094039e68a4377b (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
/* vi: set sw=4 ts=4: */
/*
 * utmp/wtmp support routines.
 *
 * Copyright (C) 2010 Denys Vlasenko
 *
 * Licensed under GPL version 2, see file LICENSE in this tarball for details.
 */
#include "libbb.h"
#include <utmp.h>

static void touch(const char *filename)
{
	if (access(filename, R_OK | W_OK) == -1)
		close(open(filename, O_WRONLY | O_CREAT, 0664));
}

// TODO: move to libbb
static const char* skip_dev_pfx(const char *tty_name)
{
	if (strncmp(tty_name, "/dev/", 5) == 0)
		tty_name += 5;
	return tty_name;
}

void FAST_FUNC write_new_utmp(pid_t pid, int new_type, const char *tty_name, const char *username, const char *hostname)
{
	struct utmp utent;
	char *id;
	unsigned width;

	memset(&utent, 0, sizeof(utent));
	utent.ut_pid = pid;
	utent.ut_type = new_type;
	tty_name = skip_dev_pfx(tty_name);
	safe_strncpy(utent.ut_line, tty_name, sizeof(utent.ut_line));
	if (username)
		safe_strncpy(utent.ut_user, username, sizeof(utent.ut_user));
	if (hostname)
		safe_strncpy(utent.ut_host, hostname, sizeof(utent.ut_host));
	utent.ut_tv.tv_sec = time(NULL);

	/* Invent our own ut_id. ut_id is only 4 chars wide.
	 * Try to fit something remotely meaningful... */
	id = utent.ut_id;
	width = sizeof(utent.ut_id);
	if (tty_name[0] == 'p') {
		/* if "ptyXXX", map to "pXXX" */
		/* if "pts/XX", map to "p/XX" */
		*id++ = 'p';
		width--;
	} /* else: usually it's "ttyXXXX", map to "XXXX" */
	if (strlen(tty_name) > 3)
		tty_name += 3;
	strncpy(id, tty_name, width);

	touch(_PATH_UTMP);
	//utmpname(_PATH_UTMP);
	setutent();
	/* Append new one (hopefully, unless we collide on ut_id) */
	pututline(&utent);
	endutent();

#if ENABLE_FEATURE_WTMP
	/* "man utmp" says wtmp file should *not* be created automagically */
	/*touch(bb_path_wtmp_file);*/
	updwtmp(bb_path_wtmp_file, &utent);
#endif
}

/*
 * Read "man utmp" to make sense out of it.
 */
void FAST_FUNC update_utmp(pid_t pid, int new_type, const char *tty_name, const char *username, const char *hostname)
{
	struct utmp utent;
	struct utmp *utp;

	touch(_PATH_UTMP);
	//utmpname(_PATH_UTMP);
	setutent();

	/* Did init/getty/telnetd/sshd/... create an entry for us?
	 * It should be (new_type-1), but we'd also reuse
	 * any other potentially stale xxx_PROCESS entry */
	while ((utp = getutent()) != NULL) {
		if (utp->ut_pid == pid
		// && ut->ut_line[0]
		 && utp->ut_id[0] /* must have nonzero id */
		 && (  utp->ut_type == INIT_PROCESS
		    || utp->ut_type == LOGIN_PROCESS
		    || utp->ut_type == USER_PROCESS
		    || utp->ut_type == DEAD_PROCESS
		    )
		) {
			if (utp->ut_type >= new_type) {
				/* Stale record. Nuke hostname */
				memset(utp->ut_host, 0, sizeof(utp->ut_host));
			}
			/* NB: pututline (see later) searches for matching utent
			 * using getutid(utent) - we must not change ut_id
			 * if we want *exactly this* record to be overwritten!
			 */
			break;
		}
	}
	//endutent(); - no need, pututline can deal with (and actually likes)
	//the situation when utmp file is positioned on found record

	if (!utp) {
		if (new_type != DEAD_PROCESS)
			write_new_utmp(pid, new_type, tty_name, username, hostname);
		else
			endutent();
		return;
	}

	/* Make a copy. We can't use *utp, pututline's internal getutid
	 * will overwrite it before it is used! */
	utent = *utp;

	utent.ut_type = new_type;
	if (tty_name)
		safe_strncpy(utent.ut_line, skip_dev_pfx(tty_name), sizeof(utent.ut_line));
	if (username)
		safe_strncpy(utent.ut_user, username, sizeof(utent.ut_user));
	if (hostname)
		safe_strncpy(utent.ut_host, hostname, sizeof(utent.ut_host));
	utent.ut_tv.tv_sec = time(NULL);

	/* Update, or append new one */
	//setutent();
	pututline(&utent);
	endutent();

#if ENABLE_FEATURE_WTMP
	/* "man utmp" says wtmp file should *not* be created automagically */
	/*touch(bb_path_wtmp_file);*/
	updwtmp(bb_path_wtmp_file, &utent);
#endif
}