1202188Sed/*-
2202188Sed * Copyright (c) 2010 Ed Schouten <ed@FreeBSD.org>
3202188Sed * All rights reserved.
4202188Sed *
5202188Sed * Redistribution and use in source and binary forms, with or without
6202188Sed * modification, are permitted provided that the following conditions
7202188Sed * are met:
8202188Sed * 1. Redistributions of source code must retain the above copyright
9202188Sed *    notice, this list of conditions and the following disclaimer.
10202188Sed * 2. Redistributions in binary form must reproduce the above copyright
11202188Sed *    notice, this list of conditions and the following disclaimer in the
12202188Sed *    documentation and/or other materials provided with the distribution.
13202188Sed *
14202188Sed * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
15202188Sed * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16202188Sed * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17202188Sed * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
18202188Sed * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19202188Sed * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20202188Sed * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21202188Sed * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22202188Sed * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23202188Sed * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24202188Sed * SUCH DAMAGE.
25202188Sed */
26202188Sed
27202188Sed#include <sys/cdefs.h>
28202188Sed__FBSDID("$FreeBSD$");
29202188Sed
30202188Sed#include "namespace.h"
31202188Sed#include <sys/endian.h>
32202188Sed#include <sys/stat.h>
33202188Sed#include <sys/uio.h>
34215310Sed#include <errno.h>
35202188Sed#include <fcntl.h>
36202188Sed#include <stdio.h>
37202188Sed#include <string.h>
38202188Sed#include <unistd.h>
39202188Sed#include <utmpx.h>
40202188Sed#include "utxdb.h"
41202188Sed#include "un-namespace.h"
42202188Sed
43202188Sedstatic FILE *
44202188Sedfutx_open(const char *file)
45202188Sed{
46219045Sed	FILE *fp;
47218846Sed	struct stat sb;
48202188Sed	int fd;
49202188Sed
50261813Sjilles	fd = _open(file, O_CREAT|O_RDWR|O_EXLOCK|O_CLOEXEC, 0644);
51202188Sed	if (fd < 0)
52202188Sed		return (NULL);
53202188Sed
54202188Sed	/* Safety check: never use broken files. */
55202188Sed	if (_fstat(fd, &sb) != -1 && sb.st_size % sizeof(struct futx) != 0) {
56202188Sed		_close(fd);
57218846Sed		errno = EFTYPE;
58202188Sed		return (NULL);
59202188Sed	}
60223576Sed
61202188Sed	fp = fdopen(fd, "r+");
62202188Sed	if (fp == NULL) {
63202188Sed		_close(fd);
64202188Sed		return (NULL);
65202188Sed	}
66202188Sed	return (fp);
67202188Sed}
68202188Sed
69214134Sedstatic int
70202188Sedutx_active_add(const struct futx *fu)
71202188Sed{
72219045Sed	FILE *fp;
73218846Sed	struct futx fe;
74219045Sed	off_t partial;
75218846Sed	int error, ret;
76202188Sed
77219045Sed	partial = -1;
78219045Sed	ret = 0;
79219045Sed
80202188Sed	/*
81202188Sed	 * Register user login sessions.  Overwrite entries of sessions
82202188Sed	 * that have already been terminated.
83202188Sed	 */
84202188Sed	fp = futx_open(_PATH_UTX_ACTIVE);
85202188Sed	if (fp == NULL)
86218846Sed		return (-1);
87218846Sed	while (fread(&fe, sizeof(fe), 1, fp) == 1) {
88202188Sed		switch (fe.fu_type) {
89202188Sed		case USER_PROCESS:
90202188Sed		case INIT_PROCESS:
91202188Sed		case LOGIN_PROCESS:
92202188Sed		case DEAD_PROCESS:
93202188Sed			/* Overwrite when ut_id matches. */
94218846Sed			if (memcmp(fu->fu_id, fe.fu_id, sizeof(fe.fu_id)) ==
95218846Sed			    0) {
96218846Sed				ret = fseeko(fp, -(off_t)sizeof(fe), SEEK_CUR);
97202188Sed				goto exact;
98202188Sed			}
99202188Sed			if (fe.fu_type != DEAD_PROCESS)
100202188Sed				break;
101202188Sed			/* FALLTHROUGH */
102202188Sed		default:
103202188Sed			/* Allow us to overwrite unused records. */
104218846Sed			if (partial == -1) {
105218846Sed				partial = ftello(fp);
106223576Sed				/*
107218846Sed				 * Distinguish errors from valid values so we
108218846Sed				 * don't overwrite good data by accident.
109218846Sed				 */
110218846Sed				if (partial != -1)
111218846Sed					partial -= (off_t)sizeof(fe);
112218846Sed			}
113202188Sed			break;
114202188Sed		}
115202188Sed	}
116218846Sed
117202188Sed	/*
118202188Sed	 * No exact match found.  Use the partial match.  If no partial
119202188Sed	 * match was found, just append a new record.
120202188Sed	 */
121202188Sed	if (partial != -1)
122218846Sed		ret = fseeko(fp, partial, SEEK_SET);
123202188Sedexact:
124218846Sed	if (ret == -1)
125218846Sed		error = errno;
126218846Sed	else if (fwrite(fu, sizeof(*fu), 1, fp) < 1)
127218846Sed		error = errno;
128218846Sed	else
129218846Sed		error = 0;
130202188Sed	fclose(fp);
131218846Sed	errno = error;
132218846Sed	return (error == 0 ? 0 : 1);
133202188Sed}
134202188Sed
135202188Sedstatic int
136202188Sedutx_active_remove(struct futx *fu)
137202188Sed{
138219045Sed	FILE *fp;
139218846Sed	struct futx fe;
140218846Sed	int error, ret;
141202188Sed
142202188Sed	/*
143202188Sed	 * Remove user login sessions, having the same ut_id.
144202188Sed	 */
145202188Sed	fp = futx_open(_PATH_UTX_ACTIVE);
146202188Sed	if (fp == NULL)
147218846Sed		return (-1);
148218846Sed	error = ESRCH;
149218846Sed	ret = -1;
150218846Sed	while (fread(&fe, sizeof(fe), 1, fp) == 1 && ret != 0)
151202188Sed		switch (fe.fu_type) {
152202188Sed		case USER_PROCESS:
153202188Sed		case INIT_PROCESS:
154202188Sed		case LOGIN_PROCESS:
155218846Sed			if (memcmp(fu->fu_id, fe.fu_id, sizeof(fe.fu_id)) != 0)
156202188Sed				continue;
157202188Sed
158202188Sed			/* Terminate session. */
159218846Sed			if (fseeko(fp, -(off_t)sizeof(fe), SEEK_CUR) == -1)
160218846Sed				error = errno;
161218846Sed			else if (fwrite(fu, sizeof(*fu), 1, fp) < 1)
162218846Sed				error = errno;
163218846Sed			else
164218846Sed				ret = 0;
165218846Sed
166202188Sed		}
167202188Sed
168202188Sed	fclose(fp);
169218846Sed	errno = error;
170218846Sed	return (ret);
171202188Sed}
172202188Sed
173202188Sedstatic void
174202188Sedutx_active_purge(void)
175202188Sed{
176202188Sed
177202188Sed	truncate(_PATH_UTX_ACTIVE, 0);
178202188Sed}
179202188Sed
180214134Sedstatic int
181202188Sedutx_lastlogin_add(const struct futx *fu)
182202188Sed{
183218846Sed	struct futx fe;
184202188Sed	FILE *fp;
185218846Sed	int error, ret;
186202188Sed
187218846Sed	ret = 0;
188218846Sed
189202188Sed	/*
190202188Sed	 * Write an entry to lastlogin.  Overwrite the entry if the
191202188Sed	 * current user already has an entry.  If not, append a new
192202188Sed	 * entry.
193202188Sed	 */
194202188Sed	fp = futx_open(_PATH_UTX_LASTLOGIN);
195202188Sed	if (fp == NULL)
196218846Sed		return (-1);
197202188Sed	while (fread(&fe, sizeof fe, 1, fp) == 1) {
198202188Sed		if (strncmp(fu->fu_user, fe.fu_user, sizeof fe.fu_user) != 0)
199202188Sed			continue;
200218846Sed
201202188Sed		/* Found a previous lastlogin entry for this user. */
202218846Sed		ret = fseeko(fp, -(off_t)sizeof fe, SEEK_CUR);
203202188Sed		break;
204202188Sed	}
205218846Sed	if (ret == -1)
206218846Sed		error = errno;
207218846Sed	else if (fwrite(fu, sizeof *fu, 1, fp) < 1) {
208218846Sed		error = errno;
209218846Sed		ret = -1;
210218846Sed	}
211202188Sed	fclose(fp);
212218846Sed	errno = error;
213218846Sed	return (ret);
214202188Sed}
215202188Sed
216202188Sedstatic void
217202188Sedutx_lastlogin_upgrade(void)
218202188Sed{
219218846Sed	struct stat sb;
220202188Sed	int fd;
221202188Sed
222261813Sjilles	fd = _open(_PATH_UTX_LASTLOGIN, O_RDWR|O_CLOEXEC, 0644);
223202188Sed	if (fd < 0)
224202188Sed		return;
225202188Sed
226202188Sed	/*
227202188Sed	 * Truncate broken lastlogin files.  In the future we should
228202188Sed	 * check for older versions of the file format here and try to
229202188Sed	 * upgrade it.
230202188Sed	 */
231202188Sed	if (_fstat(fd, &sb) != -1 && sb.st_size % sizeof(struct futx) != 0)
232202188Sed		ftruncate(fd, 0);
233202188Sed	_close(fd);
234202188Sed}
235202188Sed
236214134Sedstatic int
237202188Sedutx_log_add(const struct futx *fu)
238202188Sed{
239218846Sed	struct iovec vec[2];
240218846Sed	int error, fd;
241202188Sed	uint16_t l;
242202188Sed
243202188Sed	/*
244202188Sed	 * Append an entry to the log file.  We only need to append
245202188Sed	 * records to this file, so to conserve space, trim any trailing
246202188Sed	 * zero-bytes.  Prepend a length field, indicating the length of
247202188Sed	 * the record, excluding the length field itself.
248202188Sed	 */
249218846Sed	for (l = sizeof(*fu); l > 0 && ((const char *)fu)[l - 1] == '\0'; l--) ;
250202188Sed	vec[0].iov_base = &l;
251218846Sed	vec[0].iov_len = sizeof(l);
252202188Sed	vec[1].iov_base = __DECONST(void *, fu);
253202188Sed	vec[1].iov_len = l;
254202188Sed	l = htobe16(l);
255202188Sed
256261813Sjilles	fd = _open(_PATH_UTX_LOG, O_CREAT|O_WRONLY|O_APPEND|O_CLOEXEC, 0644);
257202188Sed	if (fd < 0)
258218846Sed		return (-1);
259218846Sed	if (_writev(fd, vec, 2) == -1)
260218846Sed		error = errno;
261218846Sed	else
262218846Sed		error = 0;
263202188Sed	_close(fd);
264218846Sed	errno = error;
265218846Sed	return (error == 0 ? 0 : 1);
266202188Sed}
267202188Sed
268202188Sedstruct utmpx *
269202188Sedpututxline(const struct utmpx *utmpx)
270202188Sed{
271202188Sed	struct futx fu;
272219045Sed	int bad;
273202188Sed
274219045Sed	bad = 0;
275219045Sed
276202188Sed	utx_to_futx(utmpx, &fu);
277218846Sed
278202188Sed	switch (fu.fu_type) {
279202188Sed	case BOOT_TIME:
280202188Sed	case SHUTDOWN_TIME:
281202188Sed		utx_active_purge();
282202188Sed		utx_lastlogin_upgrade();
283202188Sed		break;
284202188Sed	case OLD_TIME:
285202188Sed	case NEW_TIME:
286202188Sed		break;
287202188Sed	case USER_PROCESS:
288214134Sed		bad |= utx_active_add(&fu);
289214134Sed		bad |= utx_lastlogin_add(&fu);
290202188Sed		break;
291202188Sed#if 0 /* XXX: Are these records of any use to us? */
292202188Sed	case INIT_PROCESS:
293202188Sed	case LOGIN_PROCESS:
294214134Sed		bad |= utx_active_add(&fu);
295202188Sed		break;
296202188Sed#endif
297202188Sed	case DEAD_PROCESS:
298214134Sed		/*
299214134Sed		 * In case writing a logout entry fails, never attempt
300214134Sed		 * to write it to utx.log.  The logout entry's ut_id
301214134Sed		 * might be invalid.
302214134Sed		 */
303202188Sed		if (utx_active_remove(&fu) != 0)
304202188Sed			return (NULL);
305202188Sed		break;
306202188Sed	default:
307218846Sed		errno = EINVAL;
308202188Sed		return (NULL);
309202188Sed	}
310202188Sed
311214134Sed	bad |= utx_log_add(&fu);
312214134Sed	return (bad ? NULL : futx_to_utx(&fu));
313202188Sed}
314