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: releng/10.3/lib/libc/gen/pututxline.c 249593 2013-04-17 21:08:15Z jilles $");
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
50241046Sjilles	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) {
89226846Sed		case BOOT_TIME:
90226846Sed			/* Leave these intact. */
91226846Sed			break;
92202188Sed		case USER_PROCESS:
93202188Sed		case INIT_PROCESS:
94202188Sed		case LOGIN_PROCESS:
95202188Sed		case DEAD_PROCESS:
96202188Sed			/* Overwrite when ut_id matches. */
97218846Sed			if (memcmp(fu->fu_id, fe.fu_id, sizeof(fe.fu_id)) ==
98218846Sed			    0) {
99218846Sed				ret = fseeko(fp, -(off_t)sizeof(fe), SEEK_CUR);
100202188Sed				goto exact;
101202188Sed			}
102202188Sed			if (fe.fu_type != DEAD_PROCESS)
103202188Sed				break;
104202188Sed			/* FALLTHROUGH */
105202188Sed		default:
106202188Sed			/* Allow us to overwrite unused records. */
107218846Sed			if (partial == -1) {
108218846Sed				partial = ftello(fp);
109223576Sed				/*
110218846Sed				 * Distinguish errors from valid values so we
111218846Sed				 * don't overwrite good data by accident.
112218846Sed				 */
113218846Sed				if (partial != -1)
114218846Sed					partial -= (off_t)sizeof(fe);
115218846Sed			}
116202188Sed			break;
117202188Sed		}
118202188Sed	}
119218846Sed
120202188Sed	/*
121202188Sed	 * No exact match found.  Use the partial match.  If no partial
122202188Sed	 * match was found, just append a new record.
123202188Sed	 */
124202188Sed	if (partial != -1)
125218846Sed		ret = fseeko(fp, partial, SEEK_SET);
126202188Sedexact:
127218846Sed	if (ret == -1)
128218846Sed		error = errno;
129218846Sed	else if (fwrite(fu, sizeof(*fu), 1, fp) < 1)
130218846Sed		error = errno;
131218846Sed	else
132218846Sed		error = 0;
133202188Sed	fclose(fp);
134249593Sjilles	if (error != 0)
135249593Sjilles		errno = error;
136218846Sed	return (error == 0 ? 0 : 1);
137202188Sed}
138202188Sed
139202188Sedstatic int
140202188Sedutx_active_remove(struct futx *fu)
141202188Sed{
142219045Sed	FILE *fp;
143218846Sed	struct futx fe;
144218846Sed	int error, ret;
145202188Sed
146202188Sed	/*
147202188Sed	 * Remove user login sessions, having the same ut_id.
148202188Sed	 */
149202188Sed	fp = futx_open(_PATH_UTX_ACTIVE);
150202188Sed	if (fp == NULL)
151218846Sed		return (-1);
152218846Sed	error = ESRCH;
153218846Sed	ret = -1;
154218846Sed	while (fread(&fe, sizeof(fe), 1, fp) == 1 && ret != 0)
155202188Sed		switch (fe.fu_type) {
156202188Sed		case USER_PROCESS:
157202188Sed		case INIT_PROCESS:
158202188Sed		case LOGIN_PROCESS:
159218846Sed			if (memcmp(fu->fu_id, fe.fu_id, sizeof(fe.fu_id)) != 0)
160202188Sed				continue;
161202188Sed
162202188Sed			/* Terminate session. */
163218846Sed			if (fseeko(fp, -(off_t)sizeof(fe), SEEK_CUR) == -1)
164218846Sed				error = errno;
165218846Sed			else if (fwrite(fu, sizeof(*fu), 1, fp) < 1)
166218846Sed				error = errno;
167218846Sed			else
168218846Sed				ret = 0;
169218846Sed
170202188Sed		}
171202188Sed
172202188Sed	fclose(fp);
173249593Sjilles	if (ret != 0)
174249593Sjilles		errno = error;
175218846Sed	return (ret);
176202188Sed}
177202188Sed
178202188Sedstatic void
179226846Sedutx_active_init(const struct futx *fu)
180226846Sed{
181226846Sed	int fd;
182226846Sed
183226846Sed	/* Initialize utx.active with a single BOOT_TIME record. */
184226846Sed	fd = _open(_PATH_UTX_ACTIVE, O_CREAT|O_RDWR|O_TRUNC, 0644);
185226846Sed	if (fd < 0)
186226846Sed		return;
187226846Sed	_write(fd, fu, sizeof(*fu));
188226846Sed	_close(fd);
189226846Sed}
190226846Sed
191226846Sedstatic void
192202188Sedutx_active_purge(void)
193202188Sed{
194202188Sed
195202188Sed	truncate(_PATH_UTX_ACTIVE, 0);
196202188Sed}
197202188Sed
198214134Sedstatic int
199202188Sedutx_lastlogin_add(const struct futx *fu)
200202188Sed{
201218846Sed	struct futx fe;
202202188Sed	FILE *fp;
203218846Sed	int error, ret;
204202188Sed
205218846Sed	ret = 0;
206218846Sed
207202188Sed	/*
208202188Sed	 * Write an entry to lastlogin.  Overwrite the entry if the
209202188Sed	 * current user already has an entry.  If not, append a new
210202188Sed	 * entry.
211202188Sed	 */
212202188Sed	fp = futx_open(_PATH_UTX_LASTLOGIN);
213202188Sed	if (fp == NULL)
214218846Sed		return (-1);
215202188Sed	while (fread(&fe, sizeof fe, 1, fp) == 1) {
216202188Sed		if (strncmp(fu->fu_user, fe.fu_user, sizeof fe.fu_user) != 0)
217202188Sed			continue;
218218846Sed
219202188Sed		/* Found a previous lastlogin entry for this user. */
220218846Sed		ret = fseeko(fp, -(off_t)sizeof fe, SEEK_CUR);
221202188Sed		break;
222202188Sed	}
223218846Sed	if (ret == -1)
224218846Sed		error = errno;
225218846Sed	else if (fwrite(fu, sizeof *fu, 1, fp) < 1) {
226218846Sed		error = errno;
227218846Sed		ret = -1;
228218846Sed	}
229202188Sed	fclose(fp);
230249593Sjilles	if (ret == -1)
231249593Sjilles		errno = error;
232218846Sed	return (ret);
233202188Sed}
234202188Sed
235202188Sedstatic void
236202188Sedutx_lastlogin_upgrade(void)
237202188Sed{
238218846Sed	struct stat sb;
239202188Sed	int fd;
240202188Sed
241241046Sjilles	fd = _open(_PATH_UTX_LASTLOGIN, O_RDWR|O_CLOEXEC, 0644);
242202188Sed	if (fd < 0)
243202188Sed		return;
244202188Sed
245202188Sed	/*
246202188Sed	 * Truncate broken lastlogin files.  In the future we should
247202188Sed	 * check for older versions of the file format here and try to
248202188Sed	 * upgrade it.
249202188Sed	 */
250202188Sed	if (_fstat(fd, &sb) != -1 && sb.st_size % sizeof(struct futx) != 0)
251202188Sed		ftruncate(fd, 0);
252202188Sed	_close(fd);
253202188Sed}
254202188Sed
255214134Sedstatic int
256202188Sedutx_log_add(const struct futx *fu)
257202188Sed{
258218846Sed	struct iovec vec[2];
259218846Sed	int error, fd;
260202188Sed	uint16_t l;
261202188Sed
262202188Sed	/*
263202188Sed	 * Append an entry to the log file.  We only need to append
264202188Sed	 * records to this file, so to conserve space, trim any trailing
265202188Sed	 * zero-bytes.  Prepend a length field, indicating the length of
266202188Sed	 * the record, excluding the length field itself.
267202188Sed	 */
268218846Sed	for (l = sizeof(*fu); l > 0 && ((const char *)fu)[l - 1] == '\0'; l--) ;
269202188Sed	vec[0].iov_base = &l;
270218846Sed	vec[0].iov_len = sizeof(l);
271202188Sed	vec[1].iov_base = __DECONST(void *, fu);
272202188Sed	vec[1].iov_len = l;
273202188Sed	l = htobe16(l);
274202188Sed
275241046Sjilles	fd = _open(_PATH_UTX_LOG, O_CREAT|O_WRONLY|O_APPEND|O_CLOEXEC, 0644);
276202188Sed	if (fd < 0)
277218846Sed		return (-1);
278218846Sed	if (_writev(fd, vec, 2) == -1)
279218846Sed		error = errno;
280218846Sed	else
281218846Sed		error = 0;
282202188Sed	_close(fd);
283249593Sjilles	if (error != 0)
284249593Sjilles		errno = error;
285218846Sed	return (error == 0 ? 0 : 1);
286202188Sed}
287202188Sed
288202188Sedstruct utmpx *
289202188Sedpututxline(const struct utmpx *utmpx)
290202188Sed{
291202188Sed	struct futx fu;
292219045Sed	int bad;
293202188Sed
294219045Sed	bad = 0;
295219045Sed
296202188Sed	utx_to_futx(utmpx, &fu);
297218846Sed
298202188Sed	switch (fu.fu_type) {
299202188Sed	case BOOT_TIME:
300226846Sed		utx_active_init(&fu);
301226846Sed		utx_lastlogin_upgrade();
302226846Sed		break;
303202188Sed	case SHUTDOWN_TIME:
304202188Sed		utx_active_purge();
305202188Sed		break;
306202188Sed	case OLD_TIME:
307202188Sed	case NEW_TIME:
308202188Sed		break;
309202188Sed	case USER_PROCESS:
310214134Sed		bad |= utx_active_add(&fu);
311214134Sed		bad |= utx_lastlogin_add(&fu);
312202188Sed		break;
313202188Sed#if 0 /* XXX: Are these records of any use to us? */
314202188Sed	case INIT_PROCESS:
315202188Sed	case LOGIN_PROCESS:
316214134Sed		bad |= utx_active_add(&fu);
317202188Sed		break;
318202188Sed#endif
319202188Sed	case DEAD_PROCESS:
320214134Sed		/*
321214134Sed		 * In case writing a logout entry fails, never attempt
322214134Sed		 * to write it to utx.log.  The logout entry's ut_id
323214134Sed		 * might be invalid.
324214134Sed		 */
325202188Sed		if (utx_active_remove(&fu) != 0)
326202188Sed			return (NULL);
327202188Sed		break;
328202188Sed	default:
329218846Sed		errno = EINVAL;
330202188Sed		return (NULL);
331202188Sed	}
332202188Sed
333214134Sed	bad |= utx_log_add(&fu);
334214134Sed	return (bad ? NULL : futx_to_utx(&fu));
335202188Sed}
336