1 /* $Id: login.c,v 1.5 2006-01-21 22:44:06 bjk Exp $ */
3 Copyright (C) 2001-2005 Ben Kibbey <bjk@arbornet.org>
5 This program is free software; you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published by
7 the Free Software Foundation; either version 2 of the License, or
8 (at your option) any later version.
10 This program is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 GNU General Public License for more details.
15 You should have received a copy of the GNU General Public License
16 along with this program; if not, write to the Free Software
17 Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
22 #include <sys/types.h>
37 #include "../strsep.c"
44 void ui_module_init(int *chainable
)
47 fprintf(stderr
, "%s: ui_module_init()\n", __FILE__
);
58 fprintf(stderr
, "%s: ui_module_exit()\n", __FILE__
);
78 /* This is for *BSD (login process id). */
80 char *ui_module_pid(uid_t uid
, int multi
)
83 static char line
[LINE_MAX
];
86 char errbuf
[LINE_MAX
];
87 struct kinfo_proc
*kp
;
98 if ((kd
= kvm_openfiles(NULL
, NULL
, NULL
,
100 if ((kd
= kvm_openfiles(_PATH_DEVNULL
, _PATH_DEVNULL
, _PATH_DEVNULL
,
102 O_RDONLY
, errbuf
)) == NULL
) {
108 if ((kp
= kvm_getprocs(kd
, KERN_PROC_UID
, uid
, &cnt
)) == NULL
) {
109 warnx("kvm_getprocs(): %s", kvm_geterr(kd
));
113 for (i
= 0; i
< cnt
; i
++) {
116 #if __FreeBSD_version < 500000
117 if (kp
[i
].kp_eproc
.e_flag
& EPROC_SLEADER
&& kp
[i
].kp_eproc
.e_tdev
!=
119 pid
= kp
[i
].kp_eproc
.e_ppid
;
121 * pid = kp[i].kp_proc.p_pid;
127 if (kp
[i
].ki_kiflag
& KI_SLEADER
&& kp
[i
].ki_tdev
!= -1) {
130 snprintf(buf
, sizeof(buf
), "%i%c", pid
, multi
);
131 strncat(line
, buf
, sizeof(line
));
138 line
[strlen(line
) - 1] = '\0';
142 /* This is for Linux and Solaris. */
143 #elif defined(HAVE_PROCFS)
144 #include <sys/types.h>
145 #include <sys/stat.h>
156 char *ui_module_pid(uid_t uid
, int multi
)
161 static char line
[LINE_MAX
];
167 struct pstatus pstat
;
174 if (!procdir
&& firstrun
)
180 if ((procdir
= opendir("/proc")) == NULL
) {
189 while ((ent
= readdir(procdir
)) != NULL
) {
191 char filename
[FILENAME_MAX
];
199 if (!isdigit((unsigned char) *ent
->d_name
))
203 snprintf(filename
, sizeof(filename
), "/proc/%s/stat", ent
->d_name
);
205 snprintf(filename
, sizeof(filename
), "/proc/%s/status", ent
->d_name
);
208 if (stat(filename
, &st
) == -1)
212 * The current user owns this file (process id).
214 if (st
.st_uid
== uid
) {
216 if ((fd
= open(filename
, O_RDONLY
)) == -1)
219 if (pread(fd
, &pstat
, sizeof(struct pstatus
), 0) !=
220 sizeof(struct pstatus
)) {
228 if ((fp
= fopen(filename
, "r")) == NULL
)
231 if ((t
= fgets(buf
, sizeof(buf
), fp
)) == NULL
) {
237 if ((i
= sscanf(buf
, "%*i %*s %*c %*i %*i %i", &pid
)) < 1) {
241 if ((i = sscanf(buf, "%*s %*i %li", &ppid)) < 1) {
252 * Skip duplicate pids.
254 for (i
= 0; i
< pid_index
; i
++) {
259 snprintf(buf
, sizeof(buf
), "%li%c", (unsigned long) pid
, multi
);
260 strncat(line
, buf
, sizeof(line
));
263 realloc(pids
, (pid_index
+ 2) * sizeof(pid_t
*))) == NULL
) {
268 pids
[pid_index
++] = pid
;
278 line
[strlen(line
) - 1] = '\0';
282 /* Unsupported OS. */
283 char *ui_module_pid(uid_t uid
, int multi
)
290 /* Break up the last login string into sections and add the sections to the
291 * output string array if needed. */
292 static void last_strings(char *str
)
296 const char *line
, *host
, *when
;
298 line
= host
= when
= (str
) ? "-" : "!";
300 while ((buf
= strsep(&str
, ",")) != NULL
) {
319 for (i
= 0; i
< strlen(last_options
); i
++) {
320 switch (last_options
[i
]) {
322 add_string(&strings
, line
);
325 add_string(&strings
, host
);
328 add_string(&strings
, when
);
331 add_string(&strings
, line
);
332 add_string(&strings
, host
);
333 add_string(&strings
, when
);
342 /* Get the lastlog structure from the lastlog file. */
343 static char *lastlogin(uid_t uid
, char *tf
)
347 static char buf
[LINE_MAX
];
351 char tmp
[64], htmp
[UTX_HOSTSIZE
+ 1];
353 char tmp
[64], htmp
[UT_HOSTSIZE
+ 1];
356 char tmp
[64], htmp
[UT_HOSTSIZE
+ 1];
360 buf
[0] = tmp
[0] = htmp
[0] = '\0';
366 if ((lastlogfd
= open(_PATH_LASTLOG
, O_RDONLY
)) == -1) {
367 warn("%s", _PATH_LASTLOG
);
372 offset
= (long) uid
*sizeof(struct lastlog
);
374 if (lseek(lastlogfd
, offset
, SEEK_SET
) == -1) {
375 warn("%s", _PATH_LASTLOG
);
379 if ((count
= read(lastlogfd
, &last
, sizeof(struct lastlog
))) !=
380 sizeof(struct lastlog
)) {
382 warn("%s", _PATH_LASTLOG
);
387 if (last
.ll_line
[0] == '\0')
388 strncpy(buf
, "!", sizeof(buf
));
390 strncpy(buf
, last
.ll_line
, sizeof(buf
));
392 strncat(buf
, ",", sizeof(buf
));
393 strncpy(htmp
, last
.ll_host
, sizeof(htmp
));
397 htmp
[UTX_HOSTSIZE
] = '\0';
399 htmp
[UT_HOSTSIZE
] = '\0';
402 htmp
[UT_HOSTSIZE
] = '\0';
405 if (htmp
[0] && isalnum((unsigned char) htmp
[0]))
406 strncat(buf
, htmp
, sizeof(buf
));
409 * If a users tty is tty1-n, it must be a console
412 if (last
.ll_line
[0] && isdigit((unsigned char) last
.ll_line
[3]))
413 strncat(buf
, "-", sizeof(buf
));
415 strncat(buf
, "!", sizeof(buf
));
418 strncat(buf
, ",", sizeof(buf
));
421 strncat(buf
, stamp(last
.ll_time
, tf
), sizeof(buf
));
423 strncat(buf
, "!", sizeof(buf
));
428 /* This will return an array of utmp structures if a user is logged in, NULL
429 * otherwise. We'll try to keep the utmp file descriptor open if possible to
430 * speed things up a bit. */
431 static UTMP
**get_utmp(const char *user
)
433 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_name
, user
) == 0) {
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 strncpy(logins
[login_count
]->ut_name
, u
->ut_name
, UTX_NAMESIZE
);
481 strncpy(logins
[login_count
]->ut_line
, u
->ut_line
, UTX_LINESIZE
);
482 strncpy(logins
[login_count
]->ut_host
, u
->ut_host
, UTX_HOSTSIZE
);
483 logins
[login_count
]->ut_pid
= u
->ut_pid
;
485 strncpy(logins
[login_count
]->ut_name
, u
->ut_name
, UT_NAMESIZE
);
486 strncpy(logins
[login_count
]->ut_line
, u
->ut_line
, UT_LINESIZE
);
487 strncpy(logins
[login_count
]->ut_host
, u
->ut_host
, UT_HOSTSIZE
);
488 logins
[login_count
]->ut_tv
.tv_sec
= u
->ut_tv
.tv_sec
;
489 logins
[login_count
]->ut_pid
= u
->ut_pid
;
492 strncpy(logins
[login_count
]->ut_name
, u
.ut_name
, UT_NAMESIZE
);
493 strncpy(logins
[login_count
]->ut_line
, u
.ut_line
, UT_LINESIZE
);
494 strncpy(logins
[login_count
]->ut_host
, u
.ut_host
, UT_HOSTSIZE
);
495 logins
[login_count
]->ut_host
[UT_HOSTSIZE
] = '\0';
496 logins
[login_count
]->ut_time
= u
.ut_time
;
498 logins
[++login_count
] = NULL
;
505 /* The 'mesg' status of the logged in user. */
506 static char *msgstat(UTMP
** u
, int multi
)
508 static char line
[LINE_MAX
];
513 for (i
= 0; i
< login_count
; i
++) {
514 char filename
[FILENAME_MAX
];
516 char m
[2] = { multi
, '\0' };
518 snprintf(filename
, sizeof(filename
), "%s%s", _PATH_DEV
, u
[i
]->ut_line
);
520 if (stat(filename
, &st
) == -1)
521 strncat(line
, "!", sizeof(line
));
524 (st
.st_mode
& S_IWGRP
|| st
.st_mode
& S_IWOTH
) ? "1" : "0",
527 strncat(line
, m
, sizeof(line
));
533 line
[strlen(line
) - 1] = '\0';
537 /* Returns the users idle time in seconds. */
538 static char *idle(UTMP
** u
, int multi
)
540 static char line
[LINE_MAX
];
547 for (i
= 0; i
< login_count
; i
++) {
548 char buf
[FILENAME_MAX
];
549 char m
[2] = { multi
, '\0' };
551 snprintf(buf
, sizeof(buf
), "%s%s", _PATH_DEV
, u
[i
]->ut_line
);
553 if (stat(buf
, &st
) == -1) {
554 strncat(line
, "!", sizeof(line
));
555 strncat(line
, m
, sizeof(line
));
560 if (u
[i
]->ut_tv
.tv_sec
> st
.st_atime
) {
562 if (u
[i
]->ut_time
> st
.st_atime
) {
564 strncat(line
, "-", sizeof(line
));
565 strncat(line
, m
, sizeof(line
));
572 if (t
< u
[i
]->ut_tv
.tv_sec
)
573 t
= u
[i
]->ut_tv
.tv_sec
;
575 if (t
< u
[i
]->ut_time
)
579 snprintf(buf
, sizeof(buf
), "%lu", (now
- t
<= 0) ? 0 : now
- t
);
580 strncat(line
, buf
, sizeof(line
));
581 strncat(line
, m
, sizeof(line
));
587 line
[strlen(line
) - 1] = '\0';
591 /* This is output if the -h command line option is passed to the main program.
593 void ui_module_help()
596 fprintf(stderr
, "%s: ui_module_help()\n", __FILE__
);
599 printf(" Login information [-L (-%s)]:\n", LOGIN_OPTION_ORDER
);
600 printf("\t-y tty\t\t\t\t");
601 printf("-m message status\n");
602 printf("\t-t login time stamp\t\t");
603 printf("-d duration in minutes\n");
604 printf("\t-h hostname\t\t\t");
605 printf("-i seconds idle\n");
606 printf("\t-p login process id\n");
607 printf("\t-l lastlog information"
608 " (any of tt[y],[h]ostname,[t]ime, or [a]ll)\n\n");
612 /* This is the equivalent to main() only without argc and argv available. */
613 int ui_module_exec(char ***s
, const struct passwd
*pw
, const int multi
,
614 const int verbose
, char *tf
)
617 UTMP
**u
= NULL
, **up
;
621 u
= get_utmp(pw
->pw_name
);
625 char line
[LINE_MAX
] = { '\0' };
627 char m
[2] = { multi
, '\0' };
630 char htmp
[UTX_HOSTSIZE
+ 1];
632 char htmp
[UT_HOSTSIZE
+ 1];
635 char htmp
[UT_HOSTSIZE
+ 1];
640 add_string(&strings
, (u
) ? idle(u
, multi
) : "!");
643 last_strings(lastlogin(pw
->pw_uid
, tf
));
646 for (i
= 0; i
< login_count
; i
++) {
648 && isalnum((unsigned char) u
[i
]->ut_host
[0])) {
649 strcpy(htmp
, u
[i
]->ut_host
);
650 htmp
[sizeof(htmp
) - 1] = '\0';
651 strncat(line
, htmp
, sizeof(line
));
655 * If a users tty is tty1-n, it must be a console
659 && isdigit((unsigned char) u
[i
]->ut_line
[3]))
660 strncat(line
, "-", sizeof(line
));
662 strncat(line
, "!", sizeof(line
));
665 strncat(line
, m
, sizeof(line
));
669 strncpy(line
, "!", sizeof(line
));
671 line
[strlen(line
) - 1] = '\0';
673 add_string(&strings
, line
);
676 for (i
= 0; i
< login_count
; i
++) {
677 if (u
[i
]->ut_line
[0])
678 strncat(line
, u
[i
]->ut_line
, sizeof(line
));
680 strncat(line
, "!", sizeof(line
));
682 strncat(line
, m
, sizeof(line
));
686 strncpy(line
, "!", sizeof(line
));
688 line
[strlen(line
) - 1] = '\0';
690 add_string(&strings
, line
);
693 add_string(&strings
, msgstat(u
, multi
));
696 for (i
= 0; i
< login_count
; i
++) {
698 strncat(line
, stamp(u
[i
]->ut_tv
.tv_sec
, tf
), sizeof(line
));
700 strncat(line
, stamp(u
[i
]->ut_time
, tf
), sizeof(line
));
702 strncat(line
, m
, sizeof(line
));
706 strncpy(line
, "!", sizeof(line
));
708 line
[strlen(line
) - 1] = '\0';
710 add_string(&strings
, line
);
713 for (i
= 0; i
< login_count
; i
++) {
715 if ((now
- u
[i
]->ut_tv
.tv_sec
) > 60) {
716 snprintf(buf
, sizeof(buf
), "%lu",
717 ((now
- u
[i
]->ut_tv
.tv_sec
) / 60));
719 if ((now
- u
[i
]->ut_time
) > 60) {
720 snprintf(buf
, sizeof(buf
), "%lu",
721 ((now
- u
[i
]->ut_time
) / 60));
723 strncat(line
, buf
, sizeof(line
));
726 strncat(line
, "-", sizeof(line
));
728 strncat(line
, m
, sizeof(line
));
732 strncpy(line
, "!", sizeof(line
));
734 line
[strlen(line
) - 1] = '\0';
736 add_string(&strings
, line
);
740 for (i
= 0; i
< login_count
; i
++) {
742 snprintf(buf
, sizeof(buf
), "%li", (long) u
[i
]->ut_pid
);
743 strncat(line
, buf
, sizeof(line
));
746 strncat(line
, "!", sizeof(line
));
748 strncat(line
, m
, sizeof(line
));
752 strncpy(line
, "!", sizeof(line
));
754 line
[strlen(line
) - 1] = '\0';
756 add_string(&strings
, line
);
758 add_string(&strings
, (u
) ? ui_module_pid(pw
->pw_uid
, multi
) : "!");
769 for (up
= u
; *up
; *up
++)
779 /* See if the last login options (-l) are valid. */
780 static int parse_last_options(const char *args
)
784 for (i
= 0; i
< strlen(args
); i
++) {
799 char *ui_module_options_init(char **defaults
)
802 return LOGIN_OPTION_STRING
;
805 /* Check module option validity. */
806 int ui_module_options(int argc
, char **argv
)
812 fprintf(stderr
, "%s: ui_module_options()\n", __FILE__
);
815 while ((opt
= getopt(argc
, argv
, LOGIN_OPTION_STRING
)) != -1) {
818 last_options
= optarg
;
830 warnx("login: invalid option -- %c", optopt
);
836 if (parse_last_options(last_options
))
841 strncpy(options
, LOGIN_OPTION_ORDER
, sizeof(options
));