pututxline.c revision 214134
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 214134 2010-10-21 15:10:35Z 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 int
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 (1);
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	return (0);
114}
115
116static int
117utx_active_remove(struct futx *fu)
118{
119	FILE *fp;
120	struct futx fe;
121
122	/*
123	 * Remove user login sessions, having the same ut_id.
124	 */
125	fp = futx_open(_PATH_UTX_ACTIVE);
126	if (fp == NULL)
127		return (1);
128	while (fread(&fe, sizeof fe, 1, fp) == 1) {
129		switch (fe.fu_type) {
130		case USER_PROCESS:
131		case INIT_PROCESS:
132		case LOGIN_PROCESS:
133			if (memcmp(fu->fu_id, fe.fu_id, sizeof fe.fu_id) != 0)
134				continue;
135
136			/* Terminate session. */
137			fseeko(fp, -(off_t)sizeof fe, SEEK_CUR);
138			fwrite(fu, sizeof *fu, 1, fp);
139			fclose(fp);
140			return (0);
141		}
142	}
143
144	fclose(fp);
145	return (1);
146}
147
148static void
149utx_active_purge(void)
150{
151
152	truncate(_PATH_UTX_ACTIVE, 0);
153}
154
155static int
156utx_lastlogin_add(const struct futx *fu)
157{
158	FILE *fp;
159	struct futx fe;
160
161	/*
162	 * Write an entry to lastlogin.  Overwrite the entry if the
163	 * current user already has an entry.  If not, append a new
164	 * entry.
165	 */
166	fp = futx_open(_PATH_UTX_LASTLOGIN);
167	if (fp == NULL)
168		return (1);
169	while (fread(&fe, sizeof fe, 1, fp) == 1) {
170		if (strncmp(fu->fu_user, fe.fu_user, sizeof fe.fu_user) != 0)
171			continue;
172
173		/* Found a previous lastlogin entry for this user. */
174		fseeko(fp, -(off_t)sizeof fe, SEEK_CUR);
175		break;
176	}
177	fwrite(fu, sizeof *fu, 1, fp);
178	fclose(fp);
179	return (0);
180}
181
182static void
183utx_lastlogin_upgrade(void)
184{
185	int fd;
186	struct stat sb;
187
188	fd = _open(_PATH_UTX_LASTLOGIN, O_RDWR, 0644);
189	if (fd < 0)
190		return;
191
192	/*
193	 * Truncate broken lastlogin files.  In the future we should
194	 * check for older versions of the file format here and try to
195	 * upgrade it.
196	 */
197	if (_fstat(fd, &sb) != -1 && sb.st_size % sizeof(struct futx) != 0)
198		ftruncate(fd, 0);
199	_close(fd);
200}
201
202static int
203utx_log_add(const struct futx *fu)
204{
205	int fd;
206	uint16_t l;
207	struct iovec vec[2];
208
209	/*
210	 * Append an entry to the log file.  We only need to append
211	 * records to this file, so to conserve space, trim any trailing
212	 * zero-bytes.  Prepend a length field, indicating the length of
213	 * the record, excluding the length field itself.
214	 */
215	for (l = sizeof *fu; l > 0 && ((const char *)fu)[l - 1] == '\0'; l--);
216	vec[0].iov_base = &l;
217	vec[0].iov_len = sizeof l;
218	vec[1].iov_base = __DECONST(void *, fu);
219	vec[1].iov_len = l;
220	l = htobe16(l);
221
222	fd = _open(_PATH_UTX_LOG, O_CREAT|O_WRONLY|O_APPEND, 0644);
223	if (fd < 0)
224		return (1);
225	_writev(fd, vec, 2);
226	_close(fd);
227	return (0);
228}
229
230struct utmpx *
231pututxline(const struct utmpx *utmpx)
232{
233	struct futx fu;
234	int bad = 0;
235
236	utx_to_futx(utmpx, &fu);
237
238	switch (fu.fu_type) {
239	case BOOT_TIME:
240	case SHUTDOWN_TIME:
241		utx_active_purge();
242		utx_lastlogin_upgrade();
243		break;
244	case OLD_TIME:
245	case NEW_TIME:
246		break;
247	case USER_PROCESS:
248		bad |= utx_active_add(&fu);
249		bad |= utx_lastlogin_add(&fu);
250		break;
251#if 0 /* XXX: Are these records of any use to us? */
252	case INIT_PROCESS:
253	case LOGIN_PROCESS:
254		bad |= utx_active_add(&fu);
255		break;
256#endif
257	case DEAD_PROCESS:
258		/*
259		 * In case writing a logout entry fails, never attempt
260		 * to write it to utx.log.  The logout entry's ut_id
261		 * might be invalid.
262		 */
263		if (utx_active_remove(&fu) != 0)
264			return (NULL);
265		break;
266	default:
267		return (NULL);
268	}
269
270	bad |= utx_log_add(&fu);
271	return (bad ? NULL : futx_to_utx(&fu));
272}
273