]> git.saurik.com Git - apple/libc.git/blame - gen/utmpx-darwin.c
Libc-763.11.tar.gz
[apple/libc.git] / gen / utmpx-darwin.c
CommitLineData
224c7076 1/*
1f2f436a 2 * Copyright (c) 2005-2009 Apple Inc. All rights reserved.
224c7076
A
3 *
4 * @APPLE_LICENSE_HEADER_START@
5 *
6 * "Portions Copyright (c) 2004 Apple Computer, Inc. All Rights
7 * Reserved. This file contains Original Code and/or Modifications of
8 * Original Code as defined in and that are subject to the Apple Public
9 * Source License Version 1.0 (the 'License'). You may not use this file
10 * except in compliance with the License. Please obtain a copy of the
11 * License at http://www.apple.com/publicsource and read it before using
12 * this file.
13 *
14 * The Original Code and all software distributed under the License are
15 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
16 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
17 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
18 * FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT. Please see the
19 * License for the specific language governing rights and limitations
20 * under the License."
21 *
22 * @APPLE_LICENSE_HEADER_END@
23 */
24#include <sys/cdefs.h>
25#include <sys/types.h>
26#include <sys/param.h>
27#include <sys/stat.h>
28#include <sys/time.h>
29
30#include <ctype.h>
31#include <errno.h>
32#include <fcntl.h>
33#include <stdio.h>
34#include <stdlib.h>
35#include <string.h>
36#include <unistd.h>
b5d655f7 37#ifdef UTMP_COMPAT
224c7076 38#include <utmp.h>
b5d655f7 39#endif /* UTMP_COMPAT */
224c7076
A
40#include <utmpx.h>
41#include <utmpx-darwin.h>
1f2f436a 42#include <utmpx_thread.h>
224c7076
A
43#include <asl.h>
44#include <asl_private.h>
b5d655f7 45#include <asl_store.h>
224c7076
A
46#include <pwd.h>
47#include <stddef.h>
48
49#include <mach/mach.h>
50#include <mach/std_types.h>
51#include <mach/mig.h>
52#include <mach/mach_types.h>
53#include <servers/bootstrap.h>
54#include <pthread.h>
55#include <asl_ipc.h>
56
57#ifdef UTMP_COMPAT
58#include <ttyent.h>
59#endif /* UTMP_COMPAT */
60
1f2f436a
A
61__private_extern__ const char __utx_magic__[UTMPX_MAGIC] = __UTX_MAGIC__;
62
224c7076 63extern const char _utmpx_vers[]; /* in utmpx.c */
224c7076
A
64
65static void msg2lastlogx(const aslmsg, struct lastlogx *);
66static void msg2utmpx(const aslmsg, struct utmpx *);
67static void utmpx2msg(const struct utmpx *, aslmsg);
68
224c7076
A
69static int pw_size = 0;
70
224c7076
A
71#define FACILITY "Facility"
72#define WTMP_COUNT 32
73
b5d655f7
A
74/* ASL timeout in microseconds */
75#define ASL_QUERY_TIMEOUT 4000000
224c7076
A
76
77/* indirection causes argument to be substituted before stringification */
78#define STR(x) __STRING(x)
79
1f2f436a 80#ifdef UTMP_COMPAT
224c7076
A
81static char *
82_pwnam_r(const char *user, struct passwd *pw)
83{
84 struct passwd *p;
85 char *buf;
86
87 if (pw_size <= 0) {
88 pw_size = sysconf(_SC_GETPW_R_SIZE_MAX);
89 if (pw_size <= 0)
90 return NULL;
91 }
92 if ((buf = malloc(pw_size)) == NULL)
93 return NULL;
94
95 getpwnam_r(user, pw, buf, pw_size, &p);
96 if (!p) {
97 free(buf);
98 return NULL;
99 }
100 return buf;
101}
1f2f436a 102#endif
224c7076
A
103
104static char *
105_pwuid_r(uid_t uid, struct passwd *pw)
106{
107 struct passwd *p;
108 char *buf;
109
110 if (pw_size <= 0) {
111 pw_size = sysconf(_SC_GETPW_R_SIZE_MAX);
112 if (pw_size <= 0)
113 return NULL;
114 }
115 if ((buf = malloc(pw_size)) == NULL)
116 return NULL;
117
118 getpwuid_r(uid, pw, buf, pw_size, &p);
119 if (!p) {
120 free(buf);
121 return NULL;
122 }
123 return buf;
124}
125
126struct lastlogx *
127getlastlogx(uid_t uid, struct lastlogx *lx)
128{
129 char *buf;
130 struct passwd pw;
131 struct lastlogx *l;
132
133 if ((buf = _pwuid_r(uid, &pw)) == NULL)
134 return NULL;
135
136 l = getlastlogxbyname(pw.pw_name, lx);
137 free(buf);
138 return l;
139}
140
141struct lastlogx *
142getlastlogxbyname(const char *user, struct lastlogx *lx)
143{
b5d655f7
A
144 aslmsg m;
145 asl_msg_t *qm[1];
146 asl_search_result_t query, *res;
147 uint32_t status;
224c7076 148 uint64_t cmax;
224c7076 149 struct lastlogx *result = NULL;
b5d655f7 150 asl_store_t *store;
224c7076 151
b5d655f7 152 if (!user || !*user) return NULL;
224c7076 153
b5d655f7
A
154 store = NULL;
155 status = asl_store_open_read(NULL, &store);
156 if (status != 0) return NULL;
157 if (store == NULL) return NULL;
224c7076
A
158
159 /*
160 * We search for the last LASTLOG_FACILITY entry that has the
161 * ut_user entry matching the user's name.
162 */
b5d655f7
A
163 if ((m = asl_new(ASL_TYPE_QUERY)) == NULL)
164 {
165 asl_store_close(store);
166 return NULL;
224c7076
A
167 }
168
b5d655f7
A
169 asl_set_query(m, FACILITY, LASTLOG_FACILITY, ASL_QUERY_OP_EQUAL);
170 asl_set_query(m, "ut_user", user, ASL_QUERY_OP_EQUAL);
1f2f436a 171 qm[0] = (asl_msg_t *)m;
b5d655f7
A
172 query.count = 1;
173 query.msg = qm;
224c7076
A
174
175 res = NULL;
224c7076 176 cmax = 0;
b5d655f7
A
177
178 asl_store_match_timeout(store, &query, &res, &cmax, -1, 1, -1, ASL_QUERY_TIMEOUT);
179 asl_store_close(store);
180 asl_free(m);
181
182 if (status != 0) return NULL;
183 if (res == NULL) return NULL;
184
185 m = aslresponse_next(res);
186 if (m == NULL)
187 {
188 aslresponse_free(res);
189 return NULL;
224c7076
A
190 }
191
b5d655f7
A
192 if (lx == NULL)
193 {
194 if ((lx = (struct lastlogx *)malloc(sizeof(*lx))) == NULL)
195 {
196 aslresponse_free(res);
197 return NULL;
224c7076
A
198 }
199 }
b5d655f7
A
200
201 msg2lastlogx(m, lx);
202 aslresponse_free(res);
224c7076 203 result = lx;
b5d655f7 204
224c7076
A
205 return result;
206}
207
208#define IGET(e,p) if ((cp = asl_get(m, __STRING(ut_##e))) != NULL) \
209 u->p##_##e = strtol(cp, NULL, 10);
210#define LGET(e,p) IGET(e,p)
211#define SGET(e,p) if ((cp = asl_get(m, __STRING(ut_##e))) != NULL) \
212 strncpy(u->p##_##e, cp, sizeof(u->p##_##e))
213
214/* fill in a struct lastlogx from a aslmsg */
215static void
216msg2lastlogx(const aslmsg m, struct lastlogx *u)
217{
218 const char *cp;
219
220 bzero(u, sizeof(*u));
221 SGET(line, ll);
222 LGET(tv.tv_sec, ll);
223 IGET(tv.tv_usec, ll);
224 SGET(host, ll);
225}
226
227/* fill in a struct utmpx from a aslmsg */
228static void
229msg2utmpx(const aslmsg m, struct utmpx *u)
230{
231 const char *cp;
232
233 bzero(u, sizeof(*u));
234 SGET(user, ut);
235 SGET(id, ut);
236 SGET(line, ut);
237 IGET(pid, ut);
238 IGET(type, ut);
239 LGET(tv.tv_sec, ut);
240 IGET(tv.tv_usec, ut);
241 SGET(host, ut);
242}
243
244/* fill in a aslmsg from a struct utmpx */
245static void
246utmpx2msg(const struct utmpx *u, aslmsg m)
247{
248 char buf[_UTX_HOSTSIZE + 1]; /* the largest string in struct utmpx */
1f2f436a 249 const char *cp;
224c7076
A
250#define ISET(e) { snprintf(buf, sizeof(buf), "%d", u->e); \
251 asl_set(m, #e, buf); }
252#define LSET(e) { snprintf(buf, sizeof(buf), "%ld", u->e); \
253 asl_set(m, #e, buf); }
254#define SSET(e) if (*(u->e)) { \
255 strncpy(buf, u->e, sizeof(u->e)); \
256 buf[sizeof(u->e)] = 0; \
257 asl_set(m, #e, buf); \
258 }
259
260 SSET(ut_user);
1f2f436a 261 cp = (char *)u->ut_id + sizeof(u->ut_id);
224c7076
A
262 while(--cp >= u->ut_id && isprint(*cp)) {}
263 if(cp < u->ut_id) {
264 SSET(ut_id);
265 } else {
266 snprintf(buf, sizeof(buf), "0x%02x 0x%02x 0x%02x 0x%02x",
267 (unsigned)u->ut_id[0], (unsigned)u->ut_id[1],
268 (unsigned)u->ut_id[2], (unsigned)u->ut_id[3]);
269 asl_set(m, "ut_id", buf);
270 }
271 SSET(ut_line);
272 if (u->ut_pid > 0)
273 ISET(ut_pid);
274 ISET(ut_type);
275 LSET(ut_tv.tv_sec);
276 ISET(ut_tv.tv_usec);
277 SSET(ut_host);
278}
279
280static const char *utmpx_types[] = {
281 "EMPTY", /* 0 */
282 "RUN_LVL", /* 1 */
283 "BOOT_TIME", /* 2 */
284 "OLD_TIME", /* 3 */
285 "NEW_TIME", /* 4 */
286 "INIT_PROCESS", /* 5 */
287 "LOGIN_PROCESS", /* 6 */
288 "USER_PROCESS", /* 7 */
289 "DEAD_PROCESS", /* 8 */
290 "ACCOUNTING", /* 9 */
291 "SIGNATURE", /* 10 */
292 "SHUTDOWN_TIME", /* 11 */
293};
294
295/* send a struct utmpx record using asl */
296__private_extern__ void
297_utmpx_asl(const struct utmpx *u)
298{
299 aslclient asl = asl_open(NULL, NULL, ASL_OPT_NO_REMOTE); /* could be NULL, but still works */
300 aslmsg m;
301 char msg[64];
302
303 if (u->ut_type == EMPTY)
304 return;
305 if ((m = asl_new(ASL_TYPE_MSG)) == NULL) {
306 asl_close(asl);
307 return;
308 }
309 /*
310 * If the ut_type is USER_PROCESS, we use the LASTLOG_FACILITY,
311 * otherwise we use the UTMPX_FACILITY. This makes it easy to
312 * search for lastlog entries, but for wtmp, we have to search
313 * for both facilities.
314 */
315 if (u->ut_type == USER_PROCESS)
316 asl_set(m, FACILITY, LASTLOG_FACILITY);
317 else
318 asl_set(m, FACILITY, UTMPX_FACILITY);
319 asl_set(m, ASL_KEY_LEVEL, STR(ASL_LEVEL_NOTICE));
320 utmpx2msg(u, m);
321
322 /* Make a visible message for system.log */
323 switch (u->ut_type) {
324 case BOOT_TIME:
325 case OLD_TIME:
326 case NEW_TIME:
327 case SHUTDOWN_TIME:
328 sprintf(msg, "%s: %ld %d", utmpx_types[u->ut_type], u->ut_tv.tv_sec, u->ut_tv.tv_usec);
329 break;
330 case INIT_PROCESS:
331 case LOGIN_PROCESS:
332 sprintf(msg, "%s: %d", utmpx_types[u->ut_type], (int)u->ut_pid);
333 break;
334 case USER_PROCESS:
335 case DEAD_PROCESS:
336 sprintf(msg, "%s: %d %.*s", utmpx_types[u->ut_type], (int)u->ut_pid, (int)sizeof(u->ut_line), u->ut_line);
337 break;
338 default:
339 if (u->ut_type >= 0 && u->ut_type < (sizeof(utmpx_types) / sizeof(*utmpx_types)))
340 sprintf(msg, "%s", utmpx_types[u->ut_type]);
341 else
342 sprintf(msg, "ut_type=%d", (int)u->ut_type);
343 break;
344 }
345 asl_set(m, ASL_KEY_MSG, msg);
346 asl_send(asl, m);
347 asl_free(m);
348 if (asl)
349 asl_close(asl);
350}
351
352#define UT_USER (1 << 0)
353#define UT_ID (1 << 1)
354#define UT_LINE (1 << 2)
355#define UT_PID (1 << 3)
356#define UT_TV (1 << 4)
357
358__private_extern__ const struct utmpx *
359_utmpx_working_copy(const struct utmpx *utx, struct utmpx *temp, int onlyid)
360{
361 int which;
362 static char idzero[_UTX_IDSIZE];
363
364 if ((utx->ut_type & (UTMPX_AUTOFILL_MASK | UTMPX_DEAD_IF_CORRESPONDING_MASK)) == 0)
365 return utx;
366 memcpy(temp, utx, sizeof(*temp));
367 temp->ut_type &= ~(UTMPX_AUTOFILL_MASK | UTMPX_DEAD_IF_CORRESPONDING_MASK);
368
369 if ((utx->ut_type & UTMPX_AUTOFILL_MASK) == 0)
370 return temp;
371
372 which = UT_TV; /* they all need time */
373 switch(temp->ut_type) {
374 case EMPTY:
375 return temp;
376 case USER_PROCESS:
377 which |= (UT_USER | UT_LINE | UT_PID);
378 /* Set UT_ID if ut_id isn't there */
379 if (memcmp(temp->ut_id, idzero, sizeof(temp->ut_id)) == 0)
380 which |= UT_ID;
381 break;
382 case INIT_PROCESS:
383 which |= UT_PID;
384 break;
385 case LOGIN_PROCESS:
386 which |= (UT_USER | UT_PID);
387 break;
388 case DEAD_PROCESS:
389 which |= UT_PID;
390 /* Set UT_ID if ut_id isn't there. We will also need UT_LINE */
391 if (memcmp(temp->ut_id, idzero, sizeof(temp->ut_id)) == 0)
392 which |= (UT_ID | UT_LINE);
393 break;
394 }
395 /*
396 * If onlyid is set: if ut_id isn't set but is needed, then set
397 * which to (UT_LINE | UT_ID), otherwise zero
398 */
399 if (onlyid)
400 which = (which & UT_ID) ? (UT_LINE | UT_ID) : 0;
401 if ((which & UT_LINE) && !*temp->ut_line) {
402 char buf[256];
403 char *cp;
404#if __DARWIN_UNIX03
405 int err;
406
407 err = ttyname_r(0, buf, sizeof(buf));
408 if (err)
409 err = ttyname_r(1, buf, sizeof(buf));
410 if (err)
411 err = ttyname_r(2, buf, sizeof(buf));
412 if (err)
413 return NULL;
414#else /* !__DARWIN_UNIX03 */
415 cp = ttyname_r(0, buf, sizeof(buf));
416 if (!cp)
417 cp = ttyname_r(1, buf, sizeof(buf));
418 if (!cp)
419 cp = ttyname_r(2, buf, sizeof(buf));
420 if (!cp)
421 return NULL;
422#endif /* __DARWIN_UNIX03 */
423 cp = strrchr(buf, '/');
424 if (cp)
425 cp++;
426 else
427 cp = buf;
428 strncpy(temp->ut_line, cp, sizeof(temp->ut_line));
429 }
430 /* UT_ID is set only if we already know we need to add it */
431 if ((which & UT_ID)) {
432 char *cp;
433 int i = sizeof(temp->ut_line);
434
435 for(cp = temp->ut_line; i > 0 && *cp; i--)
436 cp++;
437 i = cp - temp->ut_line;
438 if(i >= sizeof(temp->ut_id))
439 memcpy(temp->ut_id, cp - sizeof(temp->ut_id), sizeof(temp->ut_id));
440 else
441 memcpy(temp->ut_id, temp->ut_line, i);
442 }
443 if ((which & UT_PID) && !temp->ut_pid)
444 temp->ut_pid = getpid();
445 if ((which & UT_USER) && !*temp->ut_user) {
446 char *buf;
447 struct passwd pw;
448
449 if ((buf = _pwuid_r(getuid(), &pw)) == NULL)
450 return NULL;
451 strncpy(temp->ut_user, pw.pw_name, sizeof(temp->ut_user));
452 free(buf);
453 }
454 if ((which & UT_TV) && !temp->ut_tv.tv_sec && !temp->ut_tv.tv_usec)
455 gettimeofday(&temp->ut_tv, NULL);
456 return temp;
457}
458
459/*
460 * We can read from either asl or from a file, so we need to switch between
461 * the two.
462 */
463static void end_asl(void);
464static void end_file(void);
465static struct utmpx *get_asl(void);
466static struct utmpx *get_file(void);
467static void set_asl(int);
468static void set_file(int);
469
470enum {WTMP_ASL, WTMP_FILE};
471
472static struct {
473 int which;
474 void (*end)(void);
475 struct utmpx *(*get)(void);
476 void (*set)(int);
477} wtmp_func = {
478 WTMP_ASL,
479 end_asl,
480 get_asl,
481 set_asl
482};
483static struct {
484 uint64_t start;
485 int dir;
486 asl_search_result_t *res;
487 char *str;
488 uint32_t len;
489 char inited;
490 char done;
491} wtmp_asl = {-1, 1};
492static struct {
493 int fd;
494 int dir;
1f2f436a 495 char *file;
224c7076
A
496 off_t off;
497 size_t count;
498#ifdef __LP64__
499 struct utmpx32 *buf;
500 struct utmpx32 *next;
501#else /* __LP64__ */
502 struct utmpx *buf;
503 struct utmpx *next;
504#endif /* __LP64__ */
505 int left;
506} wtmp_file = {-1, -1};
507
508void
509endutxent_wtmp(void)
510{
511 wtmp_func.end();
512}
513
514struct utmpx *
515getutxent_wtmp(void)
516{
517 return wtmp_func.get();
518}
519
520void
521setutxent_wtmp(int dir)
522{
523 wtmp_func.set(dir);
524}
525
526/* use the given file, or if NULL, read from asl */
527int
528wtmpxname(const char *fname)
529{
530 size_t len;
531
532 if (fname == NULL) {
533 if (wtmp_func.which == WTMP_ASL) {
534 end_asl();
535 return 1;
536 }
537 end_file();
538 wtmp_func.which = WTMP_ASL;
539 wtmp_func.end = end_asl;
540 wtmp_func.get = get_asl;
541 wtmp_func.set = set_asl;
542 return 1;
543 }
544
545 len = strlen(fname);
1f2f436a 546 if (len >= MAXPATHLEN)
224c7076
A
547 return 0;
548
549 /* must end in x! */
550 if (fname[len - 1] != 'x')
551 return 0;
552
224c7076
A
553 if (wtmp_func.which == WTMP_ASL)
554 end_asl();
555 else if (wtmp_file.fd >= 0) {
556 close(wtmp_file.fd);
557 wtmp_file.fd = -1;
558 }
1f2f436a
A
559
560 if (wtmp_file.file)
561 free(wtmp_file.file);
562
563 wtmp_file.file = strdup(fname);
564 if (wtmp_file.file == NULL)
565 return 0;
566
224c7076
A
567 wtmp_func.which = WTMP_FILE;
568 wtmp_func.end = end_file;
569 wtmp_func.get = get_file;
570 wtmp_func.set = set_file;
571 return 1;
572}
573
574static void
575end_asl(void)
576{
b5d655f7
A
577 if (wtmp_asl.res != NULL)
578 {
224c7076
A
579 aslresponse_free(wtmp_asl.res);
580 wtmp_asl.res = NULL;
581 }
b5d655f7 582
224c7076
A
583 wtmp_asl.inited = 0;
584 wtmp_asl.done = 0;
224c7076
A
585}
586
587static void
588end_file(void)
589{
590 if (wtmp_file.fd >= 0) {
591 close(wtmp_file.fd);
592 wtmp_file.fd = -1;
593 }
594 if (wtmp_file.buf) {
595 free(wtmp_file.buf);
596 wtmp_file.buf = NULL;
597 }
598}
599
600static struct utmpx *
601get_asl(void)
602{
b5d655f7 603 aslmsg m;
224c7076
A
604 static struct utmpx utx;
605
b5d655f7
A
606 if (wtmp_asl.inited == 0) set_asl(-1);
607 if (wtmp_asl.done != 0) return NULL;
608
609 m = aslresponse_next(wtmp_asl.res);
610 if (m == NULL)
611 {
224c7076
A
612 aslresponse_free(wtmp_asl.res);
613 wtmp_asl.res = NULL;
b5d655f7 614 wtmp_asl.done = 1;
224c7076 615 return NULL;
224c7076
A
616 }
617
b5d655f7
A
618 msg2utmpx(m, &utx);
619 return &utx;
224c7076 620}
b5d655f7 621
224c7076
A
622
623static struct utmpx *
624get_file(void)
625{
626 int n, r;
627 char *cp;
628#ifdef __LP64__
629 static struct utmpx ux;
630#endif /* __LP64__ */
631
632get_file_repeat:
633 if (wtmp_file.left > 0) {
634#ifdef __LP64__
635 struct utmpx32 *u = wtmp_file.next;
636#else /* __LP64__ */
637 struct utmpx *u = wtmp_file.next;
638#endif /* __LP64__ */
639 wtmp_file.next += wtmp_file.dir;
640 wtmp_file.left--;
641#ifdef __LP64__
642 _utmpx32_64(u, &ux);
643 return &ux;
644#else /* __LP64__ */
645 return u;
646#endif /* __LP64__ */
647 } else if (wtmp_file.fd < 0) {
648 set_file(-1); /* keep current read direction */
649 if (wtmp_file.fd < 0)
650 return NULL;
651 goto get_file_repeat;
652 }
653 if (wtmp_file.count <= 0)
654 return NULL;
655
656#ifdef __LP64__
657 n = WTMP_COUNT * sizeof(struct utmpx32);
658#else /* __LP64__ */
659 n = WTMP_COUNT * sizeof(struct utmpx);
660#endif /* __LP64__ */
661 if (wtmp_file.dir > 0)
662 wtmp_file.next = wtmp_file.buf;
663 else {
664 wtmp_file.next = wtmp_file.buf + WTMP_COUNT - 1;
665 wtmp_file.off -= n;
666 if (lseek(wtmp_file.fd, wtmp_file.off, SEEK_SET) < 0) {
667get_file_done:
668 wtmp_file.count = 0;
669 return NULL;
670 }
671 }
672
673 cp = (char *)wtmp_file.buf;
674 do {
675 if((r = read(wtmp_file.fd, cp, n)) <= 0) {
676 if (r < 0 && (errno == EINTR || errno == EAGAIN))
677 continue;
678 goto get_file_done;
679 }
680 cp += r;
681 } while((n -= r) > 0);
682
683 wtmp_file.left = WTMP_COUNT;
684 wtmp_file.count -= WTMP_COUNT;
685
686 goto get_file_repeat;
687}
688
689/*
690 * This sets the directions for both asl and reading from a file. If forward
691 * is negative, skip.
692 */
693static void
694_set_dir(int forward)
695{
b5d655f7
A
696 if (forward < 0) return;
697
698 if (forward == 0)
699 {
700 /* go backward */
701 wtmp_asl.dir = -1;
224c7076
A
702 wtmp_asl.start = -1;
703 wtmp_file.dir = -1;
704 }
b5d655f7
A
705 else
706 {
707 /* go forward */
708 wtmp_asl.dir = 1;
709 wtmp_asl.start = 0;
710 wtmp_file.dir = 1;
711 }
224c7076
A
712}
713
714static void
715set_asl(int forward)
716{
b5d655f7
A
717 aslmsg q0, q1;
718 asl_msg_t *m[2];
719 asl_search_result_t query;
720 uint64_t cmax;
721 asl_store_t *store;
722 uint32_t status;
723
224c7076 724 _set_dir(forward);
b5d655f7
A
725
726 wtmp_asl.inited = 0;
727 wtmp_asl.done = 0;
728
729 if (wtmp_asl.res != NULL)
730 {
224c7076
A
731 aslresponse_free(wtmp_asl.res);
732 wtmp_asl.res = NULL;
733 }
b5d655f7
A
734
735 store = NULL;
736 status = asl_store_open_read(NULL, &store);
737 if (status != 0) return;
738 if (store == NULL) return;
739
740 /*
741 * Create a search query that matches either UTMPX_FACILITY
742 * or LASTLOG_FACILITY.
743 */
744 q0 = asl_new(ASL_TYPE_QUERY);
745 q1 = asl_new(ASL_TYPE_QUERY);
746
747 if ((q0 == NULL) || (q1 == NULL))
748 {
749 asl_store_close(store);
750 if (q0 != NULL) free(q0);
751 if (q1 != NULL) free(q1);
752 return;
753 }
754
755 asl_set_query(q0, FACILITY, UTMPX_FACILITY, ASL_QUERY_OP_EQUAL);
756 asl_set_query(q1, FACILITY, LASTLOG_FACILITY, ASL_QUERY_OP_EQUAL);
757
1f2f436a
A
758 m[0] = (asl_msg_t *)q0;
759 m[1] = (asl_msg_t *)q1;
b5d655f7
A
760 query.count = 2;
761 query.msg = m;
762
763 cmax = 0;
764
765 asl_store_match_timeout(store, &query, &(wtmp_asl.res), &cmax, wtmp_asl.start, 0, wtmp_asl.dir, ASL_QUERY_TIMEOUT);
766 asl_store_close(store);
767
768 asl_free(q1);
769 asl_free(q0);
770
771 if (wtmp_asl.res == NULL) return;
772
224c7076
A
773 wtmp_asl.inited = 1;
774 wtmp_asl.done = 0;
775}
776
777static void
778set_file(int forward)
779{
780 struct stat s;
781 size_t c;
782 int n, r;
783 char *cp;
784
785 _set_dir(forward);
786#ifdef __LP64__
787 if (wtmp_file.buf == NULL &&
788 (wtmp_file.buf = (struct utmpx32 *)malloc(WTMP_COUNT * sizeof(struct utmpx32))) == NULL)
789#else /* __LP64__ */
790 if (wtmp_file.buf == NULL &&
791 (wtmp_file.buf = (struct utmpx *)malloc(WTMP_COUNT * sizeof(struct utmpx))) == NULL)
792#endif /* __LP64__ */
793 return;
794 if (wtmp_file.fd >= 0)
795 close(wtmp_file.fd);
796 if ((wtmp_file.fd = open(wtmp_file.file, O_RDONLY, 0)) < 0)
797 return;
798 if (fstat(wtmp_file.fd, &s) < 0)
799 goto set_file_error;
800 /*
801 * We must have a file at least 2 sizeof(struct utmpx) in size,
802 * with the first struct utmpx matching a signature record.
803 */
804#ifdef __LP64__
805 if ((wtmp_file.count = s.st_size / sizeof(struct utmpx32)) <= 1)
806#else /* __LP64__ */
807 if ((wtmp_file.count = s.st_size / sizeof(struct utmpx)) <= 1)
808#endif /* __LP64__ */
809 goto set_file_error;
810#ifdef __LP64__
811 if (read(wtmp_file.fd, wtmp_file.buf, sizeof(struct utmpx32)) != sizeof(struct utmpx32))
812#else /* __LP64__ */
813 if (read(wtmp_file.fd, wtmp_file.buf, sizeof(struct utmpx)) != sizeof(struct utmpx))
814#endif /* __LP64__ */
815 goto set_file_error;
816 if (strcmp(wtmp_file.buf->ut_user, _utmpx_vers) != 0 ||
817 wtmp_file.buf->ut_type != SIGNATURE)
818 goto set_file_error;
819 wtmp_file.count--;
820
821 /*
822 * We will first read any records modulo WTMP_COUNT (or WTMP_COUNT),
823 * either at the beginning or the end, so that all subsequent reads
824 * must contain WTMP_COUNT records.
825 */
826 c = WTMP_COUNT * ((wtmp_file.count - 1) / WTMP_COUNT);
827 wtmp_file.left = wtmp_file.count - c;
828 wtmp_file.count -= wtmp_file.left;
829
830 /* Seek to the end for reverse reading */
831 if (wtmp_file.dir < 0) {
832#ifdef __LP64__
833 wtmp_file.off = (c + 1) * sizeof(struct utmpx32);
834#else /* __LP64__ */
835 wtmp_file.off = (c + 1) * sizeof(struct utmpx);
836#endif /* __LP64__ */
837 if (lseek(wtmp_file.fd, wtmp_file.off, SEEK_SET) < 0)
838 goto set_file_error;
839 }
840#ifdef __LP64__
841 n = wtmp_file.left * sizeof(struct utmpx32);
842#else /* __LP64__ */
843 n = wtmp_file.left * sizeof(struct utmpx);
844#endif /* __LP64__ */
845 cp = (char *)wtmp_file.buf;
846 do {
847 if((r = read(wtmp_file.fd, cp, n)) <= 0) {
848 if (r < 0 && (errno == EINTR || errno == EAGAIN))
849 continue;
850 goto set_file_error;
851 }
852 cp += r;
853 } while((n -= r) > 0);
854
855 /* Point to either the beginning or end of the buffer */
856 if(wtmp_file.dir > 0)
857 wtmp_file.next = wtmp_file.buf;
858 else
859 wtmp_file.next = wtmp_file.buf + wtmp_file.left - 1;
860 return;
861
862set_file_error:
863 wtmp_file.left = 0;
864 close(wtmp_file.fd);
865 wtmp_file.fd = -1;
866 return;
867}
868
869#ifdef __LP64__
870/*
871 * these routines assume natural alignment so that struct utmpx32 has
872 * the same size and layout as the 32-bit struct utmpx
873 */
874__private_extern__ void
875_utmpx32_64(const struct utmpx32 *u32, struct utmpx *u)
876{
877 bzero(u, sizeof(*u));
878 memcpy(u, u32, offsetof(struct utmpx, ut_type) + sizeof(u->ut_type));
879 u->ut_tv.tv_sec = u32->ut_tv.tv_sec;
880 u->ut_tv.tv_usec = u32->ut_tv.tv_usec;
881 memcpy((char *)u + offsetof(struct utmpx, ut_host),
882 (char *)u32 + offsetof(struct utmpx32, ut_host),
883 sizeof(struct utmpx) - offsetof(struct utmpx, ut_host));
884}
885
886__private_extern__ void
887_utmpx64_32(const struct utmpx *u, struct utmpx32 *u32)
888{
889 bzero(u32, sizeof(*u32));
890 memcpy(u32, u, offsetof(struct utmpx32, ut_type) + sizeof(u32->ut_type));
891 u32->ut_tv.tv_sec = u->ut_tv.tv_sec;
892 u32->ut_tv.tv_usec = u->ut_tv.tv_usec;
893 memcpy((char *)u32 + offsetof(struct utmpx32, ut_host),
894 (char *)u + offsetof(struct utmpx, ut_host),
895 sizeof(struct utmpx32) - offsetof(struct utmpx32, ut_host));
896}
897#endif /* __LP64__ */
898
899#ifdef UTMP_COMPAT
900#ifdef __LP64__
901__private_extern__ void
902_getutmp32(const struct utmpx *ux, struct utmp32 *u)
903{
904
905 bzero(u, sizeof(*u));
906 (void)memcpy(u->ut_name, ux->ut_user, sizeof(u->ut_name));
907 (void)memcpy(u->ut_line, ux->ut_line, sizeof(u->ut_line));
908 (void)memcpy(u->ut_host, ux->ut_host, sizeof(u->ut_host));
909 u->ut_time = ux->ut_tv.tv_sec;
910}
911#endif /* __LP64__ */
912
913/*
914 * _utmp_compat converts a struct utmpx to a struct utmp, using the conventions
915 * described in utmp(5). It then returns a value that specifies what
916 * combination of utmp, wtmp and lastlog to write. UTMP_COMPAT_UTMP1 will
917 * write utmp only if a matching record with the same ut_line value is found;
918 * UTMP_COMPAT_UTMP0 replaces an existing record or writes a new one.
919 */
920__private_extern__ int
921#ifdef __LP64__
922_utmp_compat(const struct utmpx *ux, struct utmp32 *u)
923#else /* __LP64__ */
924_utmp_compat(const struct utmpx *ux, struct utmp *u)
925#endif /* __LP64__ */
926{
927#ifdef __LP64__
928 _getutmp32(ux, u);
929#else /* __LP64__ */
930 getutmp(ux, u);
931#endif /* __LP64__ */
932
933 switch (ux->ut_type) {
934 case BOOT_TIME:
935 case SHUTDOWN_TIME:
936 bzero(u->ut_line, sizeof(u->ut_line));
937 u->ut_line[0] = '~';
938 bzero(u->ut_name, sizeof(u->ut_name));
939 strcpy(u->ut_name, (ux->ut_type == BOOT_TIME ? "reboot" : "shutdown"));
940 return UTMP_COMPAT_WTMP;
941 case OLD_TIME:
942 case NEW_TIME:
943 bzero(u->ut_line, sizeof(u->ut_line));
944 u->ut_line[0] = (ux->ut_type == OLD_TIME ? '|' : '{');
945 bzero(u->ut_name, sizeof(u->ut_name));
946 strcpy(u->ut_name, "date");
947 return UTMP_COMPAT_WTMP;
948 case USER_PROCESS:
949 return UTMP_COMPAT_UTMP0 | UTMP_COMPAT_WTMP | UTMP_COMPAT_LASTLOG;
950 case DEAD_PROCESS:
951 bzero(u->ut_name, sizeof(u->ut_name));
952 bzero(u->ut_host, sizeof(u->ut_host));
953 return UTMP_COMPAT_UTMP1 | UTMP_COMPAT_WTMP;
954 }
955 return 0; /* skip */;
956}
957
958/*
959 * Write _PATH_LASTLOG given a struct utmp record. We use
960 * advisory record locking.
961 */
962__private_extern__ void
963#ifdef __LP64__
964_write_lastlog(const struct utmp32 *u, const struct utmpx *ux)
965#else /* __LP64__ */
966_write_lastlog(const struct utmp *u, const struct utmpx *ux)
967#endif /* __LP64__ */
968{
969 int fd;
970#ifdef __LP64__
971 struct lastlog32 l;
972#else /* __LP64__ */
973 struct lastlog l;
974#endif /* __LP64__ */
975 struct flock lock;
976 struct passwd pw;
977 // sizeof(ux->ut_user) > sizeof(u->ut_name)
978 char name[sizeof(ux->ut_user) + 1];
979 char *buf;
980 off_t off;
981 int retry = 10;
982
983 if (ux) {
984 if(!*ux->ut_user)
985 return;
986 strncpy(name, ux->ut_user, sizeof(ux->ut_user));
987 name[sizeof(ux->ut_user)] = 0;
988 } else {
989 if (!*u->ut_name)
990 return;
991 strncpy(name, u->ut_name, sizeof(u->ut_name));
992 name[sizeof(u->ut_name)] = 0;
993 }
994 if ((buf = _pwnam_r(name, &pw)) == NULL)
995 return;
996#ifdef __LP64__
997 off = (off_t)pw.pw_uid * sizeof(struct lastlog32);
998#else /* __LP64__ */
999 off = (off_t)pw.pw_uid * sizeof(struct lastlog);
1000#endif /* __LP64__ */
1001 free(buf);
1002
1003 if ((fd = open(_PATH_LASTLOG, O_WRONLY, 0)) < 0)
1004 return;
1005 (void)lseek(fd, off, SEEK_SET);
1006 bzero(&lock, sizeof(lock));
1007 lock.l_type = F_WRLCK;
1008 lock.l_whence = SEEK_SET;
1009 lock.l_start = off;
1010#ifdef __LP64__
1011 lock.l_len = sizeof(struct lastlog32);
1012#else /* __LP64__ */
1013 lock.l_len = sizeof(struct lastlog);
1014#endif /* __LP64__ */
1015 /* try to lock, but give up after retry times, and write anyways */
1016 while(retry-- > 0) {
1017 if (fcntl(fd, F_SETLK, &lock) == 0)
1018 break;
1019 usleep(10000);
1020 }
1021 l.ll_time = u->ut_time;
1022 strncpy(l.ll_line, u->ut_line, sizeof(l.ll_line));
1023 strncpy(l.ll_host, u->ut_host, sizeof(l.ll_host));
1024 (void) write(fd, &l, sizeof(l));
1025 lock.l_type = F_UNLCK;
1026 (void) fcntl(fd, F_SETLK, &lock);
1027 (void) close(fd);
1028}
1029
1030/*
1031 * Write _PATH_UTMP, given a struct utmp, depending on the value of
1032 * "mustexist".
1033 */
1034__private_extern__ void
1035#ifdef __LP64__
1036_write_utmp(const struct utmp32 *u, int mustexist)
1037#else /* __LP64__ */
1038_write_utmp(const struct utmp *u, int mustexist)
1039#endif /* __LP64__ */
1040{
1041 int fd, slot;
1042 struct ttyent *ttyp;
1043#ifdef __LP64__
1044 struct utmp32 tmp;
1045#else /* __LP64__ */
1046 struct utmp tmp;
1047#endif /* __LP64__ */
1048 int found = 0;
1049 static struct {
1050 char line[sizeof(u->ut_line)];
1051 int slot;
1052 } cache;
1053
1054 if ((fd = open(_PATH_UTMP, O_RDWR, 0)) < 0)
1055 return;
1056
1057 if (!strncmp(cache.line, u->ut_line, sizeof(u->ut_line))) {
1058 slot = cache.slot;
1059 found++;
1060 }
1061 /* do equivalent of ttyslot(), but using u->ut_line */
1062 if (!found) {
1063 setttyent();
1064 slot = 1;
1065 for(;;) {
1066 if ((ttyp = getttyent()) == NULL)
1067 break;
1068 if (!strncmp(ttyp->ty_name, u->ut_line, sizeof(u->ut_line))) {
1069 strncpy(cache.line, u->ut_line, sizeof(u->ut_line));
1070 cache.slot = slot;
1071 found++;
1072 break;
1073 }
1074 slot++;
1075 }
1076 endttyent();
1077 }
1078
1079 if (!found) { /* no assigned slot */
1080#ifdef __LP64__
1081 (void)lseek(fd, (off_t)slot * sizeof(struct utmp32), SEEK_SET);
1082#else /* __LP64__ */
1083 (void)lseek(fd, (off_t)slot * sizeof(struct utmp), SEEK_SET);
1084#endif /* __LP64__ */
1085 for(;;) {
1086 if (read(fd, &tmp, sizeof(tmp)) != sizeof(tmp))
1087 break;
1088 if (!strncmp(tmp.ut_line, u->ut_line, sizeof(u->ut_line))) {
1089 strncpy(cache.line, u->ut_line, sizeof(u->ut_line));
1090 cache.slot = slot;
1091 found++;
1092 break;
1093 }
1094 slot++;
1095 }
1096 }
1097
1098 if (!found && mustexist) {
1099 (void)close(fd);
1100 return;
1101 }
1102#ifdef __LP64__
1103 (void)lseek(fd, (off_t)slot * sizeof(struct utmp32), SEEK_SET);
1104 (void)write(fd, u, sizeof(struct utmp32));
1105#else /* __LP64__ */
1106 (void)lseek(fd, (off_t)slot * sizeof(struct utmp), SEEK_SET);
1107 (void)write(fd, u, sizeof(struct utmp));
1108#endif /* __LP64__ */
1109 (void)close(fd);
1110}
1111
1112/*
1113 * Write all the necessary files (utmp, wtmp, lastlog), depending on the
1114 * given struct utmpx.
1115 */
1116__private_extern__ void
1117_write_utmp_compat(const struct utmpx *ux)
1118{
1119#ifdef __LP64__
1120 struct utmp32 u;
1121#else /* __LP64__ */
1122 struct utmp u;
1123#endif /* __LP64__ */
1124 int which;
1125
1126 which = _utmp_compat(ux, &u);
1127 if (which & UTMP_COMPAT_UTMP0)
1128 _write_utmp(&u, 0);
1129 else if (which & UTMP_COMPAT_UTMP1)
1130 _write_utmp(&u, 1);
1131 if (which & UTMP_COMPAT_WTMP)
1132 _write_wtmp(&u);
1133 if (which & UTMP_COMPAT_LASTLOG)
1134 _write_lastlog(&u, ux);
1135}
1136
1137/* Append a struct wtmp to _PATH_WTMP */
1138__private_extern__ void
1139#ifdef __LP64__
1140_write_wtmp(const struct utmp32 *u)
1141#else /* __LP64__ */
1142_write_wtmp(const struct utmp *u)
1143#endif /* __LP64__ */
1144{
1145 int fd;
1146 struct stat buf;
1147
1148 if ((fd = open(_PATH_WTMP, O_WRONLY | O_APPEND, 0)) < 0)
1149 return;
1150 if (fstat(fd, &buf) == 0) {
1151 if (write(fd, u, sizeof(*u)) != sizeof(*u))
1152 (void) ftruncate(fd, buf.st_size);
1153 }
1154 (void) close(fd);
1155}
1156#endif /* UTMP_COMPAT */
1f2f436a
A
1157
1158/*
1159 * thread aware SPI
1160 */
1161utmpx_t
1162_openutx(const char *name)
1163{
1164 struct _utmpx *U;
1165
1166 if ((U = calloc(1, sizeof(struct _utmpx))) == NULL)
1167 return NULL;
1168 memcpy(U->magic, __utx_magic__, UTMPX_MAGIC);
1169 U->utmpx_mutex = (pthread_mutex_t)PTHREAD_MUTEX_INITIALIZER;
1170 if (__utmpxname(U, name) == 0) {
1171 if (!U->utfile_system)
1172 free(U->utfile);
1173 free(U);
1174 errno = EINVAL;
1175 return NULL;
1176 }
1177 return (utmpx_t)U;
1178}
1179
1180int
1181_closeutx(utmpx_t u)
1182{
1183 struct _utmpx *U = (struct _utmpx *)u;
1184
1185 if (!U || memcmp(U->magic, __utx_magic__, UTMPX_MAGIC) != 0) {
1186 errno = EINVAL;
1187 return -1;
1188 }
1189 UTMPX_LOCK(U);
1190 __endutxent(U);
1191 if (!U->utfile_system)
1192 free(U->utfile);
1193 UTMPX_UNLOCK(U);
1194 free(U);
1195 return 0;
1196}