]> andersk Git - openssh.git/blob - loginrec.c
Two Ooops. =)
[openssh.git] / loginrec.c
1 /*
2  * Copyright (c) 2000 Andre Lucas.  All rights reserved.
3  * Portions copyright (c) 1998 Todd C. Miller
4  * Portions copyright (c) 1996 Jason Downs
5  * Portions copyright (c) 1996 Theo de Raadt
6  *
7  * Redistribution and use in source and binary forms, with or without
8  * modification, are permitted provided that the following conditions
9  * are met:
10  * 1. Redistributions of source code must retain the above copyright
11  *    notice, this list of conditions and the following disclaimer.
12  * 2. Redistributions in binary form must reproduce the above copyright
13  *    notice, this list of conditions and the following disclaimer in the
14  *    documentation and/or other materials provided with the distribution.
15  * 3. All advertising materials mentioning features or use of this software
16  *    must display the following acknowledgement:
17  *      This product includes software developed by Markus Friedl.
18  * 4. The name of the author may not be used to endorse or promote products
19  *    derived from this software without specific prior written permission.
20  *
21  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
22  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
23  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
24  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
25  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
26  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
27  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
28  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
29  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
30  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
31  */
32
33 /** 
34  ** loginrec.c:  platform-independent login recording and lastlog retrieval
35  **/
36
37 /*
38   The new login code explained
39   ============================
40
41   This code attempts to provide a common interface to login recording
42   (utmp and friends) and last login time retrieval.
43
44   Its primary means of achieving this is to use 'struct logininfo', a
45   union of all the useful fields in the various different types of
46   system login record structures one finds on UNIX variants.
47
48   We depend on autoconf to define which recording methods are to be
49   used, and which fields are contained in the relevant data structures
50   on the local system. Many C preprocessor symbols affect which code
51   gets compiled here.
52
53   The code is designed to make it easy to modify a particular
54   recording method, without affecting other methods nor requiring so
55   many nested conditional compilation blocks as were commonplace in
56   the old code.
57
58   For login recording, we try to use the local system's libraries as
59   these are clearly most likely to work correctly. For utmp systems
60   this usually means login() and logout() or setutent() etc., probably
61   in libutil, along with logwtmp() etc. On these systems, we fall back
62   to writing the files directly if we have to, though this method
63   requires very thorough testing so we do not corrupt local auditing
64   information. These files and their access methods are very system
65   specific indeed.
66   
67   For utmpx systems, the corresponding library functions are
68   setutxent() etc. To the author's knowledge, all utmpx systems have
69   these library functions and so no direct write is attempted. If such
70   a system exists and needs support, direct analogues of the [uw]tmp
71   code should suffice.
72
73   Retrieving the time of last login ('lastlog') is in some ways even
74   more problemmatic than login recording. Some systems provide a
75   simple table of all users which we seek based on uid and retrieve a
76   relatively standard structure. Others record the same information in
77   a directory with a separate file, and others don't record the
78   information separately at all. For systems in the latter category,
79   we look backwards in the wtmp or wtmpx file for the last login entry
80   for our user. Naturally this is slower and on busy systems could
81   incur a significant performance penalty.
82
83   Calling the new code
84   --------------------
85   
86   In OpenSSH all login recording and retrieval is performed in
87   login.c. Here you'll find working examples. Also, in the logintest.c
88   program there are more examples.
89
90   Internal handler calling method
91   -------------------------------
92   
93   When a call is made to login_login() or login_logout(), both
94   routines set a struct logininfo flag defining which action (log in,
95   or log out) is to be taken. They both then call login_write(), which
96   calls whichever of the many structure-specific handlers autoconf
97   selects for the local system.
98
99   The handlers themselves handle system data structure specifics. Both
100   struct utmp and struct utmpx have utility functions (see
101   construct_utmp*()) to try to make it simpler to add extra systems
102   that introduce new features to either structure.
103
104   While it may seem terribly wasteful to replicate so much similar
105   code for each method, experience has shown that maintaining code to
106   write both struct utmp and utmpx in one function, whilst maintaining
107   support for all systems whether they have library support or not, is
108   a difficult and time-consuming task.
109
110   Lastlog support proceeds similarly. Functions login_get_lastlog()
111   (and its OpenSSH-tuned friend login_get_lastlog_time()) call
112   getlast_entry(), which tries one of three methods to find the last
113   login time. It uses local system lastlog support if it can,
114   otherwise it tries wtmp or wtmpx before giving up and returning 0,
115   meaning "tilt".
116
117   Maintenance
118   -----------
119
120   In many cases it's possible to tweak autoconf to select the correct
121   methods for a particular platform, either by improving the detection
122   code (best), or by presetting DISABLE_<method> or CONF_<method>_FILE
123   symbols for the platform.
124
125   Use logintest to check which symbols are defined before modifying
126   configure.in and loginrec.c. (You have to build logintest yourself
127   with 'make logintest' as it's not built by default.)
128
129   Otherwise, patches to the specific method(s) are very helpful!
130   
131 */
132
133 /**
134  ** TODO:
135  **   homegrown ttyslot()
136  **   test, test, test
137  **
138  ** Platform status:
139  ** ----------------
140  **
141  ** Known good:
142  **   Linux (Redhat 6.2, Debian)
143  **   Solaris
144  **   HP-UX 10.20 (gcc only)
145  **   IRIX
146  **   NeXT - M68k/HPPA/Sparc (4.2/3.3)
147  **
148  ** Testing required: Please send reports!
149  **   NetBSD
150  **   HP-UX 11
151  **   AIX
152  **
153  ** Platforms with known problems:
154  **   Some variants of Slackware Linux
155  **
156  **/
157
158 #include "includes.h"
159
160 #include "ssh.h"
161 #include "xmalloc.h"
162 #include "loginrec.h"
163
164 RCSID("$Id$");
165
166 #ifdef HAVE_UTIL_H
167 #  include <util.h>
168 #endif
169
170 /**
171  ** prototypes for helper functions in this file
172  **/
173
174 #if HAVE_UTMP_H
175 void set_utmp_time(struct logininfo *li, struct utmp *ut);
176 void construct_utmp(struct logininfo *li, struct utmp *ut);
177 #endif
178
179 #ifdef HAVE_UTMPX_H
180 void set_utmpx_time(struct logininfo *li, struct utmpx *ut);
181 void construct_utmpx(struct logininfo *li, struct utmpx *ut);
182 #endif
183
184 int utmp_write_entry(struct logininfo *li);
185 int utmpx_write_entry(struct logininfo *li);
186 int wtmp_write_entry(struct logininfo *li);
187 int wtmpx_write_entry(struct logininfo *li);
188 int lastlog_write_entry(struct logininfo *li);
189 int syslogin_write_entry(struct logininfo *li);
190
191 int getlast_entry(struct logininfo *li);
192 int lastlog_get_entry(struct logininfo *li);
193 int wtmp_get_entry(struct logininfo *li);
194 int wtmpx_get_entry(struct logininfo *li);
195
196 /* pick the shortest string */
197 #define MIN_SIZEOF(s1,s2) ( sizeof(s1) < sizeof(s2) ? sizeof(s1) : sizeof(s2) )
198
199 /**
200  ** platform-independent login functions
201  **/
202
203 /* login_login(struct logininfo *)     -Record a login
204  * 
205  * Call with a pointer to a struct logininfo initialised with
206  * login_init_entry() or login_alloc_entry()
207  *
208  * Returns:
209  *  >0 if successful
210  *  0  on failure (will use OpenSSH's logging facilities for diagnostics)
211  */
212 int
213 login_login (struct logininfo *li)
214 {
215         li->type = LTYPE_LOGIN;
216         return login_write(li);
217 }
218
219
220 /* login_logout(struct logininfo *)     - Record a logout
221  *
222  * Call as with login_login()
223  *
224  * Returns:
225  *  >0 if successful
226  *  0  on failure (will use OpenSSH's logging facilities for diagnostics)
227  */
228 int
229 login_logout(struct logininfo *li)
230 {
231         li->type = LTYPE_LOGOUT;
232         return login_write(li);
233 }
234
235 /* login_get_lastlog_time(int)           - Retrieve the last login time
236  *
237  * Retrieve the last login time for the given uid. Will try to use the
238  * system lastlog facilities if they are available, but will fall back
239  * to looking in wtmp/wtmpx if necessary
240  *
241  * Returns:
242  *   0 on failure, or if user has never logged in
243  *   Time in seconds from the epoch if successful
244  *
245  * Useful preprocessor symbols:
246  *   DISABLE_LASTLOG: If set, *never* even try to retrieve lastlog
247  *                    info
248  *   USE_LASTLOG: If set, indicates the presence of system lastlog
249  *                facilities. If this and DISABLE_LASTLOG are not set,
250  *                try to retrieve lastlog information from wtmp/wtmpx.
251  */
252 unsigned int
253 login_get_lastlog_time(const int uid)
254 {
255         struct logininfo li;
256
257         if (login_get_lastlog(&li, uid))
258                 return li.tv_sec;
259         else
260                 return 0;
261 }
262
263 /* login_get_lastlog(struct logininfo *, int)   - Retrieve a lastlog entry
264  *
265  * Retrieve a logininfo structure populated (only partially) with
266  * information from the system lastlog data, or from wtmp/wtmpx if no
267  * system lastlog information exists.
268  *
269  * Note this routine must be given a pre-allocated logininfo.
270  *
271  * Returns:
272  *  >0: A pointer to your struct logininfo if successful
273  *  0  on failure (will use OpenSSH's logging facilities for diagnostics)
274  *
275  */
276 struct logininfo *
277 login_get_lastlog(struct logininfo *li, const int uid)
278 {
279         struct passwd *pw;
280
281         memset(li, '\0', sizeof(*li));
282         li->uid = uid;
283
284         /* 
285          * If we don't have a 'real' lastlog, we need the username to
286          * reliably search wtmp(x) for the last login (see
287          * wtmp_get_entry().) 
288          */
289         pw = getpwuid(uid);
290         if (pw == NULL)
291                 fatal("login_get_lastlog: Cannot find account for uid %i", uid);
292                 
293         /* No MIN_SIZEOF here - we absolutely *must not* truncate the
294          * username */
295         strlcpy(li->username, pw->pw_name, sizeof(li->username));
296
297         if (getlast_entry(li))
298                 return li;
299         else
300                 return NULL;
301 }
302
303
304 /* login_alloc_entry(int, char*, char*, char*)    - Allocate and initialise
305  *                                                  a logininfo structure 
306  * 
307  * This function creates a new struct logininfo, a data structure
308  * meant to carry the information required to portably record login info.
309  *
310  * Returns a pointer to a newly created struct logininfo. If memory
311  * allocation fails, the program halts.
312  */
313 struct
314 logininfo *login_alloc_entry(int pid, const char *username,
315                              const char *hostname, const char *line)
316 {
317         struct logininfo *newli;
318
319         newli = (struct logininfo *) xmalloc (sizeof(*newli));
320         (void)login_init_entry(newli, pid, username, hostname, line);
321         return newli;
322 }
323
324
325 /* login_free_entry(struct logininfo *)    - free struct memory */
326 void
327 login_free_entry(struct logininfo *li)
328 {
329         xfree(li);
330 }
331
332
333 /* login_init_entry(struct logininfo *, int, char*, char*, char*)
334  *                                        - initialise a struct logininfo
335  * 
336  * Populates a new struct logininfo, a data structure meant to carry
337  * the information required to portably record login info.
338  *
339  * Returns: 1
340  */
341 int
342 login_init_entry(struct logininfo *li, int pid, const char *username, 
343                  const char *hostname, const char *line)
344 {
345         struct passwd *pw;
346         
347         memset(li, 0, sizeof(*li));
348   
349         li->pid = pid;
350
351         /* set the line information */
352         if (line)
353                 line_fullname(li->line, line, sizeof(li->line));
354
355         if (username) {
356                 strlcpy(li->username, username, sizeof(li->username));
357                 pw = getpwnam(li->username);
358                 if (pw == NULL)
359                         fatal("login_init_entry: Cannot find user \"%s\"", li->username);
360                 li->uid = pw->pw_uid;
361         }
362
363         if (hostname)
364                 strlcpy(li->hostname, hostname, sizeof(li->hostname));
365
366         return 1;
367 }
368
369 /* login_set_current_time(struct logininfo *)    - set the current time
370  *
371  * Set the current time in a logininfo structure. This function is
372  * meant to eliminate the need to deal with system dependencies for
373  * time handling.
374  */
375 void
376 login_set_current_time(struct logininfo *li)
377 {
378         struct timeval tv;
379
380         gettimeofday(&tv, NULL);
381         
382         li->tv_sec = tv.tv_sec;
383         li->tv_usec = tv.tv_usec;
384 }
385
386 /* copy a sockaddr_* into our logininfo */
387 void
388 login_set_addr(struct logininfo *li, const struct sockaddr *sa,
389                const unsigned int sa_size)
390 {
391         unsigned int bufsize = sa_size;
392
393         /* make sure we don't overrun our union */
394         if (sizeof(li->hostaddr) < sa_size)
395                 bufsize = sizeof(li->hostaddr);
396
397         memcpy((void *)&(li->hostaddr.sa), (const void *)sa, bufsize);
398 }
399
400
401 /**
402  ** login_write: Call low-level recording functions based on autoconf
403  ** results
404  **/
405 int
406 login_write (struct logininfo *li)
407 {
408 #ifndef HAVE_CYGWIN
409         if ((int)geteuid() != 0) {
410           log("Attempt to write login records by non-root user (aborting)");
411           return 1;
412         }
413 #endif
414
415         /* set the timestamp */
416         login_set_current_time(li);
417 #ifdef USE_LOGIN
418         syslogin_write_entry(li);
419 #endif
420 #ifdef USE_LASTLOG
421         if (li->type == LTYPE_LOGIN) {
422                 lastlog_write_entry(li);
423         }
424 #endif
425 #ifdef USE_UTMP
426         utmp_write_entry(li);
427 #endif
428 #ifdef USE_WTMP
429         wtmp_write_entry(li);
430 #endif
431 #ifdef USE_UTMPX
432         utmpx_write_entry(li);
433 #endif
434 #ifdef USE_WTMPX
435         wtmpx_write_entry(li);
436 #endif
437         return 0;
438 }
439
440 /**
441  ** getlast_entry: Call low-level functions to retrieve the last login
442  **                time.
443  **/
444
445 /* take the uid in li and return the last login time */
446 int
447 getlast_entry(struct logininfo *li)
448 {
449 #ifdef USE_LASTLOG
450         return(lastlog_get_entry(li));
451 #else /* !USE_LASTLOG */
452
453 #ifdef DISABLE_LASTLOG
454         /* On some systems we shouldn't even try to obtain last login 
455          * time, e.g. AIX */
456         return 0;
457 # else /* DISABLE_LASTLOG */
458         /* Try to retrieve the last login time from wtmp */
459 #  if defined(USE_WTMP) && (defined(HAVE_TIME_IN_UTMP) || defined(HAVE_TV_IN_UTMP))
460         /* retrieve last login time from utmp */
461         return (wtmp_get_entry(li));
462 #  else /* defined(USE_WTMP) && (defined(HAVE_TIME_IN_UTMP) || defined(HAVE_TV_IN_UTMP)) */
463         /* If wtmp isn't available, try wtmpx */
464 #   if defined(USE_WTMPX) && (defined(HAVE_TIME_IN_UTMPX) || defined(HAVE_TV_IN_UTMPX))
465         /* retrieve last login time from utmpx */
466         return (wtmpx_get_entry(li));
467 #   else
468         /* Give up: No means of retrieving last login time */
469         return 0;
470 #   endif /* USE_WTMPX && (HAVE_TIME_IN_UTMPX || HAVE_TV_IN_UTMPX) */
471 #  endif /* USE_WTMP && (HAVE_TIME_IN_UTMP || HAVE_TV_IN_UTMP) */
472 # endif /* DISABLE_LASTLOG */ 
473 #endif /* USE_LASTLOG */
474 }
475
476
477
478 /*
479  * 'line' string utility functions
480  *
481  * These functions process the 'line' string into one of three forms:
482  *
483  * 1. The full filename (including '/dev')
484  * 2. The stripped name (excluding '/dev')
485  * 3. The abbreviated name (e.g. /dev/ttyp00 -> yp00
486  *                               /dev/pts/1  -> ts/1 )
487  *
488  * Form 3 is used on some systems to identify a .tmp.? entry when
489  * attempting to remove it. Typically both addition and removal is
490  * performed by one application - say, sshd - so as long as the choice
491  * uniquely identifies a terminal it's ok.
492  */
493
494
495 /* line_fullname(): add the leading '/dev/' if it doesn't exist make
496  * sure dst has enough space, if not just copy src (ugh) */
497 char *
498 line_fullname(char *dst, const char *src, int dstsize)
499 {
500         memset(dst, '\0', dstsize);
501         if ((strncmp(src, "/dev/", 5) == 0) || (dstsize < (strlen(src) + 5))) {
502                 strlcpy(dst, src, dstsize);
503         } else {
504                 strlcpy(dst, "/dev/", dstsize);
505                 strlcat(dst, src, dstsize);
506         }
507         return dst;
508 }
509
510 /* line_stripname(): strip the leading '/dev' if it exists, return dst */
511 char *
512 line_stripname(char *dst, const char *src, int dstsize)
513 {
514         memset(dst, '\0', dstsize);
515 #ifdef sgi
516         if (strncmp(src, "/dev/tty", 8) == 0)
517                 strlcpy(dst, src + 8, dstsize);
518 #else
519         if (strncmp(src, "/dev/", 5) == 0)
520                 strlcpy(dst, src + 5, dstsize);
521 #endif
522         else
523                 strlcpy(dst, src, dstsize);
524         return dst;
525 }
526
527 /* line_abbrevname(): Return the abbreviated (usually four-character)
528  * form of the line (Just use the last <dstsize> characters of the
529  * full name.)
530  *
531  * NOTE: use strncpy because we do NOT necessarily want zero
532  * termination */
533 char *
534 line_abbrevname(char *dst, const char *src, int dstsize) 
535 {
536         size_t len;
537         
538         memset(dst, '\0', dstsize);
539         
540         /* Always skip prefix if present */
541 #ifdef sgi
542         if (strncmp(src, "/dev/tty", 8) == 0)
543                 src += 8;
544 #else
545         if (strncmp(src, "/dev/", 5) == 0)
546                 src += 5;
547 #endif
548                 
549         len = strlen(src);
550
551         if (len > 0) {
552                 if (((int)len - dstsize) > 0)
553                         src +=  ((int)len - dstsize);
554
555                 /* note: _don't_ change this to strlcpy */
556                 strncpy(dst, src, (size_t)dstsize); 
557         }
558         
559         return dst;
560 }
561
562 /**
563  ** utmp utility functions
564  **
565  ** These functions manipulate struct utmp, taking system differences
566  ** into account.
567  **/
568
569 #if defined(USE_UTMP) || defined (USE_WTMP) || defined (USE_LOGIN)
570
571 /* build the utmp structure */
572 void
573 set_utmp_time(struct logininfo *li, struct utmp *ut)
574 {
575 # ifdef HAVE_TV_IN_UTMP
576         ut->ut_tv.tv_sec = li->tv_sec;
577         ut->ut_tv.tv_usec = li->tv_usec;
578 # else
579 #  ifdef HAVE_TIME_IN_UTMP
580         ut->ut_time = li->tv_sec;
581 #  endif
582 # endif
583 }
584
585 void
586 construct_utmp(struct logininfo *li,
587                     struct utmp *ut)
588 {
589         memset(ut, '\0', sizeof(*ut));
590
591         /* First fill out fields used for both logins and logouts */
592
593 # ifdef HAVE_ID_IN_UTMP
594         line_abbrevname(ut->ut_id, li->line, sizeof(ut->ut_id));
595 # endif
596
597 # ifdef HAVE_TYPE_IN_UTMP
598         /* This is done here to keep utmp constants out of struct logininfo */
599         switch (li->type) {
600         case LTYPE_LOGIN:
601                 ut->ut_type = USER_PROCESS;
602                 break;
603         case LTYPE_LOGOUT:
604                 ut->ut_type = DEAD_PROCESS;
605                 break;
606         }
607 # endif
608         set_utmp_time(li, ut);
609
610         line_stripname(ut->ut_line, li->line, sizeof(ut->ut_line));
611
612 # ifdef HAVE_PID_IN_UTMP
613         ut->ut_pid = li->pid;
614 # endif
615
616         /* If we're logging out, leave all other fields blank */
617         if (li->type == LTYPE_LOGOUT)
618           return;
619
620         /*
621          * These fields are only used when logging in, and are blank
622          * for logouts. 
623          */
624
625         /* Use strncpy because we don't necessarily want null termination */
626         strncpy(ut->ut_name, li->username, MIN_SIZEOF(ut->ut_name, li->username));
627 # ifdef HAVE_HOST_IN_UTMP
628         strncpy(ut->ut_host, li->hostname, MIN_SIZEOF(ut->ut_host, li->hostname));
629 # endif
630 # ifdef HAVE_ADDR_IN_UTMP
631         /* this is just a 32-bit IP address */
632         if (li->hostaddr.sa.sa_family == AF_INET)
633                 ut->ut_addr = li->hostaddr.sa_in.sin_addr.s_addr;
634 # endif 
635 }
636 #endif /* USE_UTMP || USE_WTMP || USE_LOGIN */
637
638 /**
639  ** utmpx utility functions
640  **
641  ** These functions manipulate struct utmpx, accounting for system
642  ** variations.
643  **/
644
645 #if defined(USE_UTMPX) || defined (USE_WTMPX)
646 /* build the utmpx structure */
647 void
648 set_utmpx_time(struct logininfo *li, struct utmpx *utx)
649 {
650 # ifdef HAVE_TV_IN_UTMPX
651         utx->ut_tv.tv_sec = li->tv_sec;
652         utx->ut_tv.tv_usec = li->tv_usec;
653 # else /* HAVE_TV_IN_UTMPX */
654 #  ifdef HAVE_TIME_IN_UTMPX
655         utx->ut_time = li->tv_sec;
656 #  endif /* HAVE_TIME_IN_UTMPX */
657 # endif /* HAVE_TV_IN_UTMPX */
658 }
659
660 void
661 construct_utmpx(struct logininfo *li, struct utmpx *utx)
662 {
663         memset(utx, '\0', sizeof(*utx));
664 # ifdef HAVE_ID_IN_UTMPX
665         line_abbrevname(utx->ut_id, li->line, sizeof(utx->ut_id));
666 # endif
667
668         /* this is done here to keep utmp constants out of loginrec.h */
669         switch (li->type) {
670         case LTYPE_LOGIN:
671                 utx->ut_type = USER_PROCESS;
672                 break;
673         case LTYPE_LOGOUT:
674                 utx->ut_type = DEAD_PROCESS;
675                 break;
676         }
677         line_stripname(utx->ut_line, li->line, sizeof(utx->ut_line));
678         set_utmpx_time(li, utx);
679         utx->ut_pid = li->pid;
680
681         if (li->type == LTYPE_LOGOUT)
682                 return;
683
684         /*
685          * These fields are only used when logging in, and are blank
686          * for logouts. 
687          */
688
689         /* strncpy(): Don't necessarily want null termination */
690         strncpy(utx->ut_name, li->username, MIN_SIZEOF(utx->ut_name, li->username));
691 # ifdef HAVE_HOST_IN_UTMPX
692         strncpy(utx->ut_host, li->hostname, MIN_SIZEOF(utx->ut_host, li->hostname));
693 # endif
694 # ifdef HAVE_ADDR_IN_UTMPX
695         /* this is just a 32-bit IP address */
696         if (li->hostaddr.sa.sa_family == AF_INET)
697                 utx->ut_addr = li->hostaddr.sa_in.sin_addr.s_addr;
698 # endif
699 # ifdef HAVE_SYSLEN_IN_UTMPX
700         /* ut_syslen is the length of the utx_host string */
701         utx->ut_syslen = MIN(strlen(li->hostname), sizeof(utx->ut_host));
702 # endif
703 }
704 #endif /* USE_UTMPX || USE_WTMPX */
705
706 /**
707  ** Low-level utmp functions
708  **/
709
710 /* FIXME: (ATL) utmp_write_direct needs testing */
711 #ifdef USE_UTMP
712
713 /* if we can, use pututline() etc. */
714 # if !defined(DISABLE_PUTUTLINE) && defined(HAVE_SETUTENT) && \
715         defined(HAVE_PUTUTLINE)
716 #  define UTMP_USE_LIBRARY
717 # endif
718
719
720 /* write a utmp entry with the system's help (pututline() and pals) */
721 # ifdef UTMP_USE_LIBRARY
722 static int
723 utmp_write_library(struct logininfo *li, struct utmp *ut)
724 {
725         setutent();
726         pututline(ut);
727
728 #  ifdef HAVE_ENDUTENT
729         endutent();
730 #  endif
731         return 1;
732 }
733 # else /* UTMP_USE_LIBRARY */
734
735 /* write a utmp entry direct to the file */
736 /* This is a slightly modification of code in OpenBSD's login.c */
737 static int
738 utmp_write_direct(struct logininfo *li, struct utmp *ut)
739 {
740         struct utmp old_ut;
741         register int fd;
742         int tty;
743
744         /* FIXME: (ATL) ttyslot() needs local implementation */
745
746 #if defined(HAVE_GETTTYENT)
747         register struct ttyent *ty;
748
749         tty=0;
750
751         setttyent();
752         while ((struct ttyent *)0 != (ty = getttyent())) {
753                 tty++;
754                 if (!strncmp(ty->ty_name, ut->ut_line, sizeof(ut->ut_line)))
755                         break;
756         }
757         endttyent();
758
759         if((struct ttyent *)0 == ty) {
760                 log("utmp_write_entry: tty not found");
761                 return(1);
762         }
763 #else /* FIXME */
764
765         tty = ttyslot(); /* seems only to work for /dev/ttyp? style names */
766
767 #endif /* HAVE_GETTTYENT */
768
769         if (tty > 0 && (fd = open(UTMP_FILE, O_RDWR|O_CREAT, 0644)) >= 0) {
770                 (void)lseek(fd, (off_t)(tty * sizeof(struct utmp)), SEEK_SET);
771                 /*
772                  * Prevent luser from zero'ing out ut_host.
773                  * If the new ut_line is empty but the old one is not
774                  * and ut_line and ut_name match, preserve the old ut_line.
775                  */
776                 if (atomicio(read, fd, &old_ut, sizeof(old_ut)) == sizeof(old_ut) && 
777                         (ut->ut_host[0] == '\0') && (old_ut.ut_host[0] != '\0') && 
778                         (strncmp(old_ut.ut_line, ut->ut_line, sizeof(ut->ut_line)) == 0) && 
779                         (strncmp(old_ut.ut_name, ut->ut_name, sizeof(ut->ut_name)) == 0)) {
780                         (void)memcpy(ut->ut_host, old_ut.ut_host, sizeof(ut->ut_host));
781                 }
782                 
783                 (void)lseek(fd, (off_t)(tty * sizeof(struct utmp)), SEEK_SET);
784                 if (atomicio(write, fd, ut, sizeof(*ut)) != sizeof(*ut))
785                         log("utmp_write_direct: error writing %s: %s",
786                             UTMP_FILE, strerror(errno));
787       
788                 (void)close(fd);
789                 return 1;
790         } else {
791                 return 0;
792         }
793 }
794 # endif /* UTMP_USE_LIBRARY */
795
796 static int
797 utmp_perform_login(struct logininfo *li)
798 {
799         struct utmp ut;
800
801         construct_utmp(li, &ut);
802 # ifdef UTMP_USE_LIBRARY
803         if (!utmp_write_library(li, &ut)) {
804                 log("utmp_perform_login: utmp_write_library() failed");
805                 return 0;
806         }
807 # else
808         if (!utmp_write_direct(li, &ut)) {
809                 log("utmp_perform_login: utmp_write_direct() failed");
810                 return 0;
811         }
812 # endif
813         return 1;
814 }
815
816
817 static int
818 utmp_perform_logout(struct logininfo *li)
819 {
820         struct utmp ut;
821
822         construct_utmp(li, &ut);
823 # ifdef UTMP_USE_LIBRARY
824         if (!utmp_write_library(li, &ut)) {
825                 log("utmp_perform_logout: utmp_write_library() failed");
826                 return 0;
827         }
828 # else
829         if (!utmp_write_direct(li, &ut)) {
830                 log("utmp_perform_logout: utmp_write_direct() failed");
831                 return 0;
832         }
833 # endif
834         return 1;
835 }
836
837
838 int
839 utmp_write_entry(struct logininfo *li)
840 {
841         switch(li->type) {
842         case LTYPE_LOGIN:
843                 return utmp_perform_login(li);
844
845         case LTYPE_LOGOUT:
846                 return utmp_perform_logout(li);
847
848         default:
849                 log("utmp_write_entry: invalid type field");
850                 return 0;
851         }
852 }
853 #endif /* USE_UTMP */
854
855
856 /**
857  ** Low-level utmpx functions
858  **/
859
860 /* not much point if we don't want utmpx entries */
861 #ifdef USE_UTMPX
862
863 /* if we have the wherewithall, use pututxline etc. */
864 # if !defined(DISABLE_PUTUTXLINE) && defined(HAVE_SETUTXENT) && \
865         defined(HAVE_PUTUTXLINE)
866 #  define UTMPX_USE_LIBRARY
867 # endif
868
869
870 /* write a utmpx entry with the system's help (pututxline() and pals) */
871 # ifdef UTMPX_USE_LIBRARY
872 static int
873 utmpx_write_library(struct logininfo *li, struct utmpx *utx)
874 {
875         setutxent();
876         pututxline(utx);
877
878 #  ifdef HAVE_ENDUTXENT
879         endutxent();
880 #  endif
881         return 1;
882 }
883
884 # else /* UTMPX_USE_LIBRARY */
885
886 /* write a utmp entry direct to the file */
887 static int
888 utmpx_write_direct(struct logininfo *li, struct utmpx *utx)
889 {  
890         log("utmpx_write_direct: not implemented!");
891         return 0;
892 }
893 # endif /* UTMPX_USE_LIBRARY */
894
895 static int
896 utmpx_perform_login(struct logininfo *li)
897 {
898         struct utmpx utx;
899
900         construct_utmpx(li, &utx);
901 # ifdef UTMPX_USE_LIBRARY
902         if (!utmpx_write_library(li, &utx)) {
903                 log("utmpx_perform_login: utmp_write_library() failed");
904                 return 0;
905         }
906 # else
907         if (!utmpx_write_direct(li, &ut)) {
908                 log("utmpx_perform_login: utmp_write_direct() failed");
909                 return 0;
910         }
911 # endif
912         return 1;
913 }
914
915
916 static int
917 utmpx_perform_logout(struct logininfo *li)
918 {
919         struct utmpx utx;
920
921         memset(&utx, '\0', sizeof(utx));
922         set_utmpx_time(li, &utx);
923         line_stripname(utx.ut_line, li->line, sizeof(utx.ut_line));
924 # ifdef HAVE_ID_IN_UTMPX
925         line_abbrevname(utx.ut_id, li->line, sizeof(utx.ut_id));
926 # endif
927 # ifdef HAVE_TYPE_IN_UTMPX
928         utx.ut_type = DEAD_PROCESS;
929 # endif
930
931 # ifdef UTMPX_USE_LIBRARY
932         utmpx_write_library(li, &utx);
933 # else
934         utmpx_write_direct(li, &utx);
935 # endif
936         return 1;
937 }
938
939 int
940 utmpx_write_entry(struct logininfo *li)
941 {
942         switch(li->type) {
943         case LTYPE_LOGIN:
944                 return utmpx_perform_login(li);
945         case LTYPE_LOGOUT:
946                 return utmpx_perform_logout(li);
947         default:
948                 log("utmpx_write_entry: invalid type field");
949                 return 0;
950         }
951 }
952 #endif /* USE_UTMPX */
953
954
955 /**
956  ** Low-level wtmp functions
957  **/
958
959 #ifdef USE_WTMP 
960
961 /* write a wtmp entry direct to the end of the file */
962 /* This is a slight modification of code in OpenBSD's logwtmp.c */
963 static int
964 wtmp_write(struct logininfo *li, struct utmp *ut)
965 {
966         struct stat buf;
967         int fd, ret = 1;
968
969         if ((fd = open(WTMP_FILE, O_WRONLY|O_APPEND, 0)) < 0) {
970                 log("wtmp_write: problem writing %s: %s",
971                     WTMP_FILE, strerror(errno));
972                 return 0;
973         }
974         if (fstat(fd, &buf) == 0) 
975                 if (atomicio(write, fd, ut, sizeof(*ut)) != sizeof(*ut)) {
976                         ftruncate(fd, buf.st_size);
977                         log("wtmp_write: problem writing %s: %s",
978                             WTMP_FILE, strerror(errno));
979                         ret = 0;
980                 }
981         (void)close(fd);
982         return ret;
983 }
984
985 static int
986 wtmp_perform_login(struct logininfo *li)
987 {
988         struct utmp ut;
989
990         construct_utmp(li, &ut);
991         return wtmp_write(li, &ut);
992 }
993
994
995 static int
996 wtmp_perform_logout(struct logininfo *li)
997 {
998         struct utmp ut;
999
1000         construct_utmp(li, &ut);
1001         return wtmp_write(li, &ut);
1002 }
1003
1004
1005 int
1006 wtmp_write_entry(struct logininfo *li)
1007 {
1008         switch(li->type) {
1009         case LTYPE_LOGIN:
1010                 return wtmp_perform_login(li);
1011         case LTYPE_LOGOUT:
1012                 return wtmp_perform_logout(li);
1013         default:
1014                 log("wtmp_write_entry: invalid type field");
1015                 return 0;
1016         }
1017 }
1018
1019
1020 /* Notes on fetching login data from wtmp/wtmpx
1021  * 
1022  * Logouts are usually recorded with (amongst other things) a blank
1023  * username on a given tty line.  However, some systems (HP-UX is one)
1024  * leave all fields set, but change the ut_type field to DEAD_PROCESS.
1025  *
1026  * Since we're only looking for logins here, we know that the username
1027  * must be set correctly. On systems that leave it in, we check for
1028  * ut_type==USER_PROCESS (indicating a login.)
1029  *
1030  * Portability: Some systems may set something other than USER_PROCESS
1031  * to indicate a login process. I don't know of any as I write. Also,
1032  * it's possible that some systems may both leave the username in
1033  * place and not have ut_type.
1034  */
1035
1036 /* return true if this wtmp entry indicates a login */
1037 static int
1038 wtmp_islogin(struct logininfo *li, struct utmp *ut)
1039 {
1040         if (strncmp(li->username, ut->ut_name, 
1041                 MIN_SIZEOF(li->username, ut->ut_name)) == 0) {
1042 # ifdef HAVE_TYPE_IN_UTMP
1043                 if (ut->ut_type & USER_PROCESS)
1044                         return 1;
1045 # else
1046                 return 1;
1047 # endif
1048         }
1049         return 0;
1050 }
1051
1052 int
1053 wtmp_get_entry(struct logininfo *li)
1054 {
1055         struct stat st;
1056         struct utmp ut;
1057         int fd, found=0;
1058
1059         /* Clear the time entries in our logininfo */
1060         li->tv_sec = li->tv_usec = 0;
1061
1062         if ((fd = open(WTMP_FILE, O_RDONLY)) < 0) {
1063                 log("wtmp_get_entry: problem opening %s: %s",
1064                     WTMP_FILE, strerror(errno));
1065                 return 0;
1066         }
1067         if (fstat(fd, &st) != 0) {
1068                 log("wtmp_get_entry: couldn't stat %s: %s",
1069                     WTMP_FILE, strerror(errno));
1070                 close(fd);
1071                 return 0;
1072         }
1073
1074         /* Seek to the start of the last struct utmp */
1075         if (lseek(fd, (off_t)(0 - sizeof(struct utmp)), SEEK_END) == -1) {
1076                 /* Looks like we've got a fresh wtmp file */
1077                 close(fd);
1078                 return 0;
1079         }
1080
1081         while (!found) {
1082                 if (atomicio(read, fd, &ut, sizeof(ut)) != sizeof(ut)) {
1083                         log("wtmp_get_entry: read of %s failed: %s",
1084                             WTMP_FILE, strerror(errno));
1085                         close (fd);
1086                         return 0;
1087                 }
1088                 if ( wtmp_islogin(li, &ut) ) {
1089                         found = 1;
1090                         /* We've already checked for a time in struct
1091                          * utmp, in login_getlast(). */
1092 # ifdef HAVE_TIME_IN_UTMP
1093                         li->tv_sec = ut.ut_time;
1094 # else
1095 #  if HAVE_TV_IN_UTMP
1096                         li->tv_sec = ut.ut_tv.tv_sec;
1097 #  endif
1098 # endif
1099                         line_fullname(li->line, ut.ut_line,
1100                                       MIN_SIZEOF(li->line, ut.ut_line));
1101 # ifdef HAVE_HOST_IN_UTMP
1102                         strlcpy(li->hostname, ut.ut_host,
1103                                 MIN_SIZEOF(li->hostname, ut.ut_host));
1104 # endif
1105                         continue;
1106                 }
1107                 /* Seek back 2 x struct utmp */
1108                 if (lseek(fd, (off_t)(0-2*sizeof(struct utmp)), SEEK_CUR) == -1) {
1109                         /* We've found the start of the file, so quit */
1110                         close (fd);
1111                         return 0;
1112                 }
1113         }
1114
1115         /* We found an entry. Tidy up and return */
1116         close(fd);
1117         return 1;
1118 }
1119 # endif /* USE_WTMP */
1120
1121
1122 /**
1123  ** Low-level wtmpx functions
1124  **/
1125
1126 #ifdef USE_WTMPX
1127 /* write a wtmpx entry direct to the end of the file */
1128 /* This is a slight modification of code in OpenBSD's logwtmp.c */
1129 static int
1130 wtmpx_write(struct logininfo *li, struct utmpx *utx)
1131 {
1132         struct stat buf;
1133         int fd, ret = 1;
1134
1135         if ((fd = open(WTMPX_FILE, O_WRONLY|O_APPEND, 0)) < 0) {
1136                 log("wtmpx_write: problem opening %s: %s",
1137                     WTMPX_FILE, strerror(errno));
1138                 return 0;
1139         }
1140
1141         if (fstat(fd, &buf) == 0) 
1142                 if (atomicio(write, fd, utx, sizeof(*utx)) != sizeof(*utx)) {
1143                         ftruncate(fd, buf.st_size);
1144                         log("wtmpx_write: problem writing %s: %s",
1145                             WTMPX_FILE, strerror(errno));
1146                         ret = 0;
1147                 }
1148         (void)close(fd);
1149
1150         return ret;
1151 }
1152
1153
1154 static int
1155 wtmpx_perform_login(struct logininfo *li)
1156 {
1157         struct utmpx utx;
1158
1159         construct_utmpx(li, &utx);
1160         return wtmpx_write(li, &utx);
1161 }
1162
1163
1164 static int
1165 wtmpx_perform_logout(struct logininfo *li)
1166 {
1167         struct utmpx utx;
1168
1169         construct_utmpx(li, &utx);
1170         return wtmpx_write(li, &utx);
1171 }
1172
1173
1174 int
1175 wtmpx_write_entry(struct logininfo *li)
1176 {
1177         switch(li->type) {
1178         case LTYPE_LOGIN:
1179                 return wtmpx_perform_login(li);
1180         case LTYPE_LOGOUT:
1181                 return wtmpx_perform_logout(li);
1182         default:
1183                 log("wtmpx_write_entry: invalid type field");
1184                 return 0;
1185         }
1186 }
1187
1188 /* Please see the notes above wtmp_islogin() for information about the
1189    next two functions */
1190
1191 /* Return true if this wtmpx entry indicates a login */
1192 static int
1193 wtmpx_islogin(struct logininfo *li, struct utmpx *utx)
1194 {
1195         if ( strncmp(li->username, utx->ut_name,
1196                 MIN_SIZEOF(li->username, utx->ut_name)) == 0 ) {
1197 # ifdef HAVE_TYPE_IN_UTMPX
1198                 if (utx->ut_type == USER_PROCESS)
1199                         return 1;
1200 # else
1201                 return 1;
1202 # endif
1203         }
1204         return 0;
1205 }
1206
1207
1208 int
1209 wtmpx_get_entry(struct logininfo *li)
1210 {
1211         struct stat st;
1212         struct utmpx utx;
1213         int fd, found=0;
1214
1215         /* Clear the time entries */
1216         li->tv_sec = li->tv_usec = 0;
1217
1218         if ((fd = open(WTMPX_FILE, O_RDONLY)) < 0) {
1219                 log("wtmpx_get_entry: problem opening %s: %s",
1220                     WTMPX_FILE, strerror(errno));
1221                 return 0;
1222         }
1223         if (fstat(fd, &st) != 0) {
1224                 log("wtmpx_get_entry: couldn't stat %s: %s",
1225                     WTMP_FILE, strerror(errno));
1226                 close(fd);
1227                 return 0;
1228         }
1229         
1230         /* Seek to the start of the last struct utmpx */
1231         if (lseek(fd, (off_t)(0-sizeof(struct utmpx)), SEEK_END) == -1 ) {
1232                 /* probably a newly rotated wtmpx file */
1233                 close(fd);
1234                 return 0;
1235         }
1236
1237         while (!found) {
1238                 if (atomicio(read, fd, &utx, sizeof(utx)) != sizeof(utx)) {
1239                         log("wtmpx_get_entry: read of %s failed: %s",
1240                             WTMPX_FILE, strerror(errno));
1241                         close (fd);
1242                         return 0;
1243                 }
1244                 /* Logouts are recorded as a blank username on a particular line.
1245                  * So, we just need to find the username in struct utmpx */
1246                 if ( wtmpx_islogin(li, &utx) ) {
1247 # ifdef HAVE_TV_IN_UTMPX
1248                         li->tv_sec = utx.ut_tv.tv_sec;
1249 # else
1250 #  ifdef HAVE_TIME_IN_UTMPX
1251                         li->tv_sec = utx.ut_time;
1252 #  endif
1253 # endif
1254                         line_fullname(li->line, utx.ut_line, sizeof(li->line));
1255 # ifdef HAVE_HOST_IN_UTMPX
1256                         strlcpy(li->hostname, utx.ut_host,
1257                                 MIN_SIZEOF(li->hostname, utx.ut_host));
1258 # endif
1259                         continue;
1260                 }
1261                 if (lseek(fd, (off_t)(0-2*sizeof(struct utmpx)), SEEK_CUR) == -1) {
1262                         close (fd);
1263                         return 0;
1264                 }
1265         }
1266
1267         close(fd);
1268         return 1;
1269 }
1270 #endif /* USE_WTMPX */
1271
1272 /**
1273  ** Low-level libutil login() functions
1274  **/
1275
1276 #ifdef USE_LOGIN
1277 static int
1278 syslogin_perform_login(struct logininfo *li)
1279 {
1280         struct utmp *ut;
1281
1282         if (! (ut = (struct utmp *)malloc(sizeof(*ut)))) {
1283                 log("syslogin_perform_login: couldn't malloc()");
1284                 return 0;
1285         }
1286         construct_utmp(li, ut);
1287         login(ut);
1288
1289         return 1;
1290 }
1291
1292 static int
1293 syslogin_perform_logout(struct logininfo *li)
1294 {
1295 # ifdef HAVE_LOGOUT
1296         char line[8];
1297   
1298         (void)line_stripname(line, li->line, sizeof(line));
1299
1300         if (!logout(line)) {
1301                 log("syslogin_perform_logout: logout() returned an error");
1302 #  ifdef HAVE_LOGWTMP
1303         } else {
1304                 logwtmp(line, "", "");
1305 #  endif
1306         }
1307         /* FIXME: (ATL - if the need arises) What to do if we have
1308          * login, but no logout?  what if logout but no logwtmp? All
1309          * routines are in libutil so they should all be there,
1310          * but... */
1311 # endif
1312         return 1;
1313 }
1314
1315 int
1316 syslogin_write_entry(struct logininfo *li)
1317 {
1318         switch (li->type) {
1319         case LTYPE_LOGIN:
1320                 return syslogin_perform_login(li);
1321         case LTYPE_LOGOUT:
1322                 return syslogin_perform_logout(li);
1323         default:
1324                 log("syslogin_write_entry: Invalid type field");
1325                 return 0;
1326         }
1327 }
1328 #endif /* USE_LOGIN */
1329
1330 /* end of file log-syslogin.c */
1331
1332 /**
1333  ** Low-level lastlog functions
1334  **/
1335
1336 #ifdef USE_LASTLOG
1337 #define LL_FILE 1
1338 #define LL_DIR 2
1339 #define LL_OTHER 3
1340
1341 static void
1342 lastlog_construct(struct logininfo *li, struct lastlog *last)
1343 {
1344         /* clear the structure */
1345         memset(last, '\0', sizeof(*last));
1346   
1347         (void)line_stripname(last->ll_line, li->line, sizeof(last->ll_line));
1348         strlcpy(last->ll_host, li->hostname,
1349                 MIN_SIZEOF(last->ll_host, li->hostname));
1350         last->ll_time = li->tv_sec;
1351 }
1352
1353 static int
1354 lastlog_filetype(char *filename)
1355 {
1356         struct stat st;
1357
1358         if (stat(LASTLOG_FILE, &st) != 0) {
1359                 log("lastlog_perform_login: Couldn't stat %s: %s", LASTLOG_FILE, 
1360                         strerror(errno));
1361                 return 0;
1362         }
1363         if (S_ISDIR(st.st_mode))
1364                 return LL_DIR;
1365         else if (S_ISREG(st.st_mode))
1366                 return LL_FILE;
1367         else
1368                 return LL_OTHER;
1369 }
1370
1371
1372 /* open the file (using filemode) and seek to the login entry */
1373 static int
1374 lastlog_openseek(struct logininfo *li, int *fd, int filemode)
1375 {
1376         off_t offset;
1377         int type;
1378         char lastlog_file[1024];
1379
1380         type = lastlog_filetype(LASTLOG_FILE);
1381         switch (type) {
1382                 case LL_FILE:
1383                         strlcpy(lastlog_file, LASTLOG_FILE, sizeof(lastlog_file));
1384                         break;
1385                 case LL_DIR:
1386                         snprintf(lastlog_file, sizeof(lastlog_file), "%s/%s",
1387                                  LASTLOG_FILE, li->username);
1388                         break;
1389                 default:
1390                         log("lastlog_openseek: %.100s is not a file or directory!",
1391                             LASTLOG_FILE);
1392                         return 0;
1393         }
1394
1395         *fd = open(lastlog_file, filemode);
1396         if ( *fd < 0) {
1397                 debug("lastlog_openseek: Couldn't open %s: %s",
1398                     lastlog_file, strerror(errno));
1399                 return 0;
1400         }
1401         
1402         if (type == LL_FILE) {
1403                 /* find this uid's offset in the lastlog file */
1404                 offset = (off_t) ( (long)li->uid * sizeof(struct lastlog));
1405
1406                 if ( lseek(*fd, offset, SEEK_SET) != offset ) {
1407                         log("lastlog_openseek: %s->lseek(): %s",
1408                          lastlog_file, strerror(errno));
1409                         return 0;
1410                 }
1411         }
1412         
1413         return 1;
1414 }
1415
1416 static int
1417 lastlog_perform_login(struct logininfo *li)
1418 {
1419         struct lastlog last;
1420         int fd;
1421
1422         /* create our struct lastlog */
1423         lastlog_construct(li, &last);
1424
1425         if (!lastlog_openseek(li, &fd, O_RDWR|O_CREAT))
1426                 return(0);
1427                 
1428         /* write the entry */
1429         if (atomicio(write, fd, &last, sizeof(last)) != sizeof(last)) {
1430                 close(fd);
1431                 log("lastlog_write_filemode: Error writing to %s: %s",
1432                     LASTLOG_FILE, strerror(errno));
1433                 return 0;
1434         }
1435
1436         close(fd);
1437         return 1;
1438 }
1439
1440 int
1441 lastlog_write_entry(struct logininfo *li)
1442 {
1443         switch(li->type) {
1444         case LTYPE_LOGIN:
1445                 return lastlog_perform_login(li);
1446         default:
1447                 log("lastlog_write_entry: Invalid type field");
1448                 return 0;
1449         }
1450 }
1451
1452 static void
1453 lastlog_populate_entry(struct logininfo *li, struct lastlog *last)
1454 {
1455         line_fullname(li->line, last->ll_line, sizeof(li->line));
1456         strlcpy(li->hostname, last->ll_host, 
1457                 MIN_SIZEOF(li->hostname, last->ll_host));
1458         li->tv_sec = last->ll_time;
1459 }
1460
1461 int
1462 lastlog_get_entry(struct logininfo *li)
1463 {
1464         struct lastlog last;
1465         int fd;
1466
1467         if (lastlog_openseek(li, &fd, O_RDONLY)) {
1468                 if (atomicio(read, fd, &last, sizeof(last)) != sizeof(last)) {
1469                         log("lastlog_get_entry: Error reading from %s: %s",
1470                             LASTLOG_FILE, strerror(errno));
1471                         return 0;
1472                 } else {
1473                         lastlog_populate_entry(li, &last);
1474                         return 1;
1475                 }
1476         } else {
1477                 return 0;    
1478         }
1479 }
1480 #endif /* USE_LASTLOG */
This page took 4.488942 seconds and 5 git commands to generate.