utmpx.c revision 1.15 1 /* $NetBSD: utmpx.c,v 1.15 2002/11/17 20:49:33 itojun Exp $ */
2
3 /*-
4 * Copyright (c) 2002 The NetBSD Foundation, Inc.
5 * All rights reserved.
6 *
7 * This code is derived from software contributed to The NetBSD Foundation
8 * by Christos Zoulas.
9 *
10 * Redistribution and use in source and binary forms, with or without
11 * modification, are permitted provided that the following conditions
12 * are met:
13 * 1. Redistributions of source code must retain the above copyright
14 * notice, this list of conditions and the following disclaimer.
15 * 2. Redistributions in binary form must reproduce the above copyright
16 * notice, this list of conditions and the following disclaimer in the
17 * documentation and/or other materials provided with the distribution.
18 * 3. All advertising materials mentioning features or use of this software
19 * must display the following acknowledgement:
20 * This product includes software developed by the NetBSD
21 * Foundation, Inc. and its contributors.
22 * 4. Neither the name of The NetBSD Foundation nor the names of its
23 * contributors may be used to endorse or promote products derived
24 * from this software without specific prior written permission.
25 *
26 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
27 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
28 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
29 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
30 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
31 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
32 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
33 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
34 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
35 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
36 * POSSIBILITY OF SUCH DAMAGE.
37 */
38 #include <sys/cdefs.h>
39
40 #if defined(LIBC_SCCS) && !defined(lint)
41 __RCSID("$NetBSD: utmpx.c,v 1.15 2002/11/17 20:49:33 itojun Exp $");
42 #endif /* LIBC_SCCS and not lint */
43
44 #include <sys/types.h>
45 #include <sys/param.h>
46 #include <sys/wait.h>
47 #include <sys/socket.h>
48 #include <sys/time.h>
49 #include <sys/stat.h>
50
51 #include <assert.h>
52 #include <stdio.h>
53 #include <stdlib.h>
54 #include <string.h>
55 #include <vis.h>
56 #include <utmp.h>
57 #include <utmpx.h>
58 #include <unistd.h>
59 #include <fcntl.h>
60 #include <errno.h>
61 #include <db.h>
62
63 static FILE *fp;
64 static struct utmpx ut;
65 static char utfile[MAXPATHLEN] = _PATH_UTMPX;
66 static char llfile[MAXPATHLEN] = _PATH_LASTLOGX;
67
68 static struct utmpx *utmp_update(const struct utmpx *);
69
70 static const char vers[] = "utmpx-1.00";
71
72 void
73 setutxent()
74 {
75
76 (void)memset(&ut, 0, sizeof(ut));
77 if (fp == NULL)
78 return;
79 (void)fseeko(fp, (off_t)sizeof(ut), SEEK_SET);
80 }
81
82
83 void
84 endutxent()
85 {
86
87 (void)memset(&ut, 0, sizeof(ut));
88 if (fp != NULL) {
89 (void)fclose(fp);
90 fp = NULL;
91 }
92 }
93
94
95 struct utmpx *
96 getutxent()
97 {
98
99 if (fp == NULL) {
100 struct stat st;
101
102 if ((fp = fopen(utfile, "r+")) == NULL)
103 if ((fp = fopen(utfile, "w+")) == NULL)
104 if ((fp = fopen(utfile, "r")) == NULL)
105 goto fail;
106
107 /* get file size in order to check if new file */
108 if (fstat(fileno(fp), &st) == -1)
109 goto failclose;
110
111 if (st.st_size == 0) {
112 /* new file, add signature record */
113 (void)memset(&ut, 0, sizeof(ut));
114 ut.ut_type = SIGNATURE;
115 (void)memcpy(ut.ut_user, vers, sizeof(vers));
116 if (fwrite(&ut, sizeof(ut), 1, fp) != 1)
117 goto failclose;
118 } else {
119 /* old file, read signature record */
120 if (fread(&ut, sizeof(ut), 1, fp) != 1)
121 goto failclose;
122 if (memcmp(ut.ut_user, vers, sizeof(vers)) != 0 ||
123 ut.ut_type != SIGNATURE)
124 goto failclose;
125 }
126 }
127
128 if (fread(&ut, sizeof(ut), 1, fp) != 1)
129 goto fail;
130
131 return &ut;
132 failclose:
133 (void)fclose(fp);
134 fail:
135 (void)memset(&ut, 0, sizeof(ut));
136 return NULL;
137 }
138
139
140 struct utmpx *
141 getutxid(const struct utmpx *utx)
142 {
143
144 _DIAGASSERT(utx != NULL);
145
146 if (utx->ut_type == EMPTY)
147 return NULL;
148
149 do {
150 if (ut.ut_type == EMPTY)
151 continue;
152 switch (utx->ut_type) {
153 case EMPTY:
154 return NULL;
155 case RUN_LVL:
156 case BOOT_TIME:
157 case OLD_TIME:
158 case NEW_TIME:
159 if (ut.ut_type == utx->ut_type)
160 return &ut;
161 break;
162 case INIT_PROCESS:
163 case LOGIN_PROCESS:
164 case USER_PROCESS:
165 case DEAD_PROCESS:
166 switch (ut.ut_type) {
167 case INIT_PROCESS:
168 case LOGIN_PROCESS:
169 case USER_PROCESS:
170 case DEAD_PROCESS:
171 if (memcmp(ut.ut_id, utx->ut_id,
172 sizeof(ut.ut_id)) == 0)
173 return &ut;
174 break;
175 default:
176 break;
177 }
178 break;
179 default:
180 return NULL;
181 }
182 } while (getutxent() != NULL);
183 return NULL;
184 }
185
186
187 struct utmpx *
188 getutxline(const struct utmpx *utx)
189 {
190
191 _DIAGASSERT(utx != NULL);
192
193 do {
194 switch (ut.ut_type) {
195 case EMPTY:
196 break;
197 case LOGIN_PROCESS:
198 case USER_PROCESS:
199 if (strncmp(ut.ut_line, utx->ut_line,
200 sizeof(ut.ut_line)) == 0)
201 return &ut;
202 break;
203 default:
204 break;
205 }
206 } while (getutxent() != NULL);
207 return NULL;
208 }
209
210
211 struct utmpx *
212 pututxline(const struct utmpx *utx)
213 {
214 struct utmpx temp, *u = NULL;
215 int gotlock = 0;
216
217 _DIAGASSERT(utx != NULL);
218
219 if (strcmp(_PATH_UTMPX, utfile) == 0 && geteuid() != 0)
220 return utmp_update(utx);
221
222 if (utx == NULL)
223 return NULL;
224
225 (void)memcpy(&temp, utx, sizeof(temp));
226
227 if (fp == NULL) {
228 (void)getutxent();
229 if (fp == NULL)
230 return NULL;
231 }
232
233 if (getutxid(&temp) == NULL) {
234 setutxent();
235 if (getutxid(&temp) == NULL) {
236 if (lockf(fileno(fp), F_LOCK, (off_t)0) == -1)
237 return NULL;
238 gotlock++;
239 if (fseeko(fp, (off_t)0, SEEK_END) == -1)
240 goto fail;
241 }
242 }
243
244 if (!gotlock) {
245 /* we are not appending */
246 if (fseeko(fp, -(off_t)sizeof(ut), SEEK_CUR) == -1)
247 return NULL;
248 }
249
250 if (fwrite(&temp, sizeof (temp), 1, fp) != 1)
251 goto fail;
252
253 if (fflush(fp) == -1)
254 goto fail;
255
256 u = memcpy(&ut, &temp, sizeof(ut));
257 fail:
258 if (gotlock) {
259 if (lockf(fileno(fp), F_ULOCK, (off_t)0) == -1)
260 return NULL;
261 }
262 return u;
263 }
264
265
266 static struct utmpx *
267 utmp_update(const struct utmpx *utx)
268 {
269 char buf[sizeof(*utx) * 4 + 1];
270 pid_t pid;
271 int status;
272
273 _DIAGASSERT(utx != NULL);
274
275 (void)strvisx(buf, (const char *)(const void *)utx, sizeof(*utx),
276 VIS_WHITE);
277 switch (pid = fork()) {
278 case 0:
279 (void)execl(_PATH_UTMP_UPDATE,
280 strrchr(_PATH_UTMP_UPDATE, '/') + 1, buf);
281 exit(1);
282 /*NOTREACHED*/
283 case -1:
284 return NULL;
285 default:
286 if (waitpid(pid, &status, 0) == -1)
287 return NULL;
288 if (WIFEXITED(status) && WEXITSTATUS(status) == 0)
289 return memcpy(&ut, utx, sizeof(ut));
290 return NULL;
291 }
292
293 }
294
295 /*
296 * The following are extensions and not part of the X/Open spec.
297 */
298 int
299 updwtmpx(const char *file, const struct utmpx *utx)
300 {
301 int fd;
302
303 _DIAGASSERT(file != NULL);
304 _DIAGASSERT(utx != NULL);
305
306 fd = open(file, O_WRONLY|O_APPEND|O_EXLOCK);
307
308 if (fd == -1) {
309 if ((fd = open(file, O_CREAT|O_WRONLY|O_EXLOCK, 0644)) == -1)
310 return -1;
311 (void)memset(&ut, 0, sizeof(ut));
312 ut.ut_type = SIGNATURE;
313 (void)memcpy(ut.ut_user, vers, sizeof(vers));
314 if (write(fd, &ut, sizeof(ut)) == -1)
315 return -1;
316 }
317 if (write(fd, utx, sizeof(*utx)) == -1)
318 return -1;
319 if (close(fd) == -1)
320 return -1;
321 return 0;
322 }
323
324
325 int
326 utmpxname(const char *fname)
327 {
328 size_t len;
329
330 _DIAGASSERT(fname != NULL);
331
332 len = strlen(fname);
333
334 if (len >= sizeof(utfile))
335 return 0;
336
337 /* must end in x! */
338 if (fname[len - 1] != 'x')
339 return 0;
340
341 (void)strlcpy(utfile, fname, sizeof(utfile));
342 endutxent();
343 return 1;
344 }
345
346
347 void
348 getutmp(const struct utmpx *ux, struct utmp *u)
349 {
350
351 _DIAGASSERT(ux != NULL);
352 _DIAGASSERT(u != NULL);
353
354 (void)memcpy(u->ut_name, ux->ut_name, sizeof(u->ut_name));
355 (void)memcpy(u->ut_line, ux->ut_line, sizeof(u->ut_line));
356 (void)memcpy(u->ut_host, ux->ut_host, sizeof(u->ut_host));
357 u->ut_time = ux->ut_tv.tv_sec;
358 }
359
360 void
361 getutmpx(const struct utmp *u, struct utmpx *ux)
362 {
363
364 _DIAGASSERT(ux != NULL);
365 _DIAGASSERT(u != NULL);
366
367 (void)memcpy(ux->ut_name, u->ut_name, sizeof(u->ut_name));
368 (void)memcpy(ux->ut_line, u->ut_line, sizeof(u->ut_line));
369 (void)memcpy(ux->ut_host, u->ut_host, sizeof(u->ut_host));
370 ux->ut_tv.tv_sec = u->ut_time;
371 ux->ut_tv.tv_usec = 0;
372 (void)memset(&ux->ut_ss, 0, sizeof(ux->ut_ss));
373 ux->ut_pid = 0;
374 ux->ut_type = USER_PROCESS;
375 ux->ut_session = 0;
376 ux->ut_exit.e_termination = 0;
377 ux->ut_exit.e_exit = 0;
378 }
379
380 int
381 lastlogxname(const char *fname)
382 {
383 size_t len;
384
385 _DIAGASSERT(fname != NULL);
386
387 len = strlen(fname);
388
389 if (len >= sizeof(llfile))
390 return 0;
391
392 /* must end in x! */
393 if (fname[len - 1] != 'x')
394 return 0;
395
396 (void)strlcpy(llfile, fname, sizeof(llfile));
397 return 1;
398 }
399
400 struct lastlogx *
401 getlastlogx(uid_t uid, struct lastlogx *ll)
402 {
403 DBT key, data;
404 DB *db;
405
406 _DIAGASSERT(ll != NULL);
407
408 db = dbopen(llfile, O_RDONLY|O_SHLOCK, 0, DB_HASH, NULL);
409
410 if (db == NULL)
411 return NULL;
412
413 key.data = &uid;
414 key.size = sizeof(uid);
415
416 if ((db->get)(db, &key, &data, 0) != 0)
417 goto error;
418
419 if (data.size != sizeof(*ll)) {
420 errno = EFTYPE;
421 goto error;
422 }
423
424 if (ll == NULL)
425 if ((ll = malloc(sizeof(*ll))) == NULL)
426 goto done;
427
428 (void)memcpy(ll, data.data, sizeof(*ll));
429 goto done;
430 error:
431 ll = NULL;
432 done:
433 (db->close)(db);
434 return ll;
435 }
436
437 int
438 updlastlogx(const char *fname, uid_t uid, struct lastlogx *ll)
439 {
440 DBT key, data;
441 int error = 0;
442 DB *db;
443
444 _DIAGASSERT(fname != NULL);
445 _DIAGASSERT(ll != NULL);
446
447 db = dbopen(fname, O_RDWR|O_CREAT|O_EXLOCK, 0, DB_HASH, NULL);
448
449 if (db == NULL)
450 return -1;
451
452 key.data = &uid;
453 key.size = sizeof(uid);
454 data.data = ll;
455 data.size = sizeof(*ll);
456 if ((db->put)(db, &key, &data, 0) != 0)
457 error = -1;
458
459 (db->close)(db);
460 return error;
461 }
462