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