Hi all,

Any chance of adding lastlog information to the etc module?  I have a
patch, but it only works for Solaris at the moment.  Basically, it means
altering the extconf.rb file and doing the following:

Look for a last_log struct in utmp.h.
If it's not there, look for a lastlog.h file.
If either of these are present, then check for the ll_time struct member
(doesn't appear to be defined in Linux, but is defined in Solaris and
FreeBSD).

Within etc.c:

Use appropriate macros derived from extconf.rb.
Point the lastlog name to _PATH_LASTLOG if present.  If not, use
"/var/adm/lastlog".
Add the time (ll_time, if present), device name (ll_line) and host info
(ll_host) to the data returned by Etc.passwd.

Does this sound like a good idea?

I've included a Solaris patch below which works fine (just to
demonstrate), but needs adjustment for other Unix platforms.

Regards,

Dan

--- extconf.orig	Wed Feb  2 11:55:35 2005
+++ extconf.rb	Wed Feb  2 12:00:45 2005
@@ -14,5 +14,6 @@
   have_struct_member('struct passwd', 'pw_expire', 'pwd.h')
   have_struct_member('struct passwd', 'pw_passwd', 'pwd.h')
   have_struct_member('struct group', 'gr_passwd', 'grp.h')
+  have_header("lastlog.h")
   create_makefile("etc")
 end

--- etc.bak Wed Feb  2 11:54:24 2005
+++ etc.c	Wed Feb  2 13:02:53 2005
@@ -23,8 +23,17 @@
 #include <grp.h>
 #endif
 
+#ifdef HAVE_LASTLOG_H
+#include <lastlog.h>
+#include <fcntl.h>
+#endif
+
 static VALUE sPasswd, sGroup;
 
+#ifdef HAVE_LASTLOG_H
+static struct lastlog* getllnam(const char* name);
+#endif
+
 #ifndef _WIN32
 char *getenv();
 #endif
@@ -64,7 +73,18 @@
 setup_passwd(pwd)
     struct passwd *pwd;
 {
+#ifdef HAVE_LASTLOG_H
+   struct lastlog* ll_entry;
+   time_t time;
+   VALUE rbTime = Qnil;
+#endif
     if (pwd == 0) rb_sys_fail("/etc/passwd");
+#ifdef HAVE_LASTLOG_H
+    ll_entry = getllnam(pwd->pw_name);
+    time = ll_entry->ll_time;
+    if(time != 0)
+       rbTime = rb_time_new(time,0);
+#endif
     return rb_struct_new(sPasswd,
 			 safe_setup_str(pwd->pw_name),
 #ifdef HAVE_ST_PW_PASSWD
@@ -95,6 +115,11 @@
 #ifdef HAVE_ST_PW_EXPIRE
 			 INT2FIX(pwd->pw_expire),
 #endif
+#ifdef HAVE_LASTLOG_H
+          rbTime,
+          safe_setup_str(ll_entry->ll_line),
+          safe_setup_str(ll_entry->ll_host),
+#endif
 			 0		/*dummy*/
 	);
 }
@@ -157,7 +182,7 @@
     struct passwd *pw;
 
     setpwent();
-    while (pw = getpwent()) {
+    while((pw = getpwent())) {
 	rb_yield(setup_passwd(pw));
     }
     endpwent();
@@ -180,7 +205,7 @@
 	passwd_blocking = Qtrue;
 	rb_ensure(passwd_iterate, 0, passwd_ensure, 0);
     }
-    if (pw = getpwent()) {
+    if((pw = getpwent())) {
 	return setup_passwd(pw);
     }
 #endif
@@ -214,7 +239,7 @@
 #ifdef HAVE_GETPWENT
     struct passwd *pw;
 
-    if (pw = getpwent()) {
+    if((pw = getpwent())) {
 	return setup_passwd(pw);
     }
 #endif
@@ -295,7 +320,7 @@
     struct group *pw;
 
     setgrent();
-    while (pw = getgrent()) {
+    while((pw = getgrent())) {
 	rb_yield(setup_group(pw));
     }
     endgrent();
@@ -318,7 +343,7 @@
 	group_blocking = Qtrue;
 	rb_ensure(group_iterate, 0, group_ensure, 0);
     }
-    if (grp = getgrent()) {
+    if((grp = getgrent())) {
 	return setup_group(grp);
     }
 #endif
@@ -352,7 +377,7 @@
 #ifdef HAVE_GETGRENT
     struct group *gr;
 
-    if (gr = getgrent()) {
+    if((gr = getgrent())) {
 	return setup_group(gr);
     }
 #endif
@@ -359,6 +384,24 @@
     return Qnil;
 }
 
+#ifdef HAVE_LASTLOG_H
+static struct lastlog* getllnam(const char* name){
+   static struct lastlog ll_entry;
+   struct passwd* pwd;
+   int fd;
+
+   if((fd = open("/var/adm/lastlog",O_RDONLY)) == -1)
+      rb_sys_fail("/var/adm/lastlog");
+
+   if((pwd = getpwnam(name)) == NULL)
+      rb_sys_fail("getpwnam failed");
+
+   pread(fd,&ll_entry,sizeof(struct lastlog),pwd->pw_uid *
sizeof(struct lastlog));
+   close(fd);
+   return (&ll_entry);   
+}
+#endif
+
 static VALUE mEtc;
 
 void
@@ -406,6 +449,11 @@
 #ifdef HAVE_ST_PW_EXPIRE
 				"expire",
 #endif
+#ifdef HAVE_LASTLOG_H
+            "last_login",
+            "device_name",
+            "host_name",
+#endif
 				NULL);
     rb_global_variable(&sPasswd);