pututxline.c revision 202530
1/*-
2 * Copyright (c) 2010 Ed Schouten <ed@FreeBSD.org>
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 * 1. Redistributions of source code must retain the above copyright
9 *    notice, this list of conditions and the following disclaimer.
10 * 2. Redistributions in binary form must reproduce the above copyright
11 *    notice, this list of conditions and the following disclaimer in the
12 *    documentation and/or other materials provided with the distribution.
13 *
14 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
15 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17 * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
18 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24 * SUCH DAMAGE.
25 */
26
27#include <sys/cdefs.h>
28__FBSDID("$FreeBSD: head/lib/libc/gen/pututxline.c 202530 2010-01-17 21:40:05Z ed $");
29
30#include "namespace.h"
31#include <sys/endian.h>
32#include <sys/stat.h>
33#include <sys/uio.h>
34#include <fcntl.h>
35#include <stdio.h>
36#include <string.h>
37#include <unistd.h>
38#include <utmpx.h>
39#include "utxdb.h"
40#include "un-namespace.h"
41
42static FILE *
43futx_open(const char *file)
44{
45	int fd;
46	FILE *fp;
47	struct stat sb;
48
49	fd = _open(file, O_CREAT|O_RDWR|O_EXLOCK, 0644);
50	if (fd < 0)
51		return (NULL);
52
53	/* Safety check: never use broken files. */
54	if (_fstat(fd, &sb) != -1 && sb.st_size % sizeof(struct futx) != 0) {
55		_close(fd);
56		return (NULL);
57	}
58
59	fp = fdopen(fd, "r+");
60	if (fp == NULL) {
61		_close(fd);
62		return (NULL);
63	}
64
65	return (fp);
66}
67
68static void
69utx_active_add(const struct futx *fu)
70{
71	FILE *fp;
72	struct futx fe;
73	off_t partial = -1;
74
75	/*
76	 * Register user login sessions.  Overwrite entries of sessions
77	 * that have already been terminated.
78	 */
79	fp = futx_open(_PATH_UTX_ACTIVE);
80	if (fp == NULL)
81		return;
82	while (fread(&fe, sizeof fe, 1, fp) == 1) {
83		switch (fe.fu_type) {
84		case USER_PROCESS:
85		case INIT_PROCESS:
86		case LOGIN_PROCESS:
87		case DEAD_PROCESS:
88			/* Overwrite when ut_id matches. */
89			if (memcmp(fu->fu_id, fe.fu_id, sizeof fe.fu_id) == 0) {
90				fseeko(fp, -(off_t)sizeof fe, SEEK_CUR);
91				goto exact;
92			}
93			if (fe.fu_type != DEAD_PROCESS)
94				break;
95			/* FALLTHROUGH */
96		default:
97			/* Allow us to overwrite unused records. */
98			if (partial == -1)
99				partial = ftello(fp) - (off_t)sizeof fe;
100			break;
101		}
102	}
103
104	/*
105	 * No exact match found.  Use the partial match.  If no partial
106	 * match was found, just append a new record.
107	 */
108	if (partial != -1)
109		fseeko(fp, partial, SEEK_SET);
110exact:
111	fwrite(fu, sizeof *fu, 1, fp);
112	fclose(fp);
113}
114
115static int
116utx_active_remove(struct futx *fu)
117{
118	FILE *fp;
119	struct futx fe;
120
121	/*
122	 * Remove user login sessions, having the same ut_id.
123	 */
124	fp = futx_open(_PATH_UTX_ACTIVE);
125	if (fp == NULL)
126		return (0);
127	while (fread(&fe, sizeof fe, 1, fp) == 1) {
128		switch (fe.fu_type) {
129		case USER_PROCESS:
130		case INIT_PROCESS:
131		case LOGIN_PROCESS:
132			if (memcmp(fu->fu_id, fe.fu_id, sizeof fe.fu_id) != 0)
133				continue;
134
135			/*
136			 * Prevent login sessions from having a negative
137			 * timespan.
138			 */
139			if (be64toh(fu->fu_tv) < be64toh(fe.fu_tv))
140				fu->fu_tv = fe.fu_tv;
141
142			/* Terminate session. */
143			fseeko(fp, -(off_t)sizeof fe, SEEK_CUR);
144			fwrite(fu, sizeof *fu, 1, fp);
145			fclose(fp);
146			return (0);
147		}
148	}
149
150	fclose(fp);
151	return (1);
152}
153
154static void
155utx_active_purge(void)
156{
157
158	truncate(_PATH_UTX_ACTIVE, 0);
159}
160
161static void
162utx_lastlogin_add(const struct futx *fu)
163{
164	FILE *fp;
165	struct futx fe;
166
167	/*
168	 * Write an entry to lastlogin.  Overwrite the entry if the
169	 * current user already has an entry.  If not, append a new
170	 * entry.
171	 */
172	fp = futx_open(_PATH_UTX_LASTLOGIN);
173	if (fp == NULL)
174		return;
175	while (fread(&fe, sizeof fe, 1, fp) == 1) {
176		if (strncmp(fu->fu_user, fe.fu_user, sizeof fe.fu_user) != 0)
177			continue;
178
179		/* Prevent lowering the time value. */
180		if (be64toh(fu->fu_tv) <= be64toh(fe.fu_tv))
181			goto done;
182
183		/* Found a previous lastlogin entry for this user. */
184		fseeko(fp, -(off_t)sizeof fe, SEEK_CUR);
185		break;
186	}
187	fwrite(fu, sizeof *fu, 1, fp);
188done:
189	fclose(fp);
190}
191
192static void
193utx_lastlogin_upgrade(void)
194{
195	int fd;
196	struct stat sb;
197
198	fd = _open(_PATH_UTX_LASTLOGIN, O_RDWR, 0644);
199	if (fd < 0)
200		return;
201
202	/*
203	 * Truncate broken lastlogin files.  In the future we should
204	 * check for older versions of the file format here and try to
205	 * upgrade it.
206	 */
207	if (_fstat(fd, &sb) != -1 && sb.st_size % sizeof(struct futx) != 0)
208		ftruncate(fd, 0);
209	_close(fd);
210}
211
212static void
213utx_log_add(const struct futx *fu)
214{
215	int fd;
216	uint16_t l;
217	struct iovec vec[2];
218
219	/*
220	 * Append an entry to the log file.  We only need to append
221	 * records to this file, so to conserve space, trim any trailing
222	 * zero-bytes.  Prepend a length field, indicating the length of
223	 * the record, excluding the length field itself.
224	 */
225	for (l = sizeof *fu; l > 0 && ((const char *)fu)[l - 1] == '\0'; l--);
226	vec[0].iov_base = &l;
227	vec[0].iov_len = sizeof l;
228	vec[1].iov_base = __DECONST(void *, fu);
229	vec[1].iov_len = l;
230	l = htobe16(l);
231
232	fd = _open(_PATH_UTX_LOG, O_CREAT|O_WRONLY|O_APPEND, 0644);
233	if (fd < 0)
234		return;
235	_writev(fd, vec, 2);
236	_close(fd);
237}
238
239struct utmpx *
240pututxline(const struct utmpx *utmpx)
241{
242	struct futx fu;
243
244	utx_to_futx(utmpx, &fu);
245
246	switch (fu.fu_type) {
247	case BOOT_TIME:
248	case SHUTDOWN_TIME:
249		utx_active_purge();
250		utx_lastlogin_upgrade();
251		break;
252	case OLD_TIME:
253	case NEW_TIME:
254		break;
255	case USER_PROCESS:
256		utx_active_add(&fu);
257		utx_lastlogin_add(&fu);
258		break;
259#if 0 /* XXX: Are these records of any use to us? */
260	case INIT_PROCESS:
261	case LOGIN_PROCESS:
262		utx_active_add(&fu);
263		break;
264#endif
265	case DEAD_PROCESS:
266		if (utx_active_remove(&fu) != 0)
267			return (NULL);
268		break;
269	default:
270		return (NULL);
271	}
272
273	utx_log_add(&fu);
274	return (futx_to_utx(&fu));
275}
276