2 * Copyright (c) 2000 Andre Lucas. All rights reserved.
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions
7 * 1. Redistributions of source code must retain the above copyright
8 * notice, this list of conditions and the following disclaimer.
9 * 2. Redistributions in binary form must reproduce the above copyright
10 * notice, this list of conditions and the following disclaimer in the
11 * documentation and/or other materials provided with the distribution.
12 * 3. All advertising materials mentioning features or use of this software
13 * must display the following acknowledgement:
14 * This product includes software developed by Markus Friedl.
15 * 4. The name of the author may not be used to endorse or promote products
16 * derived from this software without specific prior written permission.
18 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
19 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
20 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
21 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
22 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
23 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
24 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
25 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
26 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
27 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
31 ** loginrec.c: platform-independent login recording and lastlog retrieval
36 ** sockaddr_* stuff isn't finished
42 ** Linux (Redhat 6.2, need more variants)
43 ** HP-UX 10.20 (gcc only)
45 ** Testing required: Please send reports!
51 ** Platforms with known problems:
52 ** AIX (need to port AIX stuff from old login code
59 #include <sys/types.h>
61 #include <netinet/in.h>
71 #ifdef HAVE_SYS_TIME_H
72 # include <sys/time.h>
85 ** prototypes for helper functions in this file
90 void set_utmp_time(struct logininfo *li, struct utmp *ut);
91 void construct_utmp(struct logininfo *li, struct utmp *ut);
96 void set_utmpx_time(struct logininfo *li, struct utmpx *ut);
97 void construct_utmpx(struct logininfo *li, struct utmpx *ut);
100 int utmp_write_entry(struct logininfo *li);
101 int utmpx_write_entry(struct logininfo *li);
102 int wtmp_write_entry(struct logininfo *li);
103 int wtmpx_write_entry(struct logininfo *li);
104 int lastlog_write_entry(struct logininfo *li);
105 int syslogin_write_entry(struct logininfo *li);
107 int getlast_entry(struct logininfo *li);
108 int lastlog_get_entry(struct logininfo *li);
109 int wtmp_get_entry(struct logininfo *li);
110 int wtmpx_get_entry(struct logininfo *li);
114 ** platform-independent login functions
117 /* login_alloc_entry() - allocate and initialise a logininfo */
118 struct logininfo *login_alloc_entry(int pid, const char *username,
119 const char *hostname,
121 struct logininfo *newli;
123 newli = (struct logininfo *) xmalloc (sizeof(struct logininfo));
125 if (login_init_entry(newli, pid, username, hostname, line))
129 } /* login_alloc_entry() */
132 /* login_free_entry() - free struct memory (duh) */
133 void login_free_entry(struct logininfo *li) {
134 if (li && (li->line[0] != '\0'))
137 log("login_free_entry: attempt to free invalid entry (warning)");
138 } /* login_free_entry() */
140 /* login_init_entry() - initialise a struct logininfo */
141 int login_init_entry(struct logininfo *li,
142 int pid, const char *username,
143 const char *hostname, const char *line) {
145 /* zero the structure */
146 memset(li, 0, sizeof(struct logininfo));
148 /* progname should be set outside this call */
149 /* type stays null by default */
150 login_set_pid(li, pid);
151 /* set the line information */
152 login_set_line(li, line);
153 login_set_username(li, username);
154 login_set_hostname(li, hostname);
155 /* exit status and termination stay null by default */
156 login_set_current_time(li);
157 /* sockaddr_* stuff must be set separately (for now) */
159 } /* login_init_entry() */
163 login_set_progname(struct logininfo *li, const char *progname) {
164 memset(li->progname, '\0', sizeof(li->progname));
166 strlcpy(li->progname, progname, sizeof(li->progname));
168 li->progname[0] = '\0'; /* set to null */
172 login_set_type(struct logininfo *li, int type) {
177 login_set_pid(struct logininfo *li, int pid) {
179 li->pid = (int)getpid();
185 login_set_uid(struct logininfo *li, int uid) {
189 /* now update the username */
191 strlcpy(li->username, pw->pw_name, sizeof(li->username));
195 login_set_line(struct logininfo *li, const char *line) {
197 /* canonical form is the full name, i.e. including '/dev' */
198 line_fullname(li->line, line, sizeof(li->line));
204 login_set_username(struct logininfo *li, const char *username) {
208 li->username[0] = '\0';
209 li->uid = -1; /* hmm... */
211 strlcpy(li->username, username, sizeof(li->username));
212 /* now update the uid */
213 pw = getpwnam(username);
214 li->uid = pw->pw_uid;
220 login_set_hostname(struct logininfo *li, const char *hostname) {
221 if (hostname) { /* can be null */
222 strlcpy(li->hostname, hostname, sizeof(li->hostname));
228 login_set_exitstatus(struct logininfo *li,
229 int exit, int termination) {
230 /* FIXME: (ATL) And? */
234 /* tv_usec should be null on systems without struct timeval */
236 login_set_time(struct logininfo *li,
237 unsigned int tv_sec, unsigned int tv_usec) {
239 li->tv_usec = tv_usec;
244 login_set_current_time(struct logininfo *li) {
245 #ifdef HAVE_SYS_TIME_H
248 gettimeofday(&tv, NULL);
249 li->tv_sec = tv.tv_sec ; li->tv_usec = tv.tv_usec;
253 li->tv_sec = t; li->tv_usec = 0;
258 login_set_ip4(struct logininfo *li,
259 const struct sockaddr_in *sa_in4) {
260 memcpy((void *)&(li->hostaddr.sa_in4), (const void *)sa_in4,
261 sizeof(struct sockaddr_in));
266 login_set_ip6(struct logininfo *li,
267 const struct sockaddr_in6 *sa_in6) {
268 memcpy((void *)&(li->hostaddr.sa_in4), (const void *)sa_in6,
269 sizeof(struct sockaddr_in6));
278 login_write (struct logininfo *li) {
280 if ((int)geteuid() != 0) {
281 log("Attempt to write login records by non-root user (aborting)");
284 /* set the timestamp */
285 login_set_current_time(li);
287 syslogin_write_entry(li);
290 if (li->type == LTYPE_LOGIN) {
291 lastlog_write_entry(li);
295 utmp_write_entry(li);
298 wtmp_write_entry(li);
301 utmpx_write_entry(li);
304 wtmpx_write_entry(li);
310 login_login (struct logininfo *li) {
311 li->type = LTYPE_LOGIN;
312 return login_write(li);
316 login_logout(struct logininfo *li) {
317 li->type = LTYPE_LOGOUT;
318 return login_write(li);
322 login_log_entry(struct logininfo *li) {
323 return login_write(li);
328 login_getlasttime_name(const char *username) {
331 memset(&li, '\0', sizeof(li));
332 login_set_username(&li, username);
333 if (getlast_entry(&li))
337 } /* login_getlasttime_name() */
341 login_getlasttime_uid(const int uid) {
344 memset(&li, '\0', sizeof(li));
345 login_set_uid(&li, uid);
346 if (getlast_entry(&li))
350 } /* login_getlasttime_uid() */
354 login_getlastentry_name(struct logininfo *li,
355 const char *username) {
356 login_set_username(li, username);
357 if (getlast_entry(li))
361 } /* login_getlastentry_name() */
364 login_getlastentry_uid(struct logininfo *li,
366 login_set_uid(li, uid);
367 if (getlast_entry(li))
371 } /* login_getlastentry_uid() */
375 ** 'line' string utility functions
379 * process the 'line' string into three forms:
380 * 1. The full filename (including '/dev')
381 * 2. The stripped name (excluding '/dev')
382 * 3. The abbreviated name (e.g. /dev/ttyp00
384 * Form 3 is used on some systems to identify a .tmp.? entry when
385 * attempting to remove it. Typically both addition and removal is
386 * performed by one application - say, sshd - so as long as the
387 * choice uniquely identifies a terminal and is the same at login and
388 * logout time, we're in good shape.
390 * NOTE: None of these calls actually allocate any memory -
391 * since their target is probably a structure, they don't
396 /* add the leading '/dev/' if it doesn't exist
397 * make sure dst has enough space, if not just copy src (ugh) */
399 line_fullname(char *dst, const char *src, int dstsize) {
400 memset(dst, '\0', dstsize);
401 if ((strncmp(src, "/dev/", 5) == 0) || (dstsize < (strlen(src) + 5)))
402 strlcpy(dst, src, dstsize);
404 strlcpy(dst, "/dev/", 5);
405 strlcat(dst, src, dstsize);
410 /* strip the leading '/dev' if it exists, return dst */
412 line_stripname(char *dst, const char *src, int dstsize) {
413 memset(dst, '\0', dstsize);
414 if (strncmp(src, "/dev/", 5) == 0)
415 strlcpy(dst, &src[5], dstsize);
417 strlcpy(dst, src, dstsize);
421 /* return the abbreviated (usually four-character) form *
422 * simple algorithm for making name:
423 * - first character is 'L' (arbitrary - 'lib(L)ogin' :-) )
424 * - remaining n characters are last n characters of line
425 * This is good for up to 999 ptys, I hope that's enough...
428 line_abbrevname(char *dst, const char *src, int dstsize) {
429 memset(dst, '\0', dstsize);
431 strlcpy(dst+1, &src[strlen(src)-(dstsize)], dstsize);
437 ** utmp utility functions
440 #if defined(USE_UTMP) || defined (USE_WTMP) || defined (USE_LOGIN)
446 # include <sys/time.h>
451 /* build the utmp structure */
453 set_utmp_time(struct logininfo *li, struct utmp *ut) {
454 #ifdef HAVE_TV_IN_UTMP
455 ut->ut_tv.tv_sec = li->tv_sec;
456 ut->ut_tv.tv_usec = li->tv_usec;
458 # ifdef HAVE_TIME_IN_UTMP
459 ut->ut_time = li->tv_sec;
465 construct_utmp(struct logininfo *li,
467 memset(ut, '\0', sizeof(struct utmp));
469 #ifdef HAVE_ID_IN_UTMP
470 line_abbrevname(ut->ut_id, li->line, sizeof(ut->ut_id));
473 #ifdef HAVE_TYPE_IN_UTMP
474 /* this is done here to keep utmp constants out of login.h */
477 ut->ut_type = USER_PROCESS;
480 ut->ut_type = DEAD_PROCESS;
485 #ifdef HAVE_PID_IN_UTMP
486 ut->ut_pid = li->pid;
488 line_stripname(ut->ut_line, li->line, sizeof(ut->ut_line));
489 strlcpy(ut->ut_name, li->username, sizeof(ut->ut_name));
490 set_utmp_time(li, ut);
491 #ifdef HAVE_HOST_IN_UTMP
492 strlcpy(ut->ut_host, li->hostname, sizeof(ut->ut_host));
494 #ifdef HAVE_ADDR_IN_UTMP
495 /* !!! not supported yet (can't see its big use either) */
498 } /* construct_utmp() */
501 /* USE_UTMP || USE_WTMP || USE_LOGIN */
504 ** utmpx utility functions
507 #if defined(USE_UTMPX) || defined (USE_WTMPX)
513 # include <sys/time.h>
518 /* build the utmpx structure */
520 set_utmpx_time(struct logininfo *li, struct utmpx *utx) {
521 #ifdef HAVE_TV_IN_UTMPX
522 utx->ut_tv.tv_sec = li->tv_sec;
523 utx->ut_tv.tv_usec = li->tv_usec;
525 # ifdef HAVE_TIME_IN_UTMPX
526 utx->ut_time = li->tv_sec;
532 construct_utmpx(struct logininfo *li,
534 memset(utx, '\0', sizeof(struct utmpx));
536 line_abbrevname(utx->ut_id, li->line, sizeof(utx->ut_id));
538 /* this is done here to keep utmp constants out of loginrec.h */
541 utx->ut_type = USER_PROCESS;
544 utx->ut_type = DEAD_PROCESS;
548 utx->ut_pid = li->pid;
549 line_stripname(utx->ut_line, li->line, sizeof(utx->ut_line));
550 strlcpy(utx->ut_name, li->username, sizeof(utx->ut_name));
551 set_utmpx_time(li, utx);
552 #ifdef HAVE_HOST_IN_UTMPX
553 strlcpy(utx->ut_host, li->hostname, sizeof(utx->ut_host));
555 #ifdef HAVE_ADDR_IN_UTMPX
556 /* !!! not supported yet (some issues with types of addresses) */
558 #ifdef HAVE_SYSLEN_IN_UTMPX
559 /* this is safe because of the extra nulls in logininfo */
560 utx->ut_syslen = strlen(li->hostname);
562 } /* construct_utmpx() */
565 /* USE_UTMPX || USE_WTMPX */
573 /* FIXME: (ATL) utmp_write_direct needs testing */
579 /* if we can, use pututline() etc. */
580 #if !defined(DISABLE_PUTUTLINE) && defined(HAVE_SETUTENT) && \
581 defined(HAVE_PUTUTLINE)
582 # define UTMP_USE_LIBRARY
586 /* write a utmp entry with the system's help (pututline() and pals) */
587 #ifdef UTMP_USE_LIBRARY
589 utmp_write_library(struct logininfo *li, struct utmp *ut) {
598 } /* utmp_write_library() */
602 /* write a utmp entry direct to the file */
603 /* This code is a slightly modification of code in OpenBSD's login.c
604 * (in libutil) and so is subject to the OpenBSD Licensing terms. */
606 utmp_write_direct(struct logininfo *li, struct utmp *ut) {
611 tty = ttyslot(); /* seems only to work for /dev/ttyp? style names */
613 if (tty > 0 && (fd = open(UTMP_FILE, O_RDWR|O_CREAT, 0644)) >= 0) {
614 (void)lseek(fd, (off_t)(tty * sizeof(struct utmp)), SEEK_SET);
616 * Prevent luser from zero'ing out ut_host.
617 * If the new ut_line is empty but the old one is not
618 * and ut_line and ut_name match, preserve the old ut_line.
620 if ( read(fd, &old_ut, sizeof(struct utmp)) == sizeof(struct utmp)
621 && ut->ut_host[0] == '\0'
622 && old_ut.ut_host[0] != '\0'
623 && strncmp(old_ut.ut_line, ut->ut_line, sizeof(ut->ut_line)) == 0
624 && strncmp(old_ut.ut_name, ut->ut_name, sizeof(ut->ut_name)) == 0 )
625 (void)memcpy(ut->ut_host, old_ut.ut_host, sizeof(ut->ut_host));
627 (void)lseek(fd, (off_t)(tty * sizeof(struct utmp)), SEEK_SET);
628 if (write(fd, ut, sizeof(struct utmp))==-1)
629 log("utmp_write_direct: error writing %s: %s",
630 UTMP_FILE, strerror(errno));
636 } /* utmp_write_direct() */
638 #endif /* UTMP_USE_LIBRARY */
642 utmp_perform_login(struct logininfo *li) {
645 construct_utmp(li, &ut);
647 #ifdef UTMP_USE_LIBRARY
648 if (!utmp_write_library(li, &ut)) {
649 log("utmp_perform_login: utmp_write_library() failed");
653 if (!utmp_write_direct(li, &ut)) {
654 log("utmp_perform_login: utmp_write_direct() failed");
659 } /* utmp_perform_login() */
663 utmp_perform_logout(struct logininfo *li) {
666 memset(&ut, '\0', sizeof(ut));
667 set_utmp_time(li, &ut);
668 line_stripname(ut.ut_line, li->line, sizeof(ut.ut_line));
669 #ifdef HAVE_ID_IN_UTMP
670 line_abbrevname(ut.ut_id, li->line, sizeof(ut.ut_id));
672 #ifdef HAVE_TYPE_IN_UTMP
673 ut.ut_type = DEAD_PROCESS;
676 #if !defined(DISABLE_PUTUTLINE) \
677 && defined(HAVE_SETUTENT) && defined(HAVE_PUTUTLINE)
678 utmp_write_library(li, &ut);
680 utmp_write_direct(li, &ut);
684 } /* utmp_perform_logout() */
688 utmp_write_entry(struct logininfo *li) {
692 return utmp_perform_login(li);
695 return utmp_perform_logout(li);
698 log("utmp_write_entry: invalid type field");
701 } /* utmp_write_entry() */
712 /* not much point if we don't want utmpx entries */
717 /* if we have the wherewithall, use pututxline etc. */
718 #if !defined(DISABLE_PUTUTXLINE) && defined(HAVE_SETUTXENT) \
719 && defined(HAVE_PUTUTXLINE)
720 # define UTMPX_USE_LIBRARY
724 /* write a utmpx entry with the system's help (pututxline() and pals) */
725 #ifdef UTMPX_USE_LIBRARY
727 utmpx_write_library(struct logininfo *li, struct utmpx *utx) {
732 #ifdef HAVE_ENDUTXENT
736 } /* utmpx_write_library() */
739 /* UTMPX_USE_LIBRARY */
742 /* write a utmp entry direct to the file */
744 utmpx_write_direct(struct logininfo *li, struct utmpx *utx) {
746 log("utmpx_write_direct: not implemented!");
748 } /* utmpx_write_direct() */
751 /* UTMPX_USE_LIBRARY */
754 utmpx_perform_login(struct logininfo *li) {
757 construct_utmpx(li, &utx);
759 #ifdef UTMPX_USE_LIBRARY
760 if (!utmpx_write_library(li, &utx)) {
761 log("utmpx_perform_login: utmp_write_library() failed");
765 if (!utmpx_write_direct(li, &ut)) {
766 log("utmpx_perform_login: utmp_write_direct() failed");
771 } /* utmpx_perform_login() */
775 utmpx_perform_logout(struct logininfo *li) {
778 memset(&utx, '\0', sizeof(utx));
779 set_utmpx_time(li, &utx);
780 line_stripname(utx.ut_line, li->line, sizeof(utx.ut_line));
781 #ifdef HAVE_ID_IN_UTMPX
782 line_abbrevname(utx.ut_id, li->line, sizeof(utx.ut_id));
784 #ifdef HAVE_TYPE_IN_UTMPX
785 utx.ut_type = DEAD_PROCESS;
788 #ifdef UTMPX_USE_LIBRARY
789 utmpx_write_library(li, &utx);
791 utmpx_write_direct(li, &utx);
795 } /* utmpx_perform_logout() */
799 utmpx_write_entry(struct logininfo *li) {
803 return utmpx_perform_login(li);
805 return utmpx_perform_logout(li);
807 log("utmpx_write_entry: invalid type field");
810 } /* utmpx_write_entry() */
825 /* write a wtmp entry direct to the end of the file */
826 /* This code is a slight modification of code in OpenBSD's logwtmp.c
827 * (in libutil) and so is subject to the OpenBSD licensing terms */
829 wtmp_write(struct logininfo *li, struct utmp *ut) {
833 if ((fd = open(WTMP_FILE, O_WRONLY|O_APPEND, 0)) < 0) {
834 log("wtmp_write: problem writing %s: %s",
835 WTMP_FILE, strerror(errno));
839 if (fstat(fd, &buf) == 0)
840 if (write(fd, (char *)ut, sizeof(struct utmp)) !=
841 sizeof(struct utmp)) {
842 ftruncate(fd, buf.st_size);
843 log("wtmp_write: problem writing %s: %s",
844 WTMP_FILE, strerror(errno));
855 wtmp_perform_login(struct logininfo *li) {
858 construct_utmp(li, &ut);
859 return wtmp_write(li, &ut);
860 } /* wtmp_perform_login() */
864 wtmp_perform_logout(struct logininfo *li) {
867 construct_utmp(li, &ut);
868 /* blank out unnecessary fields */
869 memset(&(ut.ut_name), '\0', sizeof(ut.ut_name));
870 #ifdef HAVE_ID_IN_UTMP
871 memset(&(ut.ut_id), '\0', sizeof(ut.ut_id));
873 #ifdef HAVE_HOST_IN_UTMP
874 memset(&(ut.ut_host), '\0', sizeof(ut.ut_host));
876 #ifdef HAVE_ADDR_IN_UTMP
877 memset(&(ut.ut_addr), '\0', sizeof(ut.ut_addr));
879 return wtmp_write(li, &ut);
880 } /* wtmp_perform_logout() */
884 wtmp_write_entry(struct logininfo *li) {
888 return wtmp_perform_login(li);
890 return wtmp_perform_logout(li);
892 log("wtmp_write_entry: invalid type field");
895 } /* wtmp_write_entry() */
900 wtmp_get_entry(struct logininfo *li) {
905 if ((fd = open(WTMP_FILE, O_RDONLY)) < 0) {
906 log("wtmp_get_entry: problem opening %s: %s",
907 WTMP_FILE, strerror(errno));
911 if (fstat(fd, &st) != 0) {
912 log("wtmp_get_entry: couldn't stat %s: %s",
913 WTMP_FILE, strerror(errno));
918 (void)lseek(fd, (off_t)(0-sizeof(struct utmp)), SEEK_END);
921 if (read(fd, &ut, sizeof(ut)) != sizeof(ut)) {
922 log("wtmp_get_entry: read of %s failed: %s",
923 WTMP_FILE, strerror(errno));
928 /* Logouts are recorded as a blank username on a particular line.
929 * So, we just need to find the username in struct utmp */
930 if ( strncmp(li->username, ut.ut_user, 8) == 0 ) {
931 /* note we've already made sure there's a time in struct utmp */
932 #ifdef HAVE_TIME_IN_UTMP
933 li->tv_sec = ut.ut_time;
936 li->tv_sec = ut.ut_tv.tv_sec;
939 line_fullname(li->line, ut.ut_line, sizeof(ut.ut_line));
940 #ifdef HAVE_HOST_IN_UTMP
941 strlcpy(li->hostname, ut.ut_host, sizeof(ut.ut_host));
944 if (lseek(fd, (off_t)(0-2*sizeof(struct utmp)), SEEK_CUR) == -1) {
948 } while (li->tv_sec == 0);
951 } /* wtmp_get_entry() */
966 /* write a wtmpx entry direct to the end of the file */
967 /* This code is a slight modification of code in OpenBSD's logwtmp.c
968 * (in libutil) and so is subject to the OpenBSD licensing terms */
970 wtmpx_write(struct logininfo *li, struct utmpx *utx) {
974 if ((fd = open(WTMPX_FILE, O_WRONLY|O_APPEND, 0)) < 0) {
975 log("wtmpx_write: problem opening %s: %s",
976 WTMPX_FILE, strerror(errno));
980 if (fstat(fd, &buf) == 0)
981 if (write(fd, (char *)utx, sizeof(struct utmpx)) !=
982 sizeof(struct utmpx)) {
983 ftruncate(fd, buf.st_size);
984 log("wtmpx_write: problem writing %s: %s",
985 WTMPX_FILE, strerror(errno));
991 } /* wtmpx_write() */
996 wtmpx_perform_login(struct logininfo *li) {
999 construct_utmpx(li, &utx);
1000 return wtmpx_write(li, &utx);
1001 } /* wtmpx_perform_login() */
1005 wtmpx_perform_logout(struct logininfo *li) {
1008 construct_utmpx(li, &utx);
1009 /* blank out unnecessary fields */
1010 memset(&(utx.ut_name), '\0', sizeof(utx.ut_name));
1011 #ifdef HAVE_ID_IN_UTMPX
1012 memset(&(utx.ut_id), '\0', sizeof(utx.ut_id));
1014 #ifdef HAVE_HOST_IN_UTMPX
1015 memset(&(utx.ut_host), '\0', sizeof(utx.ut_host));
1017 #ifdef HAVE_ADDR_IN_UTMPX
1018 memset(&(utx.ut_addr), '\0', sizeof(utx.ut_addr));
1020 return wtmpx_write(li, &utx);
1022 } /* wtmpx_perform_logout() */
1026 wtmpx_write_entry(struct logininfo *li) {
1030 return wtmpx_perform_login(li);
1032 return wtmpx_perform_logout(li);
1034 log("wtmpx_write_entry: invalid type field");
1037 } /* wtmpx_write_entry() */
1042 wtmpx_get_entry(struct logininfo *li) {
1047 if ((fd = open(WTMPX_FILE, O_RDONLY)) < 0) {
1048 log("wtmpx_get_entry: problem opening %s: %s",
1049 WTMPX_FILE, strerror(errno));
1053 if (fstat(fd, &st) != 0) {
1054 log("wtmpx_get_entry: couldn't stat %s: %s",
1055 WTMP_FILE, strerror(errno));
1060 (void)lseek(fd, (off_t)(0-sizeof(struct utmpx)), SEEK_END);
1063 if (read(fd, &utx, sizeof(utx)) != sizeof(utx)) {
1064 log("wtmpx_get_entry: read of %s failed: %s",
1065 WTMPX_FILE, strerror(errno));
1070 /* Logouts are recorded as a blank username on a particular line.
1071 * So, we just need to find the username in struct utmpx */
1072 if ( strncmp(li->username, utx.ut_user, 8) == 0 ) {
1073 /* note we've already made sure there's a time in struct utmp */
1074 #ifdef HAVE_TV_IN_UTMPX
1075 li->tv_sec = utx.ut_tv.tv_sec;
1077 # ifdef HAVE_TIME_IN_UTMPX
1078 li->tv_sec = utx.ut_time;
1081 line_fullname(li->line, utx.ut_line, sizeof(utx.ut_line));
1082 #ifdef HAVE_HOST_IN_UTMPX
1083 strlcpy(li->hostname, utx.ut_host, sizeof(utx.ut_line));
1086 if (lseek(fd, (off_t)(0-2*sizeof(struct utmpx)), SEEK_CUR) == -1) {
1090 } while (li->tv_sec == 0);
1092 } /* wtmpx_get_entry() */
1102 ** libutil login() functions
1114 # include <sys/time.h>
1120 syslogin_perform_login(struct logininfo *li) {
1123 if (! (ut = (struct utmp *)malloc(sizeof(struct utmp)))) {
1124 log("syslogin_perform_login: couldn't malloc()");
1127 construct_utmp(li, ut);
1131 } /* syslogin_perform_login() */
1134 syslogin_perform_logout(struct logininfo *li) {
1139 (void)line_stripname(line, li->line, sizeof(line));
1141 if (!logout(line)) {
1142 log("syslogin_perform_logout: logout() returned an error");
1143 # ifdef HAVE_LOGWTMP
1145 logwtmp(line, "", "");
1148 /* TODO: what to do if we have login, but no logout?
1149 * what if logout but no logwtmp? All routines are in libutil
1150 * so they should all be there, but... */
1153 } /* syslogin_perform_logout() */
1158 syslogin_write_entry(struct logininfo *li) {
1162 return syslogin_perform_login(li);
1164 return syslogin_perform_logout(li);
1166 log("syslogin_write_entry: Invalid type field");
1169 } /* utmp_write_entry() */
1175 /* end of file log-syslogin.c */
1179 ** lastlog functions
1184 #ifdef HAVE_LASTLOG_H
1185 # include <lastlog.h>
1187 # if !defined(USE_UTMP) && !defined(USE_WTMP)
1194 lastlog_construct(struct logininfo *li,
1195 struct lastlog *last) {
1196 /* clear the structure */
1197 memset(last, '\0', sizeof(struct lastlog));
1199 (void)line_stripname(last->ll_line, li->line,
1200 sizeof(last->ll_line));
1201 strlcpy(last->ll_host, li->hostname, sizeof(last->ll_host));
1202 last->ll_time = li->tv_sec;
1203 } /* lastlog_construct() */
1211 lastlog_filetype(char *filename) {
1214 if ( stat(LASTLOG_FILE, &st) != 0) {
1215 log("lastlog_perform_login: Couldn't stat %s: %s",
1216 LASTLOG_FILE, strerror(errno));
1220 if (S_ISDIR(st.st_mode))
1222 else if (S_ISREG(st.st_mode))
1226 } /* lastlog_filetype() */
1229 /* open the file (using filemode) and seek to the login entry */
1231 lastlog_openseek(struct logininfo *li, int *fd, int filemode) {
1235 char lastlog_file[1024];
1237 type = lastlog_filetype(LASTLOG_FILE);
1240 strlcpy(lastlog_file, LASTLOG_FILE, sizeof(lastlog_file));
1243 snprintf(lastlog_file, sizeof(lastlog_file), "%s/%s",
1244 LASTLOG_FILE, li->username);
1247 log("lastlog_openseek: %.100s is not a file or directory!",
1252 *fd = open(lastlog_file, filemode);
1254 log("lastlog_openseek: Couldn't open %s: %s",
1255 lastlog_file, strerror(errno));
1259 /* find this uid's offset in the lastlog file */
1260 offset = (off_t) ( (long)li->uid * sizeof(struct lastlog));
1262 if ( lseek(*fd, offset, SEEK_SET) != offset ) {
1263 log("lastlog_openseek: %s->lseek(): %s",
1264 lastlog_file, strerror(errno));
1268 } /* lastlog_openseek() */
1271 lastlog_perform_login(struct logininfo *li) {
1272 struct lastlog last;
1275 /* create our struct lastlog */
1276 lastlog_construct(li, &last);
1278 /* write the entry */
1279 if (lastlog_openseek(li, &fd, O_RDWR)) {
1280 if ( write(fd, &last, sizeof(struct lastlog))
1281 != sizeof(struct lastlog) ) {
1282 log("lastlog_write_filemode: Error writing to %s: %s",
1283 LASTLOG_FILE, strerror(errno));
1289 } /* lastlog_perform_login() */
1293 lastlog_write_entry(struct logininfo *li) {
1297 return lastlog_perform_login(li);
1299 log("lastlog_write_entry: Invalid type field");
1302 } /* lastlog_write_entry() */
1307 lastlog_populate_entry(struct logininfo *li,
1308 struct lastlog *last) {
1309 line_fullname(li->line, last->ll_line, sizeof(li->line));
1310 strlcpy(li->hostname, last->ll_host, sizeof(li->hostname));
1311 li->tv_sec = last->ll_time;
1312 } /* lastlog_populate_entry() */
1317 lastlog_get_entry(struct logininfo *li) {
1318 struct lastlog last;
1321 if (lastlog_openseek(li, &fd, O_RDONLY)) {
1322 if ( read(fd, &last, sizeof(struct lastlog))
1323 != sizeof(struct lastlog) ) {
1324 log("lastlog_write_filemode: Error reading from %s: %s",
1325 LASTLOG_FILE, strerror(errno));
1328 lastlog_populate_entry(li, &last);
1334 } /* lastlog_get_entry() */
1342 ** lastlog retrieval functions
1345 /* take the uid in li and return the last login time */
1347 getlast_entry(struct logininfo *li) {
1350 if (lastlog_get_entry(li))
1356 /* Try to retrieve the last login time from another source */
1358 # if defined(USE_WTMP) && (defined(HAVE_TIME_IN_UTMP) || defined(HAVE_TV_IN_UTMP))
1360 /* retrieve last login time from utmp */
1361 if (wtmp_get_entry(li))
1367 # if defined(USE_WTMPX) && (defined(HAVE_TIME_IN_UTMPX) || defined(HAVE_TV_IN_UTMPX))
1369 /* retrieve last login time from utmpx */
1370 if (wtmpx_get_entry(li))
1377 /* no means of retrieving last login time */