2 Copyright (C) 2001-2006 Ben Kibbey <bjk@luxsci.net>
4 This program is free software; you can redistribute it and/or modify
5 it under the terms of the GNU General Public License as published by
6 the Free Software Foundation; either version 2 of the License, or
7 (at your option) any later version.
9 This program is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 GNU General Public License for more details.
14 You should have received a copy of the GNU General Public License
15 along with this program; if not, write to the Free Software
16 Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
25 #include <sys/types.h>
33 #include "../strsep.c"
43 #define LOGIN_OPTION_ORDER "pdimyhtl"
44 #define LOGIN_OPTION_STRING "Lpdimyhtl:"
46 static char options
[9]; /* NULL terminated. */
47 static char *last_options
;
48 static char **strings
;
51 static int login_count
;
53 void add_string(char ***, const char *);
54 char *stamp(time_t, const char *);
56 void ui_module_init(int *chainable
)
59 fprintf(stderr
, "%s: ui_module_init()\n", __FILE__
);
69 fprintf(stderr
, "%s: ui_module_exit()\n", __FILE__
);
87 /* This is for *BSD (login process id). */
89 char *ui_module_pid(uid_t uid
, int multi
)
92 static char line
[LINE_MAX
];
95 char errbuf
[LINE_MAX
];
96 struct kinfo_proc
*kp
;
107 if ((kd
= kvm_openfiles(NULL
, NULL
, NULL
,
109 if ((kd
= kvm_openfiles(_PATH_DEVNULL
, _PATH_DEVNULL
, _PATH_DEVNULL
,
111 O_RDONLY
, errbuf
)) == NULL
) {
117 if ((kp
= kvm_getprocs(kd
, KERN_PROC_UID
, uid
, &cnt
)) == NULL
) {
118 warnx("kvm_getprocs(): %s", kvm_geterr(kd
));
122 for (i
= 0; i
< cnt
; i
++) {
125 #if __FreeBSD_version < 500000
126 if (kp
[i
].kp_eproc
.e_flag
& EPROC_SLEADER
&& kp
[i
].kp_eproc
.e_tdev
!=
128 pid
= kp
[i
].kp_eproc
.e_ppid
;
130 * pid = kp[i].kp_proc.p_pid;
136 if (kp
[i
].ki_kiflag
& KI_SLEADER
&& kp
[i
].ki_tdev
!= -1) {
139 snprintf(buf
, sizeof(buf
), "%i%c", pid
, multi
);
140 safe_strncat(line
, buf
, sizeof(line
));
147 line
[strlen(line
) - 1] = '\0';
151 /* This is for Linux and Solaris. */
152 #elif defined(HAVE_PROCFS)
153 #include <sys/types.h>
154 #include <sys/stat.h>
165 char *ui_module_pid(uid_t uid
, int multi
)
170 static char line
[LINE_MAX
];
176 struct pstatus pstat
;
183 if (!procdir
&& firstrun
)
189 if ((procdir
= opendir("/proc")) == NULL
) {
198 while ((ent
= readdir(procdir
)) != NULL
) {
200 char filename
[FILENAME_MAX
];
208 if (!isdigit((unsigned char) *ent
->d_name
))
212 snprintf(filename
, sizeof(filename
), "/proc/%s/stat", ent
->d_name
);
214 snprintf(filename
, sizeof(filename
), "/proc/%s/status", ent
->d_name
);
217 if (stat(filename
, &st
) == -1)
221 * The current user owns this file (process id).
223 if (st
.st_uid
== uid
) {
225 if ((fd
= open(filename
, O_RDONLY
)) == -1)
228 if (pread(fd
, &pstat
, sizeof(struct pstatus
), 0) !=
229 sizeof(struct pstatus
)) {
237 if ((fp
= fopen(filename
, "r")) == NULL
)
240 if ((t
= fgets(buf
, sizeof(buf
), fp
)) == NULL
) {
246 if ((i
= sscanf(buf
, "%*i %*s %*c %*i %*i %i", &pid
)) < 1) {
250 if ((i = sscanf(buf, "%*s %*i %li", &ppid)) < 1) {
261 * Skip duplicate pids.
263 for (i
= 0; i
< pid_index
; i
++) {
268 snprintf(buf
, sizeof(buf
), "%li%c", (unsigned long) pid
, multi
);
269 safe_strncat(line
, buf
, sizeof(line
));
272 realloc(pids
, (pid_index
+ 2) * sizeof(pid_t
*))) == NULL
) {
277 pids
[pid_index
++] = pid
;
287 line
[strlen(line
) - 1] = '\0';
291 /* Unsupported OS. */
292 char *ui_module_pid(uid_t uid
, int multi
)
299 /* Break up the last login string into sections and add the sections to the
300 * output string array if needed. */
301 static void last_strings(char *str
)
305 const char *line
, *host
, *when
;
307 line
= host
= when
= (str
) ? "-" : "!";
309 while ((buf
= strsep(&str
, ",")) != NULL
) {
328 for (i
= 0; i
< strlen(last_options
); i
++) {
329 switch (last_options
[i
]) {
331 add_string(&strings
, line
);
334 add_string(&strings
, host
);
337 add_string(&strings
, when
);
340 add_string(&strings
, line
);
341 add_string(&strings
, host
);
342 add_string(&strings
, when
);
349 /* Get the lastlog structure from the lastlog file. */
350 #if (defined(__FreeBSD_version) && __FreeBSD_version >= 900000)
351 static char *lastlogin(const struct passwd
*pw
, char *tf
)
354 static char buf
[LINE_MAX
];
356 if (setutxdb(UTXDB_LASTLOGIN
, NULL
) == -1) {
361 last
= getutxuser(pw
->pw_name
);
366 snprintf(buf
, sizeof(buf
), "%s,%s,%s",
367 !last
->ut_line
[0] ? "!" : last
->ut_line
,
368 (!last
->ut_host
[0] || !isalnum(last
->ut_host
[0])) ?
369 !isdigit(last
->ut_line
[3]) ? "!" : "-" : last
->ut_host
,
370 !last
->ut_time
[0] ? "!" : stamp(last
->ut_time
, tf
));
375 static char *lastlogin(const struct passwd
*pw
, char *tf
)
379 static char buf
[LINE_MAX
];
386 if ((lastlogfd
= open(_PATH_LASTLOG
, O_RDONLY
)) == -1) {
387 warn("%s", _PATH_LASTLOG
);
392 offset
= (long) pw
->pw_uid
* sizeof(struct lastlog
);
394 if (lseek(lastlogfd
, offset
, SEEK_SET
) == -1) {
395 warn("%s", _PATH_LASTLOG
);
399 if ((count
= read(lastlogfd
, &last
, sizeof(struct lastlog
))) !=
400 sizeof(struct lastlog
)) {
402 warn("%s", _PATH_LASTLOG
);
409 last
.ll_host
[UTX_HOSTSIZE
-1] = '\0';
410 last
.ll_line
[UTX_LINESIZE
-1] = '\0';
412 last
.ll_host
[UT_HOSTSIZE
-1] = '\0';
413 last
.ll_line
[UT_LINESIZE
-1] = '\0';
416 last
.ll_host
[UT_HOSTSIZE
-1] = '\0';
417 last
.ll_line
[UT_LINESIZE
-1] = '\0';
420 snprintf(buf
, sizeof(buf
), "%s,%s,%s",
421 !last
.ll_line
[0] ? "!" : last
.ll_line
,
422 (!last
.ll_host
[0] || !isalnum(last
.ll_host
[0])) ?
423 !isdigit(last
.ll_line
[3]) ? "!" : "-" : last
.ll_host
,
424 !last
.ll_time
? "!" : stamp(last
.ll_time
, tf
));
429 /* This will return an array of utmp structures if a user is logged in, NULL
430 * otherwise. We'll try to keep the utmp file descriptor open if possible to
431 * speed things up a bit. */
432 static UTMP
**get_utmp(const char *user
)
434 UTMP
**logins
= NULL
;
446 if ((fd
= open(_PATH_UTMP
, O_RDONLY
)) == -1) {
447 warn("%s", _PATH_UTMP
);
458 while ((u
= getutxent()) != NULL
) {
459 if (!strcmp(u
->ut_user
, user
)) {
461 lseek(fd
, 0, SEEK_SET
);
463 while ((count
= read(fd
, &u
, sizeof(UTMP
))) == sizeof(UTMP
)) {
464 if (strcmp(u
.ut_name
, user
) == 0) {
466 if ((logins
= realloc(logins
,
467 (login_count
+ 2) * sizeof(UTMP
*))) ==
473 if ((logins
[login_count
] = malloc(sizeof(UTMP
))) == NULL
) {
480 memcpy(logins
[login_count
]->ut_name
, u
->ut_name
, UTX_NAMESIZE
);
481 logins
[login_count
]->ut_name
[UTX_NAMESIZE
-1] = 0;
482 memcpy(logins
[login_count
]->ut_line
, u
->ut_line
, UTX_LINESIZE
);
483 logins
[login_count
]->ut_line
[UTX_LINESIZE
-1] = 0;
484 memcpy(logins
[login_count
]->ut_host
, u
->ut_host
, UTX_HOSTSIZE
);
485 logins
[login_count
]->ut_host
[UTX_HOSTSIZE
-1] = 0;
486 logins
[login_count
]->ut_pid
= u
->ut_pid
;
488 memcpy(logins
[login_count
]->ut_user
, u
->ut_user
, UT_NAMESIZE
);
489 logins
[login_count
]->ut_user
[UT_NAMESIZE
-1] = 0;
490 memcpy(logins
[login_count
]->ut_line
, u
->ut_line
, UT_LINESIZE
);
491 logins
[login_count
]->ut_line
[UT_LINESIZE
-1] = 0;
492 memcpy(logins
[login_count
]->ut_host
, u
->ut_host
, UT_HOSTSIZE
);
493 logins
[login_count
]->ut_host
[UT_HOSTSIZE
-1] = 0;
494 logins
[login_count
]->ut_tv
.tv_sec
= u
->ut_tv
.tv_sec
;
495 logins
[login_count
]->ut_pid
= u
->ut_pid
;
498 memcpy(logins
[login_count
]->ut_name
, u
.ut_name
, UT_NAMESIZE
);
499 logins
[login_count
]->ut_name
[UT_NAMESIZE
-1] = 0;
500 memcpy(logins
[login_count
]->ut_line
, u
.ut_line
, UT_LINESIZE
);
501 logins
[login_count
]->ut_line
[UT_LINESIZE
-1] = 0;
502 memcpy(logins
[login_count
]->ut_host
, u
.ut_host
, UT_HOSTSIZE
);
503 logins
[login_count
]->ut_host
[UT_HOSTSIZE
-1] = 0;
504 logins
[login_count
]->ut_time
= u
.ut_time
;
506 logins
[++login_count
] = NULL
;
513 /* The 'mesg' status of the logged in user. */
514 static char *msgstat(UTMP
**u
, int multi
)
516 static char line
[LINE_MAX
];
521 for (i
= 0; i
< login_count
; i
++) {
522 char filename
[FILENAME_MAX
];
524 char m
[2] = { multi
, '\0' };
526 snprintf(filename
, sizeof(filename
), "%s%s", _PATH_DEV
, u
[i
]->ut_line
);
528 if (stat(filename
, &st
) == -1)
529 safe_strncat(line
, "!", sizeof(line
));
532 (st
.st_mode
& S_IWGRP
|| st
.st_mode
& S_IWOTH
) ? "1" : "0",
535 safe_strncat(line
, m
, sizeof(line
));
541 line
[strlen(line
) - 1] = '\0';
545 /* Returns the users idle time in seconds. */
546 static char *idle(UTMP
**u
, int multi
)
548 static char line
[LINE_MAX
];
555 for (i
= 0; i
< login_count
; i
++) {
556 char buf
[FILENAME_MAX
];
557 char m
[2] = { multi
, '\0' };
559 snprintf(buf
, sizeof(buf
), "%s%s", _PATH_DEV
, u
[i
]->ut_line
);
561 if (stat(buf
, &st
) == -1) {
562 safe_strncat(line
, "!", sizeof(line
));
563 safe_strncat(line
, m
, sizeof(line
));
568 if (u
[i
]->ut_tv
.tv_sec
> st
.st_atime
) {
570 if (u
[i
]->ut_time
> st
.st_atime
) {
572 safe_strncat(line
, "-", sizeof(line
));
573 safe_strncat(line
, m
, sizeof(line
));
580 if (t
< u
[i
]->ut_tv
.tv_sec
)
581 t
= u
[i
]->ut_tv
.tv_sec
;
583 if (t
< u
[i
]->ut_time
)
587 snprintf(buf
, sizeof(buf
), "%lu", (now
- t
<= 0) ? 0 : now
- t
);
588 safe_strncat(line
, buf
, sizeof(line
));
589 safe_strncat(line
, m
, sizeof(line
));
595 line
[strlen(line
) - 1] = '\0';
599 /* This is output if the -h command line option is passed to the main program.
601 void ui_module_help()
604 fprintf(stderr
, "%s: ui_module_help()\n", __FILE__
);
607 printf(" Login information [-L (-%s)]:\n", LOGIN_OPTION_ORDER
);
608 printf("\t-y tty\t\t\t\t");
609 printf("-m message status\n");
610 printf("\t-t login time stamp\t\t");
611 printf("-d duration in minutes\n");
612 printf("\t-h hostname\t\t\t");
613 printf("-i seconds idle\n");
614 printf("\t-p login process id\n");
615 printf("\t-l lastlog information"
616 " (any of tt[y],[h]ostname,[t]ime, or [a]ll)\n\n");
620 /* This is the equivalent to main() only without argc and argv available. */
621 int ui_module_exec(char ***s
, const struct passwd
*pw
, const int multi
,
622 const int verbose
, char *tf
)
625 UTMP
**u
= NULL
, **up
;
629 u
= get_utmp(pw
->pw_name
);
633 char line
[LINE_MAX
] = { '\0' };
635 char m
[2] = { multi
, '\0' };
639 add_string(&strings
, (u
) ? idle(u
, multi
) : "!");
642 last_strings(lastlogin(pw
, tf
));
645 for (i
= 0; i
< login_count
; i
++) {
647 && isalnum((unsigned char) u
[i
]->ut_host
[0])) {
648 safe_strncat(line
, u
[i
]->ut_host
, sizeof(line
));
652 * If a users tty is tty1-n, it must be a console
656 && isdigit((unsigned char) u
[i
]->ut_line
[3]))
657 safe_strncat(line
, "-", sizeof(line
));
659 safe_strncat(line
, "!", sizeof(line
));
662 safe_strncat(line
, m
, sizeof(line
));
666 strncpy(line
, "!", sizeof(line
));
668 line
[strlen(line
) - 1] = '\0';
670 add_string(&strings
, line
);
673 for (i
= 0; i
< login_count
; i
++) {
674 if (u
[i
]->ut_line
[0])
675 safe_strncat(line
, u
[i
]->ut_line
, sizeof(line
));
677 safe_strncat(line
, "!", sizeof(line
));
679 safe_strncat(line
, m
, sizeof(line
));
683 strncpy(line
, "!", sizeof(line
));
685 line
[strlen(line
) - 1] = '\0';
687 add_string(&strings
, line
);
690 add_string(&strings
, msgstat(u
, multi
));
693 for (i
= 0; i
< login_count
; i
++) {
695 safe_strncat(line
, stamp(u
[i
]->ut_tv
.tv_sec
, tf
), sizeof(line
));
697 safe_strncat(line
, stamp(u
[i
]->ut_time
, tf
), sizeof(line
));
699 safe_strncat(line
, m
, sizeof(line
));
703 strncpy(line
, "!", sizeof(line
));
705 line
[strlen(line
) - 1] = '\0';
707 add_string(&strings
, line
);
710 for (i
= 0; i
< login_count
; i
++) {
712 if ((now
- u
[i
]->ut_tv
.tv_sec
) > 60) {
713 snprintf(buf
, sizeof(buf
), "%lu",
714 ((now
- u
[i
]->ut_tv
.tv_sec
) / 60));
716 if ((now
- u
[i
]->ut_time
) > 60) {
717 snprintf(buf
, sizeof(buf
), "%lu",
718 ((now
- u
[i
]->ut_time
) / 60));
720 safe_strncat(line
, buf
, sizeof(line
));
723 safe_strncat(line
, "-", sizeof(line
));
725 safe_strncat(line
, m
, sizeof(line
));
729 strncpy(line
, "!", sizeof(line
));
731 line
[strlen(line
) - 1] = '\0';
733 add_string(&strings
, line
);
737 for (i
= 0; i
< login_count
; i
++) {
739 snprintf(buf
, sizeof(buf
), "%li", (long) u
[i
]->ut_pid
);
740 safe_strncat(line
, buf
, sizeof(line
));
743 safe_strncat(line
, "!", sizeof(line
));
745 safe_strncat(line
, m
, sizeof(line
));
749 strncpy(line
, "!", sizeof(line
));
751 line
[strlen(line
) - 1] = '\0';
753 add_string(&strings
, line
);
755 add_string(&strings
, (u
) ? ui_module_pid(pw
->pw_uid
, multi
) : "!");
766 for (up
= u
; *up
; up
++)
776 /* See if the last login options (-l) are valid. */
777 static int parse_last_options(const char *args
)
781 for (i
= 0; i
< strlen(args
); i
++) {
796 char *ui_module_options_init(char **defaults
)
799 return LOGIN_OPTION_STRING
;
802 /* Check module option validity. */
803 int ui_module_options(int argc
, char **argv
)
809 fprintf(stderr
, "%s: ui_module_options()\n", __FILE__
);
812 while ((opt
= getopt(argc
, argv
, LOGIN_OPTION_STRING
)) != -1) {
815 last_options
= optarg
;
827 warnx("login: invalid option -- %c", optopt
);
833 if (parse_last_options(last_options
))
838 strncpy(options
, LOGIN_OPTION_ORDER
, sizeof(options
));