Deleted Added
sdiff udiff text old ( 215310 ) new ( 218846 )
full compact
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

--- 11 unchanged lines hidden (view full) ---

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 218846 2011-02-19 11:31:56Z ed $");
29
30#include "namespace.h"
31#include <sys/endian.h>
32#include <sys/stat.h>
33#include <sys/uio.h>
34#include <errno.h>
35#include <fcntl.h>
36#include <stdio.h>
37#include <string.h>
38#include <unistd.h>
39#include <utmpx.h>
40#include "utxdb.h"
41#include "un-namespace.h"
42
43static FILE *
44futx_open(const char *file)
45{
46 struct stat sb;
47 FILE *fp;
48 int fd;
49
50 fd = _open(file, O_CREAT|O_RDWR|O_EXLOCK, 0644);
51 if (fd < 0)
52 return (NULL);
53
54 /* Safety check: never use broken files. */
55 if (_fstat(fd, &sb) != -1 && sb.st_size % sizeof(struct futx) != 0) {
56 _close(fd);
57 errno = EFTYPE;
58 return (NULL);
59 }
60
61 fp = fdopen(fd, "r+");
62 if (fp == NULL) {
63 _close(fd);
64 return (NULL);
65 }
66 return (fp);
67}
68
69static int
70utx_active_add(const struct futx *fu)
71{
72 struct futx fe;
73 FILE *fp;
74 off_t partial = -1;
75 int error, ret;
76
77 /*
78 * Register user login sessions. Overwrite entries of sessions
79 * that have already been terminated.
80 */
81 fp = futx_open(_PATH_UTX_ACTIVE);
82 if (fp == NULL)
83 return (-1);
84 while (fread(&fe, sizeof(fe), 1, fp) == 1) {
85 switch (fe.fu_type) {
86 case USER_PROCESS:
87 case INIT_PROCESS:
88 case LOGIN_PROCESS:
89 case DEAD_PROCESS:
90 /* Overwrite when ut_id matches. */
91 if (memcmp(fu->fu_id, fe.fu_id, sizeof(fe.fu_id)) ==
92 0) {
93 ret = fseeko(fp, -(off_t)sizeof(fe), SEEK_CUR);
94 goto exact;
95 }
96 if (fe.fu_type != DEAD_PROCESS)
97 break;
98 /* FALLTHROUGH */
99 default:
100 /* Allow us to overwrite unused records. */
101 if (partial == -1) {
102 partial = ftello(fp);
103 /*
104 * Distinguish errors from valid values so we
105 * don't overwrite good data by accident.
106 */
107 if (partial != -1)
108 partial -= (off_t)sizeof(fe);
109 }
110 break;
111 }
112 }
113
114 /*
115 * No exact match found. Use the partial match. If no partial
116 * match was found, just append a new record.
117 */
118 if (partial != -1)
119 ret = fseeko(fp, partial, SEEK_SET);
120exact:
121 if (ret == -1)
122 error = errno;
123 else if (fwrite(fu, sizeof(*fu), 1, fp) < 1)
124 error = errno;
125 else
126 error = 0;
127 fclose(fp);
128 errno = error;
129 return (error == 0 ? 0 : 1);
130}
131
132static int
133utx_active_remove(struct futx *fu)
134{
135 struct futx fe;
136 FILE *fp;
137 int error, ret;
138
139 /*
140 * Remove user login sessions, having the same ut_id.
141 */
142 fp = futx_open(_PATH_UTX_ACTIVE);
143 if (fp == NULL)
144 return (-1);
145 error = ESRCH;
146 ret = -1;
147 while (fread(&fe, sizeof(fe), 1, fp) == 1 && ret != 0)
148 switch (fe.fu_type) {
149 case USER_PROCESS:
150 case INIT_PROCESS:
151 case LOGIN_PROCESS:
152 if (memcmp(fu->fu_id, fe.fu_id, sizeof(fe.fu_id)) != 0)
153 continue;
154
155 /* Terminate session. */
156 if (fseeko(fp, -(off_t)sizeof(fe), SEEK_CUR) == -1)
157 error = errno;
158 else if (fwrite(fu, sizeof(*fu), 1, fp) < 1)
159 error = errno;
160 else
161 ret = 0;
162
163 }
164
165 fclose(fp);
166 errno = error;
167 return (ret);
168}
169
170static void
171utx_active_purge(void)
172{
173
174 truncate(_PATH_UTX_ACTIVE, 0);
175}
176
177static int
178utx_lastlogin_add(const struct futx *fu)
179{
180 struct futx fe;
181 FILE *fp;
182 int error, ret;
183
184 ret = 0;
185
186 /*
187 * Write an entry to lastlogin. Overwrite the entry if the
188 * current user already has an entry. If not, append a new
189 * entry.
190 */
191 fp = futx_open(_PATH_UTX_LASTLOGIN);
192 if (fp == NULL)
193 return (-1);
194 while (fread(&fe, sizeof fe, 1, fp) == 1) {
195 if (strncmp(fu->fu_user, fe.fu_user, sizeof fe.fu_user) != 0)
196 continue;
197
198 /* Found a previous lastlogin entry for this user. */
199 ret = fseeko(fp, -(off_t)sizeof fe, SEEK_CUR);
200 break;
201 }
202 if (ret == -1)
203 error = errno;
204 else if (fwrite(fu, sizeof *fu, 1, fp) < 1) {
205 error = errno;
206 ret = -1;
207 }
208 fclose(fp);
209 errno = error;
210 return (ret);
211}
212
213static void
214utx_lastlogin_upgrade(void)
215{
216 struct stat sb;
217 int fd;
218
219 fd = _open(_PATH_UTX_LASTLOGIN, O_RDWR, 0644);
220 if (fd < 0)
221 return;
222
223 /*
224 * Truncate broken lastlogin files. In the future we should
225 * check for older versions of the file format here and try to
226 * upgrade it.
227 */
228 if (_fstat(fd, &sb) != -1 && sb.st_size % sizeof(struct futx) != 0)
229 ftruncate(fd, 0);
230 _close(fd);
231}
232
233static int
234utx_log_add(const struct futx *fu)
235{
236 struct iovec vec[2];
237 int error, fd;
238 uint16_t l;
239
240 /*
241 * Append an entry to the log file. We only need to append
242 * records to this file, so to conserve space, trim any trailing
243 * zero-bytes. Prepend a length field, indicating the length of
244 * the record, excluding the length field itself.
245 */
246 for (l = sizeof(*fu); l > 0 && ((const char *)fu)[l - 1] == '\0'; l--) ;
247 vec[0].iov_base = &l;
248 vec[0].iov_len = sizeof(l);
249 vec[1].iov_base = __DECONST(void *, fu);
250 vec[1].iov_len = l;
251 l = htobe16(l);
252
253 fd = _open(_PATH_UTX_LOG, O_CREAT|O_WRONLY|O_APPEND, 0644);
254 if (fd < 0)
255 return (-1);
256 if (_writev(fd, vec, 2) == -1)
257 error = errno;
258 else
259 error = 0;
260 _close(fd);
261 errno = error;
262 return (error == 0 ? 0 : 1);
263}
264
265struct utmpx *
266pututxline(const struct utmpx *utmpx)
267{
268 struct futx fu;
269 int bad = 0;
270
271 utx_to_futx(utmpx, &fu);
272
273 switch (fu.fu_type) {
274 case BOOT_TIME:
275 case SHUTDOWN_TIME:
276 utx_active_purge();
277 utx_lastlogin_upgrade();
278 break;
279 case OLD_TIME:
280 case NEW_TIME:

--- 13 unchanged lines hidden (view full) ---

294 * In case writing a logout entry fails, never attempt
295 * to write it to utx.log. The logout entry's ut_id
296 * might be invalid.
297 */
298 if (utx_active_remove(&fu) != 0)
299 return (NULL);
300 break;
301 default:
302 errno = EINVAL;
303 return (NULL);
304 }
305
306 bad |= utx_log_add(&fu);
307 return (bad ? NULL : futx_to_utx(&fu));
308}