]> andersk Git - openssh.git/blob - loginrec.c
20001228
[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 #ifdef HAVE_LIBUTIL_H
171 #   include <libutil.h>
172 #endif
173
174 /**
175  ** prototypes for helper functions in this file
176  **/
177
178 #if HAVE_UTMP_H
179 void set_utmp_time(struct logininfo *li, struct utmp *ut);
180 void construct_utmp(struct logininfo *li, struct utmp *ut);
181 #endif
182
183 #ifdef HAVE_UTMPX_H
184 void set_utmpx_time(struct logininfo *li, struct utmpx *ut);
185 void construct_utmpx(struct logininfo *li, struct utmpx *ut);
186 #endif
187
188 int utmp_write_entry(struct logininfo *li);
189 int utmpx_write_entry(struct logininfo *li);
190 int wtmp_write_entry(struct logininfo *li);
191 int wtmpx_write_entry(struct logininfo *li);
192 int lastlog_write_entry(struct logininfo *li);
193 int syslogin_write_entry(struct logininfo *li);
194
195 int getlast_entry(struct logininfo *li);
196 int lastlog_get_entry(struct logininfo *li);
197 int wtmp_get_entry(struct logininfo *li);
198 int wtmpx_get_entry(struct logininfo *li);
199
200 /* pick the shortest string */
201 #define MIN_SIZEOF(s1,s2) ( sizeof(s1) < sizeof(s2) ? sizeof(s1) : sizeof(s2) )
202
203 /**
204  ** platform-independent login functions
205  **/
206
207 /* login_login(struct logininfo *)     -Record a login
208  * 
209  * Call with a pointer to a struct logininfo initialised with
210  * login_init_entry() or login_alloc_entry()
211  *
212  * Returns:
213  *  >0 if successful
214  *  0  on failure (will use OpenSSH's logging facilities for diagnostics)
215  */
216 int
217 login_login (struct logininfo *li)
218 {
219         li->type = LTYPE_LOGIN;
220         return login_write(li);
221 }
222
223
224 /* login_logout(struct logininfo *)     - Record a logout
225  *
226  * Call as with login_login()
227  *
228  * Returns:
229  *  >0 if successful
230  *  0  on failure (will use OpenSSH's logging facilities for diagnostics)
231  */
232 int
233 login_logout(struct logininfo *li)
234 {
235         li->type = LTYPE_LOGOUT;
236         return login_write(li);
237 }
238
239 /* login_get_lastlog_time(int)           - Retrieve the last login time
240  *
241  * Retrieve the last login time for the given uid. Will try to use the
242  * system lastlog facilities if they are available, but will fall back
243  * to looking in wtmp/wtmpx if necessary
244  *
245  * Returns:
246  *   0 on failure, or if user has never logged in
247  *   Time in seconds from the epoch if successful
248  *
249  * Useful preprocessor symbols:
250  *   DISABLE_LASTLOG: If set, *never* even try to retrieve lastlog
251  *                    info
252  *   USE_LASTLOG: If set, indicates the presence of system lastlog
253  *                facilities. If this and DISABLE_LASTLOG are not set,
254  *                try to retrieve lastlog information from wtmp/wtmpx.
255  */
256 unsigned int
257 login_get_lastlog_time(const int uid)
258 {
259         struct logininfo li;
260
261         if (login_get_lastlog(&li, uid))
262                 return li.tv_sec;
263         else
264                 return 0;
265 }
266
267 /* login_get_lastlog(struct logininfo *, int)   - Retrieve a lastlog entry
268  *
269  * Retrieve a logininfo structure populated (only partially) with
270  * information from the system lastlog data, or from wtmp/wtmpx if no
271  * system lastlog information exists.
272  *
273  * Note this routine must be given a pre-allocated logininfo.
274  *
275  * Returns:
276  *  >0: A pointer to your struct logininfo if successful
277  *  0  on failure (will use OpenSSH's logging facilities for diagnostics)
278  *
279  */
280 struct logininfo *
281 login_get_lastlog(struct logininfo *li, const int uid)
282 {
283         struct passwd *pw;
284
285         memset(li, '\0', sizeof(*li));
286         li->uid = uid;
287
288         /* 
289          * If we don't have a 'real' lastlog, we need the username to
290          * reliably search wtmp(x) for the last login (see
291          * wtmp_get_entry().) 
292          */
293         pw = getpwuid(uid);
294         if (pw == NULL)
295                 fatal("login_get_lastlog: Cannot find account for uid %i", uid);
296                 
297         /* No MIN_SIZEOF here - we absolutely *must not* truncate the
298          * username */
299         strlcpy(li->username, pw->pw_name, sizeof(li->username));
300
301         if (getlast_entry(li))
302                 return li;
303         else
304                 return NULL;
305 }
306
307
308 /* login_alloc_entry(int, char*, char*, char*)    - Allocate and initialise
309  *                                                  a logininfo structure 
310  * 
311  * This function creates a new struct logininfo, a data structure
312  * meant to carry the information required to portably record login info.
313  *
314  * Returns a pointer to a newly created struct logininfo. If memory
315  * allocation fails, the program halts.
316  */
317 struct
318 logininfo *login_alloc_entry(int pid, const char *username,
319                              const char *hostname, const char *line)
320 {
321         struct logininfo *newli;
322
323         newli = (struct logininfo *) xmalloc (sizeof(*newli));
324         (void)login_init_entry(newli, pid, username, hostname, line);
325         return newli;
326 }
327
328
329 /* login_free_entry(struct logininfo *)    - free struct memory */
330 void
331 login_free_entry(struct logininfo *li)
332 {
333         xfree(li);
334 }
335
336
337 /* login_init_entry(struct logininfo *, int, char*, char*, char*)
338  *                                        - initialise a struct logininfo
339  * 
340  * Populates a new struct logininfo, a data structure meant to carry
341  * the information required to portably record login info.
342  *
343  * Returns: 1
344  */
345 int
346 login_init_entry(struct logininfo *li, int pid, const char *username, 
347                  const char *hostname, const char *line)
348 {
349         struct passwd *pw;
350         
351         memset(li, 0, sizeof(*li));
352   
353         li->pid = pid;
354
355         /* set the line information */
356         if (line)
357                 line_fullname(li->line, line, sizeof(li->line));
358
359         if (username) {
360                 strlcpy(li->username, username, sizeof(li->username));
361                 pw = getpwnam(li->username);
362                 if (pw == NULL)
363                         fatal("login_init_entry: Cannot find user \"%s\"", li->username);
364                 li->uid = pw->pw_uid;
365         }
366
367         if (hostname)
368                 strlcpy(li->hostname, hostname, sizeof(li->hostname));
369
370         return 1;
371 }
372
373 /* login_set_current_time(struct logininfo *)    - set the current time
374  *
375  * Set the current time in a logininfo structure. This function is
376  * meant to eliminate the need to deal with system dependencies for
377  * time handling.
378  */
379 void
380 login_set_current_time(struct logininfo *li)
381 {
382         struct timeval tv;
383
384         gettimeofday(&tv, NULL);
385         
386         li->tv_sec = tv.tv_sec;
387         li->tv_usec = tv.tv_usec;
388 }
389
390 /* copy a sockaddr_* into our logininfo */
391 void
392 login_set_addr(struct logininfo *li, const struct sockaddr *sa,
393                const unsigned int sa_size)
394 {
395         unsigned int bufsize = sa_size;
396
397         /* make sure we don't overrun our union */
398         if (sizeof(li->hostaddr) < sa_size)
399                 bufsize = sizeof(li->hostaddr);
400
401         memcpy((void *)&(li->hostaddr.sa), (const void *)sa, bufsize);
402 }
403
404
405 /**
406  ** login_write: Call low-level recording functions based on autoconf
407  ** results
408  **/
409 int
410 login_write (struct logininfo *li)
411 {
412 #ifndef HAVE_CYGWIN
413         if ((int)geteuid() != 0) {
414           log("Attempt to write login records by non-root user (aborting)");
415           return 1;
416         }
417 #endif
418
419         /* set the timestamp */
420         login_set_current_time(li);
421 #ifdef USE_LOGIN
422         syslogin_write_entry(li);
423 #endif
424 #ifdef USE_LASTLOG
425         if (li->type == LTYPE_LOGIN) {
426                 lastlog_write_entry(li);
427         }
428 #endif
429 #ifdef USE_UTMP
430         utmp_write_entry(li);
431 #endif
432 #ifdef USE_WTMP
433         wtmp_write_entry(li);
434 #endif
435 #ifdef USE_UTMPX
436         utmpx_write_entry(li);
437 #endif
438 #ifdef USE_WTMPX
439         wtmpx_write_entry(li);
440 #endif
441         return 0;
442 }
443
444 /**
445  ** getlast_entry: Call low-level functions to retrieve the last login
446  **                time.
447  **/
448
449 /* take the uid in li and return the last login time */
450 int
451 getlast_entry(struct logininfo *li)
452 {
453 #ifdef USE_LASTLOG
454         return(lastlog_get_entry(li));
455 #else /* !USE_LASTLOG */
456
457 #ifdef DISABLE_LASTLOG
458         /* On some systems we shouldn't even try to obtain last login 
459          * time, e.g. AIX */
460         return 0;
461 # else /* DISABLE_LASTLOG */
462         /* Try to retrieve the last login time from wtmp */
463 #  if defined(USE_WTMP) && (defined(HAVE_TIME_IN_UTMP) || defined(HAVE_TV_IN_UTMP))
464         /* retrieve last login time from utmp */
465         return (wtmp_get_entry(li));
466 #  else /* defined(USE_WTMP) && (defined(HAVE_TIME_IN_UTMP) || defined(HAVE_TV_IN_UTMP)) */
467         /* If wtmp isn't available, try wtmpx */
468 #   if defined(USE_WTMPX) && (defined(HAVE_TIME_IN_UTMPX) || defined(HAVE_TV_IN_UTMPX))
469         /* retrieve last login time from utmpx */
470         return (wtmpx_get_entry(li));
471 #   else
472         /* Give up: No means of retrieving last login time */
473         return 0;
474 #   endif /* USE_WTMPX && (HAVE_TIME_IN_UTMPX || HAVE_TV_IN_UTMPX) */
475 #  endif /* USE_WTMP && (HAVE_TIME_IN_UTMP || HAVE_TV_IN_UTMP) */
476 # endif /* DISABLE_LASTLOG */ 
477 #endif /* USE_LASTLOG */
478 }
479
480
481
482 /*
483  * 'line' string utility functions
484  *
485  * These functions process the 'line' string into one of three forms:
486  *
487  * 1. The full filename (including '/dev')
488  * 2. The stripped name (excluding '/dev')
489  * 3. The abbreviated name (e.g. /dev/ttyp00 -> yp00
490  *                               /dev/pts/1  -> ts/1 )
491  *
492  * Form 3 is used on some systems to identify a .tmp.? entry when
493  * attempting to remove it. Typically both addition and removal is
494  * performed by one application - say, sshd - so as long as the choice
495  * uniquely identifies a terminal it's ok.
496  */
497
498
499 /* line_fullname(): add the leading '/dev/' if it doesn't exist make
500  * sure dst has enough space, if not just copy src (ugh) */
501 char *
502 line_fullname(char *dst, const char *src, int dstsize)
503 {
504         memset(dst, '\0', dstsize);
505         if ((strncmp(src, "/dev/", 5) == 0) || (dstsize < (strlen(src) + 5))) {
506                 strlcpy(dst, src, dstsize);
507         } else {
508                 strlcpy(dst, "/dev/", dstsize);
509                 strlcat(dst, src, dstsize);
510         }
511         return dst;
512 }
513
514 /* line_stripname(): strip the leading '/dev' if it exists, return dst */
515 char *
516 line_stripname(char *dst, const char *src, int dstsize)
517 {
518         memset(dst, '\0', dstsize);
519         if (strncmp(src, "/dev/", 5) == 0)
520                 strlcpy(dst, src + 5, dstsize);
521         else
522                 strlcpy(dst, src, dstsize);
523         return dst;
524 }
525
526 /* line_abbrevname(): Return the abbreviated (usually four-character)
527  * form of the line (Just use the last <dstsize> characters of the
528  * full name.)
529  *
530  * NOTE: use strncpy because we do NOT necessarily want zero
531  * termination */
532 char *
533 line_abbrevname(char *dst, const char *src, int dstsize) 
534 {
535         size_t len;
536         
537         memset(dst, '\0', dstsize);
538         
539         /* Always skip prefix if present */
540 #ifdef sgi
541         if (strncmp(src, "/dev/tty", 8) == 0)
542                 src += 8;
543 #else
544         if (strncmp(src, "/dev/", 5) == 0)
545                 src += 5;
546 #endif
547                 
548         len = strlen(src);
549
550         if (len > 0) {
551                 if (((int)len - dstsize) > 0)
552                         src +=  ((int)len - dstsize);
553
554                 /* note: _don't_ change this to strlcpy */
555                 strncpy(dst, src, (size_t)dstsize); 
556         }
557         
558         return dst;
559 }
560
561 /**
562  ** utmp utility functions
563  **
564  ** These functions manipulate struct utmp, taking system differences
565  ** into account.
566  **/
567
568 #if defined(USE_UTMP) || defined (USE_WTMP) || defined (USE_LOGIN)
569
570 /* build the utmp structure */
571 void
572 set_utmp_time(struct logininfo *li, struct utmp *ut)
573 {
574 # ifdef HAVE_TV_IN_UTMP
575         ut->ut_tv.tv_sec = li->tv_sec;
576         ut->ut_tv.tv_usec = li->tv_usec;
577 # else
578 #  ifdef HAVE_TIME_IN_UTMP
579         ut->ut_time = li->tv_sec;
580 #  endif
581 # endif
582 }
583
584 void
585 construct_utmp(struct logininfo *li,
586                     struct utmp *ut)
587 {
588         memset(ut, '\0', sizeof(*ut));
589
590         /* First fill out fields used for both logins and logouts */
591
592 # ifdef HAVE_ID_IN_UTMP
593         line_abbrevname(ut->ut_id, li->line, sizeof(ut->ut_id));
594 # endif
595
596 # ifdef HAVE_TYPE_IN_UTMP
597         /* This is done here to keep utmp constants out of struct logininfo */
598         switch (li->type) {
599         case LTYPE_LOGIN:
600                 ut->ut_type = USER_PROCESS;
601                 break;
602         case LTYPE_LOGOUT:
603                 ut->ut_type = DEAD_PROCESS;
604                 break;
605         }
606 # endif
607         set_utmp_time(li, ut);
608
609         line_stripname(ut->ut_line, li->line, sizeof(ut->ut_line));
610
611 # ifdef HAVE_PID_IN_UTMP
612         ut->ut_pid = li->pid;
613 # endif
614
615         /* If we're logging out, leave all other fields blank */
616         if (li->type == LTYPE_LOGOUT)
617           return;
618
619         /*
620          * These fields are only used when logging in, and are blank
621          * for logouts. 
622          */
623
624         /* Use strncpy because we don't necessarily want null termination */
625         strncpy(ut->ut_name, li->username, MIN_SIZEOF(ut->ut_name, li->username));
626 # ifdef HAVE_HOST_IN_UTMP
627         strncpy(ut->ut_host, li->hostname, MIN_SIZEOF(ut->ut_host, li->hostname));
628 # endif
629 # ifdef HAVE_ADDR_IN_UTMP
630         /* this is just a 32-bit IP address */
631         if (li->hostaddr.sa.sa_family == AF_INET)
632                 ut->ut_addr = li->hostaddr.sa_in.sin_addr.s_addr;
633 # endif 
634 }
635 #endif /* USE_UTMP || USE_WTMP || USE_LOGIN */
636
637 /**
638  ** utmpx utility functions
639  **
640  ** These functions manipulate struct utmpx, accounting for system
641  ** variations.
642  **/
643
644 #if defined(USE_UTMPX) || defined (USE_WTMPX)
645 /* build the utmpx structure */
646 void
647 set_utmpx_time(struct logininfo *li, struct utmpx *utx)
648 {
649 # ifdef HAVE_TV_IN_UTMPX
650         utx->ut_tv.tv_sec = li->tv_sec;
651         utx->ut_tv.tv_usec = li->tv_usec;
652 # else /* HAVE_TV_IN_UTMPX */
653 #  ifdef HAVE_TIME_IN_UTMPX
654         utx->ut_time = li->tv_sec;
655 #  endif /* HAVE_TIME_IN_UTMPX */
656 # endif /* HAVE_TV_IN_UTMPX */
657 }
658
659 void
660 construct_utmpx(struct logininfo *li, struct utmpx *utx)
661 {
662         memset(utx, '\0', sizeof(*utx));
663 # ifdef HAVE_ID_IN_UTMPX
664         line_abbrevname(utx->ut_id, li->line, sizeof(utx->ut_id));
665 # endif
666
667         /* this is done here to keep utmp constants out of loginrec.h */
668         switch (li->type) {
669         case LTYPE_LOGIN:
670                 utx->ut_type = USER_PROCESS;
671                 break;
672         case LTYPE_LOGOUT:
673                 utx->ut_type = DEAD_PROCESS;
674                 break;
675         }
676         line_stripname(utx->ut_line, li->line, sizeof(utx->ut_line));
677         set_utmpx_time(li, utx);
678         utx->ut_pid = li->pid;
679
680         if (li->type == LTYPE_LOGOUT)
681                 return;
682
683         /*
684          * These fields are only used when logging in, and are blank
685          * for logouts. 
686          */
687
688         /* strncpy(): Don't necessarily want null termination */
689         strncpy(utx->ut_name, li->username, MIN_SIZEOF(utx->ut_name, li->username));
690 # ifdef HAVE_HOST_IN_UTMPX
691         strncpy(utx->ut_host, li->hostname, MIN_SIZEOF(utx->ut_host, li->hostname));
692 # endif
693 # ifdef HAVE_ADDR_IN_UTMPX
694         /* this is just a 32-bit IP address */
695         if (li->hostaddr.sa.sa_family == AF_INET)
696                 utx->ut_addr = li->hostaddr.sa_in.sin_addr.s_addr;
697 # endif
698 # ifdef HAVE_SYSLEN_IN_UTMPX
699         /* ut_syslen is the length of the utx_host string */
700         utx->ut_syslen = MIN(strlen(li->hostname), sizeof(utx->ut_host));
701 # endif
702 }
703 #endif /* USE_UTMPX || USE_WTMPX */
704
705 /**
706  ** Low-level utmp functions
707  **/
708
709 /* FIXME: (ATL) utmp_write_direct needs testing */
710 #ifdef USE_UTMP
711
712 /* if we can, use pututline() etc. */
713 # if !defined(DISABLE_PUTUTLINE) && defined(HAVE_SETUTENT) && \
714         defined(HAVE_PUTUTLINE)
715 #  define UTMP_USE_LIBRARY
716 # endif
717
718
719 /* write a utmp entry with the system's help (pututline() and pals) */
720 # ifdef UTMP_USE_LIBRARY
721 static int
722 utmp_write_library(struct logininfo *li, struct utmp *ut)
723 {
724         setutent();
725         pututline(ut);
726
727 #  ifdef HAVE_ENDUTENT
728         endutent();
729 #  endif
730         return 1;
731 }
732 # else /* UTMP_USE_LIBRARY */
733
734 /* write a utmp entry direct to the file */
735 /* This is a slightly modification of code in OpenBSD's login.c */
736 static int
737 utmp_write_direct(struct logininfo *li, struct utmp *ut)
738 {
739         struct utmp old_ut;
740         register int fd;
741         int tty;
742
743         /* FIXME: (ATL) ttyslot() needs local implementation */
744
745 #if defined(HAVE_GETTTYENT)
746         register struct ttyent *ty;
747
748         tty=0;
749
750         setttyent();
751         while ((struct ttyent *)0 != (ty = getttyent())) {
752                 tty++;
753                 if (!strncmp(ty->ty_name, ut->ut_line, sizeof(ut->ut_line)))
754                         break;
755         }
756         endttyent();
757
758         if((struct ttyent *)0 == ty) {
759                 log("utmp_write_entry: tty not found");
760                 return(1);
761         }
762 #else /* FIXME */
763
764         tty = ttyslot(); /* seems only to work for /dev/ttyp? style names */
765
766 #endif /* HAVE_GETTTYENT */
767
768         if (tty > 0 && (fd = open(UTMP_FILE, O_RDWR|O_CREAT, 0644)) >= 0) {
769                 (void)lseek(fd, (off_t)(tty * sizeof(struct utmp)), SEEK_SET);
770                 /*
771                  * Prevent luser from zero'ing out ut_host.
772                  * If the new ut_line is empty but the old one is not
773                  * and ut_line and ut_name match, preserve the old ut_line.
774                  */
775                 if (atomicio(read, fd, &old_ut, sizeof(old_ut)) == sizeof(old_ut) && 
776                         (ut->ut_host[0] == '\0') && (old_ut.ut_host[0] != '\0') && 
777                         (strncmp(old_ut.ut_line, ut->ut_line, sizeof(ut->ut_line)) == 0) && 
778                         (strncmp(old_ut.ut_name, ut->ut_name, sizeof(ut->ut_name)) == 0)) {
779                         (void)memcpy(ut->ut_host, old_ut.ut_host, sizeof(ut->ut_host));
780                 }
781                 
782                 (void)lseek(fd, (off_t)(tty * sizeof(struct utmp)), SEEK_SET);
783                 if (atomicio(write, fd, ut, sizeof(*ut)) != sizeof(*ut))
784                         log("utmp_write_direct: error writing %s: %s",
785                             UTMP_FILE, strerror(errno));
786       
787                 (void)close(fd);
788                 return 1;
789         } else {
790                 return 0;
791         }
792 }
793 # endif /* UTMP_USE_LIBRARY */
794
795 static int
796 utmp_perform_login(struct logininfo *li)
797 {
798         struct utmp ut;
799
800         construct_utmp(li, &ut);
801 # ifdef UTMP_USE_LIBRARY
802         if (!utmp_write_library(li, &ut)) {
803                 log("utmp_perform_login: utmp_write_library() failed");
804                 return 0;
805         }
806 # else
807         if (!utmp_write_direct(li, &ut)) {
808                 log("utmp_perform_login: utmp_write_direct() failed");
809                 return 0;
810         }
811 # endif
812         return 1;
813 }
814
815
816 static int
817 utmp_perform_logout(struct logininfo *li)
818 {
819         struct utmp ut;
820
821         construct_utmp(li, &ut);
822 # ifdef UTMP_USE_LIBRARY
823         if (!utmp_write_library(li, &ut)) {
824                 log("utmp_perform_logout: utmp_write_library() failed");
825                 return 0;
826         }
827 # else
828         if (!utmp_write_direct(li, &ut)) {
829                 log("utmp_perform_logout: utmp_write_direct() failed");
830                 return 0;
831         }
832 # endif
833         return 1;
834 }
835
836
837 int
838 utmp_write_entry(struct logininfo *li)
839 {
840         switch(li->type) {
841         case LTYPE_LOGIN:
842                 return utmp_perform_login(li);
843
844         case LTYPE_LOGOUT:
845                 return utmp_perform_logout(li);
846
847         default:
848                 log("utmp_write_entry: invalid type field");
849                 return 0;
850         }
851 }
852 #endif /* USE_UTMP */
853
854
855 /**
856  ** Low-level utmpx functions
857  **/
858
859 /* not much point if we don't want utmpx entries */
860 #ifdef USE_UTMPX
861
862 /* if we have the wherewithall, use pututxline etc. */
863 # if !defined(DISABLE_PUTUTXLINE) && defined(HAVE_SETUTXENT) && \
864         defined(HAVE_PUTUTXLINE)
865 #  define UTMPX_USE_LIBRARY
866 # endif
867
868
869 /* write a utmpx entry with the system's help (pututxline() and pals) */
870 # ifdef UTMPX_USE_LIBRARY
871 static int
872 utmpx_write_library(struct logininfo *li, struct utmpx *utx)
873 {
874         setutxent();
875         pututxline(utx);
876
877 #  ifdef HAVE_ENDUTXENT
878         endutxent();
879 #  endif
880         return 1;
881 }
882
883 # else /* UTMPX_USE_LIBRARY */
884
885 /* write a utmp entry direct to the file */
886 static int
887 utmpx_write_direct(struct logininfo *li, struct utmpx *utx)
888 {  
889         log("utmpx_write_direct: not implemented!");
890         return 0;
891 }
892 # endif /* UTMPX_USE_LIBRARY */
893
894 static int
895 utmpx_perform_login(struct logininfo *li)
896 {
897         struct utmpx utx;
898
899         construct_utmpx(li, &utx);
900 # ifdef UTMPX_USE_LIBRARY
901         if (!utmpx_write_library(li, &utx)) {
902                 log("utmpx_perform_login: utmp_write_library() failed");
903                 return 0;
904         }
905 # else
906         if (!utmpx_write_direct(li, &ut)) {
907                 log("utmpx_perform_login: utmp_write_direct() failed");
908                 return 0;
909         }
910 # endif
911         return 1;
912 }
913
914
915 static int
916 utmpx_perform_logout(struct logininfo *li)
917 {
918         struct utmpx utx;
919
920         memset(&utx, '\0', sizeof(utx));
921         set_utmpx_time(li, &utx);
922         line_stripname(utx.ut_line, li->line, sizeof(utx.ut_line));
923 # ifdef HAVE_ID_IN_UTMPX
924         line_abbrevname(utx.ut_id, li->line, sizeof(utx.ut_id));
925 # endif
926 # ifdef HAVE_TYPE_IN_UTMPX
927         utx.ut_type = DEAD_PROCESS;
928 # endif
929
930 # ifdef UTMPX_USE_LIBRARY
931         utmpx_write_library(li, &utx);
932 # else
933         utmpx_write_direct(li, &utx);
934 # endif
935         return 1;
936 }
937
938 int
939 utmpx_write_entry(struct logininfo *li)
940 {
941         switch(li->type) {
942         case LTYPE_LOGIN:
943                 return utmpx_perform_login(li);
944         case LTYPE_LOGOUT:
945                 return utmpx_perform_logout(li);
946         default:
947                 log("utmpx_write_entry: invalid type field");
948                 return 0;
949         }
950 }
951 #endif /* USE_UTMPX */
952
953
954 /**
955  ** Low-level wtmp functions
956  **/
957
958 #ifdef USE_WTMP 
959
960 /* write a wtmp entry direct to the end of the file */
961 /* This is a slight modification of code in OpenBSD's logwtmp.c */
962 static int
963 wtmp_write(struct logininfo *li, struct utmp *ut)
964 {
965         struct stat buf;
966         int fd, ret = 1;
967
968         if ((fd = open(WTMP_FILE, O_WRONLY|O_APPEND, 0)) < 0) {
969                 log("wtmp_write: problem writing %s: %s",
970                     WTMP_FILE, strerror(errno));
971                 return 0;
972         }
973         if (fstat(fd, &buf) == 0) 
974                 if (atomicio(write, fd, ut, sizeof(*ut)) != sizeof(*ut)) {
975                         ftruncate(fd, buf.st_size);
976                         log("wtmp_write: problem writing %s: %s",
977                             WTMP_FILE, strerror(errno));
978                         ret = 0;
979                 }
980         (void)close(fd);
981         return ret;
982 }
983
984 static int
985 wtmp_perform_login(struct logininfo *li)
986 {
987         struct utmp ut;
988
989         construct_utmp(li, &ut);
990         return wtmp_write(li, &ut);
991 }
992
993
994 static int
995 wtmp_perform_logout(struct logininfo *li)
996 {
997         struct utmp ut;
998
999         construct_utmp(li, &ut);
1000         return wtmp_write(li, &ut);
1001 }
1002
1003
1004 int
1005 wtmp_write_entry(struct logininfo *li)
1006 {
1007         switch(li->type) {
1008         case LTYPE_LOGIN:
1009                 return wtmp_perform_login(li);
1010         case LTYPE_LOGOUT:
1011                 return wtmp_perform_logout(li);
1012         default:
1013                 log("wtmp_write_entry: invalid type field");
1014                 return 0;
1015         }
1016 }
1017
1018
1019 /* Notes on fetching login data from wtmp/wtmpx
1020  * 
1021  * Logouts are usually recorded with (amongst other things) a blank
1022  * username on a given tty line.  However, some systems (HP-UX is one)
1023  * leave all fields set, but change the ut_type field to DEAD_PROCESS.
1024  *
1025  * Since we're only looking for logins here, we know that the username
1026  * must be set correctly. On systems that leave it in, we check for
1027  * ut_type==USER_PROCESS (indicating a login.)
1028  *
1029  * Portability: Some systems may set something other than USER_PROCESS
1030  * to indicate a login process. I don't know of any as I write. Also,
1031  * it's possible that some systems may both leave the username in
1032  * place and not have ut_type.
1033  */
1034
1035 /* return true if this wtmp entry indicates a login */
1036 static int
1037 wtmp_islogin(struct logininfo *li, struct utmp *ut)
1038 {
1039         if (strncmp(li->username, ut->ut_name, 
1040                 MIN_SIZEOF(li->username, ut->ut_name)) == 0) {
1041 # ifdef HAVE_TYPE_IN_UTMP
1042                 if (ut->ut_type & USER_PROCESS)
1043                         return 1;
1044 # else
1045                 return 1;
1046 # endif
1047         }
1048         return 0;
1049 }
1050
1051 int
1052 wtmp_get_entry(struct logininfo *li)
1053 {
1054         struct stat st;
1055         struct utmp ut;
1056         int fd, found=0;
1057
1058         /* Clear the time entries in our logininfo */
1059         li->tv_sec = li->tv_usec = 0;
1060
1061         if ((fd = open(WTMP_FILE, O_RDONLY)) < 0) {
1062                 log("wtmp_get_entry: problem opening %s: %s",
1063                     WTMP_FILE, strerror(errno));
1064                 return 0;
1065         }
1066         if (fstat(fd, &st) != 0) {
1067                 log("wtmp_get_entry: couldn't stat %s: %s",
1068                     WTMP_FILE, strerror(errno));
1069                 close(fd);
1070                 return 0;
1071         }
1072
1073         /* Seek to the start of the last struct utmp */
1074         if (lseek(fd, (off_t)(0 - sizeof(struct utmp)), SEEK_END) == -1) {
1075                 /* Looks like we've got a fresh wtmp file */
1076                 close(fd);
1077                 return 0;
1078         }
1079
1080         while (!found) {
1081                 if (atomicio(read, fd, &ut, sizeof(ut)) != sizeof(ut)) {
1082                         log("wtmp_get_entry: read of %s failed: %s",
1083                             WTMP_FILE, strerror(errno));
1084                         close (fd);
1085                         return 0;
1086                 }
1087                 if ( wtmp_islogin(li, &ut) ) {
1088                         found = 1;
1089                         /* We've already checked for a time in struct
1090                          * utmp, in login_getlast(). */
1091 # ifdef HAVE_TIME_IN_UTMP
1092                         li->tv_sec = ut.ut_time;
1093 # else
1094 #  if HAVE_TV_IN_UTMP
1095                         li->tv_sec = ut.ut_tv.tv_sec;
1096 #  endif
1097 # endif
1098                         line_fullname(li->line, ut.ut_line,
1099                                       MIN_SIZEOF(li->line, ut.ut_line));
1100 # ifdef HAVE_HOST_IN_UTMP
1101                         strlcpy(li->hostname, ut.ut_host,
1102                                 MIN_SIZEOF(li->hostname, ut.ut_host));
1103 # endif
1104                         continue;
1105                 }
1106                 /* Seek back 2 x struct utmp */
1107                 if (lseek(fd, (off_t)(0-2*sizeof(struct utmp)), SEEK_CUR) == -1) {
1108                         /* We've found the start of the file, so quit */
1109                         close (fd);
1110                         return 0;
1111                 }
1112         }
1113
1114         /* We found an entry. Tidy up and return */
1115         close(fd);
1116         return 1;
1117 }
1118 # endif /* USE_WTMP */
1119
1120
1121 /**
1122  ** Low-level wtmpx functions
1123  **/
1124
1125 #ifdef USE_WTMPX
1126 /* write a wtmpx entry direct to the end of the file */
1127 /* This is a slight modification of code in OpenBSD's logwtmp.c */
1128 static int
1129 wtmpx_write(struct logininfo *li, struct utmpx *utx)
1130 {
1131         struct stat buf;
1132         int fd, ret = 1;
1133
1134         if ((fd = open(WTMPX_FILE, O_WRONLY|O_APPEND, 0)) < 0) {
1135                 log("wtmpx_write: problem opening %s: %s",
1136                     WTMPX_FILE, strerror(errno));
1137                 return 0;
1138         }
1139
1140         if (fstat(fd, &buf) == 0) 
1141                 if (atomicio(write, fd, utx, sizeof(*utx)) != sizeof(*utx)) {
1142                         ftruncate(fd, buf.st_size);
1143                         log("wtmpx_write: problem writing %s: %s",
1144                             WTMPX_FILE, strerror(errno));
1145                         ret = 0;
1146                 }
1147         (void)close(fd);
1148
1149         return ret;
1150 }
1151
1152
1153 static int
1154 wtmpx_perform_login(struct logininfo *li)
1155 {
1156         struct utmpx utx;
1157
1158         construct_utmpx(li, &utx);
1159         return wtmpx_write(li, &utx);
1160 }
1161
1162
1163 static int
1164 wtmpx_perform_logout(struct logininfo *li)
1165 {
1166         struct utmpx utx;
1167
1168         construct_utmpx(li, &utx);
1169         return wtmpx_write(li, &utx);
1170 }
1171
1172
1173 int
1174 wtmpx_write_entry(struct logininfo *li)
1175 {
1176         switch(li->type) {
1177         case LTYPE_LOGIN:
1178                 return wtmpx_perform_login(li);
1179         case LTYPE_LOGOUT:
1180                 return wtmpx_perform_logout(li);
1181         default:
1182                 log("wtmpx_write_entry: invalid type field");
1183                 return 0;
1184         }
1185 }
1186
1187 /* Please see the notes above wtmp_islogin() for information about the
1188    next two functions */
1189
1190 /* Return true if this wtmpx entry indicates a login */
1191 static int
1192 wtmpx_islogin(struct logininfo *li, struct utmpx *utx)
1193 {
1194         if ( strncmp(li->username, utx->ut_name,
1195                 MIN_SIZEOF(li->username, utx->ut_name)) == 0 ) {
1196 # ifdef HAVE_TYPE_IN_UTMPX
1197                 if (utx->ut_type == USER_PROCESS)
1198                         return 1;
1199 # else
1200                 return 1;
1201 # endif
1202         }
1203         return 0;
1204 }
1205
1206
1207 int
1208 wtmpx_get_entry(struct logininfo *li)
1209 {
1210         struct stat st;
1211         struct utmpx utx;
1212         int fd, found=0;
1213
1214         /* Clear the time entries */
1215         li->tv_sec = li->tv_usec = 0;
1216
1217         if ((fd = open(WTMPX_FILE, O_RDONLY)) < 0) {
1218                 log("wtmpx_get_entry: problem opening %s: %s",
1219                     WTMPX_FILE, strerror(errno));
1220                 return 0;
1221         }
1222         if (fstat(fd, &st) != 0) {
1223                 log("wtmpx_get_entry: couldn't stat %s: %s",
1224                     WTMP_FILE, strerror(errno));
1225                 close(fd);
1226                 return 0;
1227         }
1228         
1229         /* Seek to the start of the last struct utmpx */
1230         if (lseek(fd, (off_t)(0-sizeof(struct utmpx)), SEEK_END) == -1 ) {
1231                 /* probably a newly rotated wtmpx file */
1232                 close(fd);
1233                 return 0;
1234         }
1235
1236         while (!found) {
1237                 if (atomicio(read, fd, &utx, sizeof(utx)) != sizeof(utx)) {
1238                         log("wtmpx_get_entry: read of %s failed: %s",
1239                             WTMPX_FILE, strerror(errno));
1240                         close (fd);
1241                         return 0;
1242                 }
1243                 /* Logouts are recorded as a blank username on a particular line.
1244                  * So, we just need to find the username in struct utmpx */
1245                 if ( wtmpx_islogin(li, &utx) ) {
1246 # ifdef HAVE_TV_IN_UTMPX
1247                         li->tv_sec = utx.ut_tv.tv_sec;
1248 # else
1249 #  ifdef HAVE_TIME_IN_UTMPX
1250                         li->tv_sec = utx.ut_time;
1251 #  endif
1252 # endif
1253                         line_fullname(li->line, utx.ut_line, sizeof(li->line));
1254 # ifdef HAVE_HOST_IN_UTMPX
1255                         strlcpy(li->hostname, utx.ut_host,
1256                                 MIN_SIZEOF(li->hostname, utx.ut_host));
1257 # endif
1258                         continue;
1259                 }
1260                 if (lseek(fd, (off_t)(0-2*sizeof(struct utmpx)), SEEK_CUR) == -1) {
1261                         close (fd);
1262                         return 0;
1263                 }
1264         }
1265
1266         close(fd);
1267         return 1;
1268 }
1269 #endif /* USE_WTMPX */
1270
1271 /**
1272  ** Low-level libutil login() functions
1273  **/
1274
1275 #ifdef USE_LOGIN
1276 static int
1277 syslogin_perform_login(struct logininfo *li)
1278 {
1279         struct utmp *ut;
1280
1281         if (! (ut = (struct utmp *)malloc(sizeof(*ut)))) {
1282                 log("syslogin_perform_login: couldn't malloc()");
1283                 return 0;
1284         }
1285         construct_utmp(li, ut);
1286         login(ut);
1287
1288         return 1;
1289 }
1290
1291 static int
1292 syslogin_perform_logout(struct logininfo *li)
1293 {
1294 # ifdef HAVE_LOGOUT
1295         char line[8];
1296   
1297         (void)line_stripname(line, li->line, sizeof(line));
1298
1299         if (!logout(line)) {
1300                 log("syslogin_perform_logout: logout() returned an error");
1301 #  ifdef HAVE_LOGWTMP
1302         } else {
1303                 logwtmp(line, "", "");
1304 #  endif
1305         }
1306         /* FIXME: (ATL - if the need arises) What to do if we have
1307          * login, but no logout?  what if logout but no logwtmp? All
1308          * routines are in libutil so they should all be there,
1309          * but... */
1310 # endif
1311         return 1;
1312 }
1313
1314 int
1315 syslogin_write_entry(struct logininfo *li)
1316 {
1317         switch (li->type) {
1318         case LTYPE_LOGIN:
1319                 return syslogin_perform_login(li);
1320         case LTYPE_LOGOUT:
1321                 return syslogin_perform_logout(li);
1322         default:
1323                 log("syslogin_write_entry: Invalid type field");
1324                 return 0;
1325         }
1326 }
1327 #endif /* USE_LOGIN */
1328
1329 /* end of file log-syslogin.c */
1330
1331 /**
1332  ** Low-level lastlog functions
1333  **/
1334
1335 #ifdef USE_LASTLOG
1336 #define LL_FILE 1
1337 #define LL_DIR 2
1338 #define LL_OTHER 3
1339
1340 static void
1341 lastlog_construct(struct logininfo *li, struct lastlog *last)
1342 {
1343         /* clear the structure */
1344         memset(last, '\0', sizeof(*last));
1345   
1346         (void)line_stripname(last->ll_line, li->line, sizeof(last->ll_line));
1347         strlcpy(last->ll_host, li->hostname,
1348                 MIN_SIZEOF(last->ll_host, li->hostname));
1349         last->ll_time = li->tv_sec;
1350 }
1351
1352 static int
1353 lastlog_filetype(char *filename)
1354 {
1355         struct stat st;
1356
1357         if (stat(LASTLOG_FILE, &st) != 0) {
1358                 log("lastlog_perform_login: Couldn't stat %s: %s", LASTLOG_FILE, 
1359                         strerror(errno));
1360                 return 0;
1361         }
1362         if (S_ISDIR(st.st_mode))
1363                 return LL_DIR;
1364         else if (S_ISREG(st.st_mode))
1365                 return LL_FILE;
1366         else
1367                 return LL_OTHER;
1368 }
1369
1370
1371 /* open the file (using filemode) and seek to the login entry */
1372 static int
1373 lastlog_openseek(struct logininfo *li, int *fd, int filemode)
1374 {
1375         off_t offset;
1376         int type;
1377         char lastlog_file[1024];
1378
1379         type = lastlog_filetype(LASTLOG_FILE);
1380         switch (type) {
1381                 case LL_FILE:
1382                         strlcpy(lastlog_file, LASTLOG_FILE, sizeof(lastlog_file));
1383                         break;
1384                 case LL_DIR:
1385                         snprintf(lastlog_file, sizeof(lastlog_file), "%s/%s",
1386                                  LASTLOG_FILE, li->username);
1387                         break;
1388                 default:
1389                         log("lastlog_openseek: %.100s is not a file or directory!",
1390                             LASTLOG_FILE);
1391                         return 0;
1392         }
1393
1394         *fd = open(lastlog_file, filemode);
1395         if ( *fd < 0) {
1396                 debug("lastlog_openseek: Couldn't open %s: %s",
1397                     lastlog_file, strerror(errno));
1398                 return 0;
1399         }
1400         
1401         if (type == LL_FILE) {
1402                 /* find this uid's offset in the lastlog file */
1403                 offset = (off_t) ( (long)li->uid * sizeof(struct lastlog));
1404
1405                 if ( lseek(*fd, offset, SEEK_SET) != offset ) {
1406                         log("lastlog_openseek: %s->lseek(): %s",
1407                          lastlog_file, strerror(errno));
1408                         return 0;
1409                 }
1410         }
1411         
1412         return 1;
1413 }
1414
1415 static int
1416 lastlog_perform_login(struct logininfo *li)
1417 {
1418         struct lastlog last;
1419         int fd;
1420
1421         /* create our struct lastlog */
1422         lastlog_construct(li, &last);
1423
1424         if (!lastlog_openseek(li, &fd, O_RDWR|O_CREAT))
1425                 return(0);
1426                 
1427         /* write the entry */
1428         if (atomicio(write, fd, &last, sizeof(last)) != sizeof(last)) {
1429                 close(fd);
1430                 log("lastlog_write_filemode: Error writing to %s: %s",
1431                     LASTLOG_FILE, strerror(errno));
1432                 return 0;
1433         }
1434
1435         close(fd);
1436         return 1;
1437 }
1438
1439 int
1440 lastlog_write_entry(struct logininfo *li)
1441 {
1442         switch(li->type) {
1443         case LTYPE_LOGIN:
1444                 return lastlog_perform_login(li);
1445         default:
1446                 log("lastlog_write_entry: Invalid type field");
1447                 return 0;
1448         }
1449 }
1450
1451 static void
1452 lastlog_populate_entry(struct logininfo *li, struct lastlog *last)
1453 {
1454         line_fullname(li->line, last->ll_line, sizeof(li->line));
1455         strlcpy(li->hostname, last->ll_host, 
1456                 MIN_SIZEOF(li->hostname, last->ll_host));
1457         li->tv_sec = last->ll_time;
1458 }
1459
1460 int
1461 lastlog_get_entry(struct logininfo *li)
1462 {
1463         struct lastlog last;
1464         int fd;
1465
1466         if (lastlog_openseek(li, &fd, O_RDONLY)) {
1467                 if (atomicio(read, fd, &last, sizeof(last)) != sizeof(last)) {
1468                         log("lastlog_get_entry: Error reading from %s: %s",
1469                             LASTLOG_FILE, strerror(errno));
1470                         return 0;
1471                 } else {
1472                         lastlog_populate_entry(li, &last);
1473                         return 1;
1474                 }
1475         } else {
1476                 return 0;    
1477         }
1478 }
1479 #endif /* USE_LASTLOG */
This page took 0.160878 seconds and 5 git commands to generate.