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